Before you Begin
The Intel® Context Sensing SDK for Linux* is a Node.js*, Go*, and Python*-based framework supporting the collection, storage, sharing, analysis, and use of sensor information.
This getting started guide contains steps to set up the broker and Go framework supported by the SDK, then run a sample provided in the SDK.
Additionally, this document contains tutorials to create a simple provider, sample application using the provider, a microservice to run the application, and steps to run the microservice to publish events to the broker.
Every command or chunk of code can be copy-pasted directly from the document without any required modifications unless explicitly stated.
Requirements
Software
- OS: Ubuntu* 14.04 or 16.04
- Go: 1.8.3
- Docker*: 17.0.3
Network
- hub.docker.intel.com
- hub.docker.com
Getting Started
Setting up the Broker
There are two options to set up the broker:
- Dockerized: Using the context repo from hub.docker.intel.com(preferred)
- Non-dockerized: Using the context-broker-VERSION.tgz file
This document only covers the preferred Dockerized method.
The section assumes you have Docker already set up with Intel credentials. (Refer: Setting up Docker)
The broker requires a running instance of MongoDB*.
- Use Docker to pull the mongo image onto your machine:
docker pull mongo
- Create a container named
mymongodb
and run it for the very first time:docker run --name=mymongodb -d mongo
Note: For subsequent runs, use: docker start mymongodb
- Pull the broker image:
docker pull hub.docker.intel.com/context/context-broker:v0.10.5
- Create a container named
contextbroker
and run it for the very first time:docker run --name contextbroker -it -p 8888:8888 --link mymongodb -e MONGODB_HOST=mymongodb hub.docker.intel.com/context/context-broker:v0.10.5
Note: For subsequent runs, use: docker start -i contextbroker
-i
or -it
is used to run in the foreground to see the output in the current terminal.
To stop the context broker instance, use CTRL+C
to interrupt when running foreground or docker stop contextbroker
when running in background.
In order to remove the container if it's preventing the use of Docker, use: docker rm –f contextbroker
Setting up the SDK for Go
If you haven’t set up the required Go environment on your machine (Refer: Setting up the Go Environment)
Use the command go env
to ensure both $GOPATH
and $GOROOT
are populated with paths for Go projects and Go distribution, respectively.
- Download the Go X Net Package:
go get golang.org/x/net
- Download the Logrus* package:
go get github.com/sirupsen/logrus
Note: In some cases you may encounter the error 'can't load package: package golang.org/x/net: no buildable Go source files in $GOPATH/src/golang.org/x/net'. Verify your setup by checking if $GOPATH/src/golang.org/x/net
actually contains items from https://github.com/golang/net repo.
- Copy the
context_linux_go
directory from the extracted release package to the$GOPATH/src
directory.
Running an SDK Sample
Make sure a broker instance is running.
- To run the local_ticktock sample, navigate to the
$GOPATH/context_linux_go/samples/local_ticktock
directory and enter:go run main.go
Note: All the providers and samples provided in the SDK can be found in the $GOPATH/context_linux_go/providers
and $GOPATH/context_linux_go/samples
directories respectively.
Tutorials
Creating a Simple Provider
- Create a directory named
simpleprovider
in the$GOPATH/context_linux_go/providers
directory. - Create a file named
simpleprovider.go
inside the directory.
createItem
function being specific to this tutorial. - Encapsulate all the contents of the provider in a package:
package simpleprovider
- Import the required packages, time and core:
import ( "context_linux_go/core" "time" )
- Declare a constant identifier that other providers can use to identify the data coming from our simpleprovider:
const ( // SimpleProviderType is the URN for a data from this provider SimpleProviderType string = "urn:x-intel:context:thing:simpleprovider" )
- Define a schema to register with the broker.
This will enable the broker to identify the unique identifier and perform necessary schema validation:// SimpleProviderSchema schema satisfied by this provider, the value is placed in the “data" var SimpleProviderSchema = core.JSONSchema{ "type": SimpleProviderType, "schema": core.JSONSchema{ "type": "object", "properties": core.JSONSchema{ "data ": core.JSONSchema{ "type": "string", }, }, }, "descriptions": core.JSONSchema{ "en": core.JSONSchema{ "documentation": "Simple string producer", "short_name": "SimpleString", }, }, }
- Define a struct that holds an instance of the provider.
We will use thestopChan
variable to start/stop the provider and also provide a reference to the additional options that the provider can accept:// Provider holds an instance of the simple provider. // Methods defined for this type must implement core.ProviderInterface type Provider struct { ticker *time.Ticker stopChan chan bool options *Options }
- Define the options for this provider.
We will supply the time interval after which the string should be published:// Options that are provider specific type Options struct { core.ProviderOptions Period int // Period of ticking in milliseconds }
- We can supply multiple URN identifiers in a single provider. Define a static function to return all the types supported in this provider:
// Types is a static function that returns the types this Provider supports (URN and schema) func Types() []core.ProviderType { return []core.ProviderType{ core.ProviderType{URN: SimpleProviderType, Schema: SimpleProviderSchema}} }
- Define a function that can return the
Types
supported:// Types is a provider specific function that queries the type of an ProviderInterface instance func (p *Provider) Types() []core.ProviderType { return Types() }
- Define the New function, which can set options called from our sample:
// New creates a new simpleprovider.Provider with the specified options func New(options *Options) *Provider { var dp Provider dp.options = options dp.stopChan = make(chan bool) return &dp }
- Implement the Start function. In this email, we'll supply the
ItemData
to publish and also decide when to publish with the help of the ticker:// Start begins producing events on the item and error channels func (p *Provider) Start(onItem core.ProviderItemChannel, onErr core.ErrorChannel) { p.ticker = time.NewTicker(time.Millisecond * time.Duration(p.options.Period)) go func() { for { select { case <-p.ticker.C: onItem <- p.createItem() case <-p.stopChan: close(onItem) close(onErr) return } } }() }
- Implement the createItem function. This function populates the
ItemData
with our string:// Generates a new simple provider item func (p *Provider) createItem() *core.ItemData { var item = core.ItemData{ Type: SimpleProviderType, // Value map must match the schema Value: map[string]interface{}{"data": "Hello World"}, } return &item }
- Implement the
GetItem
function:// GetItem returns a new simple provider item. Returns nil if itemType is not recognized func (p *Provider) GetItem(itemType string) *core.ItemData { if itemType != SimpleProviderType { return nil } return p.createItem() }
- Implement the
Stop
function to stop producing items:func (p *Provider) Stop() { p.stopChan <- true if p.ticker != nil { p.ticker.Stop() } }
- We must implement the
GetOptions
function to return a pointer toProviderOptions
in the Sensing core:// GetOptions returns a pointer to the core options for use within the Sensing core func (p *Provider) GetOptions() *core.ProviderOptions { return &p.options.ProviderOptions }
Creating a Sample Application Utilizing a Provider
- Create a directory named
simpleProviderSample
in the$GOPATH/context_linux_go/samples
directory.
- Create a file named
main.go
inside the directory.
In the following steps, you'll be adding code to the main.go file:
- Encapsulate all the contents of our sample in a package:
package main
- Import the required package: core, sensing, our simpleprovider, and fmt (to print to the terminal):
import ( "context_linux_go/core""context_linux_go/core/sensing""context_linux_go/providers/simpleprovider""fmt" )
- Implement the main function. We will supply the channels for onStart, onError, and onItem from the context core:
func main() { onStart := make(core.SensingStartedChannel, 5) onError := make(core.ErrorChannel, 5) onItem := make(core.ProviderItemChannel, 5)
- Supply the provider options in the main function for the sensing core such as broker ipAddress and port, an indicator to publish to the broker, the name of our sample application, onStart, and onError:
options := core.SensingOptions{ Server: "localhost:8888", Publish: true, Application: "go_simpleprovider_application", OnStarted: onStart, OnError: onError, }
- Create a new instance of Sensing and provide the sensing options in the main function:
sensing := sensing.NewSensing() sensing.Start(options)
- Create an instance of the simpleprovider and supply the time period in the provider options in the main function:
spProvider := simpleprovider.New(&simpleprovider.Options{Period: 1000, ProviderOptions: core.ProviderOptions{Publish: true}})
Note: The above line is a single line of code.
- Enable sensing and provide a reference to our provider instance. In this example, we'll print the URN type and actual data every time our provider generates ItemData. We'll stop our provider if any error is detected.
for { select { case <-onStart: fmt.Println("Started sensing") sensing.EnableSensing(spProvider, onItem, onError) case item := <-onItem: fmt.Println(item.Type, item.Value) case err := <-onError: fmt.Println("Error", err) sensing.Stop() return } } } //end of main function
Creating a Microservice
We can encapsulate an application and other dependencies inside a single service using Docker.
Dockerizing our application helps to secure the implementation (source code) and dynamically configure connections to other services, such as the broker, without modifying the source code on host machines.
- Create a file named
SimpleProviderDockerfile
in the$GOPATH/context_linux_go
directory. You'll be editing this file in the steps below.Note: There is no extension in the name of the file.
- Provide the dependencies required by the SDK, as well as the Intel proxy information:
FROM golang:1.8.3-alpine3.5 RUN mkdir /app ADD ./samples /app/ ADD . /go/src/context_linux_go/ ENV http_proxy=http://proxy-chain.intel.com:911 ENV https_proxy=http://proxy-chain.intel.com:912 RUN apk add --no-cache git \ && go get golang.org/x/net/websocket \ && go get github.com/sirupsen/logrus \ && apk del git WORKDIR /app/.
- Provide a name (
simple_provider_client
) and a path to our sample application (simpleProviderSample/main.go
), then run the sample application:RUN go build -o simple_provider_client simpleProviderSample/main.go CMD ["./simple_provider_client"]
Running your micro service
Ensure the broker is running on your machine (Refer: Setting up the Broker).
- Build the image locally with a tag using the Docker file.
docker build --tag smp:latest -f SimpleProviderDockerfile .
Note: The DOT at the end is required in the above command.
- Create a container named
smp
, tagged aslatest
. Run the container for the very first time:docker run --name=smp --network host -e http_proxy=”” -e https_proxy=”” smp:latest
Note: For subsequent runs use:
docker start -i smp
-i or -it is used to run in the foreground to see the output in the current terminal.
To stop the smp instance, use CTRL+C
to interrupt when running in the foreground or docker stop smp
when running in the background. In order to remove the container if it's preventing the use of Docker, use: docker rm –f smp
Miscellaneous
For your convenience, this section contains topics out of the scope of this document for your convenience, but that may be listed in the requirements.
Setting up Docker
If an install script was provided with this document, simply run it in the terminal: ./install_docker.sh
If not, below are steps that need to be completed to successfully install Docker:
- Follow the Docker manual installation instructions: https://docs.docker.com/engine/installation/linux/ubuntu/#install-using-the-repository
- If you are behind a corporate proxy ,you may need to set Docker's proxy and DNS settings: Proxy Instructions
- Determine your host machine's DNS servers:
nmcli dev show | grep 'IP4.DNS'
- Set up daemon.json with the 'dns' key and your DNS addresses:
Example: { "dns" : [ "10.0.0.2" , "8.8.8.8" ] }
- Add your user to the docker group:
sudo groupadd docker sudo gpasswd -a ${USER} docker sudo service docker restart newgrp docker
- Make sure you have access to hub.docker.intel.com by trying to log in in the web portal: https://hub.docker.intel.com
- Associate Docker on your machine with your user account:
docker login hub.docker.intel.com
Setting up the Go Environment
- Fetch the Golang distribution package:
wget -c https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz
- Extract the contents:
sudo tar -C /usr/local -xvzf go1.8.3.linux-amd64.tar.gz
- Append the below line into your
.bashrc
file, usually located at$Home/.bashrc
export PATH=$PATH:/usr/local/go/bin
- Apply the changes to the current session:
source ~/.bashrc
Accessing Go Documentation in your Browser
Access the Go documentation for the SDK from your browser to view additional API information and samples that are not demonstrated in this document.
- Navigate to
$GOPATH/context_linux_go
and enter:godoc -http=:6060
- In a web browser, enter the URL:
http://localhost:6060/
- Click on Packages from the menu on top of the webpage.
You should now be able to view the documentation contents of context_linux_go
under standard packages section of the webpage.