From 564690ee16f5d19165aa9596301b71813ef52328 Mon Sep 17 00:00:00 2001 From: rhoninlee Date: Sat, 12 Oct 2024 11:32:59 +0800 Subject: [PATCH 01/12] implement deviceshifu LwM2M --- Makefile | 5 + cmd/deviceshifu/cmdlwm2m/main.go | 35 ++ dockerfiles/Dockerfile.deviceshifuLwM2M | 27 ++ .../http-deviceshifu-configmap.yaml | 18 + .../http-deviceshifu-deployment.yaml | 36 ++ .../http-deviceshifu-service.yaml | 22 + .../lwm2mdeviceShifu/http-edgedevice.yaml | 13 + .../lwm2m-deviceshifu-configmap.yaml | 21 + .../lwm2m-deviceshifu-deployment.yaml | 38 ++ .../lwm2m-deviceshifu-service.yaml | 27 ++ .../lwm2mdeviceShifu/lwm2m-edgedevice.yaml | 19 + go.mod | 8 + go.sum | 52 +++ .../deviceshifulwm2m/deviceshifulwm2m.go | 252 ++++++++++++ .../deviceshifulwm2m/lwm2m/ciphersuite.go | 79 ++++ .../deviceshifulwm2m/lwm2m/lwm2m.go | 387 ++++++++++++++++++ .../deviceshifumqtt/deviceshifumqtt_test.go | 16 +- pkg/k8s/api/v1alpha1/edgedevice_types.go | 60 +++ pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go | 45 ++ .../bases/shifu.edgenesis.io_edgedevices.yaml | 18 + .../shifu.edgenesis.io_telemetryservices.yaml | 6 - pkg/k8s/crd/config/rbac/role.yaml | 27 +- pkg/k8s/crd/install/config_crd.yaml | 24 +- pkg/k8s/crd/install/config_default.yaml | 51 ++- pkg/k8s/crd/install/shifu_install.yml | 51 ++- 25 files changed, 1299 insertions(+), 38 deletions(-) create mode 100644 cmd/deviceshifu/cmdlwm2m/main.go create mode 100644 dockerfiles/Dockerfile.deviceshifuLwM2M create mode 100644 examples/lwm2mdeviceShifu/http-deviceshifu-configmap.yaml create mode 100644 examples/lwm2mdeviceShifu/http-deviceshifu-deployment.yaml create mode 100644 examples/lwm2mdeviceShifu/http-deviceshifu-service.yaml create mode 100644 examples/lwm2mdeviceShifu/http-edgedevice.yaml create mode 100644 examples/lwm2mdeviceShifu/lwm2m-deviceshifu-configmap.yaml create mode 100644 examples/lwm2mdeviceShifu/lwm2m-deviceshifu-deployment.yaml create mode 100644 examples/lwm2mdeviceShifu/lwm2m-deviceshifu-service.yaml create mode 100644 examples/lwm2mdeviceShifu/lwm2m-edgedevice.yaml create mode 100644 pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go create mode 100644 pkg/deviceshifu/deviceshifulwm2m/lwm2m/ciphersuite.go create mode 100644 pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go diff --git a/Makefile b/Makefile index 538ac902d..e8ec9224b 100644 --- a/Makefile +++ b/Makefile @@ -142,6 +142,11 @@ buildx-build-image-deviceshifu-http-http: --build-arg PROJECT_ROOT="${PROJECT_ROOT}" ${PROJECT_ROOT} \ -t edgehub/deviceshifu-http-http:${IMAGE_VERSION} --load +buildx-build-image-deviceshifu-http-lwm2m: + docker buildx build --platform=linux/$(shell go env GOARCH) -f ${PROJECT_ROOT}/dockerfiles/Dockerfile.deviceshifuLwM2M \ + --build-arg PROJECT_ROOT="${PROJECT_ROOT}" ${PROJECT_ROOT} \ + -t edgehub/deviceshifu-http-lwm2m:${IMAGE_VERSION} --load + buildx-build-image-deviceshifu-http-mqtt: docker buildx build --platform=linux/$(shell go env GOARCH) -f ${PROJECT_ROOT}/dockerfiles/Dockerfile.deviceshifuMQTT \ --build-arg PROJECT_ROOT="${PROJECT_ROOT}" ${PROJECT_ROOT} \ diff --git a/cmd/deviceshifu/cmdlwm2m/main.go b/cmd/deviceshifu/cmdlwm2m/main.go new file mode 100644 index 000000000..1f396174f --- /dev/null +++ b/cmd/deviceshifu/cmdlwm2m/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "os" + + "github.com/edgenesis/shifu/pkg/deviceshifu/deviceshifubase" + "github.com/edgenesis/shifu/pkg/deviceshifu/deviceshifulwm2m" + "github.com/edgenesis/shifu/pkg/logger" + + "k8s.io/apimachinery/pkg/util/wait" +) + +func main() { + deviceName := os.Getenv("EDGEDEVICE_NAME") + namespace := os.Getenv("EDGEDEVICE_NAMESPACE") + + deviceShifuMetadata := &deviceshifubase.DeviceShifuMetaData{ + Name: deviceName, + ConfigFilePath: deviceshifubase.DeviceConfigmapFolderPath, + KubeConfigPath: deviceshifubase.KubernetesConfigDefault, + Namespace: namespace, + } + + ds, err := deviceshifulwm2m.New(deviceShifuMetadata) + if err != nil { + panic(err.Error()) + } + + if err = ds.Start(wait.NeverStop); err != nil { + logger.Errorf("Error starting deviceshifu: %v", err) + panic(err.Error()) + } + + select {} +} diff --git a/dockerfiles/Dockerfile.deviceshifuLwM2M b/dockerfiles/Dockerfile.deviceshifuLwM2M new file mode 100644 index 000000000..59b0e731e --- /dev/null +++ b/dockerfiles/Dockerfile.deviceshifuLwM2M @@ -0,0 +1,27 @@ +# Build the manager binary +FROM --platform=$BUILDPLATFORM golang:1.22.0 as builder + +WORKDIR /shifu + +COPY go.mod go.mod +COPY go.sum go.sum +COPY pkg/k8s pkg/k8s +COPY cmd/deviceshifu/cmdlwm2m cmd/deviceshifu/cmdlwm2m +COPY pkg/deviceshifu pkg/deviceshifu +COPY pkg/logger pkg/logger + +RUN go mod download + +# Build the Go app +ARG TARGETOS +ARG TARGETARCH + +RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -a -o /output/deviceshifu cmd/deviceshifu/cmdlwm2m/main.go + +FROM gcr.io/distroless/static-debian11 +WORKDIR / +COPY --from=builder /output/deviceshifu deviceshifu + +# Command to run the executable +USER 65532:65532 +ENTRYPOINT ["/deviceshifu"] diff --git a/examples/lwm2mdeviceShifu/http-deviceshifu-configmap.yaml b/examples/lwm2mdeviceShifu/http-deviceshifu-configmap.yaml new file mode 100644 index 000000000..27c440396 --- /dev/null +++ b/examples/lwm2mdeviceShifu/http-deviceshifu-configmap.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: http-configmap-0.0.1 + namespace: deviceshifu +data: +# device name and image address + driverProperties: | + driverSku: HTTP Device + driverImage: http-device:v0.0.1 +# available instructions + instructions: | + instructions: + temperature: + protocolPropertyList: + DataFormat: PlainText + ObjectId: /3303/0/5700 + EnableObserve: true diff --git a/examples/lwm2mdeviceShifu/http-deviceshifu-deployment.yaml b/examples/lwm2mdeviceShifu/http-deviceshifu-deployment.yaml new file mode 100644 index 000000000..5a1435d6e --- /dev/null +++ b/examples/lwm2mdeviceShifu/http-deviceshifu-deployment.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: deviceshifu-http-deployment + name: deviceshifu-http-deployment + namespace: deviceshifu +spec: + replicas: 1 + selector: + matchLabels: + app: deviceshifu-http-deployment + template: + metadata: + labels: + app: deviceshifu-http-deployment + spec: + containers: + - image: edgehub/deviceshifu-http-lwm2m:nightly + name: deviceshifu-http + ports: + - containerPort: 8080 + volumeMounts: + - name: deviceshifu-config + mountPath: "/etc/edgedevice/config" + readOnly: true + env: + - name: EDGEDEVICE_NAME + value: "edgedevice-http" + - name: EDGEDEVICE_NAMESPACE + value: "devices" + volumes: + - name: deviceshifu-config + configMap: + name: http-configmap-0.0.1 + serviceAccountName: edgedevice-sa diff --git a/examples/lwm2mdeviceShifu/http-deviceshifu-service.yaml b/examples/lwm2mdeviceShifu/http-deviceshifu-service.yaml new file mode 100644 index 000000000..0d2cb4802 --- /dev/null +++ b/examples/lwm2mdeviceShifu/http-deviceshifu-service.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: deviceshifu-http-deployment + name: deviceshifu-http + namespace: deviceshifu +spec: + ports: + - name: deviceshifu + port: 80 + protocol: TCP + nodePort: 30080 + targetPort: 8080 + - name: lwm2mserver + port: 5683 + protocol: UDP + nodePort: 30000 + targetPort: 5683 + selector: + app: deviceshifu-http-deployment + type: NodePort diff --git a/examples/lwm2mdeviceShifu/http-edgedevice.yaml b/examples/lwm2mdeviceShifu/http-edgedevice.yaml new file mode 100644 index 000000000..8c1583063 --- /dev/null +++ b/examples/lwm2mdeviceShifu/http-edgedevice.yaml @@ -0,0 +1,13 @@ +apiVersion: shifu.edgenesis.io/v1alpha1 +kind: EdgeDevice +metadata: + name: edgedevice-http + namespace: devices +spec: + sku: "LwM2M Device" + connection: Ethernet + address: 10.20.30.147:5683 + protocol: LwM2M + protocolSettings: + LwM2MSettings: + endpointName: test diff --git a/examples/lwm2mdeviceShifu/lwm2m-deviceshifu-configmap.yaml b/examples/lwm2mdeviceShifu/lwm2m-deviceshifu-configmap.yaml new file mode 100644 index 000000000..b8b12e0cd --- /dev/null +++ b/examples/lwm2mdeviceShifu/lwm2m-deviceshifu-configmap.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: deviceshifu-lwm2m + namespace: deviceshifu +data: +# device name and image address + driverProperties: | + driverSku: LwM2M Device + driverImage: lwm2m-device:v0.0.1 +# available instructions + instructions: | + instructions: + temperature: + protocolPropertyList: + ObjectId: /3303/0/5700 + EnableObserve: false + reset: + protocolPropertyList: + ObjectId: /3303/0/5605 + EnableObserve: false diff --git a/examples/lwm2mdeviceShifu/lwm2m-deviceshifu-deployment.yaml b/examples/lwm2mdeviceShifu/lwm2m-deviceshifu-deployment.yaml new file mode 100644 index 000000000..f0ee6379f --- /dev/null +++ b/examples/lwm2mdeviceShifu/lwm2m-deviceshifu-deployment.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: deviceshifu-lwm2m-deployment + name: deviceshifu-lwm2m-deployment + namespace: deviceshifu +spec: + replicas: 1 + selector: + matchLabels: + app: deviceshifu-lwm2m-deployment + template: + metadata: + labels: + app: deviceshifu-lwm2m-deployment + spec: + containers: + - image: edgehub/deviceshifu-http-lwm2m:nightly + name: deviceshifu-lwm2m + ports: + - containerPort: 8080 + volumeMounts: + - name: deviceshifu-config + mountPath: "/etc/edgedevice/config" + readOnly: true + env: + - name: EDGEDEVICE_NAME + value: "edgedevice-lwm2m" + - name: EDGEDEVICE_NAMESPACE + value: "devices" + - name: LOG_LEVEL + value: debug + volumes: + - name: deviceshifu-config + configMap: + name: deviceshifu-lwm2m + serviceAccountName: edgedevice-sa diff --git a/examples/lwm2mdeviceShifu/lwm2m-deviceshifu-service.yaml b/examples/lwm2mdeviceShifu/lwm2m-deviceshifu-service.yaml new file mode 100644 index 000000000..84ea2f791 --- /dev/null +++ b/examples/lwm2mdeviceShifu/lwm2m-deviceshifu-service.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: deviceshifu-lwm2m + name: deviceshifu-lwm2m + namespace: deviceshifu +spec: + ports: + - name: deviceshifu + port: 80 + protocol: TCP + nodePort: 30080 + targetPort: 8080 + - name: lwm2mserver-coap + port: 5683 + protocol: UDP + nodePort: 30000 + targetPort: 5683 + - name: lwm2mserver-coaps + port: 5684 + protocol: UDP + nodePort: 30001 + targetPort: 5684 + selector: + app: deviceshifu-lwm2m-deployment + type: NodePort diff --git a/examples/lwm2mdeviceShifu/lwm2m-edgedevice.yaml b/examples/lwm2mdeviceShifu/lwm2m-edgedevice.yaml new file mode 100644 index 000000000..94152da1e --- /dev/null +++ b/examples/lwm2mdeviceShifu/lwm2m-edgedevice.yaml @@ -0,0 +1,19 @@ +apiVersion: shifu.edgenesis.io/v1alpha1 +kind: EdgeDevice +metadata: + name: edgedevice-lwm2m + namespace: devices +spec: + sku: "LwM2M Device" + connection: Ethernet + address: -- + protocol: LwM2M + protocolSettings: + LwM2MSettings: + endpointName: test + securityMode: DTLS + dtlsMode: PSK + cipherSuites: + - TLS_PSK_WITH_AES_128_CCM_8 + pskIdentity: hint + pskKey: ABC123 diff --git a/go.mod b/go.mod index 1337f84d3..ec1c7843f 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,8 @@ require ( github.com/mochi-co/mqtt v1.3.2 github.com/onsi/ginkgo/v2 v2.20.2 github.com/onsi/gomega v1.34.2 + github.com/pion/dtls/v2 v2.2.8-0.20240201071732-2597464081c8 + github.com/plgd-dev/go-coap/v3 v3.1.6 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 github.com/taosdata/driver-go/v3 v3.5.8 @@ -32,6 +34,7 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/dsnet/golib/memfile v1.0.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/fatih/color v1.13.0 // indirect @@ -43,13 +46,18 @@ require ( github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/minio/md5-simd v1.1.2 // indirect + github.com/pion/logging v0.2.2 // indirect + github.com/pion/transport/v3 v3.0.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rs/xid v1.6.0 // indirect github.com/x448/float16 v0.8.4 // indirect + go.uber.org/atomic v1.11.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/tools v0.24.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect diff --git a/go.sum b/go.sum index f7a12c0a4..3ff5e5ebe 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dsnet/golib/memfile v1.0.0 h1:J9pUspY2bDCbF9o+YGwcf3uG6MdyITfh/Fk3/CaEiFs= +github.com/dsnet/golib/memfile v1.0.0/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o= @@ -103,6 +105,11 @@ github.com/gopcua/opcua v0.5.3/go.mod h1:nrVl4/Rs3SDQRhNQ50EbAiI5JSpDrTG6Frx3s4H github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/icza/bitio v1.1.0 h1:ysX4vtldjdi3Ygai5m1cWy4oLkhWTAi+SyO6HC8L9T0= github.com/icza/bitio v1.1.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A= github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k= @@ -168,10 +175,18 @@ github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/pascaldekloe/goe v0.1.1 h1:Ah6WQ56rZONR3RW3qWa2NCZ6JAVvSpUcoLBaOmYFt9Q= github.com/pascaldekloe/goe v0.1.1/go.mod h1:KSyfaxQOh0HZPjDP1FL/kFtbqYqrALJTaMafFUIccqU= +github.com/pion/dtls/v2 v2.2.8-0.20240201071732-2597464081c8 h1:r7K+oQUYubeA0am08kTAvd2wT2D8PZggs/CpMGp0nkM= +github.com/pion/dtls/v2 v2.2.8-0.20240201071732-2597464081c8/go.mod h1:/gft3czh67pwl4nM1BBUvF7eTy72uGkObJXOYfxRDbA= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= +github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/plgd-dev/go-coap/v3 v3.1.6 h1:hU2ztY57G1tRz5C6soxnnJiTJaK19W/W5eUSoYyt82Y= +github.com/plgd-dev/go-coap/v3 v3.1.6/go.mod h1:O5P/Bja4MBeDw3SaNxf+9PNyfe80SHBIJKyWVwT0W5Y= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -204,6 +219,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/taosdata/driver-go/v3 v3.5.8 h1:JT5lNFUCOHD9Hs4Phjg8RBkGOWlePRnpGqq8kIRHT98= @@ -212,6 +228,9 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -221,6 +240,9 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= @@ -229,12 +251,20 @@ golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= @@ -242,6 +272,8 @@ golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbht golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -249,16 +281,34 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= @@ -268,6 +318,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go new file mode 100644 index 000000000..90f95f55b --- /dev/null +++ b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go @@ -0,0 +1,252 @@ +package deviceshifulwm2m + +import ( + "errors" + "fmt" + "io" + "net/http" + "strings" + + "github.com/edgenesis/shifu/pkg/deviceshifu/deviceshifulwm2m/lwm2m" + "github.com/edgenesis/shifu/pkg/deviceshifu/utils" + "github.com/edgenesis/shifu/pkg/logger" + + "github.com/edgenesis/shifu/pkg/deviceshifu/deviceshifubase" + "github.com/edgenesis/shifu/pkg/k8s/api/v1alpha1" +) + +// DeviceShifuLwM2M deviceshifu for HTTP +type DeviceShifuLwM2M struct { + server *lwm2m.Server + base *deviceshifubase.DeviceShifuBase +} + +// HandlerMetaData MetaData for HTTPhandler +type HandlerMetaData struct { + edgeDeviceSpec v1alpha1.EdgeDeviceSpec + instruction string + properties *deviceshifubase.DeviceShifuInstruction +} + +var ( + instructionSettings *deviceshifubase.DeviceShifuInstructionSettings +) + +const ( + DeviceNameHeaderField = "Device-Name" +) + +// New This function creates a new Device Shifu based on the configuration +func New(deviceShifuMetadata *deviceshifubase.DeviceShifuMetaData) (*DeviceShifuLwM2M, error) { + if deviceShifuMetadata.Namespace == "" { + return nil, fmt.Errorf("DeviceShifuLwM2M's namespace can't be empty") + } + + base, mux, err := deviceshifubase.New(deviceShifuMetadata) + if err != nil { + return nil, err + } + + lwM2MSettings := base.EdgeDevice.Spec.ProtocolSettings.LwM2MSettings + logger.Info("endpoint name: %s", lwM2MSettings.EndpointName) + server, err := lwm2m.NewServer(*lwM2MSettings) + if err != nil { + return nil, err + } + go func() { + panic(server.Run()) + }() + + instructionSettings = base.DeviceShifuConfig.Instructions.InstructionSettings + if instructionSettings == nil { + instructionSettings = &deviceshifubase.DeviceShifuInstructionSettings{} + } + + if instructionSettings.DefaultTimeoutSeconds == nil { + var defaultTimeoutSeconds = deviceshifubase.DeviceDefaultGlobalTimeoutInSeconds + instructionSettings.DefaultTimeoutSeconds = &defaultTimeoutSeconds + } + + if deviceShifuMetadata.KubeConfigPath != deviceshifubase.DeviceKubeconfigDoNotLoadStr { + // switch for different Shifu Protocols + switch protocol := *base.EdgeDevice.Spec.Protocol; protocol { + case v1alpha1.ProtocolLwM2M: + for instruction, properties := range base.DeviceShifuConfig.Instructions.Instructions { + HandlerMetaData := &HandlerMetaData{ + base.EdgeDevice.Spec, + instruction, + properties, + } + handler := DeviceCommandHandlerLwM2M{server, HandlerMetaData} + mux.HandleFunc("/"+instruction, handler.commandHandleFunc()) + + if properties.DeviceShifuProtocolProperties["EnableObserve"] == "true" { + server.OnRegister(func() error { + return server.Observe(properties.DeviceShifuProtocolProperties["ObjectId"], func(data interface{}) { + logger.Infof("Observe data: %v", data) + // TODO need to push data to telemetry service + }) + }) + } + } + default: + logger.Errorf("EdgeDevice protocol %v not supported in deviceShifu LwM2M", protocol) + return nil, errors.New("wrong protocol not supported in deviceShifu LwM2M") + } + } + deviceshifubase.BindDefaultHandler(mux) + + ds := &DeviceShifuLwM2M{base: base, server: server} + + ds.base.UpdateEdgeDeviceResourcePhase(v1alpha1.EdgeDevicePending) + return ds, nil +} + +// DeviceCommandHandlerLwM2M handler for http +type DeviceCommandHandlerLwM2M struct { + server *lwm2m.Server + HandlerMetaData *HandlerMetaData +} + +// commandHandleFunc handle http request +func (handler DeviceCommandHandlerLwM2M) commandHandleFunc() http.HandlerFunc { + handlerProperties := handler.HandlerMetaData.properties + handlerInstruction := handler.HandlerMetaData.instruction + handlerServer := handler.server + + if handlerProperties != nil { + // TODO: handle validation compile + for _, instructionProperty := range handlerProperties.DeviceShifuInstructionProperties { + logger.Infof("Properties of command: %v %v", handlerInstruction, instructionProperty) + } + } + objectId := handlerProperties.DeviceShifuProtocolProperties["ObjectId"] + return func(w http.ResponseWriter, r *http.Request) { + var respString string + + switch r.Method { + case http.MethodPut: + requestBody, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, "Error on parsing body", http.StatusBadRequest) + logger.Errorf("Error on parsing body" + err.Error()) + return + } + + err = handlerServer.Write(objectId, string(requestBody)) + if err != nil { + http.Error(w, "Error on writing object", http.StatusBadRequest) + logger.Errorf("Error on writing object" + err.Error()) + return + } + + respString = "Success" + case http.MethodGet: + data, err := handlerServer.Read(objectId) + if err != nil { + http.Error(w, "Error on reading object", http.StatusBadRequest) + logger.Errorf("Error on reading object" + err.Error()) + return + } + respString = data + case http.MethodPost: + requestBody, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, "Error on parsing body", http.StatusBadRequest) + logger.Errorf("Error on parsing body" + err.Error()) + return + } + + err = handlerServer.Execute(objectId, string(requestBody)) + if err != nil { + http.Error(w, "Error on executing object", http.StatusBadRequest) + logger.Errorf("Error on executing object" + err.Error()) + return + } + + respString = "Success" + default: + http.Error(w, "not supported yet", http.StatusBadRequest) + logger.Errorf("Request type %s is not supported yet!", r.Method) + return + } + + instructionFuncName, shouldUsePythonCustomProcessing := deviceshifubase.CustomInstructionsPython[handlerInstruction] + logger.Infof("Instruction %v is custom: %v", handlerInstruction, shouldUsePythonCustomProcessing) + if shouldUsePythonCustomProcessing { + logger.Infof("Instruction %v has a python customized handler configured.\n", handlerInstruction) + respString = utils.ProcessInstruction(deviceshifubase.PythonHandlersModuleName, instructionFuncName, respString, deviceshifubase.PythonScriptDir) + } + fmt.Fprintf(w, "%v", respString) + } +} + +func (ds *DeviceShifuLwM2M) collectHTTPTelemtries() (bool, error) { + if ds.server.Conn == nil { + return false, nil + } + + if err := ds.server.Conn.Ping(ds.server.Conn.Context()); err != nil { + logger.Errorf("Error checking telemetry: %v", err.Error()) + return false, err + } + + if ds.base.EdgeDevice.Spec.Protocol != nil { + switch protocol := *ds.base.EdgeDevice.Spec.Protocol; protocol { + case v1alpha1.ProtocolLwM2M: + telemetries := ds.base.DeviceShifuConfig.Telemetries.DeviceShifuTelemetries + instructions := ds.base.DeviceShifuConfig.Instructions + for telemetry, telemetryProperties := range telemetries { + if ds.base.EdgeDevice.Spec.Address == nil { + return false, fmt.Errorf("Device %v does not have an address", ds.base.Name) + } + + instructionName := telemetryProperties.DeviceShifuTelemetryProperties.DeviceInstructionName + if instructionName == nil { + return false, fmt.Errorf("Device %v telemetry %v does not have an instruction name", ds.base.Name, telemetry) + } + + if instructions.Instructions[*instructionName] == nil { + return false, fmt.Errorf("Device %v telemetry %v instruction %v does not exist", ds.base.Name, telemetry, *instructionName) + } + + objectId := instructions.Instructions[*instructionName].DeviceShifuProtocolProperties["ObjectId"] + if objectId == "" { + return false, fmt.Errorf("Device %v telemetry %v does not have an object id", ds.base.Name, telemetry) + } + data, err := ds.server.Read(objectId) + if err != nil { + return false, err + } + + resp := &http.Response{ + Body: io.NopCloser(strings.NewReader(data)), + } + + telemetryCollectionService, exist := deviceshifubase.TelemetryCollectionServiceMap[telemetry] + if exist && *telemetryCollectionService.TelemetryServiceEndpoint != "" { + err = deviceshifubase.PushTelemetryCollectionService(&telemetryCollectionService, &ds.base.EdgeDevice.Spec, resp) + if err != nil { + return false, err + } + } + return true, nil + } + default: + logger.Warnf("EdgeDevice protocol %v not supported in deviceshifu", protocol) + return false, nil + } + } + + return true, nil +} + +// Start start http telemetry +func (ds *DeviceShifuLwM2M) Start(stopCh <-chan struct{}) error { + return ds.base.Start(stopCh, ds.collectHTTPTelemtries) +} + +// Stop stop http server +func (ds *DeviceShifuLwM2M) Stop() error { + return ds.base.Stop() +} diff --git a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/ciphersuite.go b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/ciphersuite.go new file mode 100644 index 000000000..99d08b07f --- /dev/null +++ b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/ciphersuite.go @@ -0,0 +1,79 @@ +package lwm2m + +import ( + "errors" + + "github.com/edgenesis/shifu/pkg/k8s/api/v1alpha1" + "github.com/pion/dtls/v2" +) + +const ( + // AES-128-CCM + TLS_ECDHE_ECDSA_WITH_AES_128_CCM dtls.CipherSuiteID = 0xc0ac //nolint:revive,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 dtls.CipherSuiteID = 0xc0ae //nolint:revive,stylecheck + + // AES-128-GCM-SHA256 + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 dtls.CipherSuiteID = 0xc02b //nolint:revive,stylecheck + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 dtls.CipherSuiteID = 0xc02f //nolint:revive,stylecheck + + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 dtls.CipherSuiteID = 0xc02c //nolint:revive,stylecheck + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 dtls.CipherSuiteID = 0xc030 //nolint:revive,stylecheck + // AES-256-CBC-SHA + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA dtls.CipherSuiteID = 0xc00a //nolint:revive,stylecheck + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA dtls.CipherSuiteID = 0xc014 //nolint:revive,stylecheck + + TLS_PSK_WITH_AES_128_CCM dtls.CipherSuiteID = 0xc0a4 //nolint:revive,stylecheck + TLS_PSK_WITH_AES_128_CCM_8 dtls.CipherSuiteID = 0xc0a8 //nolint:revive,stylecheck + TLS_PSK_WITH_AES_256_CCM_8 dtls.CipherSuiteID = 0xc0a9 //nolint:revive,stylecheck + TLS_PSK_WITH_AES_128_GCM_SHA256 dtls.CipherSuiteID = 0x00a8 //nolint:revive,stylecheck + TLS_PSK_WITH_AES_128_CBC_SHA256 dtls.CipherSuiteID = 0x00ae //nolint:revive,stylecheck + + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 dtls.CipherSuiteID = 0xC037 //nolint:revive,stylecheck +) + +func StringToCode(ciperSuitStr v1alpha1.CiperSuite) (dtls.CipherSuiteID, error) { + switch ciperSuitStr { + case v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM: + return TLS_ECDHE_ECDSA_WITH_AES_128_CCM, nil + case v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: + return TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, nil + case v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + return TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, nil + case v1alpha1.CiperSuite_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + return TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, nil + case v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, nil + case v1alpha1.CiperSuite_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, nil + case v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_CCM: + return TLS_PSK_WITH_AES_128_CCM, nil + case v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_CCM_8: + return TLS_PSK_WITH_AES_128_CCM_8, nil + case v1alpha1.CiperSuite_TLS_PSK_WITH_AES_256_CCM_8: + return TLS_PSK_WITH_AES_256_CCM_8, nil + case v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_GCM_SHA256: + return TLS_PSK_WITH_AES_128_GCM_SHA256, nil + case v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_CBC_SHA256: + return TLS_PSK_WITH_AES_128_CBC_SHA256, nil + case v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + return TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, nil + case v1alpha1.CiperSuite_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + return TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, nil + case v1alpha1.CiperSuite_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + return TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, nil + default: + return 0, errors.New("unknown ciper suite") + } +} + +func StringsToCodes(ciperSuitStrs []v1alpha1.CiperSuite) ([]dtls.CipherSuiteID, error) { + var ciperSuitCodes []dtls.CipherSuiteID + for _, ciperSuitStr := range ciperSuitStrs { + ciperSuitCode, err := StringToCode(ciperSuitStr) + if err != nil { + return nil, err + } + ciperSuitCodes = append(ciperSuitCodes, ciperSuitCode) + } + return ciperSuitCodes, nil +} diff --git a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go new file mode 100644 index 000000000..1fdc3fe74 --- /dev/null +++ b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go @@ -0,0 +1,387 @@ +package lwm2m + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "errors" + "io" + "strconv" + "strings" + "time" + + "github.com/edgenesis/shifu/pkg/k8s/api/v1alpha1" + "github.com/edgenesis/shifu/pkg/logger" + "github.com/pion/dtls/v2" + dtlsServer "github.com/plgd-dev/go-coap/v3/dtls/server" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/mux" + "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/options" + udpClient "github.com/plgd-dev/go-coap/v3/udp/client" + udpServer "github.com/plgd-dev/go-coap/v3/udp/server" +) + +type Server struct { + router *mux.Router + + settings v1alpha1.LwM2MSettings + Conn mux.Conn + endpointName string + liftTime int + lastRegistrationTime time.Time + deviceTokenMap map[string]string // map[token]device + observeCallback map[string]func(interface{}) + onRegister []func() error +} + +const deviceId string = "shifu" + +func loggingMiddleware(next mux.Handler) mux.Handler { + return mux.HandlerFunc(func(w mux.ResponseWriter, r *mux.Message) { + logger.Debugf("ClientAddress %v, %v\n", w.Conn().RemoteAddr(), r.String()) + next.ServeCOAP(w, r) + }) +} + +func (s *Server) Execute(objectId string, args string) error { + req, err := s.Conn.NewPostRequest(s.Conn.Context(), objectId, message.TextPlain, strings.NewReader(args)) + if err != nil { + return err + } + + resp, err := s.Conn.Do(req) + if err != nil { + return err + } + + if resp.Code() != codes.Changed { + return errors.New("failed to execute object") + } + + return nil +} + +func NewServer(settings v1alpha1.LwM2MSettings) (*Server, error) { + var server = &Server{ + endpointName: settings.EndpointName, + observeCallback: make(map[string]func(interface{})), + deviceTokenMap: make(map[string]string), + settings: settings, + } + + router := mux.NewRouter() + if err := errors.Join( + router.Handle("/rd", mux.HandlerFunc(server.handleRegister)), + router.Handle("/rd/{deviceId}", mux.HandlerFunc(server.handleResourceUpdate)), + ); err != nil { + return nil, err + } + + router.DefaultHandle(mux.HandlerFunc(func(w mux.ResponseWriter, r *mux.Message) { + token := r.Token() + deviceId, exists := server.deviceTokenMap[string(token)] + if exists { + if fn, exists := server.observeCallback[deviceId]; exists { + data, err := io.ReadAll(r.Body()) + if err != nil { + _ = w.SetResponse(codes.BadRequest, message.TextPlain, bytes.NewReader([]byte("failed to read body"))) + return + } + fn(string(data)) + } + } + })) + + router.Use(loggingMiddleware) + server.router = router + return server, nil +} + +func (s *Server) Run() error { + switch *s.settings.SecurityMode { + case v1alpha1.SecurityModeDTLS: + return s.startDTLSServer() + default: + logger.Infof("securityMode not set or not support, using none security mode") + // default using none security mode + case v1alpha1.SecurityModeNone: + } + return s.startUDPServer() +} + +func (s *Server) startDTLSServer() error { + switch *s.settings.DTLSMode { + case v1alpha1.DTLSModePSK: + serverOptions := []dtlsServer.Option{ + options.WithMux(s.router), + options.WithContext(context.Background()), + options.WithKeepAlive(10, time.Minute*10, func(cc *udpClient.Conn) {}), + } + + server := dtlsServer.New(serverOptions...) + + cipersuites, err := StringsToCodes(s.settings.CipherSuites) + if err != nil { + return err + } + + psk, err := hex.DecodeString(*s.settings.PSKKey) + if err != nil { + return err + } + + dtlsConfig := dtls.Config{ + PSK: func(hint []byte) ([]byte, error) { + return []byte(psk), nil + }, + PSKIdentityHint: []byte(*s.settings.PSKIdentity), + CipherSuites: cipersuites, + } + + l, err := net.NewDTLSListener("udp4", ":5684", &dtlsConfig) + if err != nil { + return err + } + + return server.Serve(l) + case v1alpha1.DTLSModeRPK: + fallthrough + case v1alpha1.DTLSModeX509: + return errors.New("not implemented") + default: + logger.Infof("dtlsMode not set, using none security mode") + // default using none security mode + } + + return s.startUDPServer() +} + +func (s *Server) startUDPServer() error { + serverOptions := []udpServer.Option{ + options.WithMux(s.router), + options.WithContext(context.Background()), + options.WithKeepAlive(10, time.Minute*10, func(cc *udpClient.Conn) { + logger.Error("inactive connection") + }), + } + + server := udpServer.New(serverOptions...) + conn, err := net.NewListenUDP("udp", ":5683") + if err != nil { + return err + } + return server.Serve(conn) +} + +func (s *Server) handleRegister(w mux.ResponseWriter, r *mux.Message) { + query, err := r.Queries() + if err != nil { + _ = w.SetResponse(codes.BadRequest, message.TextPlain, bytes.NewReader([]byte("failed to read queries"))) + return + } + + parsedQuery, err := parseRegisterQuery(query) + if err != nil { + _ = w.SetResponse(codes.BadRequest, message.TextPlain, bytes.NewReader([]byte("failed to parse queries"))) + return + } + + if parsedQuery.EndpointName != s.endpointName { + _ = w.SetResponse(codes.BadRequest, message.TextPlain, bytes.NewReader([]byte("endpoint name mismatch"))) + return + } + + s.liftTime, _ = strconv.Atoi(parsedQuery.Lifetime) + if err := w.SetResponse(codes.Created, message.TextPlain, nil, + message.Option{ID: message.LocationPath, Value: []byte("rd")}, + message.Option{ID: message.LocationPath, Value: []byte(deviceId)}, + ); err != nil { + logger.Debug("register response failed") + } + + s.lastRegistrationTime = time.Now() + s.Conn = w.Conn() + + for _, fn := range s.onRegister { + if err := fn(); err != nil { + logger.Errorf("failed when calling register callback, error: %s", err.Error()) + _ = w.SetResponse(codes.BadRequest, message.TextPlain, bytes.NewReader([]byte("failed to register object links"))) + return + } + } +} + +func (s *Server) OnRegister(fn func() error) { + s.onRegister = append(s.onRegister, fn) +} + +// handleResourceUpdate handles UPDATE and De-register request +func (s *Server) handleResourceUpdate(w mux.ResponseWriter, r *mux.Message) { + deviceIdQuery := r.RouteParams.Vars["deviceId"] + if deviceIdQuery != deviceId { + _ = w.SetResponse(codes.BadRequest, message.TextPlain, bytes.NewReader([]byte("device id mismatch"))) + return + } + + switch r.Code() { + // De-register + case codes.DELETE: + if err := s.Conn.Close(); err != nil { + logger.Errorf("failed to close connection, error: %v", err) + } + s.Conn = nil + s.lastRegistrationTime = time.Time{} + return + // Update + case codes.POST: + // if not registered, handle register + if s.Conn == nil { + _ = w.SetResponse(codes.NotFound, message.TextPlain, nil) + return + } + s.lastRegistrationTime = time.Now() + // check if the request is from the same connection + if s.Conn.RemoteAddr() != w.Conn().RemoteAddr() { + _ = w.SetResponse(codes.BadRequest, message.TextPlain, nil) + return + } + _ = w.SetResponse(codes.Changed, message.TextPlain, nil) + default: + } + +} + +type RegisterQuery struct { + EndpointName string `json:"ep"` + LwM2MVersion string `json:"lwm2m"` + Lifetime string `json:"lt"` + BindingMode string `json:"bnd"` + SMSNumber string `json:"sms"` + ObjectLinks string `json:"b"` +} + +// parseRegisterQuery parses the register query string into a RegisterQuery struct +// example: "ep=test&lwm2m=1.0.3<=86400&bnd=U&sms=1234567890&b=1" +func parseRegisterQuery(queries []string) (*RegisterQuery, error) { + var queryMap = make(map[string]string) + for _, query := range queries { + kvPair := strings.Split(query, "=") + if len(kvPair) != 2 { + // skip invalid query + continue + } + queryMap[kvPair[0]] = kvPair[1] + } + + // convert map to json + data, err := json.Marshal(queryMap) + if err != nil { + return nil, err + } + + var registerQuery RegisterQuery + if err := json.Unmarshal(data, ®isterQuery); err != nil { + return nil, err + } + + return ®isterQuery, nil +} + +// Read reads the object value from the server +// objectId: the object id to read example: "/3442/0/120" +func (s *Server) Read(objectId string) (string, error) { + if err := s.checkRegistrationStatus(); err != nil { + return "", err + } + + request, err := s.Conn.NewGetRequest(s.Conn.Context(), objectId) + if err != nil { + return "", err + } + request.SetAccept(message.TextPlain) + + resp, err := s.Conn.Do(request) + if err != nil { + return "", err + } + + if resp.Code() == codes.NotFound { + return "", errors.New("object not found") + } + + data, err := io.ReadAll(resp.Body()) + if err != nil { + return "", err + } + + return string(data), nil +} + +// Write writes the object value to the server +// objectId: the object id to write example: "/3442/0/120" +// newValue: the new value to write +func (s *Server) Write(objectId string, newValue string) error { + if err := s.checkRegistrationStatus(); err != nil { + return err + } + + request, err := s.Conn.NewPutRequest(s.Conn.Context(), objectId, message.TextPlain, strings.NewReader(newValue)) + if err != nil { + return err + } + + resp, err := s.Conn.Do(request) + if err != nil { + return err + } + + if resp.Code() == codes.MethodNotAllowed { + return errors.New("write method not allowed") + } + + if resp.Code() != codes.Changed { + return errors.New("failed to write object") + } + + return nil +} + +func (s *Server) Observe(objectId string, callback func(newData interface{})) error { + if err := s.checkRegistrationStatus(); err != nil { + return err + } + + request, err := s.Conn.NewObserveRequest(s.Conn.Context(), objectId) + if err != nil { + return err + } + request.SetAccept(message.TextPlain) + + resp, err := s.Conn.Do(request) + if err != nil { + return err + } + + if resp.Code() != codes.Content { + return errors.New("failed to observe object") + } + token := resp.Token() + s.observeCallback[objectId] = callback + s.deviceTokenMap[string(token)] = objectId + + logger.Debugf("observe %s with token %s", objectId, token) + return nil +} + +func DoNothing(newData interface{}) {} + +func (s *Server) checkRegistrationStatus() error { + if time.Since(s.lastRegistrationTime) > time.Second*time.Duration(s.liftTime) { + return errors.New("device is offline") + } + + return nil +} diff --git a/pkg/deviceshifu/deviceshifumqtt/deviceshifumqtt_test.go b/pkg/deviceshifu/deviceshifumqtt/deviceshifumqtt_test.go index 8f861e9e4..0a70003f2 100644 --- a/pkg/deviceshifu/deviceshifumqtt/deviceshifumqtt_test.go +++ b/pkg/deviceshifu/deviceshifumqtt/deviceshifumqtt_test.go @@ -149,11 +149,11 @@ func TestCommandHandleMQTTFunc(t *testing.T) { requestBody := "moving_the_device" - // test post method when MQTTServer not connected - r := dc.Post().Body([]byte(requestBody)).Do(context.TODO()) + // test put method when MQTTServer not connected + r := dc.Put().Body([]byte(requestBody)).Do(context.TODO()) assert.Equal(t, "the server rejected our request for an unknown reason", r.Error().Error()) - // test post method when MQTTServer connected + // test put method when MQTTServer connected var token mqtt.Token // try to connect to MQTT server three times for i := 0; i < 3; i++ { @@ -168,17 +168,17 @@ func TestCommandHandleMQTTFunc(t *testing.T) { assert.Nil(t, token.Error()) ConfigFiniteStateMachine(map[string]string{"moving_the_device": "device_finish_moving"}) - r = dc.Post().Body([]byte(requestBody)).Do(context.TODO()) + r = dc.Put().Body([]byte(requestBody)).Do(context.TODO()) assert.Nil(t, r.Error()) - r = dc.Post().Body([]byte(requestBody)).Do(context.TODO()) + r = dc.Put().Body([]byte(requestBody)).Do(context.TODO()) assert.NotNil(t, r.Error()) // should be blocked // reset mutex MutexProcess("test/test1", "device_finish_moving") - r = dc.Post().Body([]byte(requestBody)).Do(context.TODO()) + r = dc.Put().Body([]byte(requestBody)).Do(context.TODO()) assert.Nil(t, r.Error()) // not blocked - // test put method - r = dc.Put().Do(context.TODO()) + // test post method + r = dc.Post().Do(context.TODO()) assert.Equal(t, "the server rejected our request for an unknown reason", r.Error().Error()) // test Cannot Encode message to json diff --git a/pkg/k8s/api/v1alpha1/edgedevice_types.go b/pkg/k8s/api/v1alpha1/edgedevice_types.go index bfb775fc3..4f8a6325b 100644 --- a/pkg/k8s/api/v1alpha1/edgedevice_types.go +++ b/pkg/k8s/api/v1alpha1/edgedevice_types.go @@ -82,6 +82,64 @@ type SocketSetting struct { NetworkType *string `json:"networkType,omitempty"` } +type SecurityMode string + +const ( + // SecurityModeNoSec No security + SecurityModeNone SecurityMode = "None" + // SecurityModePSK Pre-Shared Key + SecurityModeDTLS SecurityMode = "DTLS" +) + +type DTLSMode string + +const ( + // DTLSModePSK Pre-Shared Key + DTLSModePSK DTLSMode = "PSK" + // DTLSModeRPK Raw Public Key + DTLSModeRPK DTLSMode = "RPK" + // DTLSModeX509 X.509 + DTLSModeX509 DTLSMode = "X.509" +) + +type LwM2MSettings struct { + EndpointName string `json:"endpointName,omitempty"` + // +kubebuilder:default="None" + SecurityMode *SecurityMode `json:"securityMode,omitempty"` + DTLSMode *DTLSMode `json:"dtlsMode,omitempty"` + + CipherSuites []CiperSuite `json:"cipherSuites,omitempty"` + PSKIdentity *string `json:"pskIdentity,omitempty"` + PSKKey *string `json:"pskKey,omitempty"` +} + +type CiperSuite string + +const ( + // AES-128-CCM + CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM CiperSuite = "TLS_ECDHE_ECDSA_WITH_AES_128_CCM" + CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CiperSuite = "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8" + + // AES-128-GCM-SHA256 + CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CiperSuite = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + CiperSuite_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CiperSuite = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + + CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 CiperSuite = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" + CiperSuite_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 CiperSuite = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" + + // AES-256-CBC-SHA + CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CiperSuite = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA" + CiperSuite_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CiperSuite = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" + + CiperSuite_TLS_PSK_WITH_AES_128_CCM CiperSuite = "TLS_PSK_WITH_AES_128_CCM" + CiperSuite_TLS_PSK_WITH_AES_128_CCM_8 CiperSuite = "TLS_PSK_WITH_AES_128_CCM_8" + CiperSuite_TLS_PSK_WITH_AES_256_CCM_8 CiperSuite = "TLS_PSK_WITH_AES_256_CCM_8" + CiperSuite_TLS_PSK_WITH_AES_128_GCM_SHA256 CiperSuite = "TLS_PSK_WITH_AES_128_GCM_SHA256" + CiperSuite_TLS_PSK_WITH_AES_128_CBC_SHA256 CiperSuite = "TLS_PSK_WITH_AES_128_CBC_SHA256" + + CiperSuite_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CiperSuite = "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256" +) + // ProtocolSettings defines protocol settings when connecting to an EdgeDevice type ProtocolSettings struct { MQTTSetting *MQTTSetting `json:"MQTTSetting,omitempty"` @@ -89,6 +147,7 @@ type ProtocolSettings struct { SocketSetting *SocketSetting `json:"SocketSetting,omitempty"` PLC4XSetting *PLC4XSetting `json:"PLC4XSetting,omitempty"` TCPSetting *TCPSetting `json:"TCPSetting,omitempty"` + LwM2MSettings *LwM2MSettings `json:"LwM2MSettings,omitempty"` } // EdgeDeviceSpec defines the desired state of EdgeDevice @@ -136,6 +195,7 @@ type Protocol string // Protocol String const ( + ProtocolLwM2M Protocol = "LwM2M" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPCommandline Protocol = "HTTPCommandline" ProtocolMQTT Protocol = "MQTT" diff --git a/pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go b/pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go index 337b4520d..fb891091a 100644 --- a/pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go @@ -179,6 +179,46 @@ func (in *HTTPSetting) DeepCopy() *HTTPSetting { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LwM2MSettings) DeepCopyInto(out *LwM2MSettings) { + *out = *in + if in.SecurityMode != nil { + in, out := &in.SecurityMode, &out.SecurityMode + *out = new(SecurityMode) + **out = **in + } + if in.DTLSMode != nil { + in, out := &in.DTLSMode, &out.DTLSMode + *out = new(DTLSMode) + **out = **in + } + if in.CipherSuites != nil { + in, out := &in.CipherSuites, &out.CipherSuites + *out = make([]CiperSuite, len(*in)) + copy(*out, *in) + } + if in.PSKIdentity != nil { + in, out := &in.PSKIdentity, &out.PSKIdentity + *out = new(string) + **out = **in + } + if in.PSKKey != nil { + in, out := &in.PSKKey, &out.PSKKey + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LwM2MSettings. +func (in *LwM2MSettings) DeepCopy() *LwM2MSettings { + if in == nil { + return nil + } + out := new(LwM2MSettings) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MQTTSetting) DeepCopyInto(out *MQTTSetting) { *out = *in @@ -377,6 +417,11 @@ func (in *ProtocolSettings) DeepCopyInto(out *ProtocolSettings) { *out = new(TCPSetting) (*in).DeepCopyInto(*out) } + if in.LwM2MSettings != nil { + in, out := &in.LwM2MSettings, &out.LwM2MSettings + *out = new(LwM2MSettings) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProtocolSettings. diff --git a/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_edgedevices.yaml b/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_edgedevices.yaml index 8b1dafeff..815e91c37 100644 --- a/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_edgedevices.yaml +++ b/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_edgedevices.yaml @@ -56,6 +56,24 @@ spec: description: ProtocolSettings defines protocol settings when connecting to an EdgeDevice properties: + LwM2MSettings: + properties: + cipherSuites: + items: + type: string + type: array + dtlsMode: + type: string + endpointName: + type: string + pskIdentity: + type: string + pskKey: + type: string + securityMode: + default: None + type: string + type: object MQTTSetting: description: MQTTSetting defines MQTT specific settings when connecting to an EdgeDevice diff --git a/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_telemetryservices.yaml b/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_telemetryservices.yaml index e203b1edb..4b53134d3 100644 --- a/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_telemetryservices.yaml +++ b/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_telemetryservices.yaml @@ -87,10 +87,6 @@ spec: type: string ServerAddress: type: string - required: - - Bucket - - FileExtension - - ServerAddress type: object SQLSetting: properties: @@ -106,8 +102,6 @@ spec: type: string username: type: string - required: - - serverAddress type: object type: object telemetryServiceEndpoint: diff --git a/pkg/k8s/crd/config/rbac/role.yaml b/pkg/k8s/crd/config/rbac/role.yaml index 3f5f08837..317432e07 100644 --- a/pkg/k8s/crd/config/rbac/role.yaml +++ b/pkg/k8s/crd/config/rbac/role.yaml @@ -8,7 +8,6 @@ rules: - shifu.edgenesis.io resources: - edgedevices - - telemetryservices verbs: - create - delete @@ -21,13 +20,37 @@ rules: - shifu.edgenesis.io resources: - edgedevices/finalizers - - telemetryservices/finalizers verbs: - update - apiGroups: - shifu.edgenesis.io resources: - edgedevices/status + verbs: + - get + - patch + - update +- apiGroups: + - shifu.edgenesis.io + resources: + - telemetryservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - shifu.edgenesis.io + resources: + - telemetryservices/finalizers + verbs: + - update +- apiGroups: + - shifu.edgenesis.io + resources: - telemetryservices/status verbs: - get diff --git a/pkg/k8s/crd/install/config_crd.yaml b/pkg/k8s/crd/install/config_crd.yaml index e1c41a6c6..d57bc9537 100644 --- a/pkg/k8s/crd/install/config_crd.yaml +++ b/pkg/k8s/crd/install/config_crd.yaml @@ -55,6 +55,24 @@ spec: description: ProtocolSettings defines protocol settings when connecting to an EdgeDevice properties: + LwM2MSettings: + properties: + cipherSuites: + items: + type: string + type: array + dtlsMode: + type: string + endpointName: + type: string + pskIdentity: + type: string + pskKey: + type: string + securityMode: + default: None + type: string + type: object MQTTSetting: description: MQTTSetting defines MQTT specific settings when connecting to an EdgeDevice @@ -232,10 +250,6 @@ spec: type: string ServerAddress: type: string - required: - - Bucket - - FileExtension - - ServerAddress type: object SQLSetting: properties: @@ -251,8 +265,6 @@ spec: type: string username: type: string - required: - - serverAddress type: object type: object telemetryServiceEndpoint: diff --git a/pkg/k8s/crd/install/config_default.yaml b/pkg/k8s/crd/install/config_default.yaml index 9d40102a9..07faf8d29 100644 --- a/pkg/k8s/crd/install/config_default.yaml +++ b/pkg/k8s/crd/install/config_default.yaml @@ -62,6 +62,24 @@ spec: description: ProtocolSettings defines protocol settings when connecting to an EdgeDevice properties: + LwM2MSettings: + properties: + cipherSuites: + items: + type: string + type: array + dtlsMode: + type: string + endpointName: + type: string + pskIdentity: + type: string + pskKey: + type: string + securityMode: + default: None + type: string + type: object MQTTSetting: description: MQTTSetting defines MQTT specific settings when connecting to an EdgeDevice @@ -239,10 +257,6 @@ spec: type: string ServerAddress: type: string - required: - - Bucket - - FileExtension - - ServerAddress type: object SQLSetting: properties: @@ -258,8 +272,6 @@ spec: type: string username: type: string - required: - - serverAddress type: object type: object telemetryServiceEndpoint: @@ -333,7 +345,6 @@ rules: - shifu.edgenesis.io resources: - edgedevices - - telemetryservices verbs: - create - delete @@ -346,13 +357,37 @@ rules: - shifu.edgenesis.io resources: - edgedevices/finalizers - - telemetryservices/finalizers verbs: - update - apiGroups: - shifu.edgenesis.io resources: - edgedevices/status + verbs: + - get + - patch + - update +- apiGroups: + - shifu.edgenesis.io + resources: + - telemetryservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - shifu.edgenesis.io + resources: + - telemetryservices/finalizers + verbs: + - update +- apiGroups: + - shifu.edgenesis.io + resources: - telemetryservices/status verbs: - get diff --git a/pkg/k8s/crd/install/shifu_install.yml b/pkg/k8s/crd/install/shifu_install.yml index 16c819b25..93c658e49 100644 --- a/pkg/k8s/crd/install/shifu_install.yml +++ b/pkg/k8s/crd/install/shifu_install.yml @@ -62,6 +62,24 @@ spec: description: ProtocolSettings defines protocol settings when connecting to an EdgeDevice properties: + LwM2MSettings: + properties: + cipherSuites: + items: + type: string + type: array + dtlsMode: + type: string + endpointName: + type: string + pskIdentity: + type: string + pskKey: + type: string + securityMode: + default: None + type: string + type: object MQTTSetting: description: MQTTSetting defines MQTT specific settings when connecting to an EdgeDevice @@ -239,10 +257,6 @@ spec: type: string ServerAddress: type: string - required: - - Bucket - - FileExtension - - ServerAddress type: object SQLSetting: properties: @@ -258,8 +272,6 @@ spec: type: string username: type: string - required: - - serverAddress type: object type: object telemetryServiceEndpoint: @@ -333,7 +345,6 @@ rules: - shifu.edgenesis.io resources: - edgedevices - - telemetryservices verbs: - create - delete @@ -346,13 +357,37 @@ rules: - shifu.edgenesis.io resources: - edgedevices/finalizers - - telemetryservices/finalizers verbs: - update - apiGroups: - shifu.edgenesis.io resources: - edgedevices/status + verbs: + - get + - patch + - update +- apiGroups: + - shifu.edgenesis.io + resources: + - telemetryservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - shifu.edgenesis.io + resources: + - telemetryservices/finalizers + verbs: + - update +- apiGroups: + - shifu.edgenesis.io + resources: - telemetryservices/status verbs: - get From fd665ee14c1b669ede2c651bfcac819dc6fb8aa9 Mon Sep 17 00:00:00 2001 From: rhoninlee Date: Sat, 12 Oct 2024 11:41:58 +0800 Subject: [PATCH 02/12] pdate controller gen version sync with main --- .../deviceshifumqtt/deviceshifumqtt_test.go | 16 ++++----- .../shifu.edgenesis.io_telemetryservices.yaml | 6 ++++ pkg/k8s/crd/config/rbac/role.yaml | 27 ++------------- pkg/k8s/crd/install/config_crd.yaml | 6 ++++ pkg/k8s/crd/install/config_default.yaml | 33 +++++-------------- pkg/k8s/crd/install/shifu_install.yml | 33 +++++-------------- 6 files changed, 38 insertions(+), 83 deletions(-) diff --git a/pkg/deviceshifu/deviceshifumqtt/deviceshifumqtt_test.go b/pkg/deviceshifu/deviceshifumqtt/deviceshifumqtt_test.go index 0a70003f2..8f861e9e4 100644 --- a/pkg/deviceshifu/deviceshifumqtt/deviceshifumqtt_test.go +++ b/pkg/deviceshifu/deviceshifumqtt/deviceshifumqtt_test.go @@ -149,11 +149,11 @@ func TestCommandHandleMQTTFunc(t *testing.T) { requestBody := "moving_the_device" - // test put method when MQTTServer not connected - r := dc.Put().Body([]byte(requestBody)).Do(context.TODO()) + // test post method when MQTTServer not connected + r := dc.Post().Body([]byte(requestBody)).Do(context.TODO()) assert.Equal(t, "the server rejected our request for an unknown reason", r.Error().Error()) - // test put method when MQTTServer connected + // test post method when MQTTServer connected var token mqtt.Token // try to connect to MQTT server three times for i := 0; i < 3; i++ { @@ -168,17 +168,17 @@ func TestCommandHandleMQTTFunc(t *testing.T) { assert.Nil(t, token.Error()) ConfigFiniteStateMachine(map[string]string{"moving_the_device": "device_finish_moving"}) - r = dc.Put().Body([]byte(requestBody)).Do(context.TODO()) + r = dc.Post().Body([]byte(requestBody)).Do(context.TODO()) assert.Nil(t, r.Error()) - r = dc.Put().Body([]byte(requestBody)).Do(context.TODO()) + r = dc.Post().Body([]byte(requestBody)).Do(context.TODO()) assert.NotNil(t, r.Error()) // should be blocked // reset mutex MutexProcess("test/test1", "device_finish_moving") - r = dc.Put().Body([]byte(requestBody)).Do(context.TODO()) + r = dc.Post().Body([]byte(requestBody)).Do(context.TODO()) assert.Nil(t, r.Error()) // not blocked - // test post method - r = dc.Post().Do(context.TODO()) + // test put method + r = dc.Put().Do(context.TODO()) assert.Equal(t, "the server rejected our request for an unknown reason", r.Error().Error()) // test Cannot Encode message to json diff --git a/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_telemetryservices.yaml b/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_telemetryservices.yaml index 4b53134d3..e203b1edb 100644 --- a/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_telemetryservices.yaml +++ b/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_telemetryservices.yaml @@ -87,6 +87,10 @@ spec: type: string ServerAddress: type: string + required: + - Bucket + - FileExtension + - ServerAddress type: object SQLSetting: properties: @@ -102,6 +106,8 @@ spec: type: string username: type: string + required: + - serverAddress type: object type: object telemetryServiceEndpoint: diff --git a/pkg/k8s/crd/config/rbac/role.yaml b/pkg/k8s/crd/config/rbac/role.yaml index 317432e07..3f5f08837 100644 --- a/pkg/k8s/crd/config/rbac/role.yaml +++ b/pkg/k8s/crd/config/rbac/role.yaml @@ -8,31 +8,6 @@ rules: - shifu.edgenesis.io resources: - edgedevices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - shifu.edgenesis.io - resources: - - edgedevices/finalizers - verbs: - - update -- apiGroups: - - shifu.edgenesis.io - resources: - - edgedevices/status - verbs: - - get - - patch - - update -- apiGroups: - - shifu.edgenesis.io - resources: - telemetryservices verbs: - create @@ -45,12 +20,14 @@ rules: - apiGroups: - shifu.edgenesis.io resources: + - edgedevices/finalizers - telemetryservices/finalizers verbs: - update - apiGroups: - shifu.edgenesis.io resources: + - edgedevices/status - telemetryservices/status verbs: - get diff --git a/pkg/k8s/crd/install/config_crd.yaml b/pkg/k8s/crd/install/config_crd.yaml index d57bc9537..3d5f8d931 100644 --- a/pkg/k8s/crd/install/config_crd.yaml +++ b/pkg/k8s/crd/install/config_crd.yaml @@ -250,6 +250,10 @@ spec: type: string ServerAddress: type: string + required: + - Bucket + - FileExtension + - ServerAddress type: object SQLSetting: properties: @@ -265,6 +269,8 @@ spec: type: string username: type: string + required: + - serverAddress type: object type: object telemetryServiceEndpoint: diff --git a/pkg/k8s/crd/install/config_default.yaml b/pkg/k8s/crd/install/config_default.yaml index 07faf8d29..e1d6e5f1e 100644 --- a/pkg/k8s/crd/install/config_default.yaml +++ b/pkg/k8s/crd/install/config_default.yaml @@ -257,6 +257,10 @@ spec: type: string ServerAddress: type: string + required: + - Bucket + - FileExtension + - ServerAddress type: object SQLSetting: properties: @@ -272,6 +276,8 @@ spec: type: string username: type: string + required: + - serverAddress type: object type: object telemetryServiceEndpoint: @@ -345,31 +351,6 @@ rules: - shifu.edgenesis.io resources: - edgedevices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - shifu.edgenesis.io - resources: - - edgedevices/finalizers - verbs: - - update -- apiGroups: - - shifu.edgenesis.io - resources: - - edgedevices/status - verbs: - - get - - patch - - update -- apiGroups: - - shifu.edgenesis.io - resources: - telemetryservices verbs: - create @@ -382,12 +363,14 @@ rules: - apiGroups: - shifu.edgenesis.io resources: + - edgedevices/finalizers - telemetryservices/finalizers verbs: - update - apiGroups: - shifu.edgenesis.io resources: + - edgedevices/status - telemetryservices/status verbs: - get diff --git a/pkg/k8s/crd/install/shifu_install.yml b/pkg/k8s/crd/install/shifu_install.yml index 93c658e49..0fa70f9d1 100644 --- a/pkg/k8s/crd/install/shifu_install.yml +++ b/pkg/k8s/crd/install/shifu_install.yml @@ -257,6 +257,10 @@ spec: type: string ServerAddress: type: string + required: + - Bucket + - FileExtension + - ServerAddress type: object SQLSetting: properties: @@ -272,6 +276,8 @@ spec: type: string username: type: string + required: + - serverAddress type: object type: object telemetryServiceEndpoint: @@ -345,31 +351,6 @@ rules: - shifu.edgenesis.io resources: - edgedevices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - shifu.edgenesis.io - resources: - - edgedevices/finalizers - verbs: - - update -- apiGroups: - - shifu.edgenesis.io - resources: - - edgedevices/status - verbs: - - get - - patch - - update -- apiGroups: - - shifu.edgenesis.io - resources: - telemetryservices verbs: - create @@ -382,12 +363,14 @@ rules: - apiGroups: - shifu.edgenesis.io resources: + - edgedevices/finalizers - telemetryservices/finalizers verbs: - update - apiGroups: - shifu.edgenesis.io resources: + - edgedevices/status - telemetryservices/status verbs: - get From 1d307d039cdbaa843ee98c7857e9bcf13cf2297c Mon Sep 17 00:00:00 2001 From: rhoninlee Date: Sat, 12 Oct 2024 13:13:05 +0800 Subject: [PATCH 03/12] remove unused example --- .../http-deviceshifu-configmap.yaml | 18 ---------- .../http-deviceshifu-deployment.yaml | 36 ------------------- .../http-deviceshifu-service.yaml | 22 ------------ .../lwm2mdeviceShifu/http-edgedevice.yaml | 13 ------- 4 files changed, 89 deletions(-) delete mode 100644 examples/lwm2mdeviceShifu/http-deviceshifu-configmap.yaml delete mode 100644 examples/lwm2mdeviceShifu/http-deviceshifu-deployment.yaml delete mode 100644 examples/lwm2mdeviceShifu/http-deviceshifu-service.yaml delete mode 100644 examples/lwm2mdeviceShifu/http-edgedevice.yaml diff --git a/examples/lwm2mdeviceShifu/http-deviceshifu-configmap.yaml b/examples/lwm2mdeviceShifu/http-deviceshifu-configmap.yaml deleted file mode 100644 index 27c440396..000000000 --- a/examples/lwm2mdeviceShifu/http-deviceshifu-configmap.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: http-configmap-0.0.1 - namespace: deviceshifu -data: -# device name and image address - driverProperties: | - driverSku: HTTP Device - driverImage: http-device:v0.0.1 -# available instructions - instructions: | - instructions: - temperature: - protocolPropertyList: - DataFormat: PlainText - ObjectId: /3303/0/5700 - EnableObserve: true diff --git a/examples/lwm2mdeviceShifu/http-deviceshifu-deployment.yaml b/examples/lwm2mdeviceShifu/http-deviceshifu-deployment.yaml deleted file mode 100644 index 5a1435d6e..000000000 --- a/examples/lwm2mdeviceShifu/http-deviceshifu-deployment.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: deviceshifu-http-deployment - name: deviceshifu-http-deployment - namespace: deviceshifu -spec: - replicas: 1 - selector: - matchLabels: - app: deviceshifu-http-deployment - template: - metadata: - labels: - app: deviceshifu-http-deployment - spec: - containers: - - image: edgehub/deviceshifu-http-lwm2m:nightly - name: deviceshifu-http - ports: - - containerPort: 8080 - volumeMounts: - - name: deviceshifu-config - mountPath: "/etc/edgedevice/config" - readOnly: true - env: - - name: EDGEDEVICE_NAME - value: "edgedevice-http" - - name: EDGEDEVICE_NAMESPACE - value: "devices" - volumes: - - name: deviceshifu-config - configMap: - name: http-configmap-0.0.1 - serviceAccountName: edgedevice-sa diff --git a/examples/lwm2mdeviceShifu/http-deviceshifu-service.yaml b/examples/lwm2mdeviceShifu/http-deviceshifu-service.yaml deleted file mode 100644 index 0d2cb4802..000000000 --- a/examples/lwm2mdeviceShifu/http-deviceshifu-service.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - app: deviceshifu-http-deployment - name: deviceshifu-http - namespace: deviceshifu -spec: - ports: - - name: deviceshifu - port: 80 - protocol: TCP - nodePort: 30080 - targetPort: 8080 - - name: lwm2mserver - port: 5683 - protocol: UDP - nodePort: 30000 - targetPort: 5683 - selector: - app: deviceshifu-http-deployment - type: NodePort diff --git a/examples/lwm2mdeviceShifu/http-edgedevice.yaml b/examples/lwm2mdeviceShifu/http-edgedevice.yaml deleted file mode 100644 index 8c1583063..000000000 --- a/examples/lwm2mdeviceShifu/http-edgedevice.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: shifu.edgenesis.io/v1alpha1 -kind: EdgeDevice -metadata: - name: edgedevice-http - namespace: devices -spec: - sku: "LwM2M Device" - connection: Ethernet - address: 10.20.30.147:5683 - protocol: LwM2M - protocolSettings: - LwM2MSettings: - endpointName: test From 56235d5eb51d3e6ee2494a86054343fd3cdfcbd1 Mon Sep 17 00:00:00 2001 From: rhoninlee Date: Sat, 12 Oct 2024 15:24:26 +0800 Subject: [PATCH 04/12] define lwm2m instruction struct and make ciphersuite package with prefix --- dockerfiles/Dockerfile.deviceshifuLwM2M | 2 +- .../lwm2mdeviceShifu/lwm2m-edgedevice.yaml | 1 - .../deviceshifulwm2m/deviceshifulwm2m.go | 58 +++++++-------- .../deviceshifulwm2mconfig.go | 39 ++++++++++ .../deviceshifulwm2m/lwm2m/ciphersuite.go | 71 +++++++++---------- .../deviceshifulwm2m/lwm2m/lwm2m.go | 2 +- 6 files changed, 99 insertions(+), 74 deletions(-) create mode 100644 pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2mconfig.go diff --git a/dockerfiles/Dockerfile.deviceshifuLwM2M b/dockerfiles/Dockerfile.deviceshifuLwM2M index 59b0e731e..cd2e1d2dd 100644 --- a/dockerfiles/Dockerfile.deviceshifuLwM2M +++ b/dockerfiles/Dockerfile.deviceshifuLwM2M @@ -1,5 +1,5 @@ # Build the manager binary -FROM --platform=$BUILDPLATFORM golang:1.22.0 as builder +FROM --platform=$BUILDPLATFORM golang:1.23.1 as builder WORKDIR /shifu diff --git a/examples/lwm2mdeviceShifu/lwm2m-edgedevice.yaml b/examples/lwm2mdeviceShifu/lwm2m-edgedevice.yaml index 94152da1e..4a7ebb98d 100644 --- a/examples/lwm2mdeviceShifu/lwm2m-edgedevice.yaml +++ b/examples/lwm2mdeviceShifu/lwm2m-edgedevice.yaml @@ -6,7 +6,6 @@ metadata: spec: sku: "LwM2M Device" connection: Ethernet - address: -- protocol: LwM2M protocolSettings: LwM2MSettings: diff --git a/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go index 90f95f55b..ef4b10887 100644 --- a/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go +++ b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go @@ -15,27 +15,20 @@ import ( "github.com/edgenesis/shifu/pkg/k8s/api/v1alpha1" ) -// DeviceShifuLwM2M deviceshifu for HTTP +// DeviceShifuLwM2M deviceshifu for LwM2M type DeviceShifuLwM2M struct { - server *lwm2m.Server - base *deviceshifubase.DeviceShifuBase + server *lwm2m.Server + base *deviceshifubase.DeviceShifuBase + instructionSettings *deviceshifubase.DeviceShifuInstructionSettings } // HandlerMetaData MetaData for HTTPhandler type HandlerMetaData struct { edgeDeviceSpec v1alpha1.EdgeDeviceSpec instruction string - properties *deviceshifubase.DeviceShifuInstruction + properties *LwM2MProtocolProperty } -var ( - instructionSettings *deviceshifubase.DeviceShifuInstructionSettings -) - -const ( - DeviceNameHeaderField = "Device-Name" -) - // New This function creates a new Device Shifu based on the configuration func New(deviceShifuMetadata *deviceshifubase.DeviceShifuMetaData) (*DeviceShifuLwM2M, error) { if deviceShifuMetadata.Namespace == "" { @@ -48,30 +41,37 @@ func New(deviceShifuMetadata *deviceshifubase.DeviceShifuMetaData) (*DeviceShifu } lwM2MSettings := base.EdgeDevice.Spec.ProtocolSettings.LwM2MSettings - logger.Info("endpoint name: %s", lwM2MSettings.EndpointName) + logger.Info("LwM2M endpoint is: %s", lwM2MSettings.EndpointName) server, err := lwm2m.NewServer(*lwM2MSettings) if err != nil { return nil, err } + + ds := &DeviceShifuLwM2M{base: base, server: server} + go func() { - panic(server.Run()) + if err := server.Run(); err != nil { + logger.Fatalf("Error starting LwM2M server: %v", err) + } }() - instructionSettings = base.DeviceShifuConfig.Instructions.InstructionSettings - if instructionSettings == nil { - instructionSettings = &deviceshifubase.DeviceShifuInstructionSettings{} + ds.instructionSettings = base.DeviceShifuConfig.Instructions.InstructionSettings + if ds.instructionSettings == nil { + ds.instructionSettings = &deviceshifubase.DeviceShifuInstructionSettings{} } - if instructionSettings.DefaultTimeoutSeconds == nil { + if ds.instructionSettings.DefaultTimeoutSeconds == nil { var defaultTimeoutSeconds = deviceshifubase.DeviceDefaultGlobalTimeoutInSeconds - instructionSettings.DefaultTimeoutSeconds = &defaultTimeoutSeconds + ds.instructionSettings.DefaultTimeoutSeconds = &defaultTimeoutSeconds } + lwM2MInstructions := CreateLwM2MInstructions(&base.DeviceShifuConfig.Instructions) + if deviceShifuMetadata.KubeConfigPath != deviceshifubase.DeviceKubeconfigDoNotLoadStr { // switch for different Shifu Protocols switch protocol := *base.EdgeDevice.Spec.Protocol; protocol { case v1alpha1.ProtocolLwM2M: - for instruction, properties := range base.DeviceShifuConfig.Instructions.Instructions { + for instruction, properties := range lwM2MInstructions.Instructions { HandlerMetaData := &HandlerMetaData{ base.EdgeDevice.Spec, instruction, @@ -80,9 +80,9 @@ func New(deviceShifuMetadata *deviceshifubase.DeviceShifuMetaData) (*DeviceShifu handler := DeviceCommandHandlerLwM2M{server, HandlerMetaData} mux.HandleFunc("/"+instruction, handler.commandHandleFunc()) - if properties.DeviceShifuProtocolProperties["EnableObserve"] == "true" { + if properties.EnableObserve { server.OnRegister(func() error { - return server.Observe(properties.DeviceShifuProtocolProperties["ObjectId"], func(data interface{}) { + return server.Observe(properties.ObjectId, func(data interface{}) { logger.Infof("Observe data: %v", data) // TODO need to push data to telemetry service }) @@ -96,8 +96,6 @@ func New(deviceShifuMetadata *deviceshifubase.DeviceShifuMetaData) (*DeviceShifu } deviceshifubase.BindDefaultHandler(mux) - ds := &DeviceShifuLwM2M{base: base, server: server} - ds.base.UpdateEdgeDeviceResourcePhase(v1alpha1.EdgeDevicePending) return ds, nil } @@ -114,13 +112,7 @@ func (handler DeviceCommandHandlerLwM2M) commandHandleFunc() http.HandlerFunc { handlerInstruction := handler.HandlerMetaData.instruction handlerServer := handler.server - if handlerProperties != nil { - // TODO: handle validation compile - for _, instructionProperty := range handlerProperties.DeviceShifuInstructionProperties { - logger.Infof("Properties of command: %v %v", handlerInstruction, instructionProperty) - } - } - objectId := handlerProperties.DeviceShifuProtocolProperties["ObjectId"] + objectId := handlerProperties.ObjectId return func(w http.ResponseWriter, r *http.Request) { var respString string @@ -166,8 +158,8 @@ func (handler DeviceCommandHandlerLwM2M) commandHandleFunc() http.HandlerFunc { respString = "Success" default: - http.Error(w, "not supported yet", http.StatusBadRequest) - logger.Errorf("Request type %s is not supported yet!", r.Method) + http.Error(w, "request method not support", http.StatusMethodNotAllowed) + logger.Errorf("Request method %s is not support!", r.Method) return } diff --git a/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2mconfig.go b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2mconfig.go new file mode 100644 index 000000000..7240635b7 --- /dev/null +++ b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2mconfig.go @@ -0,0 +1,39 @@ +package deviceshifulwm2m + +import ( + "github.com/edgenesis/shifu/pkg/deviceshifu/deviceshifubase" + "github.com/edgenesis/shifu/pkg/logger" +) + +const ( + objectIdStr string = "ObjectId" + enableObserveStr string = "EnableObserve" +) + +type LwM2MInstruction struct { + Instructions map[string]*LwM2MProtocolProperty +} + +type LwM2MProtocolProperty struct { + EnableObserve bool + ObjectId string +} + +func CreateLwM2MInstructions(dsInstructions *deviceshifubase.DeviceShifuInstructions) *LwM2MInstruction { + instructions := LwM2MInstruction{ + Instructions: make(map[string]*LwM2MProtocolProperty), + } + for key, dsInstruction := range dsInstructions.Instructions { + if dsInstruction.DeviceShifuProtocolProperties != nil && dsInstruction.DeviceShifuProtocolProperties[objectIdStr] == "" { + logger.Fatalf("Error when Read ObjectId From DeviceShifuInstructions, error: instruction %v has an empty object id", key) + } + if dsInstruction.DeviceShifuProtocolProperties != nil && dsInstruction.DeviceShifuProtocolProperties[enableObserveStr] == "" { + logger.Fatalf("Error when Read EnableObserve From DeviceShifuInstructions, error: instruction %v has an empty enable observe", key) + } + instructions.Instructions[objectIdStr] = &LwM2MProtocolProperty{ + ObjectId: dsInstruction.DeviceShifuProtocolProperties[objectIdStr], + EnableObserve: dsInstruction.DeviceShifuProtocolProperties[enableObserveStr] == "true", + } + } + return &instructions +} diff --git a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/ciphersuite.go b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/ciphersuite.go index 99d08b07f..c1f76d3ef 100644 --- a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/ciphersuite.go +++ b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/ciphersuite.go @@ -4,9 +4,13 @@ import ( "errors" "github.com/edgenesis/shifu/pkg/k8s/api/v1alpha1" + "github.com/edgenesis/shifu/pkg/logger" "github.com/pion/dtls/v2" ) +// Reference: +// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4 +// https://github.com/pion/dtls/blob/98a05d681d3affae2d055a70d3273cbb35425b5a/cipher_suite.go#L25-L45 const ( // AES-128-CCM TLS_ECDHE_ECDSA_WITH_AES_128_CCM dtls.CipherSuiteID = 0xc0ac //nolint:revive,stylecheck @@ -31,49 +35,40 @@ const ( TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 dtls.CipherSuiteID = 0xC037 //nolint:revive,stylecheck ) -func StringToCode(ciperSuitStr v1alpha1.CiperSuite) (dtls.CipherSuiteID, error) { - switch ciperSuitStr { - case v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM: - return TLS_ECDHE_ECDSA_WITH_AES_128_CCM, nil - case v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: - return TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, nil - case v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: - return TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, nil - case v1alpha1.CiperSuite_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: - return TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, nil - case v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: - return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, nil - case v1alpha1.CiperSuite_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: - return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, nil - case v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_CCM: - return TLS_PSK_WITH_AES_128_CCM, nil - case v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_CCM_8: - return TLS_PSK_WITH_AES_128_CCM_8, nil - case v1alpha1.CiperSuite_TLS_PSK_WITH_AES_256_CCM_8: - return TLS_PSK_WITH_AES_256_CCM_8, nil - case v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_GCM_SHA256: - return TLS_PSK_WITH_AES_128_GCM_SHA256, nil - case v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_CBC_SHA256: - return TLS_PSK_WITH_AES_128_CBC_SHA256, nil - case v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: - return TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, nil - case v1alpha1.CiperSuite_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: - return TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, nil - case v1alpha1.CiperSuite_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: - return TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, nil - default: - return 0, errors.New("unknown ciper suite") +var cipherSuiteMap = map[v1alpha1.CiperSuite]dtls.CipherSuiteID{ + v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM: TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + v1alpha1.CiperSuite_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + v1alpha1.CiperSuite_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_CCM: TLS_PSK_WITH_AES_128_CCM, + v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_CCM_8: TLS_PSK_WITH_AES_128_CCM_8, + v1alpha1.CiperSuite_TLS_PSK_WITH_AES_256_CCM_8: TLS_PSK_WITH_AES_256_CCM_8, + v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_GCM_SHA256: TLS_PSK_WITH_AES_128_GCM_SHA256, + v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_CBC_SHA256: TLS_PSK_WITH_AES_128_CBC_SHA256, + v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + v1alpha1.CiperSuite_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + v1alpha1.CiperSuite_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, +} + +func CipherSuiteStringToCode(ciperSuiteStr v1alpha1.CiperSuite) (dtls.CipherSuiteID, error) { + ciperSuitCode, ok := cipherSuiteMap[ciperSuiteStr] + if !ok { + logger.Errorf("unknown cipher suite: %v", ciperSuiteStr) + return 0, errors.New("unknown cipher suite") } + return ciperSuitCode, nil } -func StringsToCodes(ciperSuitStrs []v1alpha1.CiperSuite) ([]dtls.CipherSuiteID, error) { - var ciperSuitCodes []dtls.CipherSuiteID - for _, ciperSuitStr := range ciperSuitStrs { - ciperSuitCode, err := StringToCode(ciperSuitStr) +func CipherSuiteStringsToCodes(ciperSuiteStrs []v1alpha1.CiperSuite) ([]dtls.CipherSuiteID, error) { + var ciperSuiteCodes = make([]dtls.CipherSuiteID, 0, len(ciperSuiteStrs)) + for _, ciperSuitStr := range ciperSuiteStrs { + ciperSuitCode, err := CipherSuiteStringToCode(ciperSuitStr) if err != nil { return nil, err } - ciperSuitCodes = append(ciperSuitCodes, ciperSuitCode) + ciperSuiteCodes = append(ciperSuiteCodes, ciperSuitCode) } - return ciperSuitCodes, nil + return ciperSuiteCodes, nil } diff --git a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go index 1fdc3fe74..d4e8a9bd3 100644 --- a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go +++ b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go @@ -123,7 +123,7 @@ func (s *Server) startDTLSServer() error { server := dtlsServer.New(serverOptions...) - cipersuites, err := StringsToCodes(s.settings.CipherSuites) + cipersuites, err := CipherSuiteStringsToCodes(s.settings.CipherSuites) if err != nil { return err } From ef6794b8e6b3b709bea440f5aa61704525be9636 Mon Sep 17 00:00:00 2001 From: rhoninlee Date: Sat, 12 Oct 2024 17:08:32 +0800 Subject: [PATCH 05/12] rename lwm2msettings --- .../deviceshifulwm2m/deviceshifulwm2m.go | 6 +++--- pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go | 4 ++-- pkg/k8s/api/v1alpha1/edgedevice_types.go | 14 +++++++++----- pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go | 14 +++++++------- .../bases/shifu.edgenesis.io_edgedevices.yaml | 2 +- pkg/k8s/crd/install/config_crd.yaml | 2 +- pkg/k8s/crd/install/config_default.yaml | 2 +- pkg/k8s/crd/install/shifu_install.yml | 2 +- 8 files changed, 25 insertions(+), 21 deletions(-) diff --git a/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go index ef4b10887..ef03c728e 100644 --- a/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go +++ b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go @@ -40,9 +40,9 @@ func New(deviceShifuMetadata *deviceshifubase.DeviceShifuMetaData) (*DeviceShifu return nil, err } - lwM2MSettings := base.EdgeDevice.Spec.ProtocolSettings.LwM2MSettings - logger.Info("LwM2M endpoint is: %s", lwM2MSettings.EndpointName) - server, err := lwm2m.NewServer(*lwM2MSettings) + lwM2MSetting := base.EdgeDevice.Spec.ProtocolSettings.LwM2MSetting + logger.Info("LwM2M endpoint is: %s", lwM2MSetting.EndpointName) + server, err := lwm2m.NewServer(*lwM2MSetting) if err != nil { return nil, err } diff --git a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go index d4e8a9bd3..c97d01e92 100644 --- a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go +++ b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go @@ -27,7 +27,7 @@ import ( type Server struct { router *mux.Router - settings v1alpha1.LwM2MSettings + settings v1alpha1.LwM2MSetting Conn mux.Conn endpointName string liftTime int @@ -64,7 +64,7 @@ func (s *Server) Execute(objectId string, args string) error { return nil } -func NewServer(settings v1alpha1.LwM2MSettings) (*Server, error) { +func NewServer(settings v1alpha1.LwM2MSetting) (*Server, error) { var server = &Server{ endpointName: settings.EndpointName, observeCallback: make(map[string]func(interface{})), diff --git a/pkg/k8s/api/v1alpha1/edgedevice_types.go b/pkg/k8s/api/v1alpha1/edgedevice_types.go index 4f8a6325b..e017a514c 100644 --- a/pkg/k8s/api/v1alpha1/edgedevice_types.go +++ b/pkg/k8s/api/v1alpha1/edgedevice_types.go @@ -102,7 +102,8 @@ const ( DTLSModeX509 DTLSMode = "X.509" ) -type LwM2MSettings struct { +type LwM2MSetting struct { + // +kubebuilder:validation:Required EndpointName string `json:"endpointName,omitempty"` // +kubebuilder:default="None" SecurityMode *SecurityMode `json:"securityMode,omitempty"` @@ -115,6 +116,9 @@ type LwM2MSettings struct { type CiperSuite string +// Reference: +// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4 +// https://github.com/pion/dtls/blob/98a05d681d3affae2d055a70d3273cbb35425b5a/cipher_suite.go#L25-L45 const ( // AES-128-CCM CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM CiperSuite = "TLS_ECDHE_ECDSA_WITH_AES_128_CCM" @@ -147,7 +151,7 @@ type ProtocolSettings struct { SocketSetting *SocketSetting `json:"SocketSetting,omitempty"` PLC4XSetting *PLC4XSetting `json:"PLC4XSetting,omitempty"` TCPSetting *TCPSetting `json:"TCPSetting,omitempty"` - LwM2MSettings *LwM2MSettings `json:"LwM2MSettings,omitempty"` + LwM2MSetting *LwM2MSetting `json:"LwM2MSetting,omitempty"` } // EdgeDeviceSpec defines the desired state of EdgeDevice @@ -195,15 +199,15 @@ type Protocol string // Protocol String const ( - ProtocolLwM2M Protocol = "LwM2M" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPCommandline Protocol = "HTTPCommandline" + ProtocolLwM2M Protocol = "LwM2M" ProtocolMQTT Protocol = "MQTT" ProtocolOPCUA Protocol = "OPCUA" - ProtocolSocket Protocol = "Socket" ProtocolPLC4X Protocol = "PLC4X" - ProtocolUSB Protocol = "USB" + ProtocolSocket Protocol = "Socket" ProtocolTCP Protocol = "TCP" + ProtocolUSB Protocol = "USB" ) type Encoding string diff --git a/pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go b/pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go index fb891091a..b32568b7b 100644 --- a/pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go @@ -180,7 +180,7 @@ func (in *HTTPSetting) DeepCopy() *HTTPSetting { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LwM2MSettings) DeepCopyInto(out *LwM2MSettings) { +func (in *LwM2MSetting) DeepCopyInto(out *LwM2MSetting) { *out = *in if in.SecurityMode != nil { in, out := &in.SecurityMode, &out.SecurityMode @@ -209,12 +209,12 @@ func (in *LwM2MSettings) DeepCopyInto(out *LwM2MSettings) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LwM2MSettings. -func (in *LwM2MSettings) DeepCopy() *LwM2MSettings { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LwM2MSetting. +func (in *LwM2MSetting) DeepCopy() *LwM2MSetting { if in == nil { return nil } - out := new(LwM2MSettings) + out := new(LwM2MSetting) in.DeepCopyInto(out) return out } @@ -417,9 +417,9 @@ func (in *ProtocolSettings) DeepCopyInto(out *ProtocolSettings) { *out = new(TCPSetting) (*in).DeepCopyInto(*out) } - if in.LwM2MSettings != nil { - in, out := &in.LwM2MSettings, &out.LwM2MSettings - *out = new(LwM2MSettings) + if in.LwM2MSetting != nil { + in, out := &in.LwM2MSetting, &out.LwM2MSetting + *out = new(LwM2MSetting) (*in).DeepCopyInto(*out) } } diff --git a/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_edgedevices.yaml b/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_edgedevices.yaml index 815e91c37..55fe70cdb 100644 --- a/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_edgedevices.yaml +++ b/pkg/k8s/crd/config/edgedevice/bases/shifu.edgenesis.io_edgedevices.yaml @@ -56,7 +56,7 @@ spec: description: ProtocolSettings defines protocol settings when connecting to an EdgeDevice properties: - LwM2MSettings: + LwM2MSetting: properties: cipherSuites: items: diff --git a/pkg/k8s/crd/install/config_crd.yaml b/pkg/k8s/crd/install/config_crd.yaml index 3d5f8d931..8602a4c26 100644 --- a/pkg/k8s/crd/install/config_crd.yaml +++ b/pkg/k8s/crd/install/config_crd.yaml @@ -55,7 +55,7 @@ spec: description: ProtocolSettings defines protocol settings when connecting to an EdgeDevice properties: - LwM2MSettings: + LwM2MSetting: properties: cipherSuites: items: diff --git a/pkg/k8s/crd/install/config_default.yaml b/pkg/k8s/crd/install/config_default.yaml index e1d6e5f1e..b01b2a494 100644 --- a/pkg/k8s/crd/install/config_default.yaml +++ b/pkg/k8s/crd/install/config_default.yaml @@ -62,7 +62,7 @@ spec: description: ProtocolSettings defines protocol settings when connecting to an EdgeDevice properties: - LwM2MSettings: + LwM2MSetting: properties: cipherSuites: items: diff --git a/pkg/k8s/crd/install/shifu_install.yml b/pkg/k8s/crd/install/shifu_install.yml index 0fa70f9d1..a59f3f20f 100644 --- a/pkg/k8s/crd/install/shifu_install.yml +++ b/pkg/k8s/crd/install/shifu_install.yml @@ -62,7 +62,7 @@ spec: description: ProtocolSettings defines protocol settings when connecting to an EdgeDevice properties: - LwM2MSettings: + LwM2MSetting: properties: cipherSuites: items: From 382a1773ec8cc5a4448011457027ff47ce50c030 Mon Sep 17 00:00:00 2001 From: rhoninlee Date: Mon, 14 Oct 2024 14:06:13 +0800 Subject: [PATCH 06/12] update log and add some comments --- cmd/deviceshifu/cmdlwm2m/main.go | 7 +++---- .../lwm2mdeviceShifu/lwm2m-deviceshifu-service.yaml | 2 +- pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go | 12 +++++++----- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cmd/deviceshifu/cmdlwm2m/main.go b/cmd/deviceshifu/cmdlwm2m/main.go index 1f396174f..c7622a313 100644 --- a/cmd/deviceshifu/cmdlwm2m/main.go +++ b/cmd/deviceshifu/cmdlwm2m/main.go @@ -23,12 +23,11 @@ func main() { ds, err := deviceshifulwm2m.New(deviceShifuMetadata) if err != nil { - panic(err.Error()) + logger.Fatalf("Error creating deviceshifu: %v", err) } - if err = ds.Start(wait.NeverStop); err != nil { - logger.Errorf("Error starting deviceshifu: %v", err) - panic(err.Error()) + if err := ds.Start(wait.NeverStop); err != nil { + logger.Fatalf("Error starting deviceshifu: %v", err) } select {} diff --git a/examples/lwm2mdeviceShifu/lwm2m-deviceshifu-service.yaml b/examples/lwm2mdeviceShifu/lwm2m-deviceshifu-service.yaml index 84ea2f791..6a6f38bbd 100644 --- a/examples/lwm2mdeviceShifu/lwm2m-deviceshifu-service.yaml +++ b/examples/lwm2mdeviceShifu/lwm2m-deviceshifu-service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: labels: - app: deviceshifu-lwm2m + app: deviceshifu-lwm2m-deployment name: deviceshifu-lwm2m namespace: deviceshifu spec: diff --git a/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go index ef03c728e..809241685 100644 --- a/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go +++ b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go @@ -17,6 +17,7 @@ import ( // DeviceShifuLwM2M deviceshifu for LwM2M type DeviceShifuLwM2M struct { + // lwm2m server which device will connect to, it will be used to send command to device server *lwm2m.Server base *deviceshifubase.DeviceShifuBase instructionSettings *deviceshifubase.DeviceShifuInstructionSettings @@ -41,9 +42,10 @@ func New(deviceShifuMetadata *deviceshifubase.DeviceShifuMetaData) (*DeviceShifu } lwM2MSetting := base.EdgeDevice.Spec.ProtocolSettings.LwM2MSetting - logger.Info("LwM2M endpoint is: %s", lwM2MSetting.EndpointName) + logger.Debugf("LwM2M endpoint is: %s", lwM2MSetting.EndpointName) server, err := lwm2m.NewServer(*lwM2MSetting) if err != nil { + logger.Errorf("Error creating LwM2M server: %v", err) return nil, err } @@ -83,7 +85,7 @@ func New(deviceShifuMetadata *deviceshifubase.DeviceShifuMetaData) (*DeviceShifu if properties.EnableObserve { server.OnRegister(func() error { return server.Observe(properties.ObjectId, func(data interface{}) { - logger.Infof("Observe data: %v", data) + logger.Debugf("Observe data: %v", data) // TODO need to push data to telemetry service }) }) @@ -164,9 +166,9 @@ func (handler DeviceCommandHandlerLwM2M) commandHandleFunc() http.HandlerFunc { } instructionFuncName, shouldUsePythonCustomProcessing := deviceshifubase.CustomInstructionsPython[handlerInstruction] - logger.Infof("Instruction %v is custom: %v", handlerInstruction, shouldUsePythonCustomProcessing) + logger.Debugf("Instruction %v is custom: %v", handlerInstruction, shouldUsePythonCustomProcessing) if shouldUsePythonCustomProcessing { - logger.Infof("Instruction %v has a python customized handler configured.\n", handlerInstruction) + logger.Debugf("Instruction %v has a python customized handler configured.", handlerInstruction) respString = utils.ProcessInstruction(deviceshifubase.PythonHandlersModuleName, instructionFuncName, respString, deviceshifubase.PythonScriptDir) } fmt.Fprintf(w, "%v", respString) @@ -179,7 +181,7 @@ func (ds *DeviceShifuLwM2M) collectHTTPTelemtries() (bool, error) { } if err := ds.server.Conn.Ping(ds.server.Conn.Context()); err != nil { - logger.Errorf("Error checking telemetry: %v", err.Error()) + logger.Errorf("cannot ping the device, please check device is online, error: %v", err.Error()) return false, err } From 7d0beeb10d16ed716edc64ccdd554ea745b62137 Mon Sep 17 00:00:00 2001 From: rhoninlee Date: Mon, 14 Oct 2024 18:38:44 +0800 Subject: [PATCH 07/12] remove unused code and fix typo cipherSuite --- .../deviceshifu/design-deviceShifu-lwm2m.md | 6 +-- .../deviceshifulwm2m/lwm2m/ciphersuite.go | 46 +++++++++---------- .../deviceshifulwm2m/lwm2m/lwm2m.go | 12 +++-- pkg/k8s/api/v1alpha1/edgedevice_types.go | 36 +++++++-------- pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go | 2 +- 5 files changed, 52 insertions(+), 50 deletions(-) diff --git a/docs/design/deviceshifu/design-deviceShifu-lwm2m.md b/docs/design/deviceshifu/design-deviceShifu-lwm2m.md index a00184935..21e14b957 100644 --- a/docs/design/deviceshifu/design-deviceShifu-lwm2m.md +++ b/docs/design/deviceshifu/design-deviceShifu-lwm2m.md @@ -133,7 +133,7 @@ type LwM2MSetting struct { SecurityMode *SecurityMode `json:"securityMode,omitempty"` DTLSMode *DTLSMode `json:"dtlsMode,omitempty"` - CipherSuites []CiperSuite `json:"cipherSuites,omitempty"` + CipherSuites []CipherSuite `json:"cipherSuites,omitempty"` PSKIdentity *string `json:"pskIdentity,omitempty"` PSKKey *string `json:"pskKey,omitempty"` } @@ -145,10 +145,10 @@ const ( // ... ) -type CiperSuite string +type CipherSuite string const ( - CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM CiperSuite = "TLS_ECDHE_ECDSA_WITH_AES_128_CCM" + CipherSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM CipherSuite = "TLS_ECDHE_ECDSA_WITH_AES_128_CCM" // ... ) ``` diff --git a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/ciphersuite.go b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/ciphersuite.go index c1f76d3ef..0f5d8fe4a 100644 --- a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/ciphersuite.go +++ b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/ciphersuite.go @@ -35,40 +35,40 @@ const ( TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 dtls.CipherSuiteID = 0xC037 //nolint:revive,stylecheck ) -var cipherSuiteMap = map[v1alpha1.CiperSuite]dtls.CipherSuiteID{ - v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM: TLS_ECDHE_ECDSA_WITH_AES_128_CCM, - v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, - v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - v1alpha1.CiperSuite_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - v1alpha1.CiperSuite_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_CCM: TLS_PSK_WITH_AES_128_CCM, - v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_CCM_8: TLS_PSK_WITH_AES_128_CCM_8, - v1alpha1.CiperSuite_TLS_PSK_WITH_AES_256_CCM_8: TLS_PSK_WITH_AES_256_CCM_8, - v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_GCM_SHA256: TLS_PSK_WITH_AES_128_GCM_SHA256, - v1alpha1.CiperSuite_TLS_PSK_WITH_AES_128_CBC_SHA256: TLS_PSK_WITH_AES_128_CBC_SHA256, - v1alpha1.CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - v1alpha1.CiperSuite_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - v1alpha1.CiperSuite_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, +var cipherSuiteMap = map[v1alpha1.CipherSuite]dtls.CipherSuiteID{ + v1alpha1.CipherSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM: TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + v1alpha1.CipherSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + v1alpha1.CipherSuite_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + v1alpha1.CipherSuite_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + v1alpha1.CipherSuite_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + v1alpha1.CipherSuite_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + v1alpha1.CipherSuite_TLS_PSK_WITH_AES_128_CCM: TLS_PSK_WITH_AES_128_CCM, + v1alpha1.CipherSuite_TLS_PSK_WITH_AES_128_CCM_8: TLS_PSK_WITH_AES_128_CCM_8, + v1alpha1.CipherSuite_TLS_PSK_WITH_AES_256_CCM_8: TLS_PSK_WITH_AES_256_CCM_8, + v1alpha1.CipherSuite_TLS_PSK_WITH_AES_128_GCM_SHA256: TLS_PSK_WITH_AES_128_GCM_SHA256, + v1alpha1.CipherSuite_TLS_PSK_WITH_AES_128_CBC_SHA256: TLS_PSK_WITH_AES_128_CBC_SHA256, + v1alpha1.CipherSuite_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + v1alpha1.CipherSuite_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + v1alpha1.CipherSuite_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, } -func CipherSuiteStringToCode(ciperSuiteStr v1alpha1.CiperSuite) (dtls.CipherSuiteID, error) { - ciperSuitCode, ok := cipherSuiteMap[ciperSuiteStr] +func CipherSuiteStringToCode(cipherSuitesStr v1alpha1.CipherSuite) (dtls.CipherSuiteID, error) { + ciperSuitCode, ok := cipherSuiteMap[cipherSuitesStr] if !ok { - logger.Errorf("unknown cipher suite: %v", ciperSuiteStr) + logger.Errorf("unknown cipher suite: %v", cipherSuitesStr) return 0, errors.New("unknown cipher suite") } return ciperSuitCode, nil } -func CipherSuiteStringsToCodes(ciperSuiteStrs []v1alpha1.CiperSuite) ([]dtls.CipherSuiteID, error) { - var ciperSuiteCodes = make([]dtls.CipherSuiteID, 0, len(ciperSuiteStrs)) - for _, ciperSuitStr := range ciperSuiteStrs { +func CipherSuiteStringsToCodes(cipherSuiteStrs []v1alpha1.CipherSuite) ([]dtls.CipherSuiteID, error) { + var cipherSuiteCodes = make([]dtls.CipherSuiteID, 0, len(cipherSuiteStrs)) + for _, ciperSuitStr := range cipherSuiteStrs { ciperSuitCode, err := CipherSuiteStringToCode(ciperSuitStr) if err != nil { return nil, err } - ciperSuiteCodes = append(ciperSuiteCodes, ciperSuitCode) + cipherSuiteCodes = append(cipherSuiteCodes, ciperSuitCode) } - return ciperSuiteCodes, nil + return cipherSuiteCodes, nil } diff --git a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go index c97d01e92..5c055f8f9 100644 --- a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go +++ b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go @@ -123,7 +123,7 @@ func (s *Server) startDTLSServer() error { server := dtlsServer.New(serverOptions...) - cipersuites, err := CipherSuiteStringsToCodes(s.settings.CipherSuites) + cipherSuites, err := CipherSuiteStringsToCodes(s.settings.CipherSuites) if err != nil { return err } @@ -138,7 +138,7 @@ func (s *Server) startDTLSServer() error { return []byte(psk), nil }, PSKIdentityHint: []byte(*s.settings.PSKIdentity), - CipherSuites: cipersuites, + CipherSuites: cipherSuites, } l, err := net.NewDTLSListener("udp4", ":5684", &dtlsConfig) @@ -218,9 +218,13 @@ func (s *Server) OnRegister(fn func() error) { s.onRegister = append(s.onRegister, fn) } +const ( + deviceIdObjectParam = "deviceId" +) + // handleResourceUpdate handles UPDATE and De-register request func (s *Server) handleResourceUpdate(w mux.ResponseWriter, r *mux.Message) { - deviceIdQuery := r.RouteParams.Vars["deviceId"] + deviceIdQuery := r.RouteParams.Vars[deviceIdObjectParam] if deviceIdQuery != deviceId { _ = w.SetResponse(codes.BadRequest, message.TextPlain, bytes.NewReader([]byte("device id mismatch"))) return @@ -376,8 +380,6 @@ func (s *Server) Observe(objectId string, callback func(newData interface{})) er return nil } -func DoNothing(newData interface{}) {} - func (s *Server) checkRegistrationStatus() error { if time.Since(s.lastRegistrationTime) > time.Second*time.Duration(s.liftTime) { return errors.New("device is offline") diff --git a/pkg/k8s/api/v1alpha1/edgedevice_types.go b/pkg/k8s/api/v1alpha1/edgedevice_types.go index e017a514c..a4ba4f3c8 100644 --- a/pkg/k8s/api/v1alpha1/edgedevice_types.go +++ b/pkg/k8s/api/v1alpha1/edgedevice_types.go @@ -109,39 +109,39 @@ type LwM2MSetting struct { SecurityMode *SecurityMode `json:"securityMode,omitempty"` DTLSMode *DTLSMode `json:"dtlsMode,omitempty"` - CipherSuites []CiperSuite `json:"cipherSuites,omitempty"` - PSKIdentity *string `json:"pskIdentity,omitempty"` - PSKKey *string `json:"pskKey,omitempty"` + CipherSuites []CipherSuite `json:"cipherSuites,omitempty"` + PSKIdentity *string `json:"pskIdentity,omitempty"` + PSKKey *string `json:"pskKey,omitempty"` } -type CiperSuite string +type CipherSuite string // Reference: // https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4 // https://github.com/pion/dtls/blob/98a05d681d3affae2d055a70d3273cbb35425b5a/cipher_suite.go#L25-L45 const ( // AES-128-CCM - CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM CiperSuite = "TLS_ECDHE_ECDSA_WITH_AES_128_CCM" - CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CiperSuite = "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8" + CipherSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM CipherSuite = "TLS_ECDHE_ECDSA_WITH_AES_128_CCM" + CipherSuite_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuite = "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8" // AES-128-GCM-SHA256 - CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CiperSuite = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" - CiperSuite_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CiperSuite = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + CipherSuite_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuite = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + CipherSuite_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuite = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" - CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 CiperSuite = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" - CiperSuite_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 CiperSuite = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" + CipherSuite_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 CipherSuite = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" + CipherSuite_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 CipherSuite = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" // AES-256-CBC-SHA - CiperSuite_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CiperSuite = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA" - CiperSuite_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CiperSuite = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" + CipherSuite_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuite = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA" + CipherSuite_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuite = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" - CiperSuite_TLS_PSK_WITH_AES_128_CCM CiperSuite = "TLS_PSK_WITH_AES_128_CCM" - CiperSuite_TLS_PSK_WITH_AES_128_CCM_8 CiperSuite = "TLS_PSK_WITH_AES_128_CCM_8" - CiperSuite_TLS_PSK_WITH_AES_256_CCM_8 CiperSuite = "TLS_PSK_WITH_AES_256_CCM_8" - CiperSuite_TLS_PSK_WITH_AES_128_GCM_SHA256 CiperSuite = "TLS_PSK_WITH_AES_128_GCM_SHA256" - CiperSuite_TLS_PSK_WITH_AES_128_CBC_SHA256 CiperSuite = "TLS_PSK_WITH_AES_128_CBC_SHA256" + CipherSuite_TLS_PSK_WITH_AES_128_CCM CipherSuite = "TLS_PSK_WITH_AES_128_CCM" + CipherSuite_TLS_PSK_WITH_AES_128_CCM_8 CipherSuite = "TLS_PSK_WITH_AES_128_CCM_8" + CipherSuite_TLS_PSK_WITH_AES_256_CCM_8 CipherSuite = "TLS_PSK_WITH_AES_256_CCM_8" + CipherSuite_TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuite = "TLS_PSK_WITH_AES_128_GCM_SHA256" + CipherSuite_TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuite = "TLS_PSK_WITH_AES_128_CBC_SHA256" - CiperSuite_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CiperSuite = "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256" + CipherSuite_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CipherSuite = "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256" ) // ProtocolSettings defines protocol settings when connecting to an EdgeDevice diff --git a/pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go b/pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go index b32568b7b..bd2b88c5a 100644 --- a/pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/k8s/api/v1alpha1/zz_generated.deepcopy.go @@ -194,7 +194,7 @@ func (in *LwM2MSetting) DeepCopyInto(out *LwM2MSetting) { } if in.CipherSuites != nil { in, out := &in.CipherSuites, &out.CipherSuites - *out = make([]CiperSuite, len(*in)) + *out = make([]CipherSuite, len(*in)) copy(*out, *in) } if in.PSKIdentity != nil { From 5195fa47ee2a410a777f1fc968ad1bde8e40c68d Mon Sep 17 00:00:00 2001 From: rhoninlee Date: Mon, 14 Oct 2024 18:51:31 +0800 Subject: [PATCH 08/12] add TODO for parse register message --- pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go index 5c055f8f9..fcbca4794 100644 --- a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go +++ b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go @@ -177,6 +177,7 @@ func (s *Server) startUDPServer() error { } func (s *Server) handleRegister(w mux.ResponseWriter, r *mux.Message) { + // TODO: parse register message to get object links query, err := r.Queries() if err != nil { _ = w.SetResponse(codes.BadRequest, message.TextPlain, bytes.NewReader([]byte("failed to read queries"))) From 2ec5b25a07b9649a46a620b60ff93f79e635c2ed Mon Sep 17 00:00:00 2001 From: rhoninlee Date: Tue, 15 Oct 2024 10:25:37 +0800 Subject: [PATCH 09/12] make some value const --- .../deviceshifulwm2m/lwm2m/lwm2m.go | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go index fcbca4794..065d2bd53 100644 --- a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go +++ b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go @@ -37,7 +37,20 @@ type Server struct { onRegister []func() error } -const deviceId string = "shifu" +const () + +const ( + LwM2MServerRegisterURI = "/rd" + LwM2MServerHandlerURI = "/rd/{deviceId}" + deviceId string = "shifu" + registerResponseValue string = "rd" + + defaultLwM2MNetwork string = "udp" + defaultCoapListenPort string = ":5683" + defaultCoapsListenPort string = ":5684" + keepAliveTimeoutSec = 10 * 60 + keepAliveRetryTimes = 10 +) func loggingMiddleware(next mux.Handler) mux.Handler { return mux.HandlerFunc(func(w mux.ResponseWriter, r *mux.Message) { @@ -74,8 +87,8 @@ func NewServer(settings v1alpha1.LwM2MSetting) (*Server, error) { router := mux.NewRouter() if err := errors.Join( - router.Handle("/rd", mux.HandlerFunc(server.handleRegister)), - router.Handle("/rd/{deviceId}", mux.HandlerFunc(server.handleResourceUpdate)), + router.Handle(LwM2MServerRegisterURI, mux.HandlerFunc(server.handleRegister)), + router.Handle(LwM2MServerHandlerURI, mux.HandlerFunc(server.handleResourceUpdate)), ); err != nil { return nil, err } @@ -118,7 +131,9 @@ func (s *Server) startDTLSServer() error { serverOptions := []dtlsServer.Option{ options.WithMux(s.router), options.WithContext(context.Background()), - options.WithKeepAlive(10, time.Minute*10, func(cc *udpClient.Conn) {}), + options.WithKeepAlive(keepAliveRetryTimes, time.Second*keepAliveTimeoutSec, func(cc *udpClient.Conn) { + // TODO: handle inactive connection + }), } server := dtlsServer.New(serverOptions...) @@ -141,7 +156,7 @@ func (s *Server) startDTLSServer() error { CipherSuites: cipherSuites, } - l, err := net.NewDTLSListener("udp4", ":5684", &dtlsConfig) + l, err := net.NewDTLSListener(defaultLwM2MNetwork, defaultCoapsListenPort, &dtlsConfig) if err != nil { return err } @@ -163,13 +178,13 @@ func (s *Server) startUDPServer() error { serverOptions := []udpServer.Option{ options.WithMux(s.router), options.WithContext(context.Background()), - options.WithKeepAlive(10, time.Minute*10, func(cc *udpClient.Conn) { + options.WithKeepAlive(keepAliveRetryTimes, time.Second*keepAliveRetryTimes, func(cc *udpClient.Conn) { logger.Error("inactive connection") }), } server := udpServer.New(serverOptions...) - conn, err := net.NewListenUDP("udp", ":5683") + conn, err := net.NewListenUDP(defaultLwM2MNetwork, defaultCoapListenPort) if err != nil { return err } @@ -197,7 +212,7 @@ func (s *Server) handleRegister(w mux.ResponseWriter, r *mux.Message) { s.liftTime, _ = strconv.Atoi(parsedQuery.Lifetime) if err := w.SetResponse(codes.Created, message.TextPlain, nil, - message.Option{ID: message.LocationPath, Value: []byte("rd")}, + message.Option{ID: message.LocationPath, Value: []byte(registerResponseValue)}, message.Option{ID: message.LocationPath, Value: []byte(deviceId)}, ); err != nil { logger.Debug("register response failed") @@ -383,6 +398,7 @@ func (s *Server) Observe(objectId string, callback func(newData interface{})) er func (s *Server) checkRegistrationStatus() error { if time.Since(s.lastRegistrationTime) > time.Second*time.Duration(s.liftTime) { + // TODO: handle re-register return errors.New("device is offline") } From 846ba6079ba9d1b3452c41dbca12d05c2f485382 Mon Sep 17 00:00:00 2001 From: rhoninlee Date: Tue, 15 Oct 2024 15:19:50 +0800 Subject: [PATCH 10/12] close previous connection with register --- pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go index 065d2bd53..fb35d872a 100644 --- a/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go +++ b/pkg/deviceshifu/deviceshifulwm2m/lwm2m/lwm2m.go @@ -219,6 +219,13 @@ func (s *Server) handleRegister(w mux.ResponseWriter, r *mux.Message) { } s.lastRegistrationTime = time.Now() + if s.Conn != nil { + // try to close the previous connection if exists + if err := s.Conn.Close(); err != nil { + // log error but continue + logger.Errorf("failed to close connection, error: %v", err) + } + } s.Conn = w.Conn() for _, fn := range s.onRegister { From c6070723ece07da6e0c71bdbf991157b6092447b Mon Sep 17 00:00:00 2001 From: rhoninlee Date: Tue, 15 Oct 2024 15:29:25 +0800 Subject: [PATCH 11/12] add TODO for it and add push command in Makefile --- Makefile | 5 +++++ pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go | 1 + 2 files changed, 6 insertions(+) diff --git a/Makefile b/Makefile index e8ec9224b..7e0a7ba41 100644 --- a/Makefile +++ b/Makefile @@ -71,6 +71,11 @@ buildx-push-image-deviceshifu-tcp-tcp: --build-arg PROJECT_ROOT="${PROJECT_ROOT}" ${PROJECT_ROOT} \ -t edgehub/deviceshifu-tcp-tcp:${IMAGE_VERSION} --push +buildx-build-image-deviceshifu-http-lwm2m: + docker buildx build --platform=linux/amd64,linux/arm64,linux/arm -f ${PROJECT_ROOT}/dockerfiles/Dockerfile.deviceshifuLwM2M \ + --build-arg PROJECT_ROOT="${PROJECT_ROOT}" ${PROJECT_ROOT} \ + -t edgehub/deviceshifu-http-lwm2m:${IMAGE_VERSION} --push + buildx-push-image-shifu-controller: docker buildx build --platform=linux/amd64,linux/arm64,linux/arm -f $(PROJECT_ROOT)/pkg/k8s/crd/Dockerfile \ --build-arg PROJECT_ROOT="$(PROJECT_ROOT)" $(PROJECT_ROOT) \ diff --git a/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go index 809241685..fed3496ec 100644 --- a/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go +++ b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2m.go @@ -1,3 +1,4 @@ +// TODO: need to test for deviceshifu LwM2M package deviceshifulwm2m import ( From d81d8b556c196328cb64ad7a21e5ee1242e9387a Mon Sep 17 00:00:00 2001 From: rhoninlee Date: Tue, 15 Oct 2024 16:29:17 +0800 Subject: [PATCH 12/12] update example config settings and fix bad key of instruction map --- examples/lwm2mdeviceShifu/lwm2m-edgedevice.yaml | 2 +- pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2mconfig.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/lwm2mdeviceShifu/lwm2m-edgedevice.yaml b/examples/lwm2mdeviceShifu/lwm2m-edgedevice.yaml index 4a7ebb98d..ee4977030 100644 --- a/examples/lwm2mdeviceShifu/lwm2m-edgedevice.yaml +++ b/examples/lwm2mdeviceShifu/lwm2m-edgedevice.yaml @@ -8,7 +8,7 @@ spec: connection: Ethernet protocol: LwM2M protocolSettings: - LwM2MSettings: + LwM2MSetting: endpointName: test securityMode: DTLS dtlsMode: PSK diff --git a/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2mconfig.go b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2mconfig.go index 7240635b7..88c756f4d 100644 --- a/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2mconfig.go +++ b/pkg/deviceshifu/deviceshifulwm2m/deviceshifulwm2mconfig.go @@ -30,7 +30,7 @@ func CreateLwM2MInstructions(dsInstructions *deviceshifubase.DeviceShifuInstruct if dsInstruction.DeviceShifuProtocolProperties != nil && dsInstruction.DeviceShifuProtocolProperties[enableObserveStr] == "" { logger.Fatalf("Error when Read EnableObserve From DeviceShifuInstructions, error: instruction %v has an empty enable observe", key) } - instructions.Instructions[objectIdStr] = &LwM2MProtocolProperty{ + instructions.Instructions[key] = &LwM2MProtocolProperty{ ObjectId: dsInstruction.DeviceShifuProtocolProperties[objectIdStr], EnableObserve: dsInstruction.DeviceShifuProtocolProperties[enableObserveStr] == "true", }