Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement onboard/offboard device to/from cloud #4

Merged
merged 5 commits into from
May 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,22 @@ language: minimal
services:
- docker

install:
install:
- if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6';
fi
- docker build . --network=host -t sdk:build
- docker build . -f Dockerfile.device-simulator --network=host -t sdk:device-simulator
- docker run -d --network=host sdk:device-simulator /device-simulator
- sleep 2
after_failure:
- docker logs $(docker ps -a -q --filter ancestor=sdk:device-simulator --format="{{.ID}}")

jobs:
include:
- stage: test
if: type == pull_request
script:
script:
- docker run --network=host sdk:build go test ./...

- stage: test_and_cover
Expand Down
9 changes: 9 additions & 0 deletions Dockerfile.device-simulator
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM ubuntu:18.04 AS device-simulator

RUN apt-get update
RUN apt-get install -y git gcc make
RUN git clone --recursive https://github.com/iotivity/iotivity-lite.git
RUN make -C /iotivity-lite/port/linux CLOUD=1 SECURE=0 DEBUG=1 cloud_linux
RUN cp /iotivity-lite/port/linux/cloud_linux /device-simulator

ENTRYPOINT ["/device-simulator"]
2 changes: 1 addition & 1 deletion local/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ var testCfg = ocf.Config{
Protocol: "tcp",
Resource: resource.Config{
ResourceHrefExpiration: time.Hour,
DiscoveryTimeout: time.Second,
DiscoveryTimeout: time.Second * 3,
DiscoveryDelay: 100 * time.Millisecond,

Errors: func(error) {},
Expand Down
6 changes: 3 additions & 3 deletions local/device/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ func (it *QueryResourceIterator) Next(ctx context.Context, v interface{}) bool {
return true
}

// DeviceID
func (c *Client) DeviceID() {
return
// DeviceID returns id of device
func (c *Client) DeviceID() string {
return c.links.ID
}

// GetResourceLinks returns all resource links.
Expand Down
14 changes: 14 additions & 0 deletions local/getResource.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
gocoap "github.com/go-ocf/go-coap"
"github.com/go-ocf/kit/codec/coap"
"github.com/go-ocf/sdk/local/resource"
"github.com/go-ocf/sdk/schema"
)

// coapContentFormat values can be found here
Expand Down Expand Up @@ -39,6 +40,19 @@ func (c *Client) GetResourceCBOR(
return nil
}

func (c *Client) GetResourceDiscovery(
ctx context.Context,
deviceID string,
response *[]schema.DeviceLinks,
) error {
codec := resource.DiscoveryResourceCodec{}
err := c.getResource(ctx, deviceID, "/oic/res", "", codec, response)
if err != nil {
return err
}
return nil
}

func (c *Client) getResource(
ctx context.Context,
deviceID, href string,
Expand Down
16 changes: 16 additions & 0 deletions local/offboardDevice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package local

import (
"context"
"fmt"
)

func (c *Client) OffboardDevice(
ctx context.Context,
deviceID string,
) error {
const errMsg = "cannot offboard device %v: %v"
return c.onboardOffboardDevice(ctx, deviceID, "", "", "", func(err error) error {
return fmt.Errorf(errMsg, deviceID, err)
})
}
37 changes: 37 additions & 0 deletions local/offboardDevice_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package local_test

import (
"context"
"testing"
"time"

ocf "github.com/go-ocf/sdk/local"
"github.com/stretchr/testify/require"
)

func TestClient_OffboardDevice(t *testing.T) {
testCfg.Protocol = "tcp"
testCfg.Resource.DiscoveryTimeout = time.Second * 3

c, err := ocf.NewClientFromConfig(testCfg, nil)
require := require.New(t)
require.NoError(err)

timeout, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
h := testOnboardDeviceHandler{}
err = c.GetDevices(timeout, []string{"oic.d.cloudDevice"}, &h)
require.NoError(err)
deviceIds := h.PopDeviceIds()
require.NotEmpty(deviceIds)

func() {
for deviceId, _ := range deviceIds {
timeout, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err = c.OffboardDevice(timeout, deviceId)
require.NoError(err)
}
}()

}
71 changes: 71 additions & 0 deletions local/onboardDevice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package local

import (
"context"
"fmt"

"github.com/go-ocf/sdk/schema"
)

func (c *Client) onboardOffboardDevice(
ctx context.Context,
deviceID, authorizationProvider, authorizationCode, url string, errFunc func(err error) error,
) error {
switch {
case deviceID == "":
return errFunc(fmt.Errorf("invalid deviceID"))
}

var devices []schema.DeviceLinks
err := c.GetResourceDiscovery(ctx, deviceID, &devices)
if err != nil {
return errFunc(err)
}

cloudResourceHref := ""
Loop:
for _, device := range devices {
for _, link := range device.Links {
for _, resType := range link.ResourceTypes {
if resType == schema.CloudResourceType {
cloudResourceHref = link.Href
break Loop
}
}
}
}

if cloudResourceHref == "" {
return errFunc(fmt.Errorf("cloud resource not found"))
}

req := schema.CloudUpdateRequest{
AuthorizationProvider: authorizationProvider,
AuthorizationCode: authorizationCode,
URL: url,
}
var resp schema.CloudResponse
err = c.UpdateResourceCBOR(ctx, deviceID, cloudResourceHref, "", req, &resp)
if err != nil {
return errFunc(err)
}
return nil
}

func (c *Client) OnboardDevice(
ctx context.Context,
deviceID, authorizationProvider, authorizationCode, url string,
) error {
const errMsg = "cannot onboard device %v: %v"
switch {
case authorizationProvider == "":
return fmt.Errorf(errMsg, deviceID, "invalid authorizationProvider")
case authorizationCode == "":
return fmt.Errorf(errMsg, deviceID, "invalid authorizationCode")
case url == "":
return fmt.Errorf(errMsg, deviceID, "invalid url")
}
return c.onboardOffboardDevice(ctx, deviceID, authorizationProvider, authorizationCode, url, func(err error) error {
return fmt.Errorf(errMsg, deviceID, err)
})
}
114 changes: 114 additions & 0 deletions local/onboardDevice_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package local_test

import (
"context"
"sync"
"testing"
"time"

ocf "github.com/go-ocf/sdk/local"
"github.com/go-ocf/sdk/local/device"
"github.com/stretchr/testify/require"
)

type testOnboardDeviceHandler struct {
lock sync.Mutex
deviceIds map[string]bool
}

func (h *testOnboardDeviceHandler) Handle(ctx context.Context, client *device.Client) {
h.lock.Lock()
defer h.lock.Unlock()
if h.deviceIds == nil {
h.deviceIds = make(map[string]bool)
}
h.deviceIds[client.DeviceID()] = true
}

func (h *testOnboardDeviceHandler) PopDeviceIds() map[string]bool {
h.lock.Lock()
defer h.lock.Unlock()
tmp := h.deviceIds
h.deviceIds = nil
return tmp
}

func (h *testOnboardDeviceHandler) Error(err error) {
}

func TestClient_OnboardDevice(t *testing.T) {
type args struct {
authorizationProvider string
authorizationCode string
url string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "invalid authorizationProvider",
args: args{
authorizationCode: "b",
url: "c",
},
wantErr: true,
},
{
name: "invalid authorizationCode",
args: args{
authorizationProvider: "a",
url: "c",
},
wantErr: true,
},
{
name: "invalid url",
args: args{
authorizationProvider: "a",
authorizationCode: "b",
},
wantErr: true,
},
{
name: "valid",
args: args{
authorizationProvider: "a",
authorizationCode: "b",
url: "c",
},
},
}

testCfg.Protocol = "tcp"
testCfg.Resource.DiscoveryTimeout = time.Second * 3

c, err := ocf.NewClientFromConfig(testCfg, nil)
require := require.New(t)
require.NoError(err)

timeout, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
h := testOnboardDeviceHandler{}
err = c.GetDevices(timeout, []string{"oic.d.cloudDevice"}, &h)
require.NoError(err)
deviceIds := h.PopDeviceIds()
require.NotEmpty(deviceIds)

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for deviceId, _ := range deviceIds {
timeout, cancel := context.WithTimeout(context.Background(), 10*time.Second)
err := c.OnboardDevice(timeout, deviceId, tt.args.authorizationProvider, tt.args.authorizationCode, tt.args.url)
cancel()
if tt.wantErr {
require.Error(err)
} else {
require.NoError(err)
}

}
})
}
}
6 changes: 4 additions & 2 deletions local/resource/discoverDevices.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,16 @@ func runDiscovery(
}

func handleResponse(ctx context.Context, handler DiscoveryHandler) func(req *gocoap.Request) {
codec := coap.CBORCodec{}
return func(req *gocoap.Request) {
if req.Msg.Code() != gocoap.Content {
handler.Error(fmt.Errorf("request failed: %s", coap.Dump(req.Msg)))
return
}

var devices []schema.DeviceLinks
if err := codec.Decode(req.Msg, &devices); err != nil {
var codec DiscoveryResourceCodec
err := codec.Decode(req.Msg, &devices)
if err != nil {
handler.Error(fmt.Errorf("decoding failed: %v: %s", err, coap.DumpHeader(req.Msg)))
return
}
Expand Down
Loading