Skip to content

Commit

Permalink
feat: add opcua adaptor
Browse files Browse the repository at this point in the history
add opcua adaptor logic

Co-authored-by: shanewxy <592491808@qq.com>
Signed-off-by: shanewxy <592491808@qq.com>
  • Loading branch information
Frank Mai and shanewxy committed Apr 13, 2020
1 parent c32d898 commit fdfab9d
Show file tree
Hide file tree
Showing 26 changed files with 1,997 additions and 0 deletions.
12 changes: 12 additions & 0 deletions adaptors/opcua/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM --platform=$TARGETPLATFORM scratch

# NB(thxCode): automatic platform ARGs, ref to:
# - https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
ARG TARGETPLATFORM
ARG TARGETOS
ARG TARGETARCH

WORKDIR /
VOLUME /var/lib/octopus/adaptors
COPY bin/opcua_${TARGETOS}_${TARGETARCH} /opcua
ENTRYPOINT ["/opcua"]
36 changes: 36 additions & 0 deletions adaptors/opcua/Dockerfile.dapper
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
FROM golang:1.13.9-buster
RUN apt-get update && \
apt-get install -y xz-utils unzip

# -- for make rules
## install docker
RUN curl -sSfL "https://get.docker.com" | sh -s VERSION=19.03; \
docker --version
## install kubectl
RUN curl -fL "https://storage.googleapis.com/kubernetes-release/release/v1.17.2/bin/$(go env GOOS)/$(go env GOARCH)/kubectl" -o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl; \
kubectl version --short --client
## install golangci-lint
RUN if [ "$(go env GOARCH)" = "amd64" ]; then \
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "$(go env GOPATH)/bin" v1.24.0; \
golangci-lint --version; \
fi
## install controller-gen
RUN if [ "$(go env GOARCH)" = "amd64" ]; then \
GO111MODULE=on go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.2.5; \
controller-gen --version; \
fi
# -- for make rules

# -- for dapper
ENV DAPPER_RUN_ARGS --privileged --network host
ENV GO111MODULE=off
ENV CROSS=false
ENV DAPPER_ENV CROSS LOCAL_CLUSTER_KIND DOCKER_USERNAME DOCKER_PASSWORD WITHOUT_MANIFEST ONLY_MANIFEST IGNORE_MISSING DRONE_TAG REPO TAG OS ARCH IMAGE_NAME
ENV DAPPER_SOURCE /go/src/github.com/rancher/octopus/
ENV DAPPER_OUTPUT ./adaptors/opcua/bin ./adaptors/opcua/dist ./adaptors/opcua/deploy ./adaptors/opcua/api
ENV DAPPER_DOCKER_SOCKET true
ENV HOME ${DAPPER_SOURCE}
# -- for dapper

WORKDIR ${DAPPER_SOURCE}
ENTRYPOINT ["make", "-se", "adaptor"]
49 changes: 49 additions & 0 deletions adaptors/opcua/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
SHELL := /bin/bash

# Borrowed from https://stackoverflow.com/questions/18136918/how-to-get-current-relative-directory-of-your-makefile
curr_dir := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))

# Borrowed from https://stackoverflow.com/questions/2214575/passing-arguments-to-make-run
rest_args := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
$(eval $(rest_args):;@:)

all: help

help:
# Building process.
#
# Usage:
# make adaptor {adaptor-name} <stage> [only]
#
# Stage:
# a "stage" consists of serval actions, actions follow as below:
# generate -> mod -> lint -> build -> containerize -> deploy
# \ -> test -> verify -> e2e
# for convenience, the name of the "action" also represents the current "stage".
# choosing to execute a certain "stage" will execute all actions in the previous sequence.
#
# Actions:
# - generate, g : generate deployment manifests and code implementations via `controller-gen`.
# - mod, m : download code dependencies.
# - lint, l : verify code via `golangci-lint`,
# roll back to `go fmt` and `go vet` if the installation fails.
# - build, b : compile code.
# - package, p : package docker image.
# - deploy, d : push docker image.
# - test, t : run unit tests.
# - verify, v : run integration tests.
# - e2e, e : run e2e tests.
# only executing the corresponding "action" of a "stage" needs the `only` suffix.
# integrate with dapper via `BY=dapper`.
#
# Example:
# - make adaptor opcua : execute `build` stage for "opcua" adaptor.
# - make adaptor opcua test : execute `test` stage for "opcua" adaptor.
# - make adaptor opcua build only : only execute `build` action for "opcua" adaptor, during `build` stage.
@echo

make_rules := $(shell ls $(curr_dir)/hack/make-rules | sed 's/.sh//g')
$(make_rules):
@$(curr_dir)/hack/make-rules/$@.sh $(rest_args)

.PHONY: $(make_rules) test deploy pkg
110 changes: 110 additions & 0 deletions adaptors/opcua/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# OPCUA Adaptor

## Introduction

OPCUA Adaptor is used for connecting to and manipulating opcua devices on the edge.

## Registration Information

| Versions | Register Name | Endpoint Socket | Available |
|:---:|:---:|:---:|:---:|
| `v1alpha1` | `adaptors.edge.cattle.io/opcua` | `opcua.socket` | * |

## Support Model

| Kind | Group | Version | Available |
|:---:|:---:|:---:|:---:|
| `OPCUADevice` | `devices.edge.cattle.io` | `v1alpha1` | * |

## Support Platform

| OS | Arch |
|:---:|:---|
| `linux` | `amd64` |
| `linux` | `arm` |
| `linux` | `arm64` |

## Usage

```shell script
kubectl apply -f ./deploy/e2e/all_in_one.yaml
```

## Authority

Grant permissions to Octopus as below:

```text
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
opcuadevices.devices.edge.cattle.io [] [] [create delete get list patch update watch]
opcuadevices.devices.edge.cattle.io/status [] [] [get patch update]
```

## DeviceLink CRD
example deviceLink CRD
```yaml
apiVersion: edge.cattle.io/v1alpha1
kind: DeviceLink
metadata:
name: opcua
spec:
adaptor:
node: edge-worker
name: adaptors.edge.cattle.io/opcua
parameters:
syncInterval: 5
timout: 10
model:
apiVersion: "devices.edge.cattle.io/v1alpha1"
kind: "OPCUADevice"
template:
metadata:
labels:
device: opcua
spec:
protocol:
url: opc.tcp://wang-2.local:53530/OPCUA/SimulationServer
username: dadmin
password: admin
properties:
- name: counter
description: enable data collection of temperature sensor
readOnly: true
visitor:
nodeID: ns=3;s=Counter
dataType: int32
- name: random
description: enable data collection of temperature sensor
readOnly: true
visitor:
nodeID: ns=3;s=Random
dataType: double
- name: constant
description: enable data collection of temperature sensor
readOnly: false
visitor:
nodeID: ns=3;s=Constant
value: "2.33"
dataType: float
```

### Protocol Parameters

| Parameter | Description | Type | Default |
|:--|:--|:--|:--|
| url | Required. The URL for opc server endpoint. | string |
| username | Optional. Username for access opc server. | string |
| password | Optional. Password for access opc server. | string |
| securityPolicy | Optional. Valid values are "None", "Basic128Rsa15", "Basic256", "Basic256Sha256", "Aes128Sha256RsaOaep", "Aes256Sha256RsaPss". | string | none |
| securityMode | Optional. Valid values are "None", "Sign", and "SignAndEncrypt". |string | none |

<!-- | certificateFile | Optional. File of the certificate to access opc server. |string| |
| privateKeyFile | Optional. File of the private key to access opc server. |string| |
-->

### Property Visitor
| Parameter | Description | Type |
|:--|:--|:--|
| nodeID | Required. The ID of opc-ua node, e.g. "ns=1,i=1005" | string
| browseName | Optional. The name of opc-ua node | string
35 changes: 35 additions & 0 deletions adaptors/opcua/api/v1alpha1/groupversion_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package v1alpha1 contains API Schema definitions for the edge v1alpha1 API group
// +kubebuilder:object:generate=true
// +groupName=devices.edge.cattle.io
package v1alpha1

import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)

var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "devices.edge.cattle.io", Version: "v1alpha1"}

// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}

// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
106 changes: 106 additions & 0 deletions adaptors/opcua/api/v1alpha1/opcuadevice_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package v1alpha1

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

const (
PropertyDataTypeInt64 PropertyDataType = "int64"
PropertyDataTypeInt32 PropertyDataType = "int32"
PropertyDataTypeInt16 PropertyDataType = "int16"

PropertyDataTypeUInt64 PropertyDataType = "uint64"
PropertyDataTypeUInt32 PropertyDataType = "uint32"
PropertyDataTypeUInt16 PropertyDataType = "uint16"

PropertyDataTypeString PropertyDataType = "string"
PropertyDataTypeFloat PropertyDataType = "float"
PropertyDataTypeDouble PropertyDataType = "double"
PropertyDataTypeBoolean PropertyDataType = "boolean"
)

// OPCUADeviceSpec defines the desired state of OPCUADevice
type OPCUADeviceSpec struct {
ProtocolConfig *OPCUAProtocolConfig `json:"protocol,omitempty"`
Properties []DeviceProperty `json:"properties,omitempty"`
}

type OPCUAProtocolConfig struct {
// Required: The URL for opc-ua server endpoint.
URL string `json:"url"`
// Username for accessing opc-ua server.
UserName string `json:"userName,omitempty"`
// Password for accessing opc-ua server.
Password string `json:"password,omitempty"`
// Defaults to "None". Valid values are "None", "Basic128Rsa15", "Basic256", "Basic256Sha256", "Aes128Sha256RsaOaep", "Aes256Sha256RsaPss".
// +kubebuilder:validation:Enum=None;Basic128Rsa15;Basic256;Basic256Sha256;Aes128Sha256RsaOaep;Aes256Sha256RsaPss
SecurityPolicy string `json:"securityPolicy,omitempty"`
// Defaults to "None". Valid values are "None", "Sign", and "SignAndEncrypt".
// +kubebuilder:validation:Enum=None;Sign;SignAndEncrypt
SecurityMode string `json:"securityMode,omitempty"`
// Certificate file for accessing opc-ua server.
CertificateFile string `json:"certificateFile,omitempty"`
// PrivateKey file for accessing opc-ua server.
PrivateKeyFile string `json:"privateKeyFile,omitempty"`
}

// DeviceProperty describes an individual device property / attribute like temperature / humidity etc.
type DeviceProperty struct {
// The device property name.
Name string `json:"name"`
// The device property description.
// +optional
Description string `json:"description,omitempty"`
ReadOnly bool `json:"readOnly,omitempty"`
// PropertyDataType represents the type and data validation of the property.
DataType PropertyDataType `json:"dataType"`
Visitor PropertyVisitor `json:"visitor"`
Value string `json:"value,omitempty"`
}

// The property data type.
// +kubebuilder:validation:Enum=float;double;int64;int32;int16;uint64;uint32;uint16;string;boolean
type PropertyDataType string

type PropertyVisitor struct {
// Required: The ID of opc-ua node, e.g. "ns=1,i=1005"
NodeID string `json:"nodeID,omitempty"`
// The name of opc-ua node
BrowseName string `json:"browseName,omitempty"`
}

// OPCUADeviceStatus defines the observed state of OPCUADevice
type OPCUADeviceStatus struct {
Properties []StatusProperties `json:"properties,omitempty"`
}

type StatusProperties struct {
Name string `json:"name,omitempty"`
Value string `json:"value,omitempty"`
DataType PropertyDataType `json:"dataType,omitempty"`
UpdatedAt metav1.Time `json:"updatedAt,omitempty"`
}

// +kubebuilder:object:root=true
// +k8s:openapi-gen=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="ENDPOINT",type="string",JSONPath=".spec.protocol.url"
// OPCUADevice is the Schema for the OPCUA device API
type OPCUADevice struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec OPCUADeviceSpec `json:"spec,omitempty"`
Status OPCUADeviceStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true
// OPCUADeviceList contains a list of OPCUA devices
type OPCUADeviceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []OPCUADevice `json:"items"`
}

func init() {
SchemeBuilder.Register(&OPCUADevice{}, &OPCUADeviceList{})
}
Loading

0 comments on commit fdfab9d

Please sign in to comment.