diff --git a/README.md b/README.md
index 8c88ca35..436ec592 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,10 @@ Octopus is an edge device management system based on Kubernetes, it is very ligh
- [Idea](#idea)
- [Workflow](#workflow)
- [Walkthrough](#walkthrough)
+ + [Deploy Octopus](#deploy-octopus)
+ + [Deploy Device Model & Device Adaptor](#deploy-device-model--device-adaptor)
+ + [Create DeviceLink](#create-devicelink)
+ + [Manage Device](#manage-device)
- [Documentation](#documentation)
- [License](#license)
@@ -79,7 +83,7 @@ In this walkthrough, we try to use Octopus to manage a dummy device. We will per
1. Deploy Octopus
1. Deploy Device Model & Device Adaptor
-1. Create Device Link
+1. Create DeviceLink
1. Manage Device
### Deploy Octopus
@@ -257,15 +261,17 @@ octopus-adaptor-dummy-manager-rolebinding 43s
```
-### Create Device Link
+### Create DeviceLink
-Next, we are going to connect a device via `DeviceLink`. A link consists of 3 parts: `Adaptor`, `Model` and `Device spec`:
+Next, we are going to connect a device via `DeviceLink`. A link mainly consists of 3 fields: `adaptor`, `model` and `template(device spec)`:
-- `Adaptor` describes how to access the device, this connection process calls adaptation. In order to connect a device, we should indicate the name of the adaptor, the name of the device-connectable node and the parameters of this connection.
-- `Model` describes the model of device, it is the [TypeMeta](https://github.com/kubernetes/apimachinery/blob/master/pkg/apis/meta/v1/types.go) of the device model CRD.
-- `Device spec` describes the desired status of device, it is determined by the device model CRD.
+- `adaptor` describes how to access the device, this accessing process calls **Adaptation**. In order to adapt a device, we need to indicate the name of the adaptor and the name of the device-connectable node.
+- `model` describes the model of device, it is the [TypeMeta](https://github.com/kubernetes/apimachinery/blob/master/pkg/apis/meta/v1/types.go) of the device model CRD.
+- `template(device spec)` describes the desired status of device, it is determined by the device model CRD.
-We can imagine that there is a device named `living-room-fan` on the `edge-worker` node, we can try to connect it in.
+In addition, we can also use the `references` field to refer the [ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/) and [Secret](https://kubernetes.io/docs/concepts/configuration/secret/) under the same Namespace, even use the downward API to fetch the information in `DeviceLink`.
+
+We can imagine that there is a device named `living-room-fan` on the `edge-worker` node, and then we can connect it by Octopus:
```yaml
apiVersion: edge.cattle.io/v1alpha1
@@ -292,7 +298,7 @@ spec:
```
-There are [several states](./docs/octopus/state_of_devicelink.md) of a link, if we found the **DeviceConnected** `PHASE` is on **Healthy** `STATUS`, we can query the same name instance of device model CRD, now the device is in our cluster:
+After deployed the above `DeviceLink` into a cluster, we could find that there are [several states](./docs/octopus/state_of_devicelink.md) of a link. If the **DeviceConnected** `PHASE` is on **Healthy** `STATUS`, we can query the same name instance of device model CRD, now the device is in our cluster:
```shell script
$ kubectl get devicelink living-room-fan -n default
diff --git a/api/v1alpha1/devicelink_types.go b/api/v1alpha1/devicelink_types.go
index e8164feb..1fdbbc71 100644
--- a/api/v1alpha1/devicelink_types.go
+++ b/api/v1alpha1/devicelink_types.go
@@ -1,10 +1,83 @@
package v1alpha1
import (
+ corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
+// DeviceLinkReferenceSecretSource defines the source of a same name Secret instance.
+type DeviceLinkReferenceSecretSource struct {
+ // Specifies the name of the Secret in the same Namespace to use.
+ // +kubebuilder:validation:Required
+ Name string `json:"name"`
+
+ // Specifies the key of the Secret's data.
+ // If not specified, all keys of the Secret will be projected into the parameter values.
+ // If specified, the listed keys will be projected into the parameter value.
+ // If a key is specified which is not present in the Secret,
+ // the connection will error unless it is marked optional.
+ // +optional
+ Items []string `json:"items,omitempty"`
+}
+
+// DeviceLinkReferenceConfigMapSource defines the source of a same name ConfigMap instance.
+type DeviceLinkReferenceConfigMapSource struct {
+ // Specifies the name of the ConfigMap in the same Namespace to use.
+ // +kubebuilder:validation:Required
+ Name string `json:"name"`
+
+ // Specifies the key of the ConfigMap's data.
+ // If not specified, all keys of the ConfigMap will be projected into the parameter values.
+ // If specified, the listed keys will be projected into the parameter value.
+ // If a key is specified which is not present in the ConfigMap,
+ // the connection will error unless it is marked optional.
+ // +optional
+ Items []string `json:"items,omitempty"`
+}
+
+// DeviceLinkReferenceDownwardAPISourceItem defines the downward API item for projecting the DeviceLink.
+type DeviceLinkReferenceDownwardAPISourceItem struct {
+ // Specifies the key of the downward API's data.
+ // +kubebuilder:validation:Required
+ Name string `json:"name"`
+
+ // Specifies that how to select a field of the DeviceLink,
+ // only annotations, labels, name, namespace and status are supported.
+ // +kubebuilder:validation:Required
+ FieldRef *corev1.ObjectFieldSelector `json:"fieldRef"`
+}
+
+// DeviceLinkReferenceDownwardAPISource defines the downward API for projecting the DeviceLink.
+type DeviceLinkReferenceDownwardAPISource struct {
+ // Specifies a list of downward API.
+ // +kubebuilder:validation:MinItems=1
+ Items []DeviceLinkReferenceDownwardAPISourceItem `json:"items"`
+}
+
+// DeviceLinkReferenceSource defines the parameter source.
+type DeviceLinkReferenceSource struct {
+ // Secret represents a Secret of the same Namespace that should populate this connection.
+ // +optional
+ Secret *DeviceLinkReferenceSecretSource `json:"secret,omitempty"`
+
+ // ConfigMap represents a ConfigMap of the same Namespace that should populate this connection.
+ // +optional
+ ConfigMap *DeviceLinkReferenceConfigMapSource `json:"configMap,omitempty"`
+
+ // DownwardAPI represents the downward API about the DeviceLink.¬
+ // +optional
+ DownwardAPI *DeviceLinkReferenceDownwardAPISource `json:"downwardAPI,omitempty"`
+}
+
+// DeviceLinkReference defines the parameter that should be passed to the adaptor during connecting.
+type DeviceLinkReference struct {
+ DeviceLinkReferenceSource `json:",inline"`
+
+ // Specifies the name of the parameter.
+ Name string `json:"name,omitempty"`
+}
+
// DeviceAdaptor defines the properties of device adaptor
type DeviceAdaptor struct {
// Specifies the node of adaptor to be matched.
@@ -12,10 +85,12 @@ type DeviceAdaptor struct {
Node string `json:"node,omitempty"`
// Specifies the name of adaptor to be used.
- // +optional
+ // +kubebuilder:validation:Required
Name string `json:"name,omitempty"`
- // Specifies the parameter of adaptor to be used.
+ // [Deprecated] Specifies the parameter of adaptor to be used.
+ // This field has been deprecated, it should define the connection parameter
+ // as a part of device model.
// +kubebuilder:pruning:PreserveUnknownFields
// +optional
Parameters *runtime.RawExtension `json:"parameters,omitempty"`
@@ -96,6 +171,10 @@ type DeviceLinkSpec struct {
// +kubebuilder:validation:Required
Model metav1.TypeMeta `json:"model"`
+ // Specifies the references of device to be used.
+ // +optional
+ References []DeviceLinkReference `json:"references,omitempty"`
+
// Describe the device that will be created.
// +kubebuilder:validation:Required
Template DeviceTemplateSpec `json:"template"`
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 0e056852..3114e269 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -20,6 +20,7 @@ limitations under the License.
package v1alpha1
import (
+ "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
)
@@ -119,11 +120,146 @@ func (in *DeviceLinkList) DeepCopyObject() runtime.Object {
return nil
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DeviceLinkReference) DeepCopyInto(out *DeviceLinkReference) {
+ *out = *in
+ in.DeviceLinkReferenceSource.DeepCopyInto(&out.DeviceLinkReferenceSource)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceLinkReference.
+func (in *DeviceLinkReference) DeepCopy() *DeviceLinkReference {
+ if in == nil {
+ return nil
+ }
+ out := new(DeviceLinkReference)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DeviceLinkReferenceConfigMapSource) DeepCopyInto(out *DeviceLinkReferenceConfigMapSource) {
+ *out = *in
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceLinkReferenceConfigMapSource.
+func (in *DeviceLinkReferenceConfigMapSource) DeepCopy() *DeviceLinkReferenceConfigMapSource {
+ if in == nil {
+ return nil
+ }
+ out := new(DeviceLinkReferenceConfigMapSource)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DeviceLinkReferenceDownwardAPISource) DeepCopyInto(out *DeviceLinkReferenceDownwardAPISource) {
+ *out = *in
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]DeviceLinkReferenceDownwardAPISourceItem, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceLinkReferenceDownwardAPISource.
+func (in *DeviceLinkReferenceDownwardAPISource) DeepCopy() *DeviceLinkReferenceDownwardAPISource {
+ if in == nil {
+ return nil
+ }
+ out := new(DeviceLinkReferenceDownwardAPISource)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DeviceLinkReferenceDownwardAPISourceItem) DeepCopyInto(out *DeviceLinkReferenceDownwardAPISourceItem) {
+ *out = *in
+ if in.FieldRef != nil {
+ in, out := &in.FieldRef, &out.FieldRef
+ *out = new(v1.ObjectFieldSelector)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceLinkReferenceDownwardAPISourceItem.
+func (in *DeviceLinkReferenceDownwardAPISourceItem) DeepCopy() *DeviceLinkReferenceDownwardAPISourceItem {
+ if in == nil {
+ return nil
+ }
+ out := new(DeviceLinkReferenceDownwardAPISourceItem)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DeviceLinkReferenceSecretSource) DeepCopyInto(out *DeviceLinkReferenceSecretSource) {
+ *out = *in
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceLinkReferenceSecretSource.
+func (in *DeviceLinkReferenceSecretSource) DeepCopy() *DeviceLinkReferenceSecretSource {
+ if in == nil {
+ return nil
+ }
+ out := new(DeviceLinkReferenceSecretSource)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DeviceLinkReferenceSource) DeepCopyInto(out *DeviceLinkReferenceSource) {
+ *out = *in
+ if in.Secret != nil {
+ in, out := &in.Secret, &out.Secret
+ *out = new(DeviceLinkReferenceSecretSource)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.ConfigMap != nil {
+ in, out := &in.ConfigMap, &out.ConfigMap
+ *out = new(DeviceLinkReferenceConfigMapSource)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.DownwardAPI != nil {
+ in, out := &in.DownwardAPI, &out.DownwardAPI
+ *out = new(DeviceLinkReferenceDownwardAPISource)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceLinkReferenceSource.
+func (in *DeviceLinkReferenceSource) DeepCopy() *DeviceLinkReferenceSource {
+ if in == nil {
+ return nil
+ }
+ out := new(DeviceLinkReferenceSource)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeviceLinkSpec) DeepCopyInto(out *DeviceLinkSpec) {
*out = *in
in.Adaptor.DeepCopyInto(&out.Adaptor)
out.Model = in.Model
+ if in.References != nil {
+ in, out := &in.References, &out.References
+ *out = make([]DeviceLinkReference, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
in.Template.DeepCopyInto(&out.Template)
}
diff --git a/deploy/e2e/all_in_one.yaml b/deploy/e2e/all_in_one.yaml
index 03368281..1941b56b 100644
--- a/deploy/e2e/all_in_one.yaml
+++ b/deploy/e2e/all_in_one.yaml
@@ -89,7 +89,9 @@ spec:
description: Specifies the node of adaptor to be matched.
type: string
parameters:
- description: Specifies the parameter of adaptor to be used.
+ description: '[Deprecated] Specifies the parameter of adaptor
+ to be used. This field has been deprecated, it should define
+ the connection parameter as a part of device model.'
type: object
x-kubernetes-preserve-unknown-fields: true
type: object
@@ -109,6 +111,98 @@ spec:
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
type: object
+ references:
+ description: Specifies the references of device to be used.
+ items:
+ description: DeviceLinkReference defines the parameter that should
+ be passed to the adaptor during connecting.
+ properties:
+ configMap:
+ description: ConfigMap represents a ConfigMap of the same Namespace
+ that should populate this connection.
+ properties:
+ items:
+ description: Specifies the key of the ConfigMap's data.
+ If not specified, all keys of the ConfigMap will be projected
+ into the parameter values. If specified, the listed keys
+ will be projected into the parameter value. If a key is
+ specified which is not present in the ConfigMap, the connection
+ will error unless it is marked optional.
+ items:
+ type: string
+ type: array
+ name:
+ description: Specifies the name of the ConfigMap in the
+ same Namespace to use.
+ type: string
+ required:
+ - name
+ type: object
+ downwardAPI:
+ description: DownwardAPI represents the downward API about the
+ DeviceLink.¬
+ properties:
+ items:
+ description: Specifies a list of downward API.
+ items:
+ description: DeviceLinkReferenceDownwardAPISourceItem
+ defines the downward API item for projecting the DeviceLink.
+ properties:
+ fieldRef:
+ description: Specifies that how to select a field
+ of the DeviceLink, only annotations, labels, name,
+ namespace and status are supported.
+ properties:
+ apiVersion:
+ description: Version of the schema the FieldPath
+ is written in terms of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to select in the
+ specified API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ name:
+ description: Specifies the key of the downward API's
+ data.
+ type: string
+ required:
+ - fieldRef
+ - name
+ type: object
+ minItems: 1
+ type: array
+ required:
+ - items
+ type: object
+ name:
+ description: Specifies the name of the parameter.
+ type: string
+ secret:
+ description: Secret represents a Secret of the same Namespace
+ that should populate this connection.
+ properties:
+ items:
+ description: Specifies the key of the Secret's data. If
+ not specified, all keys of the Secret will be projected
+ into the parameter values. If specified, the listed keys
+ will be projected into the parameter value. If a key is
+ specified which is not present in the Secret, the connection
+ will error unless it is marked optional.
+ items:
+ type: string
+ type: array
+ name:
+ description: Specifies the name of the Secret in the same
+ Namespace to use.
+ type: string
+ required:
+ - name
+ type: object
+ type: object
+ type: array
template:
description: Describe the device that will be created.
properties:
@@ -345,6 +439,14 @@ metadata:
app.kubernetes.io/version: master
name: octopus-manager-role
rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
- apiGroups:
- ""
resources:
@@ -362,6 +464,14 @@ rules:
- patch
- update
- watch
+- apiGroups:
+ - ""
+ resources:
+ - secrets
+ verbs:
+ - get
+ - list
+ - watch
- apiGroups:
- apiextensions.k8s.io
resources:
diff --git a/deploy/e2e/all_in_one_without_webhook.yaml b/deploy/e2e/all_in_one_without_webhook.yaml
index f8283508..3df300ff 100644
--- a/deploy/e2e/all_in_one_without_webhook.yaml
+++ b/deploy/e2e/all_in_one_without_webhook.yaml
@@ -76,7 +76,9 @@ spec:
description: Specifies the node of adaptor to be matched.
type: string
parameters:
- description: Specifies the parameter of adaptor to be used.
+ description: '[Deprecated] Specifies the parameter of adaptor
+ to be used. This field has been deprecated, it should define
+ the connection parameter as a part of device model.'
type: object
x-kubernetes-preserve-unknown-fields: true
type: object
@@ -96,6 +98,98 @@ spec:
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
type: object
+ references:
+ description: Specifies the references of device to be used.
+ items:
+ description: DeviceLinkReference defines the parameter that should
+ be passed to the adaptor during connecting.
+ properties:
+ configMap:
+ description: ConfigMap represents a ConfigMap of the same Namespace
+ that should populate this connection.
+ properties:
+ items:
+ description: Specifies the key of the ConfigMap's data.
+ If not specified, all keys of the ConfigMap will be projected
+ into the parameter values. If specified, the listed keys
+ will be projected into the parameter value. If a key is
+ specified which is not present in the ConfigMap, the connection
+ will error unless it is marked optional.
+ items:
+ type: string
+ type: array
+ name:
+ description: Specifies the name of the ConfigMap in the
+ same Namespace to use.
+ type: string
+ required:
+ - name
+ type: object
+ downwardAPI:
+ description: DownwardAPI represents the downward API about the
+ DeviceLink.¬
+ properties:
+ items:
+ description: Specifies a list of downward API.
+ items:
+ description: DeviceLinkReferenceDownwardAPISourceItem
+ defines the downward API item for projecting the DeviceLink.
+ properties:
+ fieldRef:
+ description: Specifies that how to select a field
+ of the DeviceLink, only annotations, labels, name,
+ namespace and status are supported.
+ properties:
+ apiVersion:
+ description: Version of the schema the FieldPath
+ is written in terms of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to select in the
+ specified API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ name:
+ description: Specifies the key of the downward API's
+ data.
+ type: string
+ required:
+ - fieldRef
+ - name
+ type: object
+ minItems: 1
+ type: array
+ required:
+ - items
+ type: object
+ name:
+ description: Specifies the name of the parameter.
+ type: string
+ secret:
+ description: Secret represents a Secret of the same Namespace
+ that should populate this connection.
+ properties:
+ items:
+ description: Specifies the key of the Secret's data. If
+ not specified, all keys of the Secret will be projected
+ into the parameter values. If specified, the listed keys
+ will be projected into the parameter value. If a key is
+ specified which is not present in the Secret, the connection
+ will error unless it is marked optional.
+ items:
+ type: string
+ type: array
+ name:
+ description: Specifies the name of the Secret in the same
+ Namespace to use.
+ type: string
+ required:
+ - name
+ type: object
+ type: object
+ type: array
template:
description: Describe the device that will be created.
properties:
@@ -262,6 +356,14 @@ metadata:
app.kubernetes.io/version: master
name: octopus-manager-role
rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
- apiGroups:
- ""
resources:
@@ -279,6 +381,14 @@ rules:
- patch
- update
- watch
+- apiGroups:
+ - ""
+ resources:
+ - secrets
+ verbs:
+ - get
+ - list
+ - watch
- apiGroups:
- apiextensions.k8s.io
resources:
diff --git a/deploy/manifests/crd/base/edge.cattle.io_devicelinks.yaml b/deploy/manifests/crd/base/edge.cattle.io_devicelinks.yaml
index bf8a91a8..04104b7b 100644
--- a/deploy/manifests/crd/base/edge.cattle.io_devicelinks.yaml
+++ b/deploy/manifests/crd/base/edge.cattle.io_devicelinks.yaml
@@ -67,7 +67,9 @@ spec:
description: Specifies the node of adaptor to be matched.
type: string
parameters:
- description: Specifies the parameter of adaptor to be used.
+ description: '[Deprecated] Specifies the parameter of adaptor
+ to be used. This field has been deprecated, it should define
+ the connection parameter as a part of device model.'
type: object
x-kubernetes-preserve-unknown-fields: true
type: object
@@ -87,6 +89,98 @@ spec:
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
type: object
+ references:
+ description: Specifies the references of device to be used.
+ items:
+ description: DeviceLinkReference defines the parameter that should
+ be passed to the adaptor during connecting.
+ properties:
+ configMap:
+ description: ConfigMap represents a ConfigMap of the same Namespace
+ that should populate this connection.
+ properties:
+ items:
+ description: Specifies the key of the ConfigMap's data.
+ If not specified, all keys of the ConfigMap will be projected
+ into the parameter values. If specified, the listed keys
+ will be projected into the parameter value. If a key is
+ specified which is not present in the ConfigMap, the connection
+ will error unless it is marked optional.
+ items:
+ type: string
+ type: array
+ name:
+ description: Specifies the name of the ConfigMap in the
+ same Namespace to use.
+ type: string
+ required:
+ - name
+ type: object
+ downwardAPI:
+ description: DownwardAPI represents the downward API about the
+ DeviceLink.¬
+ properties:
+ items:
+ description: Specifies a list of downward API.
+ items:
+ description: DeviceLinkReferenceDownwardAPISourceItem
+ defines the downward API item for projecting the DeviceLink.
+ properties:
+ fieldRef:
+ description: Specifies that how to select a field
+ of the DeviceLink, only annotations, labels, name,
+ namespace and status are supported.
+ properties:
+ apiVersion:
+ description: Version of the schema the FieldPath
+ is written in terms of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to select in the
+ specified API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ name:
+ description: Specifies the key of the downward API's
+ data.
+ type: string
+ required:
+ - fieldRef
+ - name
+ type: object
+ minItems: 1
+ type: array
+ required:
+ - items
+ type: object
+ name:
+ description: Specifies the name of the parameter.
+ type: string
+ secret:
+ description: Secret represents a Secret of the same Namespace
+ that should populate this connection.
+ properties:
+ items:
+ description: Specifies the key of the Secret's data. If
+ not specified, all keys of the Secret will be projected
+ into the parameter values. If specified, the listed keys
+ will be projected into the parameter value. If a key is
+ specified which is not present in the Secret, the connection
+ will error unless it is marked optional.
+ items:
+ type: string
+ type: array
+ name:
+ description: Specifies the name of the Secret in the same
+ Namespace to use.
+ type: string
+ required:
+ - name
+ type: object
+ type: object
+ type: array
template:
description: Describe the device that will be created.
properties:
diff --git a/deploy/manifests/rbac/role.yaml b/deploy/manifests/rbac/role.yaml
index 36e2683a..01f3642e 100644
--- a/deploy/manifests/rbac/role.yaml
+++ b/deploy/manifests/rbac/role.yaml
@@ -6,6 +6,14 @@ metadata:
creationTimestamp: null
name: manager-role
rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
- apiGroups:
- ""
resources:
@@ -23,6 +31,14 @@ rules:
- patch
- update
- watch
+- apiGroups:
+ - ""
+ resources:
+ - secrets
+ verbs:
+ - get
+ - list
+ - watch
- apiGroups:
- apiextensions.k8s.io
resources:
diff --git a/docs/adaptors/design_of_adaptor.md b/docs/adaptors/design_of_adaptor.md
index 581f24d5..061eadab 100644
--- a/docs/adaptors/design_of_adaptor.md
+++ b/docs/adaptors/design_of_adaptor.md
@@ -3,7 +3,10 @@
- [Idea](#idea)
-- [Available Adaptor List](#available-adaptor-list)
+ + [APIs](#apis)
+ + [Registration](#registration)
+ + [Connection](#connection)
+- [Available Adaptors](#available-adaptors)
@@ -80,9 +83,17 @@ At the same time, the implementation of adapter can be connected to a single dev
Please view [here](./develop.md) for more detail about developing an adaptor.
-The access management of adaptors takes inspiration from [Kubernetes Device Plugins management](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/). The workflow includes the following steps:
+### APIs
-1. The `limb` starts a gRPC service with a Unix socket on host path to receive registration requests from adaptors:
+The access management of adaptors takes inspiration from [Kubernetes Device Plugins management](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/). The available version of access management APIs is `v1alpha1`.
+
+| Versions | Available | Current |
+|:---:|:---:|:---:|
+| [`v1alpha1`](./design_of_adaptor.md) | * | * |
+
+Following the below steps can allow an adaptor to interact with `limb`:
+
+1. The `limb` starts a gRPC service with a Unix socket on host path to receive registration requests from adaptors:
```proto
// Registration is the service advertised by the Limb,
// any adaptor start its service until Limb approved this register request.
@@ -91,7 +102,7 @@ The access management of adaptors takes inspiration from [Kubernetes Device Plug
}
message RegisterRequest {
- // Name of the adaptor in the form `adaptor-vendor.com/adaptor-vendor`.
+ // Name of the adaptor in the form `adaptor-vendor.com/adaptor-name`.
string name = 1;
// Version of the API the adaptor was built against.
string version = 2;
@@ -99,20 +110,26 @@ The access management of adaptors takes inspiration from [Kubernetes Device Plug
string endpoint = 3;
}
```
-1. The adaptor starts a gRPC service with a Unix socket under host path `/var/lib/octopus/adaptors`, that implements the following interfaces:
+1. The adaptor starts a gRPC service with a Unix socket under host path `/var/lib/octopus/adaptors`, that implements the following interfaces:
```proto
// Connection is the service advertised by the adaptor.
service Connection {
rpc Connect (stream ConnectRequest) returns (stream ConnectResponse) {}
}
+ message ConnectRequestReferenceEntry {
+ map items = 1;
+ }
+
message ConnectRequest {
- // Parameters for the connection, it's in form JSON bytes.
+ // [Deprecated] Parameters for the connection, it's in form JSON bytes.
bytes parameters = 1;
// Model for the device.
k8s.io.apimachinery.pkg.apis.meta.v1.TypeMeta model = 2;
// Desired device, it's in form JSON bytes.
bytes device = 3;
+ // References for the device, i.e: Secret, ConfigMap and Downward API.
+ map references = 4;
}
message ConnectResponse {
@@ -123,7 +140,29 @@ The access management of adaptors takes inspiration from [Kubernetes Device Plug
1. The adaptor registers itself with the `limb` through the Unix socket at host path `/var/lib/octopus/adaptors/limb.socket`.
1. After successfully registering itself, the adaptor runs in serving mode, during which it keeps connecting devices and reports back to the `limb` upon any device state changes.
-## Available Adaptor List
+#### Registration
+
+The **Registration** can let the `limb` to know the existence of an adaptor, on this phase, the `limb` acts as a server and the adaptor acts as a client. The adaptor constructs a registration request with its `name`, `version` and accessing `endpoint`, and then request to `limb`. After successful registered, the `limb` will keep watching the adaptor and notify those DeviceLinks related to the registered adaptor.
+
+- The `name` is the name of the adaptor, it's strongly recommended that to use this pattern `adaptor-vendor.com/adaptor-name` to named an adaptor, each adaptor must have one unique `name`.
+ > The second adaptor who has the same `name` will overwrite the previous one.
+- The `version` is the API version of accessing management, for now, it's fixed in `v1alpha1`.
+- The accessing `endpiont` is the name of unix socket, each adaptor must have one unique `endpoint`.
+ > The second adaptor who has the same registered `endpoint` will never register successfully until the previous one quits.
+
+#### Connection
+
+The **Connection** can let the `limb` to connect to an adaptor, on this phase, the adaptor acts as a server and the `limb` acts as a client. The `limb` constructs a connection request with the `parameters`, `model`, `device` and `references`, and then request to the target adaptor.
+
+- The `parameters` is the parameters used for connection, it's in form JSON bytes.
+ > This `parameters` field has been **DEPRECATED**, it should define the connection parameter as a part of device model.
+- The `model` is the device's model, it's useful to help adaptor to distinguish multiple models, or maintain the compatibility if there are difference versions in one model.
+- The `device` is the device's instance, it's in form JSON bytes, which is JSON bytes of a complete `model` instance and contains `spec` and `status` data.
+ > The adaptor should select the corresponding deserialized receiving object according to the `model` to receive this field's data.
+ > Since the receiving object (device instance) is a legal CRD instance, it strictly conforms to the resource management convention of Kubernetes, so a device can be uniquely identified by Namespace and Name.
+- The `references` is the reference sources of the device, it allows the device to use the ConfigMap and Secret under the same Namespace, or downward API of the parent DeviceLink instance.
+
+## Available Adaptors
- [dummy](../../adaptors/dummy)
- [ble](../../adaptors/ble)
diff --git a/docs/octopus/state_of_devicelink.md b/docs/octopus/state_of_devicelink.md
index 85b34b45..1cf22c5e 100644
--- a/docs/octopus/state_of_devicelink.md
+++ b/docs/octopus/state_of_devicelink.md
@@ -24,8 +24,6 @@ spec:
adaptor:
node: edge-worker
name: adaptors.edge.cattle.io/dummy
- parameters:
- ip: 192.168.2.47
model:
apiVersion: "devices.edge.cattle.io/v1alpha1"
kind: "DummyDevice"
@@ -114,7 +112,7 @@ You can view [Design of Adaptor](../adaptors/design_of_adaptor.md) to learn how
'
```
-After the device instance is successfully created, the `limb` will use the `spec.adaptor.parameters` and `spec.template.spec` to connect that real device via adaptor:
+After the device instance is successfully created, the `limb` will use the `spec.template.spec` to connect that real device via adaptor:
```text
│
diff --git a/pkg/adaptor/api/v1alpha1/api.pb.go b/pkg/adaptor/api/v1alpha1/api.pb.go
index e4f724fb..44e4cd6e 100644
--- a/pkg/adaptor/api/v1alpha1/api.pb.go
+++ b/pkg/adaptor/api/v1alpha1/api.pb.go
@@ -22,6 +22,7 @@ import (
fmt "fmt"
_ "github.com/gogo/protobuf/gogoproto"
proto "github.com/gogo/protobuf/proto"
+ github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
@@ -80,7 +81,7 @@ func (m *Empty) XXX_DiscardUnknown() {
var xxx_messageInfo_Empty proto.InternalMessageInfo
type RegisterRequest struct {
- // Name of the adaptor in the form `adaptor-vendor.com/adaptor-vendor`.
+ // Name of the adaptor in the form `adaptor-vendor.com/adaptor-name`.
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// Version of the API the adaptor was built against.
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
@@ -141,19 +142,64 @@ func (m *RegisterRequest) GetEndpoint() string {
return ""
}
+type ConnectRequestReferenceEntry struct {
+ Items map[string][]byte `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+}
+
+func (m *ConnectRequestReferenceEntry) Reset() { *m = ConnectRequestReferenceEntry{} }
+func (*ConnectRequestReferenceEntry) ProtoMessage() {}
+func (*ConnectRequestReferenceEntry) Descriptor() ([]byte, []int) {
+ return fileDescriptor_00212fb1f9d3bf1c, []int{2}
+}
+func (m *ConnectRequestReferenceEntry) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *ConnectRequestReferenceEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_ConnectRequestReferenceEntry.Marshal(b, m, deterministic)
+ } else {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+ }
+}
+func (m *ConnectRequestReferenceEntry) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ConnectRequestReferenceEntry.Merge(m, src)
+}
+func (m *ConnectRequestReferenceEntry) XXX_Size() int {
+ return m.Size()
+}
+func (m *ConnectRequestReferenceEntry) XXX_DiscardUnknown() {
+ xxx_messageInfo_ConnectRequestReferenceEntry.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ConnectRequestReferenceEntry proto.InternalMessageInfo
+
+func (m *ConnectRequestReferenceEntry) GetItems() map[string][]byte {
+ if m != nil {
+ return m.Items
+ }
+ return nil
+}
+
type ConnectRequest struct {
- // Parameters for the connection, it's in form JSON bytes.
+ // [Deprecated] Parameters for the connection, it's in form JSON bytes.
Parameters []byte `protobuf:"bytes,1,opt,name=parameters,proto3" json:"parameters,omitempty"`
// Model for the device.
Model *v1.TypeMeta `protobuf:"bytes,2,opt,name=model,proto3" json:"model,omitempty"`
// Desired device, it's in form JSON bytes.
Device []byte `protobuf:"bytes,3,opt,name=device,proto3" json:"device,omitempty"`
+ // References for the device, i.e: Secret, ConfigMap and Downward API.
+ References map[string]*ConnectRequestReferenceEntry `protobuf:"bytes,4,rep,name=references,proto3" json:"references,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
func (m *ConnectRequest) Reset() { *m = ConnectRequest{} }
func (*ConnectRequest) ProtoMessage() {}
func (*ConnectRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_00212fb1f9d3bf1c, []int{2}
+ return fileDescriptor_00212fb1f9d3bf1c, []int{3}
}
func (m *ConnectRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -203,6 +249,13 @@ func (m *ConnectRequest) GetDevice() []byte {
return nil
}
+func (m *ConnectRequest) GetReferences() map[string]*ConnectRequestReferenceEntry {
+ if m != nil {
+ return m.References
+ }
+ return nil
+}
+
type ConnectResponse struct {
// Observed device, it's in form JSON bytes.
Device []byte `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"`
@@ -211,7 +264,7 @@ type ConnectResponse struct {
func (m *ConnectResponse) Reset() { *m = ConnectResponse{} }
func (*ConnectResponse) ProtoMessage() {}
func (*ConnectResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_00212fb1f9d3bf1c, []int{3}
+ return fileDescriptor_00212fb1f9d3bf1c, []int{4}
}
func (m *ConnectResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -250,39 +303,50 @@ func (m *ConnectResponse) GetDevice() []byte {
func init() {
proto.RegisterType((*Empty)(nil), "v1alpha1.Empty")
proto.RegisterType((*RegisterRequest)(nil), "v1alpha1.RegisterRequest")
+ proto.RegisterType((*ConnectRequestReferenceEntry)(nil), "v1alpha1.ConnectRequestReferenceEntry")
+ proto.RegisterMapType((map[string][]byte)(nil), "v1alpha1.ConnectRequestReferenceEntry.ItemsEntry")
proto.RegisterType((*ConnectRequest)(nil), "v1alpha1.ConnectRequest")
+ proto.RegisterMapType((map[string]*ConnectRequestReferenceEntry)(nil), "v1alpha1.ConnectRequest.ReferencesEntry")
proto.RegisterType((*ConnectResponse)(nil), "v1alpha1.ConnectResponse")
}
func init() { proto.RegisterFile("api.proto", fileDescriptor_00212fb1f9d3bf1c) }
var fileDescriptor_00212fb1f9d3bf1c = []byte{
- // 397 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x51, 0xb1, 0x8e, 0xd4, 0x30,
- 0x10, 0x8d, 0x81, 0xbb, 0xdd, 0x1b, 0x56, 0xac, 0xe4, 0x02, 0xe5, 0x52, 0x58, 0x28, 0xa2, 0x38,
- 0x0a, 0x1c, 0x76, 0xa1, 0xb8, 0x1a, 0x0e, 0x89, 0x86, 0x26, 0xa2, 0xa3, 0xf2, 0x26, 0x43, 0xd6,
- 0xda, 0x8b, 0x6d, 0x6c, 0x6f, 0xa4, 0xed, 0xf8, 0x02, 0xc4, 0x67, 0x5d, 0x79, 0xe5, 0x95, 0x5c,
- 0xf6, 0x47, 0x50, 0x9c, 0xcd, 0x6d, 0x40, 0x74, 0x7e, 0x6f, 0xde, 0x78, 0xe6, 0xbd, 0x81, 0x33,
- 0x61, 0x24, 0x37, 0x56, 0x7b, 0x4d, 0xa7, 0xcd, 0x42, 0x5c, 0x9b, 0xb5, 0x58, 0x24, 0xaf, 0x2b,
- 0xe9, 0xd7, 0xdb, 0x15, 0x2f, 0x74, 0x9d, 0x55, 0xba, 0xd2, 0x59, 0x10, 0xac, 0xb6, 0xdf, 0x02,
- 0x0a, 0x20, 0xbc, 0xfa, 0xc6, 0xe4, 0xdd, 0xe6, 0xd2, 0x71, 0xa9, 0x33, 0x61, 0x64, 0x2d, 0x8a,
- 0xb5, 0x54, 0x68, 0x77, 0x99, 0xd9, 0x54, 0x1d, 0xe1, 0xb2, 0x1a, 0xbd, 0xc8, 0x9a, 0x45, 0x56,
- 0xa1, 0x42, 0x2b, 0x3c, 0x96, 0x7d, 0x57, 0x3a, 0x81, 0x93, 0x8f, 0xb5, 0xf1, 0xbb, 0xf4, 0x2b,
- 0xcc, 0x73, 0xac, 0xa4, 0xf3, 0x68, 0x73, 0xfc, 0xbe, 0x45, 0xe7, 0x29, 0x85, 0x27, 0x4a, 0xd4,
- 0x18, 0x93, 0x17, 0xe4, 0xe2, 0x2c, 0x0f, 0x6f, 0x1a, 0xc3, 0xa4, 0x41, 0xeb, 0xa4, 0x56, 0xf1,
- 0xa3, 0x40, 0x0f, 0x90, 0x26, 0x30, 0x45, 0x55, 0x1a, 0x2d, 0x95, 0x8f, 0x1f, 0x87, 0xd2, 0x03,
- 0x4e, 0x7f, 0x12, 0x78, 0xf6, 0x41, 0x2b, 0x85, 0x85, 0x1f, 0x3e, 0x67, 0x00, 0x46, 0x58, 0x51,
- 0xa3, 0x47, 0xeb, 0xc2, 0x88, 0x59, 0x3e, 0x62, 0xe8, 0x15, 0x9c, 0xd4, 0xba, 0xc4, 0xeb, 0x30,
- 0xe6, 0xe9, 0x92, 0xf3, 0xde, 0x1e, 0x1f, 0xdb, 0xe3, 0x66, 0x53, 0x75, 0x84, 0xe3, 0x9d, 0x3d,
- 0xde, 0x2c, 0xf8, 0x97, 0x9d, 0xc1, 0xcf, 0xe8, 0x45, 0xde, 0x37, 0xd3, 0xe7, 0x70, 0x5a, 0x62,
- 0x23, 0x0b, 0x0c, 0x2b, 0xcd, 0xf2, 0x03, 0x4a, 0x5f, 0xc1, 0xfc, 0x61, 0x1f, 0x67, 0xb4, 0x72,
- 0x38, 0x92, 0x92, 0xb1, 0x74, 0xf9, 0x09, 0x66, 0x7d, 0x30, 0x56, 0xf8, 0xce, 0xe7, 0x25, 0x4c,
- 0x87, 0xa0, 0xe8, 0x39, 0x1f, 0xae, 0xc5, 0xff, 0x09, 0x2f, 0x99, 0x1f, 0x4b, 0x7d, 0xc0, 0xd1,
- 0x32, 0x07, 0x38, 0x0c, 0xed, 0xfe, 0xb9, 0x82, 0xc9, 0x01, 0xd1, 0xf8, 0xa8, 0xfd, 0x3b, 0xa5,
- 0xe4, 0xfc, 0x3f, 0x95, 0x7e, 0xdf, 0x34, 0xba, 0x20, 0x6f, 0xc8, 0xfb, 0x97, 0x37, 0xf7, 0x8c,
- 0xdc, 0xdd, 0xb3, 0xe8, 0x47, 0xcb, 0xc8, 0x4d, 0xcb, 0xc8, 0x6d, 0xcb, 0xc8, 0xef, 0x96, 0x91,
- 0x5f, 0x7b, 0x16, 0xdd, 0xee, 0x59, 0x74, 0xb7, 0x67, 0xd1, 0xea, 0x34, 0x1c, 0xfb, 0xed, 0x9f,
- 0x00, 0x00, 0x00, 0xff, 0xff, 0x72, 0x93, 0x17, 0x4f, 0x68, 0x02, 0x00, 0x00,
+ // 515 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xc1, 0x6e, 0xd3, 0x40,
+ 0x10, 0xf5, 0x26, 0x4d, 0x93, 0x4e, 0x22, 0x82, 0x56, 0x08, 0xb9, 0x16, 0xb2, 0xaa, 0x08, 0xa1,
+ 0x70, 0x60, 0x4d, 0x02, 0x87, 0x08, 0x71, 0x82, 0x56, 0x94, 0x03, 0x17, 0x8b, 0x1b, 0xa7, 0x4d,
+ 0x32, 0x75, 0xac, 0xc4, 0xde, 0x65, 0x77, 0x63, 0x29, 0x37, 0x3e, 0x81, 0x5f, 0xe0, 0x4b, 0xb8,
+ 0xf6, 0xd8, 0x63, 0x8f, 0x34, 0xf9, 0x11, 0xe4, 0xb5, 0x93, 0xb8, 0xa8, 0x45, 0xbd, 0xcd, 0x9b,
+ 0xf1, 0x9b, 0xd9, 0x79, 0xf3, 0x0c, 0x47, 0x5c, 0xc6, 0x4c, 0x2a, 0x61, 0x04, 0x6d, 0x65, 0x03,
+ 0xbe, 0x90, 0x33, 0x3e, 0xf0, 0x5e, 0x45, 0xb1, 0x99, 0x2d, 0xc7, 0x6c, 0x22, 0x92, 0x20, 0x12,
+ 0x91, 0x08, 0xec, 0x07, 0xe3, 0xe5, 0x85, 0x45, 0x16, 0xd8, 0xa8, 0x20, 0x7a, 0x6f, 0xe7, 0x23,
+ 0xcd, 0x62, 0x11, 0x70, 0x19, 0x27, 0x7c, 0x32, 0x8b, 0x53, 0x54, 0xab, 0x40, 0xce, 0xa3, 0x3c,
+ 0xa1, 0x83, 0x04, 0x0d, 0x0f, 0xb2, 0x41, 0x10, 0x61, 0x8a, 0x8a, 0x1b, 0x9c, 0x16, 0xac, 0x5e,
+ 0x13, 0x1a, 0x67, 0x89, 0x34, 0xab, 0xde, 0x37, 0xe8, 0x86, 0x18, 0xc5, 0xda, 0xa0, 0x0a, 0xf1,
+ 0xfb, 0x12, 0xb5, 0xa1, 0x14, 0x0e, 0x52, 0x9e, 0xa0, 0x4b, 0x4e, 0x48, 0xff, 0x28, 0xb4, 0x31,
+ 0x75, 0xa1, 0x99, 0xa1, 0xd2, 0xb1, 0x48, 0xdd, 0x9a, 0x4d, 0x6f, 0x21, 0xf5, 0xa0, 0x85, 0xe9,
+ 0x54, 0x8a, 0x38, 0x35, 0x6e, 0xdd, 0x96, 0x76, 0xb8, 0xf7, 0x8b, 0xc0, 0xb3, 0x8f, 0x22, 0x4d,
+ 0x71, 0x62, 0xca, 0xe6, 0x21, 0x5e, 0xa0, 0xc2, 0x74, 0x82, 0x67, 0xa9, 0x51, 0x2b, 0xfa, 0x09,
+ 0x1a, 0xb1, 0xc1, 0x44, 0xbb, 0xe4, 0xa4, 0xde, 0x6f, 0x0f, 0x07, 0x6c, 0xab, 0x02, 0xfb, 0x1f,
+ 0x8d, 0x7d, 0xce, 0x39, 0x36, 0x0c, 0x0b, 0xbe, 0x37, 0x02, 0xd8, 0x27, 0xe9, 0x63, 0xa8, 0xcf,
+ 0x71, 0x55, 0x2e, 0x90, 0x87, 0xf4, 0x09, 0x34, 0x32, 0xbe, 0x58, 0xa2, 0x7d, 0x7d, 0x27, 0x2c,
+ 0xc0, 0xbb, 0xda, 0x88, 0xf4, 0x7e, 0xd7, 0xe0, 0xd1, 0xed, 0x61, 0xd4, 0x07, 0x90, 0x5c, 0xf1,
+ 0x04, 0x0d, 0x2a, 0x6d, 0xbb, 0x74, 0xc2, 0x4a, 0x86, 0x9e, 0x42, 0x23, 0x11, 0x53, 0x5c, 0xd8,
+ 0x66, 0xed, 0x21, 0x63, 0xc5, 0x09, 0x58, 0xf5, 0x04, 0x4c, 0xce, 0xa3, 0x3c, 0xa1, 0x59, 0x7e,
+ 0x02, 0x96, 0x0d, 0xd8, 0xd7, 0x95, 0xc4, 0x2f, 0x68, 0x78, 0x58, 0x90, 0xe9, 0x53, 0x38, 0x9c,
+ 0x62, 0x16, 0x4f, 0xd0, 0xca, 0xd6, 0x09, 0x4b, 0x44, 0xcf, 0x01, 0xd4, 0x76, 0x5d, 0xed, 0x1e,
+ 0x58, 0x61, 0xfa, 0xf7, 0x09, 0xc3, 0x76, 0xca, 0x94, 0x7a, 0x54, 0xb8, 0x1e, 0xe6, 0xb7, 0xbd,
+ 0x55, 0xbe, 0x43, 0x99, 0xf7, 0x55, 0x65, 0xda, 0xc3, 0x17, 0x0f, 0x3b, 0x41, 0x55, 0xc1, 0x97,
+ 0xd0, 0xdd, 0x7d, 0xaa, 0xa5, 0x48, 0x35, 0x56, 0x76, 0x23, 0xd5, 0xdd, 0x86, 0xe7, 0xd0, 0x29,
+ 0xdc, 0xa6, 0xb8, 0xc9, 0xcd, 0x33, 0x82, 0xd6, 0xd6, 0x7d, 0xf4, 0x78, 0x3f, 0xf9, 0x1f, 0x47,
+ 0x7a, 0xdd, 0x7d, 0xa9, 0x70, 0xad, 0x33, 0x0c, 0x01, 0xca, 0xa1, 0x79, 0x9f, 0x53, 0x68, 0x96,
+ 0x88, 0xba, 0xf7, 0x2d, 0xe0, 0x1d, 0xdf, 0x51, 0x29, 0xde, 0xdb, 0x73, 0xfa, 0xe4, 0x35, 0xf9,
+ 0xf0, 0xfc, 0xf2, 0xc6, 0x27, 0xd7, 0x37, 0xbe, 0xf3, 0x63, 0xed, 0x93, 0xcb, 0xb5, 0x4f, 0xae,
+ 0xd6, 0x3e, 0xf9, 0xb3, 0xf6, 0xc9, 0xcf, 0x8d, 0xef, 0x5c, 0x6d, 0x7c, 0xe7, 0x7a, 0xe3, 0x3b,
+ 0xe3, 0x43, 0xfb, 0x07, 0xbd, 0xf9, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xcb, 0x96, 0x7e, 0xa6, 0xbd,
+ 0x03, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -536,6 +600,50 @@ func (m *RegisterRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
+func (m *ConnectRequestReferenceEntry) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *ConnectRequestReferenceEntry) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *ConnectRequestReferenceEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if len(m.Items) > 0 {
+ for k := range m.Items {
+ v := m.Items[k]
+ baseI := i
+ if len(v) > 0 {
+ i -= len(v)
+ copy(dAtA[i:], v)
+ i = encodeVarintApi(dAtA, i, uint64(len(v)))
+ i--
+ dAtA[i] = 0x12
+ }
+ i -= len(k)
+ copy(dAtA[i:], k)
+ i = encodeVarintApi(dAtA, i, uint64(len(k)))
+ i--
+ dAtA[i] = 0xa
+ i = encodeVarintApi(dAtA, i, uint64(baseI-i))
+ i--
+ dAtA[i] = 0xa
+ }
+ }
+ return len(dAtA) - i, nil
+}
+
func (m *ConnectRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -556,6 +664,32 @@ func (m *ConnectRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
+ if len(m.References) > 0 {
+ for k := range m.References {
+ v := m.References[k]
+ baseI := i
+ if v != nil {
+ {
+ size, err := v.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintApi(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x12
+ }
+ i -= len(k)
+ copy(dAtA[i:], k)
+ i = encodeVarintApi(dAtA, i, uint64(len(k)))
+ i--
+ dAtA[i] = 0xa
+ i = encodeVarintApi(dAtA, i, uint64(baseI-i))
+ i--
+ dAtA[i] = 0x22
+ }
+ }
if len(m.Device) > 0 {
i -= len(m.Device)
copy(dAtA[i:], m.Device)
@@ -656,6 +790,27 @@ func (m *RegisterRequest) Size() (n int) {
return n
}
+func (m *ConnectRequestReferenceEntry) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if len(m.Items) > 0 {
+ for k, v := range m.Items {
+ _ = k
+ _ = v
+ l = 0
+ if len(v) > 0 {
+ l = 1 + len(v) + sovApi(uint64(len(v)))
+ }
+ mapEntrySize := 1 + len(k) + sovApi(uint64(len(k))) + l
+ n += mapEntrySize + 1 + sovApi(uint64(mapEntrySize))
+ }
+ }
+ return n
+}
+
func (m *ConnectRequest) Size() (n int) {
if m == nil {
return 0
@@ -674,6 +829,19 @@ func (m *ConnectRequest) Size() (n int) {
if l > 0 {
n += 1 + l + sovApi(uint64(l))
}
+ if len(m.References) > 0 {
+ for k, v := range m.References {
+ _ = k
+ _ = v
+ l = 0
+ if v != nil {
+ l = v.Size()
+ l += 1 + sovApi(uint64(l))
+ }
+ mapEntrySize := 1 + len(k) + sovApi(uint64(len(k))) + l
+ n += mapEntrySize + 1 + sovApi(uint64(mapEntrySize))
+ }
+ }
return n
}
@@ -717,14 +885,45 @@ func (this *RegisterRequest) String() string {
}, "")
return s
}
+func (this *ConnectRequestReferenceEntry) String() string {
+ if this == nil {
+ return "nil"
+ }
+ keysForItems := make([]string, 0, len(this.Items))
+ for k, _ := range this.Items {
+ keysForItems = append(keysForItems, k)
+ }
+ github_com_gogo_protobuf_sortkeys.Strings(keysForItems)
+ mapStringForItems := "map[string][]byte{"
+ for _, k := range keysForItems {
+ mapStringForItems += fmt.Sprintf("%v: %v,", k, this.Items[k])
+ }
+ mapStringForItems += "}"
+ s := strings.Join([]string{`&ConnectRequestReferenceEntry{`,
+ `Items:` + mapStringForItems + `,`,
+ `}`,
+ }, "")
+ return s
+}
func (this *ConnectRequest) String() string {
if this == nil {
return "nil"
}
+ keysForReferences := make([]string, 0, len(this.References))
+ for k, _ := range this.References {
+ keysForReferences = append(keysForReferences, k)
+ }
+ github_com_gogo_protobuf_sortkeys.Strings(keysForReferences)
+ mapStringForReferences := "map[string]*ConnectRequestReferenceEntry{"
+ for _, k := range keysForReferences {
+ mapStringForReferences += fmt.Sprintf("%v: %v,", k, this.References[k])
+ }
+ mapStringForReferences += "}"
s := strings.Join([]string{`&ConnectRequest{`,
`Parameters:` + fmt.Sprintf("%v", this.Parameters) + `,`,
`Model:` + strings.Replace(fmt.Sprintf("%v", this.Model), "TypeMeta", "v1.TypeMeta", 1) + `,`,
`Device:` + fmt.Sprintf("%v", this.Device) + `,`,
+ `References:` + mapStringForReferences + `,`,
`}`,
}, "")
return s
@@ -949,6 +1148,187 @@ func (m *RegisterRequest) Unmarshal(dAtA []byte) error {
}
return nil
}
+func (m *ConnectRequestReferenceEntry) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowApi
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: ConnectRequestReferenceEntry: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: ConnectRequestReferenceEntry: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowApi
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthApi
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthApi
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.Items == nil {
+ m.Items = make(map[string][]byte)
+ }
+ var mapkey string
+ mapvalue := []byte{}
+ for iNdEx < postIndex {
+ entryPreIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowApi
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ if fieldNum == 1 {
+ var stringLenmapkey uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowApi
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLenmapkey |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLenmapkey := int(stringLenmapkey)
+ if intStringLenmapkey < 0 {
+ return ErrInvalidLengthApi
+ }
+ postStringIndexmapkey := iNdEx + intStringLenmapkey
+ if postStringIndexmapkey < 0 {
+ return ErrInvalidLengthApi
+ }
+ if postStringIndexmapkey > l {
+ return io.ErrUnexpectedEOF
+ }
+ mapkey = string(dAtA[iNdEx:postStringIndexmapkey])
+ iNdEx = postStringIndexmapkey
+ } else if fieldNum == 2 {
+ var mapbyteLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowApi
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ mapbyteLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intMapbyteLen := int(mapbyteLen)
+ if intMapbyteLen < 0 {
+ return ErrInvalidLengthApi
+ }
+ postbytesIndex := iNdEx + intMapbyteLen
+ if postbytesIndex < 0 {
+ return ErrInvalidLengthApi
+ }
+ if postbytesIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ mapvalue = make([]byte, mapbyteLen)
+ copy(mapvalue, dAtA[iNdEx:postbytesIndex])
+ iNdEx = postbytesIndex
+ } else {
+ iNdEx = entryPreIndex
+ skippy, err := skipApi(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthApi
+ }
+ if (iNdEx + skippy) > postIndex {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+ m.Items[mapkey] = mapvalue
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipApi(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthApi
+ }
+ if (iNdEx + skippy) < 0 {
+ return ErrInvalidLengthApi
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
func (m *ConnectRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@@ -1082,6 +1462,135 @@ func (m *ConnectRequest) Unmarshal(dAtA []byte) error {
m.Device = []byte{}
}
iNdEx = postIndex
+ case 4:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field References", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowApi
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthApi
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthApi
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.References == nil {
+ m.References = make(map[string]*ConnectRequestReferenceEntry)
+ }
+ var mapkey string
+ var mapvalue *ConnectRequestReferenceEntry
+ for iNdEx < postIndex {
+ entryPreIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowApi
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ if fieldNum == 1 {
+ var stringLenmapkey uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowApi
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLenmapkey |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLenmapkey := int(stringLenmapkey)
+ if intStringLenmapkey < 0 {
+ return ErrInvalidLengthApi
+ }
+ postStringIndexmapkey := iNdEx + intStringLenmapkey
+ if postStringIndexmapkey < 0 {
+ return ErrInvalidLengthApi
+ }
+ if postStringIndexmapkey > l {
+ return io.ErrUnexpectedEOF
+ }
+ mapkey = string(dAtA[iNdEx:postStringIndexmapkey])
+ iNdEx = postStringIndexmapkey
+ } else if fieldNum == 2 {
+ var mapmsglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowApi
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ mapmsglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if mapmsglen < 0 {
+ return ErrInvalidLengthApi
+ }
+ postmsgIndex := iNdEx + mapmsglen
+ if postmsgIndex < 0 {
+ return ErrInvalidLengthApi
+ }
+ if postmsgIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ mapvalue = &ConnectRequestReferenceEntry{}
+ if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil {
+ return err
+ }
+ iNdEx = postmsgIndex
+ } else {
+ iNdEx = entryPreIndex
+ skippy, err := skipApi(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthApi
+ }
+ if (iNdEx + skippy) > postIndex {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+ m.References[mapkey] = mapvalue
+ iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipApi(dAtA[iNdEx:])
diff --git a/pkg/adaptor/api/v1alpha1/api.proto b/pkg/adaptor/api/v1alpha1/api.proto
index 138e637d..b88f0302 100644
--- a/pkg/adaptor/api/v1alpha1/api.proto
+++ b/pkg/adaptor/api/v1alpha1/api.proto
@@ -24,7 +24,7 @@ service Registration {
}
message RegisterRequest {
- // Name of the adaptor in the form `adaptor-vendor.com/adaptor-vendor`.
+ // Name of the adaptor in the form `adaptor-vendor.com/adaptor-name`.
string name = 1;
// Version of the API the adaptor was built against.
string version = 2;
@@ -38,13 +38,19 @@ service Connection {
}
}
+message ConnectRequestReferenceEntry {
+ map items = 1;
+}
+
message ConnectRequest {
- // Parameters for the connection, it's in form JSON bytes.
+ // [Deprecated] Parameters for the connection, it's in form JSON bytes.
bytes parameters = 1;
// Model for the device.
k8s.io.apimachinery.pkg.apis.meta.v1.TypeMeta model = 2;
// Desired device, it's in form JSON bytes.
bytes device = 3;
+ // References for the device, i.e: Secret, ConfigMap and Downward API.
+ map references = 4;
}
message ConnectResponse {
diff --git a/pkg/limb/controller/devicelink.go b/pkg/limb/controller/devicelink.go
index 32ba12ce..2674c19b 100644
--- a/pkg/limb/controller/devicelink.go
+++ b/pkg/limb/controller/devicelink.go
@@ -2,14 +2,18 @@ package controller
import (
"context"
+ "fmt"
"reflect"
+ "time"
"github.com/go-logr/logr"
+ corev1 "k8s.io/api/core/v1"
apierrs "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -21,6 +25,7 @@ import (
"github.com/rancher/octopus/pkg/suctioncup"
"github.com/rancher/octopus/pkg/util/collection"
"github.com/rancher/octopus/pkg/util/converter"
+ "github.com/rancher/octopus/pkg/util/fieldpath"
"github.com/rancher/octopus/pkg/util/model"
"github.com/rancher/octopus/pkg/util/object"
)
@@ -44,6 +49,8 @@ type DeviceLinkReconciler struct {
// +kubebuilder:rbac:groups=edge.cattle.io,resources=devicelinks,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=edge.cattle.io,resources=devicelinks/status,verbs=get;update;patch
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
+// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
+// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch
func (r *DeviceLinkReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
var ctx = context.Background()
@@ -280,7 +287,14 @@ func (r *DeviceLinkReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error)
// this status changes maybe can drive by suction cup.
return ctrl.Result{}, nil
case metav1.ConditionTrue:
- if err := r.SuctionCup.Send(&device, &link); err != nil {
+ // fetches the device references if needed
+ var references, err = r.fetchReferences(ctx, &link)
+ if err != nil {
+ log.Error(err, "Unable to fetch the reference parameters of DeviceLink")
+ return ctrl.Result{Requeue: true, RequeueAfter: 10 * time.Second}, nil
+ }
+
+ if err := r.SuctionCup.Send(references, &device, &link); err != nil {
devicelink.FailOnDeviceConnected(&link.Status, "cannot send data to adaptor")
r.Eventf(&link, "Warning", "FailedSent", "cannot send data to adaptor: %v", err)
@@ -328,6 +342,97 @@ func (r *DeviceLinkReconciler) SetupWithManager(ctrlMgr ctrl.Manager, suctionCup
Complete(r)
}
+// fetchReferences fetches the references of deviceLink.
+func (r *DeviceLinkReconciler) fetchReferences(ctx context.Context, deviceLink *edgev1alpha1.DeviceLink) (map[string]map[string][]byte, error) {
+ var references = deviceLink.Spec.References
+ var namespace = deviceLink.Namespace
+
+ var referencesData map[string]map[string][]byte
+ if len(references) != 0 {
+ referencesData = make(map[string]map[string][]byte, len(references))
+
+ for _, rp := range references {
+ var name = rp.Name
+
+ // fetches secret references
+ if rp.Secret != nil {
+ var desiredName = rp.Secret.Name
+ var desiredItems = rp.Secret.Items
+
+ var secret corev1.Secret
+ if err := r.Get(ctx, types.NamespacedName{Namespace: namespace, Name: desiredName}, &secret); err != nil {
+ return nil, err
+ }
+
+ var items = secret.Data
+ if len(desiredItems) != 0 {
+ items = make(map[string][]byte, len(desiredItems))
+ for _, sk := range desiredItems {
+ var sv, exist = secret.Data[sk]
+ if !exist {
+ return nil, apierrs.NewNotFound(corev1.Resource(corev1.ResourceSecrets.String()), fmt.Sprintf("%s.data(%s)", desiredName, sk))
+ }
+ items[sk] = sv
+ }
+ }
+
+ referencesData[name] = items
+ continue
+ }
+
+ // fetches configMap references
+ if rp.ConfigMap != nil {
+ var desiredName = rp.ConfigMap.Name
+ var desiredItems = rp.ConfigMap.Items
+
+ var configMap corev1.ConfigMap
+ if err := r.Get(ctx, types.NamespacedName{Namespace: namespace, Name: desiredName}, &configMap); err != nil {
+ return nil, err
+ }
+
+ var items map[string][]byte
+ if len(desiredItems) != 0 {
+ items = make(map[string][]byte, len(desiredItems))
+ for _, cmk := range desiredItems {
+ var cmv, exist = configMap.Data[cmk]
+ if !exist {
+ return nil, apierrs.NewNotFound(corev1.Resource(corev1.ResourceConfigMaps.String()), fmt.Sprintf("%s.data(%s)", desiredName, cmk))
+ }
+ items[cmk] = []byte(cmv)
+ }
+ } else {
+ items = make(map[string][]byte, len(configMap.Data))
+ for cmk, cmv := range configMap.Data {
+ items[cmk] = []byte(cmv)
+ }
+ }
+
+ referencesData[name] = items
+ continue
+ }
+
+ // fetches downward API references
+ if rp.DownwardAPI != nil {
+ var desiredItems = rp.DownwardAPI.Items
+
+ // the length of items should not be less than 1
+ var items = make(map[string][]byte, len(desiredItems))
+ for _, dk := range desiredItems {
+ var err error
+ items[dk.Name], err = fieldpath.ExtractDeviceLinkFieldPathAsBytes(deviceLink, dk.FieldRef.FieldPath)
+ if err != nil {
+ return nil, apierrs.NewNotFound(edgev1alpha1.GroupResourceDeviceLink, fmt.Sprintf("%s.downwardapi(%s)", deviceLink.Name, dk.FieldRef.FieldPath))
+ }
+ }
+
+ referencesData[name] = items
+ }
+ }
+ }
+
+ return referencesData, nil
+}
+
// isDeviceSpecChanged returns true if there is any changed from deviceLink's template and applies the changes into device.
func isDeviceSpecChanged(deviceLink *edgev1alpha1.DeviceLink, device *unstructured.Unstructured) bool {
var deviceTemplate = deviceLink.Spec.Template
diff --git a/pkg/limb/limb.go b/pkg/limb/limb.go
index a640ab2d..69465fb4 100644
--- a/pkg/limb/limb.go
+++ b/pkg/limb/limb.go
@@ -7,6 +7,7 @@ import (
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/sync/errgroup"
+ corev1 "k8s.io/api/core/v1"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/runtime"
ctrl "sigs.k8s.io/controller-runtime"
@@ -100,7 +101,13 @@ func Run(name string, opts *options.Options) error {
}
func RegisterScheme(scheme *k8sruntime.Scheme) error {
- return edgev1alpha1.AddToScheme(scheme)
+ if err := edgev1alpha1.AddToScheme(scheme); err != nil {
+ return err
+ }
+ if err := corev1.AddToScheme(scheme); err != nil {
+ return err
+ }
+ return nil
}
func RegisterMetrics(registry prometheus.Registerer) error {
diff --git a/pkg/suctioncup/connection/connection.go b/pkg/suctioncup/connection/connection.go
index caa31fbc..45c6dc74 100644
--- a/pkg/suctioncup/connection/connection.go
+++ b/pkg/suctioncup/connection/connection.go
@@ -18,8 +18,8 @@ type Connection interface {
// GetName returns the name of connection
GetName() types.NamespacedName
- // Send sends the parameters, device model and desired data to connection
- Send(parameters []byte, model *metav1.TypeMeta, device []byte) error
+ // Send sends the parameters, device model, desired data and references to connection
+ Send(parameters []byte, model *metav1.TypeMeta, device []byte, references map[string]*api.ConnectRequestReferenceEntry) error
// Stop stops the connection
Stop() error
@@ -61,11 +61,12 @@ func (c *connection) Stop() error {
return c.stop()
}
-func (c *connection) Send(parameters []byte, model *metav1.TypeMeta, device []byte) error {
+func (c *connection) Send(parameters []byte, model *metav1.TypeMeta, device []byte, references map[string]*api.ConnectRequestReferenceEntry) error {
return c.conn.Send(&api.ConnectRequest{
Parameters: parameters,
Model: model,
Device: device,
+ References: references,
})
}
diff --git a/pkg/suctioncup/neurons.go b/pkg/suctioncup/neurons.go
index cc69c3e2..19f964e2 100644
--- a/pkg/suctioncup/neurons.go
+++ b/pkg/suctioncup/neurons.go
@@ -1,12 +1,11 @@
package suctioncup
import (
- "time"
-
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
edgev1alpha1 "github.com/rancher/octopus/api/v1alpha1"
+ api "github.com/rancher/octopus/pkg/adaptor/api/v1alpha1"
"github.com/rancher/octopus/pkg/metrics"
"github.com/rancher/octopus/pkg/util/object"
)
@@ -65,21 +64,12 @@ func (m *manager) Disconnect(by *edgev1alpha1.DeviceLink) (exist bool) {
return adaptor.DeleteConnection(name)
}
-func (m *manager) Send(data *unstructured.Unstructured, by *edgev1alpha1.DeviceLink) (berr error) {
+func (m *manager) Send(referencesData map[string]map[string][]byte, device *unstructured.Unstructured, by *edgev1alpha1.DeviceLink) error {
var adaptorName = by.Status.AdaptorName
if adaptorName == "" {
return errors.New("could not find blank name adaptor")
}
- // records metrics
- var sendStartTS = time.Now()
- defer func() {
- metrics.GetLimbMetricsRecorder().ObserveSendLatency(adaptorName, time.Since(sendStartTS))
- if berr != nil {
- metrics.GetLimbMetricsRecorder().IncreaseSendErrors(adaptorName)
- }
- }()
-
var adaptor = m.adaptors.Get(adaptorName)
if adaptor == nil {
return errors.Errorf("could not find adaptor %s", adaptorName)
@@ -92,7 +82,7 @@ func (m *manager) Send(data *unstructured.Unstructured, by *edgev1alpha1.DeviceL
}
// NB(thxCode) the data should never be nil
- var sendDevice, err = data.MarshalJSON()
+ var sendDevice, err = device.MarshalJSON()
if err != nil {
return errors.Wrapf(err, "could not marshal data as JSON")
}
@@ -101,6 +91,19 @@ func (m *manager) Send(data *unstructured.Unstructured, by *edgev1alpha1.DeviceL
sendParameters = by.Spec.Adaptor.Parameters.Raw
}
var sendModel = &by.Status.Model
+ var sendReferences map[string]*api.ConnectRequestReferenceEntry
+ if len(referencesData) != 0 {
+ sendReferences = make(map[string]*api.ConnectRequestReferenceEntry, len(referencesData))
+ for rpName, rp := range referencesData {
+ var reference = &api.ConnectRequestReferenceEntry{
+ Items: make(map[string][]byte, len(rp)),
+ }
+ for ripName, rip := range rp {
+ reference.Items[ripName] = rip
+ }
+ sendReferences[rpName] = reference
+ }
+ }
- return conn.Send(sendParameters, sendModel, sendDevice)
+ return conn.Send(sendParameters, sendModel, sendDevice, sendReferences)
}
diff --git a/pkg/suctioncup/types.go b/pkg/suctioncup/types.go
index 6798550a..65a96d21 100644
--- a/pkg/suctioncup/types.go
+++ b/pkg/suctioncup/types.go
@@ -31,6 +31,6 @@ type Neurons interface {
// Disconnect stops a connection by link, the return value represents whether there is a disconnected target.
Disconnect(by *edgev1alpha1.DeviceLink) (exist bool)
- // Send sends the data by link
- Send(data *unstructured.Unstructured, by *edgev1alpha1.DeviceLink) error
+ // Send sends references and device by link
+ Send(referencesData map[string]map[string][]byte, device *unstructured.Unstructured, by *edgev1alpha1.DeviceLink) error
}
diff --git a/pkg/util/collection/string_map.go b/pkg/util/collection/string_map.go
index 6a8c3baf..a9d515ef 100644
--- a/pkg/util/collection/string_map.go
+++ b/pkg/util/collection/string_map.go
@@ -1,5 +1,11 @@
package collection
+import (
+ "strings"
+
+ "k8s.io/apimachinery/pkg/util/sets"
+)
+
func StringMapCopy(source map[string]string) map[string]string {
return StringMapCopyInto(source, make(map[string]string, len(source)))
}
@@ -28,3 +34,31 @@ func DiffStringMap(left, right map[string]string) bool {
}
return false
}
+
+func FormatStringMap(m map[string]string, splitter string) string {
+ if splitter == "" {
+ splitter = ","
+ }
+
+ var keySet = sets.NewString()
+ for k := range m {
+ keySet.Insert(k)
+ }
+ var keysLen = keySet.Len()
+ var keys = keySet.List()
+
+ var builder strings.Builder
+ for _, k := range keys {
+ builder.WriteString(k)
+ builder.WriteString("=")
+ builder.WriteString(`"`)
+ builder.WriteString(m[k])
+ builder.WriteString(`"`)
+
+ if keysLen > 1 {
+ builder.WriteString(splitter)
+ }
+ keysLen--
+ }
+ return builder.String()
+}
diff --git a/pkg/util/collection/string_map_test.go b/pkg/util/collection/string_map_test.go
index 136cf68f..757e8e04 100644
--- a/pkg/util/collection/string_map_test.go
+++ b/pkg/util/collection/string_map_test.go
@@ -144,3 +144,44 @@ func TestDiffStringMap(t *testing.T) {
}
}
}
+
+func TestFormatStringMap(t *testing.T) {
+ type given struct {
+ m map[string]string
+ splitter string
+ }
+ var testCases = []struct {
+ given given
+ expect string
+ }{
+ {
+ given: given{
+ m: map[string]string{
+ "c": "d",
+ "x": "z",
+ "a": "b",
+ },
+ splitter: "",
+ },
+ expect: `a="b",c="d",x="z"`,
+ },
+ {
+ given: given{
+ m: map[string]string{
+ "c": "d",
+ "x": "z",
+ "a": "b",
+ },
+ splitter: ";",
+ },
+ expect: `a="b";c="d";x="z"`,
+ },
+ }
+
+ for i, tc := range testCases {
+ var ret = FormatStringMap(tc.given.m, tc.given.splitter)
+ if ret != tc.expect {
+ t.Errorf("case %v: expected %v, got %v", i+1, tc.expect, ret)
+ }
+ }
+}
diff --git a/pkg/util/converter/bytes.go b/pkg/util/converter/bytes.go
new file mode 100644
index 00000000..7f38c1a8
--- /dev/null
+++ b/pkg/util/converter/bytes.go
@@ -0,0 +1,19 @@
+package converter
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+func UnsafeBytesToString(bs []byte) string {
+ return *(*string)(unsafe.Pointer(&bs))
+}
+
+func UnsafeStringToBytes(s string) (bytes []byte) {
+ var slice = (*reflect.SliceHeader)(unsafe.Pointer(&bytes))
+ var str = (*reflect.StringHeader)(unsafe.Pointer(&s))
+ slice.Len = str.Len
+ slice.Cap = str.Len
+ slice.Data = str.Data
+ return bytes
+}
diff --git a/pkg/util/converter/bytes_test.go b/pkg/util/converter/bytes_test.go
new file mode 100644
index 00000000..a99f9fb5
--- /dev/null
+++ b/pkg/util/converter/bytes_test.go
@@ -0,0 +1,41 @@
+package converter
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestUnsafeStringToBytes(t *testing.T) {
+ var testCases = []struct {
+ given string
+ expect []byte
+ }{
+ {
+ given: "octopus",
+ expect: []byte("octopus"),
+ },
+ }
+
+ for i, tc := range testCases {
+ var ret = UnsafeStringToBytes(tc.given)
+ assert.Equal(t, tc.expect, ret, "case %v", i+1)
+ }
+}
+
+func TestUnsafeBytesToString(t *testing.T) {
+ var testCases = []struct {
+ given []byte
+ expect string
+ }{
+ {
+ given: []byte("octopus"),
+ expect: "octopus",
+ },
+ }
+
+ for i, tc := range testCases {
+ var ret = UnsafeBytesToString(tc.given)
+ assert.Equal(t, tc.expect, ret, "case %v", i+1)
+ }
+}
diff --git a/pkg/util/fieldpath/devicelink.go b/pkg/util/fieldpath/devicelink.go
new file mode 100644
index 00000000..f856004b
--- /dev/null
+++ b/pkg/util/fieldpath/devicelink.go
@@ -0,0 +1,34 @@
+package fieldpath
+
+import (
+ "github.com/pkg/errors"
+
+ edgev1alpha1 "github.com/rancher/octopus/api/v1alpha1"
+ "github.com/rancher/octopus/pkg/util/converter"
+)
+
+// ExtractDeviceLinkFieldPathAsBytes is extracts the field from the given DeviceLink
+// and returns it as a byte array.
+func ExtractDeviceLinkFieldPathAsBytes(link *edgev1alpha1.DeviceLink, fieldPath string) ([]byte, error) {
+ if link == nil {
+ return nil, errors.New("link is nil")
+ }
+
+ var status = link.Status
+ var str string
+ switch fieldPath {
+ case "status.nodeHostName":
+ str = status.NodeHostName
+ case "status.nodeInternalIP":
+ str = status.NodeInternalIP
+ case "status.nodeInternalDNS":
+ str = status.NodeInternalDNS
+ case "status.nodeExternalIP":
+ str = status.NodeExternalIP
+ case "status.nodeExternalDNS":
+ str = status.NodeExternalDNS
+ default:
+ return ExtractObjectFieldPathAsBytes(link, fieldPath)
+ }
+ return converter.UnsafeStringToBytes(str), nil
+}
diff --git a/pkg/util/fieldpath/devicelink_test.go b/pkg/util/fieldpath/devicelink_test.go
new file mode 100644
index 00000000..c02cd7fe
--- /dev/null
+++ b/pkg/util/fieldpath/devicelink_test.go
@@ -0,0 +1,121 @@
+package fieldpath
+
+import (
+ "testing"
+
+ "github.com/pkg/errors"
+ "github.com/stretchr/testify/assert"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ edgev1alpha1 "github.com/rancher/octopus/api/v1alpha1"
+)
+
+func TestExtractDeviceLinkFieldPathAsBytes(t *testing.T) {
+ type given struct {
+ link *edgev1alpha1.DeviceLink
+ fieldPath string
+ }
+ type expect struct {
+ ret []byte
+ err error
+ }
+
+ var targetObject = &edgev1alpha1.DeviceLink{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ Namespace: "default",
+ UID: "uid1",
+ Annotations: map[string]string{
+ "annotation-key-1": "v1",
+ "annotation-key-2": "v2",
+ },
+ Labels: map[string]string{
+ "lb1": "v1",
+ "lb2": "v2",
+ },
+ },
+ Status: edgev1alpha1.DeviceLinkStatus{
+ NodeName: "edge-worker",
+ NodeHostName: "test-node-1",
+ NodeInternalIP: "192.168.1.34",
+ },
+ }
+
+ var testCases = []struct {
+ given given
+ expect expect
+ }{
+ {
+ given: given{
+ link: targetObject.DeepCopy(),
+ fieldPath: "metadata.annotations",
+ },
+ expect: expect{
+ ret: []byte(`annotation-key-1="v1";annotation-key-2="v2"`),
+ err: nil,
+ },
+ },
+ {
+ given: given{
+ link: targetObject.DeepCopy(),
+ fieldPath: "metadata.name",
+ },
+ expect: expect{
+ ret: []byte(`test`),
+ err: nil,
+ },
+ },
+ {
+ given: given{
+ link: targetObject.DeepCopy(),
+ fieldPath: "metadata.labels['lb3']",
+ },
+ expect: expect{
+ ret: nil,
+ err: nil,
+ },
+ },
+ {
+ given: given{
+ link: targetObject.DeepCopy(),
+ fieldPath: `status.nodeName`,
+ },
+ expect: expect{
+ ret: nil,
+ err: errors.Errorf("unsupported fieldPath: status.nodeName"),
+ },
+ },
+ {
+ given: given{
+ link: targetObject.DeepCopy(),
+ fieldPath: `status.nodeHostName`,
+ },
+ expect: expect{
+ ret: []byte(`test-node-1`),
+ err: nil,
+ },
+ },
+ {
+ given: given{
+ link: targetObject.DeepCopy(),
+ fieldPath: `status.xxxx`,
+ },
+ expect: expect{
+ ret: nil,
+ err: errors.Errorf("unsupported fieldPath: status.xxxx"),
+ },
+ },
+ }
+
+ for i, tc := range testCases {
+ var actualBytes, actualErr = ExtractDeviceLinkFieldPathAsBytes(tc.given.link, tc.given.fieldPath)
+ if actualErr != nil {
+ if tc.expect.err != nil {
+ assert.EqualError(t, actualErr, tc.expect.err.Error(), "case %v", i+1)
+ } else {
+ assert.NoError(t, actualErr, "case %v ", i+1)
+ }
+ }
+ assert.Equal(t, actualBytes, tc.expect.ret, "case %v", i+1)
+ }
+}
diff --git a/pkg/util/fieldpath/object.go b/pkg/util/fieldpath/object.go
new file mode 100644
index 00000000..17b95005
--- /dev/null
+++ b/pkg/util/fieldpath/object.go
@@ -0,0 +1,84 @@
+package fieldpath
+
+// Borrowed from k8s.io/kubernetes/pkg/fieldpath/fieldpath.go
+
+import (
+ "strings"
+
+ "github.com/pkg/errors"
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/util/validation"
+
+ "github.com/rancher/octopus/pkg/util/collection"
+ "github.com/rancher/octopus/pkg/util/converter"
+)
+
+// ExtractObjectFieldPathAsBytes is extracts the field from the given Object
+// and returns it as a byte array.
+func ExtractObjectFieldPathAsBytes(obj runtime.Object, fieldPath string) ([]byte, error) {
+ var accessor, err = meta.Accessor(obj)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to access obj: %v", obj)
+ }
+
+ var str string
+ switch fieldPath {
+ case "metadata.annotations":
+ str = collection.FormatStringMap(accessor.GetAnnotations(), ";")
+ case "metadata.labels":
+ str = collection.FormatStringMap(accessor.GetLabels(), ";")
+ case "metadata.name":
+ str = accessor.GetName()
+ case "metadata.namespace":
+ str = accessor.GetNamespace()
+ case "metadata.uid":
+ str = string(accessor.GetUID())
+ default:
+ var path, subscript, ok = splitMaybeSubscriptedPath(fieldPath)
+ if !ok {
+ return nil, errors.Errorf("unsupported fieldPath: %s", fieldPath)
+ }
+ switch path {
+ case "metadata.annotations":
+ if errs := validation.IsQualifiedName(strings.ToLower(subscript)); len(errs) != 0 {
+ return nil, errors.Errorf("invalid key subscript in %s: %s", fieldPath, strings.Join(errs, ";"))
+ }
+ str = accessor.GetAnnotations()[subscript]
+ case "metadata.labels":
+ if errs := validation.IsQualifiedName(subscript); len(errs) != 0 {
+ return nil, errors.Errorf("invalid key subscript in %s: %s", fieldPath, strings.Join(errs, ";"))
+ }
+ str = accessor.GetLabels()[subscript]
+ default:
+ return nil, errors.Errorf("fieldPath %s doesn't support subscript", fieldPath)
+ }
+ }
+ return converter.UnsafeStringToBytes(str), nil
+}
+
+// splitMaybeSubscriptedPath checks whether the specified fieldPath is
+// subscripted, and
+// - if yes, this function splits the fieldPath into path and subscript, and
+// returns (path, subscript, true).
+// - if no, this function returns (fieldPath, "", false).
+//
+// Example inputs and outputs:
+// - "metadata.annotations['myKey']" --> ("metadata.annotations", "myKey", true)
+// - "metadata.annotations['a[b]c']" --> ("metadata.annotations", "a[b]c", true)
+// - "metadata.labels['']" --> ("metadata.labels", "", true)
+// - "metadata.labels" --> ("metadata.labels", "", false)
+func splitMaybeSubscriptedPath(fieldPath string) (string, string, bool) {
+ if !strings.HasSuffix(fieldPath, "']") {
+ return fieldPath, "", false
+ }
+ s := strings.TrimSuffix(fieldPath, "']")
+ parts := strings.SplitN(s, "['", 2)
+ if len(parts) < 2 {
+ return fieldPath, "", false
+ }
+ if len(parts[0]) == 0 {
+ return fieldPath, "", false
+ }
+ return parts[0], parts[1], true
+}
diff --git a/pkg/util/fieldpath/object_test.go b/pkg/util/fieldpath/object_test.go
new file mode 100644
index 00000000..079ff498
--- /dev/null
+++ b/pkg/util/fieldpath/object_test.go
@@ -0,0 +1,112 @@
+package fieldpath
+
+import (
+ "testing"
+
+ "github.com/pkg/errors"
+ "github.com/stretchr/testify/assert"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+
+ edgev1alpha1 "github.com/rancher/octopus/api/v1alpha1"
+)
+
+func TestExtractObjectFieldPathAsBytes(t *testing.T) {
+ type given struct {
+ obj runtime.Object
+ fieldPath string
+ }
+ type expect struct {
+ ret []byte
+ err error
+ }
+
+ var targetObject = &edgev1alpha1.DeviceLink{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ Namespace: "default",
+ UID: "uid1",
+ Annotations: map[string]string{
+ "annotation-key-1": "v1",
+ "annotation-key-2": "v2",
+ },
+ Labels: map[string]string{
+ "lb1": "v1",
+ "lb2": "v2",
+ },
+ },
+ Status: edgev1alpha1.DeviceLinkStatus{
+ NodeName: "edge-worker",
+ NodeHostName: "test-node-1",
+ NodeInternalIP: "192.168.1.34",
+ },
+ }
+
+ var testCases = []struct {
+ given given
+ expect expect
+ }{
+ {
+ given: given{
+ obj: targetObject.DeepCopy(),
+ fieldPath: "metadata.labels",
+ },
+ expect: expect{
+ ret: []byte(`lb1="v1";lb2="v2"`),
+ err: nil,
+ },
+ },
+ {
+ given: given{
+ obj: targetObject.DeepCopy(),
+ fieldPath: `metadata.annotations['annotation-key-1']`,
+ },
+ expect: expect{
+ ret: []byte(`v1`),
+ err: nil,
+ },
+ },
+ {
+ given: given{
+ obj: targetObject.DeepCopy(),
+ fieldPath: `metadata.namespace`,
+ },
+ expect: expect{
+ ret: []byte(`default`),
+ err: nil,
+ },
+ },
+ {
+ given: given{
+ obj: targetObject.DeepCopy(),
+ fieldPath: `metadata.annotations['annotation-key-3']`,
+ },
+ expect: expect{
+ ret: nil,
+ err: nil,
+ },
+ },
+ {
+ given: given{
+ obj: targetObject.DeepCopy(),
+ fieldPath: `status.nodeName`,
+ },
+ expect: expect{
+ ret: nil,
+ err: errors.Errorf("unsupported fieldPath: status.nodeName"),
+ },
+ },
+ }
+
+ for i, tc := range testCases {
+ var actualBytes, actualErr = ExtractObjectFieldPathAsBytes(tc.given.obj, tc.given.fieldPath)
+ if actualErr != nil {
+ if tc.expect.err != nil {
+ assert.EqualError(t, actualErr, tc.expect.err.Error(), "case %v", i+1)
+ } else {
+ assert.NoError(t, actualErr, "case %v ", i+1)
+ }
+ }
+ assert.Equal(t, actualBytes, tc.expect.ret, "case %v", i+1)
+ }
+}
diff --git a/test/integration/limb/devicelink_controller_test.go b/test/integration/limb/devicelink_controller_test.go
index d509b238..9a45ef8a 100644
--- a/test/integration/limb/devicelink_controller_test.go
+++ b/test/integration/limb/devicelink_controller_test.go
@@ -10,6 +10,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
edgev1alpha1 "github.com/rancher/octopus/api/v1alpha1"
+ api "github.com/rancher/octopus/pkg/adaptor/api/v1alpha1"
status "github.com/rancher/octopus/pkg/status/devicelink"
"github.com/rancher/octopus/pkg/suctioncup/connection"
"github.com/rancher/octopus/pkg/util/model"
@@ -274,6 +275,6 @@ func (c fakeDummyConnection) Stop() error {
return nil
}
-func (c fakeDummyConnection) Send(parameters []byte, model *metav1.TypeMeta, device []byte) error {
+func (c fakeDummyConnection) Send([]byte, *metav1.TypeMeta, []byte, map[string]*api.ConnectRequestReferenceEntry) error {
return nil
}
diff --git a/test/util/content/raw_extension.go b/test/util/content/raw_extension.go
index 8d22e00e..3bb4f816 100644
--- a/test/util/content/raw_extension.go
+++ b/test/util/content/raw_extension.go
@@ -1,8 +1,9 @@
package content
import (
- jsoniter "github.com/json-iterator/go"
"k8s.io/apimachinery/pkg/runtime"
+
+ "github.com/rancher/octopus/pkg/util/converter"
)
func ToRawExtension(content interface{}) *runtime.RawExtension {
@@ -15,7 +16,6 @@ func ToRawExtension(content interface{}) *runtime.RawExtension {
case string:
return &runtime.RawExtension{Raw: []byte(t)}
default:
- bs, _ := jsoniter.Marshal(content)
- return &runtime.RawExtension{Raw: bs}
+ return &runtime.RawExtension{Raw: converter.TryMarshalJSON(content)}
}
}