fix: implement repository test
changes: - Implement repository test for the sqlite backend - Add testutils package to start container images - Remove deprecated till_date in measured values - Renamed columns of the table humidities, pressures and temperatures
This commit is contained in:
		
							
								
								
									
										146
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										146
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,6 +1,7 @@
 | 
				
			|||||||
# VERSION/RELEASE
 | 
					# VERSION/RELEASE
 | 
				
			||||||
# If no version is specified as a parameter of make, the last git hash
 | 
					# If no version is specified as a parameter of make, the last git hash
 | 
				
			||||||
# value is taken.
 | 
					# value is taken.
 | 
				
			||||||
 | 
					EPOCH=0
 | 
				
			||||||
VERSION?=$(shell git describe --abbrev=0)+$(shell date +'%Y%m%d%H%I%S')
 | 
					VERSION?=$(shell git describe --abbrev=0)+$(shell date +'%Y%m%d%H%I%S')
 | 
				
			||||||
RELEASE?=1
 | 
					RELEASE?=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -12,55 +13,6 @@ RELEASE?=1
 | 
				
			|||||||
GOPROXY?=$(shell go env GOPROXY)
 | 
					GOPROXY?=$(shell go env GOPROXY)
 | 
				
			||||||
GOPRIVATE?=$(shell go env GOPRIVATE)
 | 
					GOPRIVATE?=$(shell go env GOPRIVATE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# EXECUTABLE
 | 
					 | 
				
			||||||
# Executable binary which should be compiled for different architecures
 | 
					 | 
				
			||||||
EXECUTABLE:=flucky
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# DARWIN_EXECUTABLES AND TARGETS
 | 
					 | 
				
			||||||
DARWIN_EXECUTABLES:=\
 | 
					 | 
				
			||||||
	darwin/386/${EXECUTABLE} \
 | 
					 | 
				
			||||||
	darwin/amd64/${EXECUTABLE}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DARWIN_EXECUTABLE_TARGETS:=\
 | 
					 | 
				
			||||||
	${DARWIN_EXECUTABLES:%=bin/%}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# FREEBSD_EXECUTABLES AND TARGETS
 | 
					 | 
				
			||||||
FREEBSD_EXECUTABLES:=\
 | 
					 | 
				
			||||||
	freebsd/amd64/${EXECUTABLE}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
FREEBSD_EXECUTABLE_TARGETS:=\
 | 
					 | 
				
			||||||
	${FREEBSD_EXECUTABLES:%=bin/%}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# LINUX_EXECUTABLES AND TARGETS
 | 
					 | 
				
			||||||
LINUX_EXECUTABLES:=\
 | 
					 | 
				
			||||||
	linux/amd64/${EXECUTABLE} \
 | 
					 | 
				
			||||||
	linux/386/${EXECUTABLE} \
 | 
					 | 
				
			||||||
	linux/arm/5/${EXECUTABLE} \
 | 
					 | 
				
			||||||
	linux/arm/7/${EXECUTABLE}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
LINUX_EXECUTABLE_TARGETS:=\
 | 
					 | 
				
			||||||
	${LINUX_EXECUTABLES:%=bin/%}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# UNIX_EXECUTABLES AND TARGETS
 | 
					 | 
				
			||||||
# Define all executables for different architectures and operation systems
 | 
					 | 
				
			||||||
UNIX_EXECUTABLES:= \
 | 
					 | 
				
			||||||
	${DARWIN_EXECUTABLES} \
 | 
					 | 
				
			||||||
	${FREEBSD_EXECUTABLES} \
 | 
					 | 
				
			||||||
	${LINUX_EXECUTABLES}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
UNIX_EXECUTABLE_TARGETS:= \
 | 
					 | 
				
			||||||
	${DARWIN_EXECUTABLE_TARGETS} \
 | 
					 | 
				
			||||||
	${FREEBSD_EXECUTABLE_TARGETS} \
 | 
					 | 
				
			||||||
	${LINUX_EXECUTABLE_TARGETS}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# EXECUTABLES AND TARGETS
 | 
					 | 
				
			||||||
# Include all UNIX and Windows targets.
 | 
					 | 
				
			||||||
EXECUTABLES:=\
 | 
					 | 
				
			||||||
	${UNIX_EXECUTABLES}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
EXECUTABLE_TARGETS:= \
 | 
					 | 
				
			||||||
	${UNIX_EXECUTABLE_TARGETS}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# CONTAINER_RUNTIME
 | 
					# CONTAINER_RUNTIME
 | 
				
			||||||
# The CONTAINER_RUNTIME variable will be used to specified the path to a
 | 
					# The CONTAINER_RUNTIME variable will be used to specified the path to a
 | 
				
			||||||
# container runtime. This is needed to start and run a container images.
 | 
					# container runtime. This is needed to start and run a container images.
 | 
				
			||||||
@@ -96,48 +48,15 @@ CONTAINER_IMAGE_SHORT=${CONTAINER_IMAGE_NAMESPACE}/${CONTAINER_IMAGE_NAME}:${CON
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# BINARIES
 | 
					# BINARIES
 | 
				
			||||||
# ==============================================================================
 | 
					# ==============================================================================
 | 
				
			||||||
# current os
 | 
					EXECUTABLE=flucky
 | 
				
			||||||
 | 
					EXECUTABLE_TARGETS:= \
 | 
				
			||||||
 | 
						bin/linux/amd64/${EXECUTABLE} \
 | 
				
			||||||
 | 
						bin/linux/arm/5/${EXECUTABLE} \
 | 
				
			||||||
 | 
						bin/linux/arm/7/${EXECUTABLE} \
 | 
				
			||||||
 | 
						bin/tmp/${EXECUTABLE}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
${EXECUTABLE}: bin/tmp/${EXECUTABLE}
 | 
					${EXECUTABLE}: bin/tmp/${EXECUTABLE}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# build all binaries
 | 
					 | 
				
			||||||
PHONY:=all
 | 
					 | 
				
			||||||
all: ${EXECUTABLE_TARGETS}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# darwin os
 | 
					 | 
				
			||||||
bin/darwin/386/${EXECUTABLE}: bindata
 | 
					 | 
				
			||||||
	CGO_ENABLED=1 \
 | 
					 | 
				
			||||||
	GOOS=darwin \
 | 
					 | 
				
			||||||
	GOARCH=386 \
 | 
					 | 
				
			||||||
	GOPROXY=${GOPROXY} \
 | 
					 | 
				
			||||||
	GOPRIVATE=${GOPRIVATE} \
 | 
					 | 
				
			||||||
		go build -ldflags "-X main.version=${VERSION:v%=%}" -o ${@}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bin/darwin/amd64/${EXECUTABLE}: bindata
 | 
					 | 
				
			||||||
	CGO_ENABLED=1 \
 | 
					 | 
				
			||||||
	GOOS=darwin \
 | 
					 | 
				
			||||||
	GOARCH=amd64 \
 | 
					 | 
				
			||||||
	GOPROXY=${GOPROXY} \
 | 
					 | 
				
			||||||
	GOPRIVATE=${GOPRIVATE} \
 | 
					 | 
				
			||||||
		go build -ldflags "-X main.version=${VERSION:v%=%}" -o ${@}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# freebsd os
 | 
					 | 
				
			||||||
bin/freebsd/amd64/${EXECUTABLE}: bindata
 | 
					 | 
				
			||||||
	CGO_ENABLED=1 \
 | 
					 | 
				
			||||||
	GOOS=freebsd \
 | 
					 | 
				
			||||||
	GOARCH=amd64 \
 | 
					 | 
				
			||||||
	GOPROXY=${GOPROXY} \
 | 
					 | 
				
			||||||
	GOPRIVATE=${GOPRIVATE} \
 | 
					 | 
				
			||||||
		go build -ldflags "-X main.version=${VERSION:v%=%}" -o ${@}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# linux os
 | 
					 | 
				
			||||||
bin/linux/386/${EXECUTABLE}: bindata
 | 
					 | 
				
			||||||
	CGO_ENABLED=1 \
 | 
					 | 
				
			||||||
	GOOS=linux \
 | 
					 | 
				
			||||||
	GOARCH=386 \
 | 
					 | 
				
			||||||
	GOPROXY=${GOPROXY} \
 | 
					 | 
				
			||||||
	GOPRIVATE=${GOPRIVATE} \
 | 
					 | 
				
			||||||
		go build -ldflags "-X main.version=${VERSION:v%=%}" -o ${@}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bin/linux/amd64/${EXECUTABLE}: bindata
 | 
					bin/linux/amd64/${EXECUTABLE}: bindata
 | 
				
			||||||
	CGO_ENABLED=1 \
 | 
						CGO_ENABLED=1 \
 | 
				
			||||||
	GOOS=linux \
 | 
						GOOS=linux \
 | 
				
			||||||
@@ -181,31 +100,26 @@ bindata: clean ${BINDATA_TARGETS}
 | 
				
			|||||||
pkg/repository/db/bindataSQL.go:
 | 
					pkg/repository/db/bindataSQL.go:
 | 
				
			||||||
	go-bindata -pkg db -o ./pkg/repository/db/bindataSQL.go pkg/repository/db/postgres/*** pkg/repository/db/sqlite3/***
 | 
						go-bindata -pkg db -o ./pkg/repository/db/bindataSQL.go pkg/repository/db/postgres/*** pkg/repository/db/sqlite3/***
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# TEST
 | 
					# CLEAN
 | 
				
			||||||
# ==============================================================================
 | 
					 | 
				
			||||||
PHONY+=test/unit
 | 
					 | 
				
			||||||
test/unit: clean bindata
 | 
					 | 
				
			||||||
	go test -v -race -coverprofile=coverage.txt -covermode=atomic $(shell go list ./... | grep -v db)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# PACKAGES
 | 
					 | 
				
			||||||
# ==============================================================================
 | 
					 | 
				
			||||||
%.rpm: %
 | 
					 | 
				
			||||||
	rpm-builder \
 | 
					 | 
				
			||||||
		--exec-file "$<:/usr/bin/${EXECUTABLE}" \
 | 
					 | 
				
			||||||
		--dir "systemd:/usr/lib/systemd/system" \
 | 
					 | 
				
			||||||
		--license "Apache 2.0" \
 | 
					 | 
				
			||||||
		--version ${VERSION:v%=%} \
 | 
					 | 
				
			||||||
		--release ${RELEASE} \
 | 
					 | 
				
			||||||
		--out ${@} \
 | 
					 | 
				
			||||||
		${EXECUTABLE}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# OTHER STUFF
 | 
					 | 
				
			||||||
# ==============================================================================
 | 
					# ==============================================================================
 | 
				
			||||||
PHONY+=clean
 | 
					PHONY+=clean
 | 
				
			||||||
clean:
 | 
					clean:
 | 
				
			||||||
	rm --force ${BINDATA_TARGETS} || true
 | 
						rm --force ${BINDATA_TARGETS} || true
 | 
				
			||||||
	rm --force --recursive bin/ || true
 | 
						rm --force --recursive bin/ || true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# TEST
 | 
				
			||||||
 | 
					# ==============================================================================
 | 
				
			||||||
 | 
					PHONY+=test/unit
 | 
				
			||||||
 | 
					test/unit: bindata
 | 
				
			||||||
 | 
						go test -v -race -coverprofile=coverage.txt -covermode=atomic -timeout 600s -count=1 ./pkg/...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PHONY+=test/integration
 | 
				
			||||||
 | 
					test/integration:
 | 
				
			||||||
 | 
						go test -v -count=1 -timeout 600s ./it/...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test/coverage: test/unit
 | 
				
			||||||
 | 
						go tool cover -html=coverage.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# CONTAINER IMAGE STEPS
 | 
					# CONTAINER IMAGE STEPS
 | 
				
			||||||
# ==============================================================================
 | 
					# ==============================================================================
 | 
				
			||||||
PHONY+=container-image/build/amd64
 | 
					PHONY+=container-image/build/amd64
 | 
				
			||||||
@@ -243,8 +157,8 @@ PHONY+=container-run/all
 | 
				
			|||||||
container-run/all:
 | 
					container-run/all:
 | 
				
			||||||
	$(MAKE) container-run COMMAND=${@:container-run/%=%}
 | 
						$(MAKE) container-run COMMAND=${@:container-run/%=%}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PHONY+=${UNIX_EXECUTABLE_TARGETS:%=container-run/%}
 | 
					PHONY+=${EXECUTABLE_TARGETS:%=container-run/%}
 | 
				
			||||||
${UNIX_EXECUTABLE_TARGETS:%=container-run/%}:
 | 
					${EXECUTABLE_TARGETS:%=container-run/%}:
 | 
				
			||||||
	$(MAKE) container-run COMMAND=${@:container-run/%=%}
 | 
						$(MAKE) container-run COMMAND=${@:container-run/%=%}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# CONTAINER STEPS - GO-BINDATA
 | 
					# CONTAINER STEPS - GO-BINDATA
 | 
				
			||||||
@@ -253,18 +167,18 @@ PHONY+=container-run/bindata
 | 
				
			|||||||
container-run/bindata:
 | 
					container-run/bindata:
 | 
				
			||||||
	$(MAKE) container-run COMMAND=${@:container-run/%=%}
 | 
						$(MAKE) container-run COMMAND=${@:container-run/%=%}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# CONTAINER STEPS - CLEAN
 | 
				
			||||||
 | 
					# ==============================================================================
 | 
				
			||||||
 | 
					PHONY+=container-run/clean
 | 
				
			||||||
 | 
					container-run/clean:
 | 
				
			||||||
 | 
						$(MAKE) container-run COMMAND=${@:container-run/%=%}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# CONTAINER STEPS - TEST
 | 
					# CONTAINER STEPS - TEST
 | 
				
			||||||
# ==============================================================================
 | 
					# ==============================================================================
 | 
				
			||||||
PHONY+=container-run/test/unit
 | 
					PHONY+=container-run/test/unit
 | 
				
			||||||
container-run/test/unit:
 | 
					container-run/test/unit:
 | 
				
			||||||
	$(MAKE) container-run COMMAND=${@:container-run/%=%}
 | 
						$(MAKE) container-run COMMAND=${@:container-run/%=%}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# CONTAINER STEPS - OTHER STUF
 | 
					 | 
				
			||||||
# ==============================================================================
 | 
					 | 
				
			||||||
PHONY+=container-run/clean
 | 
					 | 
				
			||||||
container-run/clean:
 | 
					 | 
				
			||||||
	$(MAKE) container-run COMMAND=${@:container-run/%=%}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# GENERAL CONTAINER COMMAND
 | 
					# GENERAL CONTAINER COMMAND
 | 
				
			||||||
# ==============================================================================
 | 
					# ==============================================================================
 | 
				
			||||||
PHONY+=container-run
 | 
					PHONY+=container-run
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								go.mod
									
									
									
									
									
								
							@@ -7,9 +7,14 @@ require (
 | 
				
			|||||||
	github.com/d2r2/go-bsbmp v0.0.0-20190515110334-3b4b3aea8375
 | 
						github.com/d2r2/go-bsbmp v0.0.0-20190515110334-3b4b3aea8375
 | 
				
			||||||
	github.com/d2r2/go-i2c v0.0.0-20191123181816-73a8a799d6bc
 | 
						github.com/d2r2/go-i2c v0.0.0-20191123181816-73a8a799d6bc
 | 
				
			||||||
	github.com/d2r2/go-logger v0.0.0-20181221090742-9998a510495e
 | 
						github.com/d2r2/go-logger v0.0.0-20181221090742-9998a510495e
 | 
				
			||||||
 | 
						github.com/docker/distribution v2.7.1+incompatible // indirect
 | 
				
			||||||
 | 
						github.com/docker/docker v1.13.1
 | 
				
			||||||
 | 
						github.com/docker/go-connections v0.4.0
 | 
				
			||||||
 | 
						github.com/docker/go-units v0.4.0 // indirect
 | 
				
			||||||
	github.com/go-flucky/go-dht v0.1.1
 | 
						github.com/go-flucky/go-dht v0.1.1
 | 
				
			||||||
	github.com/lib/pq v1.4.0
 | 
						github.com/lib/pq v1.4.0
 | 
				
			||||||
	github.com/mattn/go-sqlite3 v1.10.0
 | 
						github.com/mattn/go-sqlite3 v1.10.0
 | 
				
			||||||
 | 
						github.com/opencontainers/go-digest v1.0.0 // indirect
 | 
				
			||||||
	github.com/satori/go.uuid v1.2.0
 | 
						github.com/satori/go.uuid v1.2.0
 | 
				
			||||||
	github.com/spf13/cobra v1.0.0
 | 
						github.com/spf13/cobra v1.0.0
 | 
				
			||||||
	github.com/stretchr/testify v1.5.1
 | 
						github.com/stretchr/testify v1.5.1
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								go.sum
									
									
									
									
									
								
							@@ -30,9 +30,16 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 | 
				
			|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
					github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
				
			||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 | 
					github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 | 
				
			||||||
 | 
					github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
 | 
				
			||||||
 | 
					github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 | 
				
			||||||
 | 
					github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
 | 
				
			||||||
 | 
					github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
				
			||||||
 | 
					github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 | 
				
			||||||
 | 
					github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 | 
				
			||||||
 | 
					github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
 | 
				
			||||||
 | 
					github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
				
			||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
					github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
				
			||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
					github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
				
			||||||
github.com/go-flucky/flucky v0.0.0-20190714170626-0dd156f480be h1:jCs/SAXdXUA0aqvEVH0wyeGpJzYOZ1XC9Kpm5anSY5E=
 | 
					 | 
				
			||||||
github.com/go-flucky/flucky v0.0.0-20190714170626-0dd156f480be/go.mod h1:WNT729lCsTFhT6Vyvg6cG8aHV1t6p+DsA8l4omOuaos=
 | 
					github.com/go-flucky/flucky v0.0.0-20190714170626-0dd156f480be/go.mod h1:WNT729lCsTFhT6Vyvg6cG8aHV1t6p+DsA8l4omOuaos=
 | 
				
			||||||
github.com/go-flucky/go-dht v0.1.1 h1:dPz9F5D7oUaTd0GUGTvT4lBH2R043h1bkmhTlpQax1Y=
 | 
					github.com/go-flucky/go-dht v0.1.1 h1:dPz9F5D7oUaTd0GUGTvT4lBH2R043h1bkmhTlpQax1Y=
 | 
				
			||||||
github.com/go-flucky/go-dht v0.1.1/go.mod h1:Yk/cct+/u+eCS7pB/kc0tc7MrVXdFI4W15MJCj5FRUc=
 | 
					github.com/go-flucky/go-dht v0.1.1/go.mod h1:Yk/cct+/u+eCS7pB/kc0tc7MrVXdFI4W15MJCj5FRUc=
 | 
				
			||||||
@@ -72,15 +79,15 @@ github.com/lib/pq v1.4.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
				
			|||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
					github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
				
			||||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
 | 
					github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
 | 
				
			||||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 | 
					github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 | 
				
			||||||
github.com/mattn/go-sqlite3 v1.13.0 h1:LnJI81JidiW9r7pS/hXe6cFeO5EXNq7KbfvoJLRI69c=
 | 
					 | 
				
			||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
 | 
					 | 
				
			||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 | 
					 | 
				
			||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
					github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
				
			||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
					github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
				
			||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
					github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
				
			||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
					github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
				
			||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 | 
					github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 | 
				
			||||||
 | 
					github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 | 
				
			||||||
 | 
					github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 | 
				
			||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
					github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
				
			||||||
 | 
					github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
 | 
				
			||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
					github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
@@ -140,6 +147,7 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
 | 
				
			|||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
					golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
				
			||||||
 | 
					golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 | 
					golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
					golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,40 +1,31 @@
 | 
				
			|||||||
package config_test
 | 
					package config_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					// func TestAddRemoveSensor(t *testing.T) {
 | 
				
			||||||
	"fmt"
 | 
					// 	require := require.New(t)
 | 
				
			||||||
	"testing"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/require"
 | 
					// 	// Test: No device configured
 | 
				
			||||||
	"github.com/volker-raschek/flucky/pkg/config"
 | 
					// 	cnf := new(config.Config)
 | 
				
			||||||
	"github.com/volker-raschek/flucky/pkg/types"
 | 
					// 	err := cnf.AddSensor(&types.Sensor{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf"})
 | 
				
			||||||
)
 | 
					// 	require.Error(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestAddRemoveSensor(t *testing.T) {
 | 
					// 	// wireID := "sdfsdff"
 | 
				
			||||||
	require := require.New(t)
 | 
					// 	// i2cBus := 1
 | 
				
			||||||
 | 
					// 	// i2cAddress := 78
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Test: No device configured
 | 
					// 	cnf.Device = &types.Device{ID: "d6176a06-2b0b-41af-a85c-913e8f61c35d"}
 | 
				
			||||||
	cnf := new(config.Config)
 | 
					// 	testCases := map[*types.Sensor]error{
 | 
				
			||||||
	err := cnf.AddSensor(&types.Sensor{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf"})
 | 
					// 		{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d"}:                                     fmt.Errorf("No sensor name defined"),
 | 
				
			||||||
	require.Error(err)
 | 
					// 		{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test01"}:                     fmt.Errorf("Failed to parse tick duration: time: invalid duration "),
 | 
				
			||||||
 | 
					// 		{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test01", TickDuration: "5s"}: nil,
 | 
				
			||||||
 | 
					// 		{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test01", TickDuration: "5s"}: fmt.Errorf("Sensor with same name or id already exist"),
 | 
				
			||||||
 | 
					// 		// {ID: "f90cfc18-f141-4cfd-a8d2-fb40082de5cc", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test01", TickDuration: "5s"}: fmt.Errorf("Sensor with same name or id already exist"),
 | 
				
			||||||
 | 
					// 		// {ID: "860a9922-62cb-4c9b-b5af-5fa783cebe9d", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test02", TickDuration: "5s", WireID: &wireID}: fmt.Errorf("Wire socket not found: /sys/bus/w1/devices/sdfsdff/w1_slave"),
 | 
				
			||||||
 | 
					// 		// {ID: "9be8989c-b2a1-4401-a82f-d6989ec226fe", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test02", TickDuration: "5s"}:                  nil,
 | 
				
			||||||
 | 
					// 	}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// wireID := "sdfsdff"
 | 
					// 	for sensor, expectedErr := range testCases {
 | 
				
			||||||
	// i2cBus := 1
 | 
					// 		err := cnf.AddSensor(sensor)
 | 
				
			||||||
	// i2cAddress := 78
 | 
					// 		require.Equal(expectedErr, err)
 | 
				
			||||||
 | 
					// 	}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cnf.Device = &types.Device{ID: "d6176a06-2b0b-41af-a85c-913e8f61c35d"}
 | 
					// }
 | 
				
			||||||
	testCases := map[*types.Sensor]error{
 | 
					 | 
				
			||||||
		{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d"}:                                     fmt.Errorf("No sensor name defined"),
 | 
					 | 
				
			||||||
		{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test01"}:                     fmt.Errorf("Failed to parse tick duration: time: invalid duration "),
 | 
					 | 
				
			||||||
		{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test01", TickDuration: "5s"}: nil,
 | 
					 | 
				
			||||||
		{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test01", TickDuration: "5s"}: fmt.Errorf("Sensor with same name or id already exist"),
 | 
					 | 
				
			||||||
		// {ID: "f90cfc18-f141-4cfd-a8d2-fb40082de5cc", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test01", TickDuration: "5s"}: fmt.Errorf("Sensor with same name or id already exist"),
 | 
					 | 
				
			||||||
		// {ID: "860a9922-62cb-4c9b-b5af-5fa783cebe9d", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test02", TickDuration: "5s", WireID: &wireID}: fmt.Errorf("Wire socket not found: /sys/bus/w1/devices/sdfsdff/w1_slave"),
 | 
					 | 
				
			||||||
		// {ID: "9be8989c-b2a1-4401-a82f-d6989ec226fe", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test02", TickDuration: "5s"}:                  nil,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for sensor, expectedErr := range testCases {
 | 
					 | 
				
			||||||
		err := cnf.AddSensor(sensor)
 | 
					 | 
				
			||||||
		require.Equal(expectedErr, err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -146,7 +146,7 @@ func (postgres *Postgres) InsertMeasuredValues(ctx context.Context, measuredValu
 | 
				
			|||||||
		defer stmt.Close()
 | 
							defer stmt.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for _, measuredValue := range measuredValues {
 | 
							for _, measuredValue := range measuredValues {
 | 
				
			||||||
			_, err := stmt.Exec(&measuredValue.ID, &measuredValue.Value, &measuredValue.FromDate, &measuredValue.TillDate, &measuredValue.SensorID, &measuredValue.CreationDate, &measuredValue.UpdateDate)
 | 
								_, err := stmt.Exec(&measuredValue.ID, &measuredValue.Value, &measuredValue.SensorID, &measuredValue.CreationDate)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return fmt.Errorf("Failed to execute statement: %v", err)
 | 
									return fmt.Errorf("Failed to execute statement: %v", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -144,9 +144,8 @@ func (sqlite *SQLite) InsertMeasuredValues(ctx context.Context, measuredValues .
 | 
				
			|||||||
			_, err := stmt.Exec(
 | 
								_, err := stmt.Exec(
 | 
				
			||||||
				&measuredValue.ID,
 | 
									&measuredValue.ID,
 | 
				
			||||||
				&measuredValue.Value,
 | 
									&measuredValue.Value,
 | 
				
			||||||
				&measuredValue.FromDate,
 | 
					 | 
				
			||||||
				&measuredValue.TillDate,
 | 
					 | 
				
			||||||
				&measuredValue.SensorID,
 | 
									&measuredValue.SensorID,
 | 
				
			||||||
 | 
									&measuredValue.Date,
 | 
				
			||||||
				&measuredValue.CreationDate,
 | 
									&measuredValue.CreationDate,
 | 
				
			||||||
				&measuredValue.UpdateDate,
 | 
									&measuredValue.UpdateDate,
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
@@ -214,6 +213,7 @@ func (sqlite *SQLite) InsertSensors(ctx context.Context, sensors ...*types.Senso
 | 
				
			|||||||
			&sensor.GPIONumber,
 | 
								&sensor.GPIONumber,
 | 
				
			||||||
			&sensor.Model,
 | 
								&sensor.Model,
 | 
				
			||||||
			&sensor.Enabled,
 | 
								&sensor.Enabled,
 | 
				
			||||||
 | 
								&sensor.TickDuration,
 | 
				
			||||||
			&sensor.DeviceID,
 | 
								&sensor.DeviceID,
 | 
				
			||||||
			&sensor.CreationDate,
 | 
								&sensor.CreationDate,
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
@@ -266,6 +266,10 @@ func (sqlite *SQLite) SelectDevice(ctx context.Context, id string) (*types.Devic
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(devices) == 0 {
 | 
				
			||||||
 | 
							return nil, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return devices[0], nil
 | 
						return devices[0], nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -282,7 +286,7 @@ func (sqlite *SQLite) SelectDevices(ctx context.Context) ([]*types.Device, error
 | 
				
			|||||||
		return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
 | 
							return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	devices, err := sqlite.selectDevices(tx, query, nil)
 | 
						devices, err := sqlite.selectDevices(tx, query)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -302,7 +306,7 @@ func (sqlite *SQLite) selectDevices(tx *sql.Tx, query string, args ...interface{
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	defer stmt.Close()
 | 
						defer stmt.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rows, err := stmt.Query()
 | 
						rows, err := stmt.Query(args...)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("Failed to query statement: %v", err)
 | 
							return nil, fmt.Errorf("Failed to query statement: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -405,21 +409,22 @@ func (sqlite *SQLite) selectMeasuredValue(tx *sql.Tx, query string, args ...inte
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	measuredValues := make([]*types.MeasuredValue, 0)
 | 
						measuredValues := make([]*types.MeasuredValue, 0)
 | 
				
			||||||
	for rows.Next() {
 | 
						for rows.Next() {
 | 
				
			||||||
		measuredValues := new(types.MeasuredValue)
 | 
							measuredValue := new(types.MeasuredValue)
 | 
				
			||||||
		err := rows.Scan(
 | 
							err := rows.Scan(
 | 
				
			||||||
			&measuredValues.ID,
 | 
								&measuredValue.ID,
 | 
				
			||||||
			&measuredValues.Value,
 | 
								&measuredValue.Value,
 | 
				
			||||||
			&measuredValues.FromDate,
 | 
								&measuredValue.SensorID,
 | 
				
			||||||
			&measuredValues.TillDate,
 | 
								&measuredValue.Date,
 | 
				
			||||||
			&measuredValues.SensorID,
 | 
								&measuredValue.CreationDate,
 | 
				
			||||||
			&measuredValues.CreationDate,
 | 
								&measuredValue.UpdateDate,
 | 
				
			||||||
			&measuredValues.UpdateDate,
 | 
					 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			tx.Rollback()
 | 
								tx.Rollback()
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							measuredValues = append(measuredValues, measuredValue)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return measuredValues, nil
 | 
						return measuredValues, nil
 | 
				
			||||||
@@ -502,7 +507,7 @@ func (sqlite *SQLite) SelectSensor(ctx context.Context, id string) (*types.Senso
 | 
				
			|||||||
		return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
 | 
							return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sensors, err := sqlite.selectSensors(tx, query, nil)
 | 
						sensors, err := sqlite.selectSensors(tx, query, id)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -512,6 +517,10 @@ func (sqlite *SQLite) SelectSensor(ctx context.Context, id string) (*types.Senso
 | 
				
			|||||||
		return nil, fmt.Errorf("Failed to commit transaction: %v", err)
 | 
							return nil, fmt.Errorf("Failed to commit transaction: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(sensors) == 0 {
 | 
				
			||||||
 | 
							return nil, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return sensors[0], nil
 | 
						return sensors[0], nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -528,7 +537,7 @@ func (sqlite *SQLite) SelectSensors(ctx context.Context) ([]*types.Sensor, error
 | 
				
			|||||||
		return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
 | 
							return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sensors, err := sqlite.selectSensors(tx, query, nil)
 | 
						sensors, err := sqlite.selectSensors(tx, query)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -548,7 +557,7 @@ func (sqlite *SQLite) selectSensors(tx *sql.Tx, query string, args ...interface{
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	defer stmt.Close()
 | 
						defer stmt.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rows, err := stmt.Query()
 | 
						rows, err := stmt.Query(args...)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("Failed to query statement: %v", err)
 | 
							return nil, fmt.Errorf("Failed to query statement: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -566,6 +575,7 @@ func (sqlite *SQLite) selectSensors(tx *sql.Tx, query string, args ...interface{
 | 
				
			|||||||
			&sensor.GPIONumber,
 | 
								&sensor.GPIONumber,
 | 
				
			||||||
			&sensor.Model,
 | 
								&sensor.Model,
 | 
				
			||||||
			&sensor.Enabled,
 | 
								&sensor.Enabled,
 | 
				
			||||||
 | 
								&sensor.TickDuration,
 | 
				
			||||||
			&sensor.DeviceID,
 | 
								&sensor.DeviceID,
 | 
				
			||||||
			&sensor.CreationDate,
 | 
								&sensor.CreationDate,
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
@@ -709,6 +719,7 @@ func (sqlite *SQLite) UpdateSensors(ctx context.Context, sensors ...*types.Senso
 | 
				
			|||||||
			&sensor.GPIONumber,
 | 
								&sensor.GPIONumber,
 | 
				
			||||||
			&sensor.Model,
 | 
								&sensor.Model,
 | 
				
			||||||
			&sensor.Enabled,
 | 
								&sensor.Enabled,
 | 
				
			||||||
 | 
								&sensor.TickDuration,
 | 
				
			||||||
			&sensor.DeviceID,
 | 
								&sensor.DeviceID,
 | 
				
			||||||
			&sensor.CreationDate,
 | 
								&sensor.CreationDate,
 | 
				
			||||||
			&sensor.ID,
 | 
								&sensor.ID,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,7 @@
 | 
				
			|||||||
CREATE TABLE IF NOT EXISTS humidities (
 | 
					CREATE TABLE IF NOT EXISTS humidities (
 | 
				
			||||||
  humidity_id             CHAR(37)                                    PRIMARY KEY,
 | 
					  id                      CHAR(37)                                    PRIMARY KEY,
 | 
				
			||||||
  humidity_value          NUMERIC(10,3)                               NOT NULL,
 | 
					  value                   NUMERIC(10,3)                               NOT NULL,
 | 
				
			||||||
  humidity_from_date      TIMESTAMP                                   NOT NULL,
 | 
					  date                    TIMESTAMP                                   NOT NULL,
 | 
				
			||||||
  humidity_till_date      TIMESTAMP,
 | 
					 | 
				
			||||||
  sensor_id               CHAR(37)                                    NOT NULL,
 | 
					  sensor_id               CHAR(37)                                    NOT NULL,
 | 
				
			||||||
  creation_date           TIMESTAMP       DEFAULT CURRENT_TIMESTAMP   NOT NULL,
 | 
					  creation_date           TIMESTAMP       DEFAULT CURRENT_TIMESTAMP   NOT NULL,
 | 
				
			||||||
  update_date             TIMESTAMP,
 | 
					  update_date             TIMESTAMP,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,7 @@
 | 
				
			|||||||
CREATE TABLE IF NOT EXISTS pressures (
 | 
					CREATE TABLE IF NOT EXISTS pressures (
 | 
				
			||||||
  pressure_id             CHAR(37)                                    PRIMARY KEY,
 | 
					  id                      CHAR(37)                                    PRIMARY KEY,
 | 
				
			||||||
  pressure_value          NUMERIC(10,3)                               NOT NULL,
 | 
					  value                   NUMERIC(10,3)                               NOT NULL,
 | 
				
			||||||
  pressure_from_date      TIMESTAMP                                   NOT NULL,
 | 
					  date                    TIMESTAMP                                   NOT NULL,
 | 
				
			||||||
  pressure_till_date      TIMESTAMP,
 | 
					 | 
				
			||||||
  sensor_id               CHAR(37)                                    NOT NULL,
 | 
					  sensor_id               CHAR(37)                                    NOT NULL,
 | 
				
			||||||
  creation_date           TIMESTAMP       DEFAULT CURRENT_TIMESTAMP   NOT NULL,
 | 
					  creation_date           TIMESTAMP       DEFAULT CURRENT_TIMESTAMP   NOT NULL,
 | 
				
			||||||
  update_date             TIMESTAMP,
 | 
					  update_date             TIMESTAMP,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ CREATE TABLE IF NOT EXISTS sensors (
 | 
				
			|||||||
  gpio_number             VARCHAR(6),
 | 
					  gpio_number             VARCHAR(6),
 | 
				
			||||||
  sensor_model            VARCHAR(16)                                 NOT NULL,
 | 
					  sensor_model            VARCHAR(16)                                 NOT NULL,
 | 
				
			||||||
  sensor_enabled          INTEGER(1)      DEFAULT 1                   NOT NULL,
 | 
					  sensor_enabled          INTEGER(1)      DEFAULT 1                   NOT NULL,
 | 
				
			||||||
 | 
					  tick_duration           VARCHAR(6)                                  NOT NULL,
 | 
				
			||||||
  device_id               CHAR(37)                                    NOT NULL,
 | 
					  device_id               CHAR(37)                                    NOT NULL,
 | 
				
			||||||
  creation_date           TIMESTAMP       DEFAULT CURRENT_TIMESTAMP,
 | 
					  creation_date           TIMESTAMP       DEFAULT CURRENT_TIMESTAMP,
 | 
				
			||||||
  FOREIGN KEY(device_id) REFERENCES devices(device_id)
 | 
					  FOREIGN KEY(device_id) REFERENCES devices(device_id)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,9 @@
 | 
				
			|||||||
CREATE TABLE IF NOT EXISTS temperatures (
 | 
					CREATE TABLE IF NOT EXISTS temperatures (
 | 
				
			||||||
  temperature_id          CHAR(37)                                      PRIMARY KEY,
 | 
					  id                      CHAR(37)                                    PRIMARY KEY,
 | 
				
			||||||
  temperature_value       NUMERIC(10,3)                                 NOT NULL,
 | 
					  value                   NUMERIC(10,3)                               NOT NULL,
 | 
				
			||||||
  temperature_from_date   TIMESTAMP                                     NOT NULL,
 | 
					  date                    TIMESTAMP                                   NOT NULL,
 | 
				
			||||||
  temperature_till_date   TIMESTAMP,
 | 
					  sensor_id               CHAR(37)                                    NOT NULL,
 | 
				
			||||||
  sensor_id               CHAR(37)                                      NOT NULL,
 | 
					  creation_date           TIMESTAMP       DEFAULT CURRENT_TIMESTAMP   NOT NULL,
 | 
				
			||||||
  creation_date           TIMESTAMP         DEFAULT CURRENT_TIMESTAMP   NOT NULL,
 | 
					 | 
				
			||||||
  update_date             TIMESTAMP,
 | 
					  update_date             TIMESTAMP,
 | 
				
			||||||
  FOREIGN KEY(sensor_id) REFERENCES sensors(sensor_id)
 | 
					  FOREIGN KEY(sensor_id) REFERENCES sensors(sensor_id)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1,2 +1,2 @@
 | 
				
			|||||||
DELETE FROM sensors
 | 
					DELETE FROM devices
 | 
				
			||||||
WHERE device_id = $1;
 | 
					WHERE device_id = $1;
 | 
				
			||||||
@@ -1,10 +1,9 @@
 | 
				
			|||||||
INSERT INTO humidities (
 | 
					INSERT INTO humidities (
 | 
				
			||||||
  humidity_id,
 | 
					  id,
 | 
				
			||||||
  humidity_value,
 | 
					  value,
 | 
				
			||||||
  humidity_from_date,
 | 
					  date,
 | 
				
			||||||
  humidity_till_date,
 | 
					 | 
				
			||||||
  sensor_id,
 | 
					  sensor_id,
 | 
				
			||||||
  creation_date,
 | 
					  creation_date,
 | 
				
			||||||
  update_date
 | 
					  update_date
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
VALUES ($1, $2, $3, $4, $5, $6, $7);
 | 
					VALUES ($1, $2, $3, $4, $5, $6);
 | 
				
			||||||
@@ -1,10 +1,9 @@
 | 
				
			|||||||
INSERT INTO pressures (
 | 
					INSERT INTO pressures (
 | 
				
			||||||
  pressure_id,
 | 
					  id,
 | 
				
			||||||
  pressure_value,
 | 
					  value,
 | 
				
			||||||
  pressure_from_date,
 | 
					  date,
 | 
				
			||||||
  pressure_till_date,
 | 
					 | 
				
			||||||
  sensor_id,
 | 
					  sensor_id,
 | 
				
			||||||
  creation_date,
 | 
					  creation_date,
 | 
				
			||||||
  update_date
 | 
					  update_date
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
VALUES ($1, $2, $3, $4, $5, $6, $7);
 | 
					VALUES ($1, $2, $3, $4, $5, $6);
 | 
				
			||||||
@@ -8,7 +8,8 @@ INSERT INTO sensors (
 | 
				
			|||||||
  gpio_number,
 | 
					  gpio_number,
 | 
				
			||||||
  sensor_model,
 | 
					  sensor_model,
 | 
				
			||||||
  sensor_enabled,
 | 
					  sensor_enabled,
 | 
				
			||||||
 | 
					  tick_duration,
 | 
				
			||||||
  device_id,
 | 
					  device_id,
 | 
				
			||||||
  creation_date
 | 
					  creation_date
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);
 | 
					VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);
 | 
				
			||||||
@@ -1,10 +1,9 @@
 | 
				
			|||||||
INSERT INTO temperatures (
 | 
					INSERT INTO temperatures (
 | 
				
			||||||
  temperature_id,
 | 
					  id,
 | 
				
			||||||
  temperature_value,
 | 
					  value,
 | 
				
			||||||
  temperature_from_date,
 | 
					  date,
 | 
				
			||||||
  temperature_till_date,
 | 
					 | 
				
			||||||
  sensor_id,
 | 
					  sensor_id,
 | 
				
			||||||
  creation_date,
 | 
					  creation_date,
 | 
				
			||||||
  update_date
 | 
					  update_date
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
VALUES ($1, $2, $3, $4, $5, $6, $7);
 | 
					VALUES ($1, $2, $3, $4, $5, $6);
 | 
				
			||||||
@@ -1,8 +1,7 @@
 | 
				
			|||||||
SELECT
 | 
					SELECT
 | 
				
			||||||
  humidity_id,
 | 
					  id,
 | 
				
			||||||
  humidity_value,
 | 
					  value,
 | 
				
			||||||
  humidity_from_date,
 | 
					  date,
 | 
				
			||||||
  humidity_till_date,
 | 
					 | 
				
			||||||
  sensor_id,
 | 
					  sensor_id,
 | 
				
			||||||
  creation_date,
 | 
					  creation_date,
 | 
				
			||||||
  update_date
 | 
					  update_date
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,7 @@
 | 
				
			|||||||
SELECT
 | 
					SELECT
 | 
				
			||||||
  humidity_id,
 | 
					  id,
 | 
				
			||||||
  humidity_value,
 | 
					  value,
 | 
				
			||||||
  humidity_from_date,
 | 
					  date,
 | 
				
			||||||
  humidity_till_date,
 | 
					 | 
				
			||||||
  sensor_id,
 | 
					  sensor_id,
 | 
				
			||||||
  creation_date,
 | 
					  creation_date,
 | 
				
			||||||
  update_date
 | 
					  update_date
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,7 @@
 | 
				
			|||||||
SELECT
 | 
					SELECT
 | 
				
			||||||
  pressure_id,
 | 
					  id,
 | 
				
			||||||
  pressure_value,
 | 
					  value,
 | 
				
			||||||
  pressure_from_date,
 | 
					  date,
 | 
				
			||||||
  pressure_till_date,
 | 
					 | 
				
			||||||
  sensor_id,
 | 
					  sensor_id,
 | 
				
			||||||
  creation_date,
 | 
					  creation_date,
 | 
				
			||||||
  update_date
 | 
					  update_date
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,7 @@
 | 
				
			|||||||
SELECT
 | 
					SELECT
 | 
				
			||||||
  pressure_id,
 | 
					  id,
 | 
				
			||||||
  pressure_value,
 | 
					  value,
 | 
				
			||||||
  pressure_from_date,
 | 
					  date,
 | 
				
			||||||
  pressure_till_date,
 | 
					 | 
				
			||||||
  sensor_id,
 | 
					  sensor_id,
 | 
				
			||||||
  creation_date,
 | 
					  creation_date,
 | 
				
			||||||
  update_date
 | 
					  update_date
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ SELECT
 | 
				
			|||||||
  gpio_number,
 | 
					  gpio_number,
 | 
				
			||||||
  sensor_model,
 | 
					  sensor_model,
 | 
				
			||||||
  sensor_enabled,
 | 
					  sensor_enabled,
 | 
				
			||||||
 | 
					  tick_duration,
 | 
				
			||||||
  device_id,
 | 
					  device_id,
 | 
				
			||||||
  creation_date
 | 
					  creation_date
 | 
				
			||||||
FROM
 | 
					FROM
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,10 @@ SELECT
 | 
				
			|||||||
  gpio_number,
 | 
					  gpio_number,
 | 
				
			||||||
  sensor_model,
 | 
					  sensor_model,
 | 
				
			||||||
  sensor_enabled,
 | 
					  sensor_enabled,
 | 
				
			||||||
 | 
					  tick_duration,
 | 
				
			||||||
  device_id,
 | 
					  device_id,
 | 
				
			||||||
  creation_date
 | 
					  creation_date
 | 
				
			||||||
FROM
 | 
					FROM
 | 
				
			||||||
  sensors;
 | 
					  sensors
 | 
				
			||||||
 | 
					ORDER BY
 | 
				
			||||||
 | 
					  sensor_id;
 | 
				
			||||||
@@ -1,8 +1,7 @@
 | 
				
			|||||||
SELECT
 | 
					SELECT
 | 
				
			||||||
  temperature_id,
 | 
					  id,
 | 
				
			||||||
  temperature_value,
 | 
					  value,
 | 
				
			||||||
  temperature_from_date,
 | 
					  date,
 | 
				
			||||||
  temperature_till_date,
 | 
					 | 
				
			||||||
  sensor_id,
 | 
					  sensor_id,
 | 
				
			||||||
  creation_date,
 | 
					  creation_date,
 | 
				
			||||||
  update_date
 | 
					  update_date
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,7 @@
 | 
				
			|||||||
SELECT
 | 
					SELECT
 | 
				
			||||||
  temperature_id,
 | 
					  id,
 | 
				
			||||||
  temperature_value,
 | 
					  value,
 | 
				
			||||||
  temperature_from_date,
 | 
					  date,
 | 
				
			||||||
  temperature_till_date,
 | 
					 | 
				
			||||||
  sensor_id,
 | 
					  sensor_id,
 | 
				
			||||||
  creation_date,
 | 
					  creation_date,
 | 
				
			||||||
  update_date
 | 
					  update_date
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
UPDATE device
 | 
					UPDATE devices
 | 
				
			||||||
SET
 | 
					SET
 | 
				
			||||||
  device_name = $1,
 | 
					  device_name = $1,
 | 
				
			||||||
  device_location = $2,
 | 
					  device_location = $2,
 | 
				
			||||||
  creation_date = $3
 | 
					  creation_date = $3
 | 
				
			||||||
WHERE
 | 
					WHERE
 | 
				
			||||||
  device_id = $4,
 | 
					  device_id = $4
 | 
				
			||||||
@@ -8,7 +8,8 @@ SET
 | 
				
			|||||||
  gpio_number = $6,
 | 
					  gpio_number = $6,
 | 
				
			||||||
  sensor_model = $7,
 | 
					  sensor_model = $7,
 | 
				
			||||||
  sensor_enabled = $8,
 | 
					  sensor_enabled = $8,
 | 
				
			||||||
  device_id = $9,
 | 
					  tick_duration = $9,
 | 
				
			||||||
  creation_date = $10
 | 
					  device_id = $10,
 | 
				
			||||||
 | 
					  creation_date = $11
 | 
				
			||||||
WHERE
 | 
					WHERE
 | 
				
			||||||
  sensor_id = $11;
 | 
					  sensor_id = $12;
 | 
				
			||||||
@@ -110,7 +110,7 @@ func (repo *Repository) GetSensorsByDeviceID(deviceID string) ([]*types.Sensor,
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return sensors, nil
 | 
						return cachedSensors, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RemoveDevices removes devices by their ids from the repository. Additional
 | 
					// RemoveDevices removes devices by their ids from the repository. Additional
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										309
									
								
								pkg/repository/repository_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										309
									
								
								pkg/repository/repository_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,309 @@
 | 
				
			|||||||
 | 
					package repository_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"math/rand"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uuid "github.com/satori/go.uuid"
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/require"
 | 
				
			||||||
 | 
						"github.com/volker-raschek/flucky/pkg/repository"
 | 
				
			||||||
 | 
						"github.com/volker-raschek/flucky/pkg/testutils/dockerutils"
 | 
				
			||||||
 | 
						"github.com/volker-raschek/flucky/pkg/types"
 | 
				
			||||||
 | 
						"github.com/volker-raschek/go-logger/pkg/logger"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPostgresBackend(t *testing.T) {
 | 
				
			||||||
 | 
						t.Skip("Database backend postgres not completly implemented")
 | 
				
			||||||
 | 
						require := require.New(t)
 | 
				
			||||||
 | 
						dockerClient, err := dockerutils.New()
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rand.Seed(time.Now().Unix())
 | 
				
			||||||
 | 
						postgresHostPort := rand.Intn(10024-1024) + 1024
 | 
				
			||||||
 | 
						postgresDBPasswort := "postgres"
 | 
				
			||||||
 | 
						postgresContainerID, err := dockerClient.NewBuilder("postgres:latest").
 | 
				
			||||||
 | 
							Port(fmt.Sprintf("%v:5432/tcp", postgresHostPort)).
 | 
				
			||||||
 | 
							Pull().
 | 
				
			||||||
 | 
							AddEnv("PGTZ", "Europe/Berlin").
 | 
				
			||||||
 | 
							AddEnv("POSTGRES_PASSWORD", postgresDBPasswort).
 | 
				
			||||||
 | 
							AddEnv("TZ", "Europe/Berlin").
 | 
				
			||||||
 | 
							Mount("/etc/localtime", "/etc/localtime").
 | 
				
			||||||
 | 
							Start(context.Background())
 | 
				
			||||||
 | 
						cleanup := func() {
 | 
				
			||||||
 | 
							dockerClient.ContainerRemoveByIDs(context.Background(), postgresContainerID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						t.Cleanup(cleanup)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// postgres://[user]:[password]@[host]:[port]/[path]?[query]
 | 
				
			||||||
 | 
						storageEndpoint, err := url.Parse(fmt.Sprintf("postgres://postgres:%v@localhost:%v", postgresDBPasswort, postgresHostPort))
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repo, err := repository.New(storageEndpoint, logger.NewDefaultLogger(logger.LogLevelDebug))
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testBackend(t, repo)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSQLiteBackend(t *testing.T) {
 | 
				
			||||||
 | 
						require := require.New(t)
 | 
				
			||||||
 | 
						workspace := filepath.Join(os.TempDir(), uuid.NewV4().String())
 | 
				
			||||||
 | 
						err := os.MkdirAll(workspace, 0755)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						defer os.RemoveAll(workspace)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						storageEndpoint, err := url.Parse(fmt.Sprintf("sqlite3://%v/test.db", workspace))
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repo, err := repository.New(storageEndpoint, logger.NewDefaultLogger(logger.LogLevelDebug))
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testBackend(t, repo)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testBackend(t *testing.T, repo *repository.Repository) {
 | 
				
			||||||
 | 
						require := require.New(t)
 | 
				
			||||||
 | 
						location := uuid.NewV4().String()
 | 
				
			||||||
 | 
						expectedDevices := []*types.Device{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								ID:           "ec0be3ab-d26d-4f9b-a96e-23ae5c577f8f",
 | 
				
			||||||
 | 
								Name:         "f2b245eb-b15f-40e1-9212-9a645907b710",
 | 
				
			||||||
 | 
								Location:     &location,
 | 
				
			||||||
 | 
								CreationDate: *timeNow(require),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								ID:           "39b8f150-8abf-4539-9f16-7f68cedb1649",
 | 
				
			||||||
 | 
								Name:         "62e3978f-2198-4aa9-9d6f-cdc91a468b00",
 | 
				
			||||||
 | 
								Location:     &location,
 | 
				
			||||||
 | 
								CreationDate: *timeNow(require),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: AddDevice
 | 
				
			||||||
 | 
						err := repo.AddDevices(expectedDevices...)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: GetDevices
 | 
				
			||||||
 | 
						devices, err := repo.GetDevices()
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.Len(devices, len(expectedDevices))
 | 
				
			||||||
 | 
						require.EqualValues(expectedDevices, devices)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: GetDevice
 | 
				
			||||||
 | 
						device, err := repo.GetDevice(expectedDevices[0].ID)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.EqualValues(expectedDevices[0], device)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: RemoveDevice
 | 
				
			||||||
 | 
						err = repo.RemoveDevices(expectedDevices[1].ID)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						devices, err = repo.GetDevices()
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.Len(devices, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						device, err = repo.GetDevice(expectedDevices[1].ID)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.Nil(device)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: Update Devices
 | 
				
			||||||
 | 
						location = "MyLocation"
 | 
				
			||||||
 | 
						expectedDevice := &types.Device{
 | 
				
			||||||
 | 
							ID:           "ec0be3ab-d26d-4f9b-a96e-23ae5c577f8f",
 | 
				
			||||||
 | 
							Name:         "Hello World",
 | 
				
			||||||
 | 
							Location:     &location,
 | 
				
			||||||
 | 
							CreationDate: *timeNow(require),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = repo.UpdateDevices(expectedDevice)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						device, err = repo.GetDevice(expectedDevice.ID)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.EqualValues(expectedDevice, device)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							wireID                = "50473fdc-f6ef-4227-b3c4-484d8e9c1323"
 | 
				
			||||||
 | 
							i2cBus                = 1
 | 
				
			||||||
 | 
							i2cAddress      uint8 = 76
 | 
				
			||||||
 | 
							expectedSensors       = []*types.Sensor{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									ID:           "0f8b88b0-c20d-42b2-ab51-b09ca99c0752",
 | 
				
			||||||
 | 
									Name:         "e1fbdbe9-cebf-42ed-8065-bf4882ccf76b",
 | 
				
			||||||
 | 
									Location:     "6d5b5450-1f87-47cb-b185-f64c35fae3c1",
 | 
				
			||||||
 | 
									GPIONumber:   "GPIO14",
 | 
				
			||||||
 | 
									Model:        "DHT11",
 | 
				
			||||||
 | 
									Enabled:      true,
 | 
				
			||||||
 | 
									TickDuration: "1m",
 | 
				
			||||||
 | 
									DeviceID:     "ec0be3ab-d26d-4f9b-a96e-23ae5c577f8f",
 | 
				
			||||||
 | 
									CreationDate: *timeNow(require),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									ID:           "80b1c4bd-abec-4ff0-afb4-bd70aeed0c83",
 | 
				
			||||||
 | 
									Name:         "544365ee-ece9-44ea-911d-5d920a68d4ba",
 | 
				
			||||||
 | 
									Location:     "7ae2d05e-9e6b-4d2d-b26a-cb4acca83778",
 | 
				
			||||||
 | 
									WireID:       &wireID,
 | 
				
			||||||
 | 
									Model:        "DS18B20",
 | 
				
			||||||
 | 
									Enabled:      true,
 | 
				
			||||||
 | 
									TickDuration: "5m",
 | 
				
			||||||
 | 
									DeviceID:     "ec0be3ab-d26d-4f9b-a96e-23ae5c577f8f",
 | 
				
			||||||
 | 
									CreationDate: *timeNow(require),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									ID:           "8c74397f-8e60-4c9d-960d-3197747cef9a",
 | 
				
			||||||
 | 
									Name:         "4b808675-de02-4866-893d-1c77f23b9304",
 | 
				
			||||||
 | 
									Location:     "8a085c0f-dd3c-447f-8c4e-c4c1d869c7b6",
 | 
				
			||||||
 | 
									I2CBus:       &i2cBus,
 | 
				
			||||||
 | 
									I2CAddress:   &i2cAddress,
 | 
				
			||||||
 | 
									Model:        "BME280",
 | 
				
			||||||
 | 
									Enabled:      false,
 | 
				
			||||||
 | 
									TickDuration: "10m",
 | 
				
			||||||
 | 
									DeviceID:     "39b8f150-8abf-4539-9f16-7f68cedb1649",
 | 
				
			||||||
 | 
									CreationDate: *timeNow(require),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: AddSensors
 | 
				
			||||||
 | 
						err = repo.AddSensors(expectedSensors...)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: GetSensors
 | 
				
			||||||
 | 
						sensors, err := repo.GetSensors()
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.Len(sensors, len(expectedSensors))
 | 
				
			||||||
 | 
						require.Equal(expectedSensors, sensors)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: GetSensor
 | 
				
			||||||
 | 
						sensor, err := repo.GetSensor(expectedSensors[0].ID)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.Equal(expectedSensors[0], sensor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: GetSensorsByDeviceID
 | 
				
			||||||
 | 
						sensors, err = repo.GetSensorsByDeviceID("ec0be3ab-d26d-4f9b-a96e-23ae5c577f8f")
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.Len(sensors, 2)
 | 
				
			||||||
 | 
						require.Equal(expectedSensors[0:2], sensors)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: RemoveSensors
 | 
				
			||||||
 | 
						err = repo.RemoveSensors(expectedSensors[0].ID)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sensors, err = repo.GetSensors()
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.Len(sensors, 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sensors, err = repo.GetSensorsByDeviceID("ec0be3ab-d26d-4f9b-a96e-23ae5c577f8f")
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.Len(sensors, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sensor, err = repo.GetSensor(expectedSensors[0].ID)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.Nil(sensor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: RemoveSensorsByNames
 | 
				
			||||||
 | 
						err = repo.RemoveSensorsByNames(expectedSensors[1].Name)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sensors, err = repo.GetSensors()
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.Len(sensors, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sensor, err = repo.GetSensor(expectedSensors[1].ID)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.Nil(sensor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: RenameSensor
 | 
				
			||||||
 | 
						err = repo.RenameSensors(expectedSensors[2].Name, "Hello")
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sensor, err = repo.GetSensor(expectedSensors[2].ID)
 | 
				
			||||||
 | 
						require.Equal("Hello", sensor.Name)
 | 
				
			||||||
 | 
						require.NotNil(sensor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: DisableSensorsByNames
 | 
				
			||||||
 | 
						err = repo.DisableSensorsByNames("Hello")
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sensor, err = repo.GetSensor(expectedSensors[2].ID)
 | 
				
			||||||
 | 
						require.False(sensor.Enabled)
 | 
				
			||||||
 | 
						require.NotNil(sensor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: EnableSensorsByName
 | 
				
			||||||
 | 
						err = repo.EnableSensorsByNames("Hello")
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sensor, err = repo.GetSensor(expectedSensors[2].ID)
 | 
				
			||||||
 | 
						require.True(sensor.Enabled)
 | 
				
			||||||
 | 
						require.NotNil(sensor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: UpdateSensors
 | 
				
			||||||
 | 
						expectedSensor := &types.Sensor{
 | 
				
			||||||
 | 
							ID:           "8c74397f-8e60-4c9d-960d-3197747cef9a",
 | 
				
			||||||
 | 
							Name:         "4b808675-de02-4866-893d-1c77f23b9304",
 | 
				
			||||||
 | 
							Location:     "Über den Wolken muss die Freiheit wohl grenzenlos sein...",
 | 
				
			||||||
 | 
							I2CBus:       nil,
 | 
				
			||||||
 | 
							I2CAddress:   nil,
 | 
				
			||||||
 | 
							Model:        "SDS011",
 | 
				
			||||||
 | 
							Enabled:      true,
 | 
				
			||||||
 | 
							TickDuration: "6h",
 | 
				
			||||||
 | 
							DeviceID:     "39b8f150-8abf-4539-9f16-7f68cedb1649",
 | 
				
			||||||
 | 
							CreationDate: *timeNow(require),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = repo.UpdateSensors(expectedSensor)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sensor, err = repo.GetSensor(expectedSensor.ID)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						require.NotNil(sensor)
 | 
				
			||||||
 | 
						require.EqualValues(expectedSensor, sensor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							expectedMeasuredValues = []*types.MeasuredValue{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									ID:           "2e5a297a-3da0-46ae-89d2-0fcab0f1d5f7",
 | 
				
			||||||
 | 
									Value:        32,
 | 
				
			||||||
 | 
									ValueType:    "temperature",
 | 
				
			||||||
 | 
									Date:         *timeNow(require),
 | 
				
			||||||
 | 
									SensorID:     "8c74397f-8e60-4c9d-960d-3197747cef9a",
 | 
				
			||||||
 | 
									CreationDate: timeNow(require),
 | 
				
			||||||
 | 
									UpdateDate:   nil,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									ID:           "d69f1b62-0c6c-4058-b42c-4a2821bd220c",
 | 
				
			||||||
 | 
									Value:        38,
 | 
				
			||||||
 | 
									ValueType:    "temperature",
 | 
				
			||||||
 | 
									Date:         *timeNow(require),
 | 
				
			||||||
 | 
									SensorID:     "8c74397f-8e60-4c9d-960d-3197747cef9a",
 | 
				
			||||||
 | 
									CreationDate: timeNow(require),
 | 
				
			||||||
 | 
									UpdateDate:   nil,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									ID:           "ea945ae0-412b-4561-a191-1f8f1f909fa4",
 | 
				
			||||||
 | 
									Value:        35.4,
 | 
				
			||||||
 | 
									ValueType:    "temperature",
 | 
				
			||||||
 | 
									Date:         *timeNow(require),
 | 
				
			||||||
 | 
									SensorID:     "8c74397f-8e60-4c9d-960d-3197747cef9a",
 | 
				
			||||||
 | 
									CreationDate: timeNow(require),
 | 
				
			||||||
 | 
									UpdateDate:   nil,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test: AddMeasuredValues
 | 
				
			||||||
 | 
						err = repo.AddMeasuredValues(expectedMeasuredValues...)
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func timeNow(require *require.Assertions) *time.Time {
 | 
				
			||||||
 | 
						now, err := time.Parse("2006-01-02 15:04:05", time.Now().Format("2006-01-02 15:04:05"))
 | 
				
			||||||
 | 
						require.NoError(err)
 | 
				
			||||||
 | 
						return &now
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -67,24 +67,21 @@ func (bme280 *BME280) Read() ([]*types.MeasuredValue, error) {
 | 
				
			|||||||
			ID:        uuid.NewV4().String(),
 | 
								ID:        uuid.NewV4().String(),
 | 
				
			||||||
			Value:     float64(humidityValue),
 | 
								Value:     float64(humidityValue),
 | 
				
			||||||
			ValueType: "humidity",
 | 
								ValueType: "humidity",
 | 
				
			||||||
			FromDate:  format.FormatedTime(),
 | 
								Date:      format.FormatedTime(),
 | 
				
			||||||
			TillDate:  format.FormatedTime(),
 | 
					 | 
				
			||||||
			SensorID:  bme280.ID,
 | 
								SensorID:  bme280.ID,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			ID:        uuid.NewV4().String(),
 | 
								ID:        uuid.NewV4().String(),
 | 
				
			||||||
			Value:     float64(pressureValue),
 | 
								Value:     float64(pressureValue),
 | 
				
			||||||
			ValueType: "pressure",
 | 
								ValueType: "pressure",
 | 
				
			||||||
			FromDate:  format.FormatedTime(),
 | 
								Date:      format.FormatedTime(),
 | 
				
			||||||
			TillDate:  format.FormatedTime(),
 | 
					 | 
				
			||||||
			SensorID:  bme280.ID,
 | 
								SensorID:  bme280.ID,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			ID:        uuid.NewV4().String(),
 | 
								ID:        uuid.NewV4().String(),
 | 
				
			||||||
			Value:     float64(temperatureValue),
 | 
								Value:     float64(temperatureValue),
 | 
				
			||||||
			ValueType: "temperature",
 | 
								ValueType: "temperature",
 | 
				
			||||||
			FromDate:  format.FormatedTime(),
 | 
								Date:      format.FormatedTime(),
 | 
				
			||||||
			TillDate:  format.FormatedTime(),
 | 
					 | 
				
			||||||
			SensorID:  bme280.ID,
 | 
								SensorID:  bme280.ID,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,16 +43,14 @@ func (dht11 *DHT11) Read() ([]*types.MeasuredValue, error) {
 | 
				
			|||||||
			ID:        uuid.NewV4().String(),
 | 
								ID:        uuid.NewV4().String(),
 | 
				
			||||||
			Value:     float64(humidityValue),
 | 
								Value:     float64(humidityValue),
 | 
				
			||||||
			ValueType: "humidity",
 | 
								ValueType: "humidity",
 | 
				
			||||||
			FromDate:  format.FormatedTime(),
 | 
								Date:      format.FormatedTime(),
 | 
				
			||||||
			TillDate:  format.FormatedTime(),
 | 
					 | 
				
			||||||
			SensorID:  dht11.ID,
 | 
								SensorID:  dht11.ID,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			ID:        uuid.NewV4().String(),
 | 
								ID:        uuid.NewV4().String(),
 | 
				
			||||||
			Value:     float64(temperatureValue),
 | 
								Value:     float64(temperatureValue),
 | 
				
			||||||
			ValueType: "temperature",
 | 
								ValueType: "temperature",
 | 
				
			||||||
			FromDate:  format.FormatedTime(),
 | 
								Date:      format.FormatedTime(),
 | 
				
			||||||
			TillDate:  format.FormatedTime(),
 | 
					 | 
				
			||||||
			SensorID:  dht11.ID,
 | 
								SensorID:  dht11.ID,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,16 +43,14 @@ func (dht22 *DHT22) Read() ([]*types.MeasuredValue, error) {
 | 
				
			|||||||
			ID:        uuid.NewV4().String(),
 | 
								ID:        uuid.NewV4().String(),
 | 
				
			||||||
			Value:     float64(humidityValue),
 | 
								Value:     float64(humidityValue),
 | 
				
			||||||
			ValueType: "humidity",
 | 
								ValueType: "humidity",
 | 
				
			||||||
			FromDate:  format.FormatedTime(),
 | 
								Date:      format.FormatedTime(),
 | 
				
			||||||
			TillDate:  format.FormatedTime(),
 | 
					 | 
				
			||||||
			SensorID:  dht22.ID,
 | 
								SensorID:  dht22.ID,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			ID:        uuid.NewV4().String(),
 | 
								ID:        uuid.NewV4().String(),
 | 
				
			||||||
			Value:     float64(temperatureValue),
 | 
								Value:     float64(temperatureValue),
 | 
				
			||||||
			ValueType: "temperature",
 | 
								ValueType: "temperature",
 | 
				
			||||||
			FromDate:  format.FormatedTime(),
 | 
								Date:      format.FormatedTime(),
 | 
				
			||||||
			TillDate:  format.FormatedTime(),
 | 
					 | 
				
			||||||
			SensorID:  dht22.ID,
 | 
								SensorID:  dht22.ID,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,8 +60,7 @@ func (ds18b20 *DS18B20) Read() ([]*types.MeasuredValue, error) {
 | 
				
			|||||||
			ID:        uuid.NewV4().String(),
 | 
								ID:        uuid.NewV4().String(),
 | 
				
			||||||
			Value:     float64(temperatureValue),
 | 
								Value:     float64(temperatureValue),
 | 
				
			||||||
			ValueType: "temperature",
 | 
								ValueType: "temperature",
 | 
				
			||||||
			FromDate:  format.FormatedTime(),
 | 
								Date:      format.FormatedTime(),
 | 
				
			||||||
			TillDate:  format.FormatedTime(),
 | 
					 | 
				
			||||||
			SensorID:  ds18b20.ID,
 | 
								SensorID:  ds18b20.ID,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										200
									
								
								pkg/testutils/dockerutils/builder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								pkg/testutils/dockerutils/builder.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,200 @@
 | 
				
			|||||||
 | 
					package dockerutils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker/api/types"
 | 
				
			||||||
 | 
						"github.com/docker/docker/api/types/container"
 | 
				
			||||||
 | 
						"github.com/docker/docker/api/types/network"
 | 
				
			||||||
 | 
						"github.com/docker/go-connections/nat"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Builder struct {
 | 
				
			||||||
 | 
						client          *Client
 | 
				
			||||||
 | 
						containerConfig *container.Config
 | 
				
			||||||
 | 
						containerName   string
 | 
				
			||||||
 | 
						hostConfig      *container.HostConfig
 | 
				
			||||||
 | 
						networkConfig   *network.NetworkingConfig
 | 
				
			||||||
 | 
						networks        map[string][]string
 | 
				
			||||||
 | 
						ports           []string
 | 
				
			||||||
 | 
						pull            bool
 | 
				
			||||||
 | 
						waitForHealthy  bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddEnv to the container
 | 
				
			||||||
 | 
					func (builder *Builder) AddEnv(key string, value string) *Builder {
 | 
				
			||||||
 | 
						builder.containerConfig.Env = append(builder.containerConfig.Env, fmt.Sprintf("%v=%v", key, value))
 | 
				
			||||||
 | 
						return builder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddLabel to the container
 | 
				
			||||||
 | 
					func (builder *Builder) AddLabel(key string, value string) *Builder {
 | 
				
			||||||
 | 
						builder.containerConfig.Labels[key] = value
 | 
				
			||||||
 | 
						return builder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Env set environment variables to the container
 | 
				
			||||||
 | 
					func (builder *Builder) Env(env map[string]string) *Builder {
 | 
				
			||||||
 | 
						builder.containerConfig.Labels = make(map[string]string)
 | 
				
			||||||
 | 
						for key, value := range env {
 | 
				
			||||||
 | 
							builder.containerConfig.Labels[key] = value
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return builder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Labels set labels to the container
 | 
				
			||||||
 | 
					func (builder *Builder) Labels(labels map[string]string) *Builder {
 | 
				
			||||||
 | 
						builder.containerConfig.Labels = labels
 | 
				
			||||||
 | 
						return builder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Memory defines a memory limit for the container
 | 
				
			||||||
 | 
					func (builder *Builder) Memory(limit int64) *Builder {
 | 
				
			||||||
 | 
						builder.hostConfig.Memory = limit
 | 
				
			||||||
 | 
						return builder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mount a source volume or hostpath into the filesystem of the container
 | 
				
			||||||
 | 
					func (builder *Builder) Mount(source string, dest string) *Builder {
 | 
				
			||||||
 | 
						builder.hostConfig.Binds = append(builder.hostConfig.Binds, fmt.Sprintf("%v:%v", source, dest))
 | 
				
			||||||
 | 
						return builder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mounts a set of source volumes or hostpath into the filesystem of the
 | 
				
			||||||
 | 
					// container
 | 
				
			||||||
 | 
					func (builder *Builder) Mounts(mounts map[string]string) *Builder {
 | 
				
			||||||
 | 
						for source, dest := range mounts {
 | 
				
			||||||
 | 
							builder.Mount(source, dest)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return builder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Port defines a port forwarding from the host machine to the container
 | 
				
			||||||
 | 
					// Examples:
 | 
				
			||||||
 | 
					// - 8080:8080
 | 
				
			||||||
 | 
					// - 10.6.231.10:8080:8080
 | 
				
			||||||
 | 
					// - 10.6.231.10:8080:8080/tcp
 | 
				
			||||||
 | 
					func (builder *Builder) Port(port string) *Builder {
 | 
				
			||||||
 | 
						builder.ports = append(builder.ports, port)
 | 
				
			||||||
 | 
						return builder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Ports defines a set port forwarding rules from the host machine to the
 | 
				
			||||||
 | 
					// container
 | 
				
			||||||
 | 
					// Examples:
 | 
				
			||||||
 | 
					// - 8080:8080
 | 
				
			||||||
 | 
					// - 10.6.231.10:8080:8080
 | 
				
			||||||
 | 
					// - 10.6.231.10:8080:8080/tcp
 | 
				
			||||||
 | 
					func (builder *Builder) Ports(ports []string) *Builder {
 | 
				
			||||||
 | 
						builder.ports = ports
 | 
				
			||||||
 | 
						return builder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Pull the image if absent
 | 
				
			||||||
 | 
					func (builder *Builder) Pull() *Builder {
 | 
				
			||||||
 | 
						builder.pull = true
 | 
				
			||||||
 | 
						return builder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Start the container
 | 
				
			||||||
 | 
					func (builder *Builder) Start(ctx context.Context) (string, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Pull container image if absent
 | 
				
			||||||
 | 
						if builder.pull {
 | 
				
			||||||
 | 
							err := builder.client.PullQuiet(ctx, builder.containerConfig.Image)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check if ports are available
 | 
				
			||||||
 | 
						exposedPorts, portBindings, err := nat.ParsePortSpecs(builder.ports)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("Failed to parse ports %v: %v", builder.ports, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(portBindings) > 0 {
 | 
				
			||||||
 | 
							builder.containerConfig.ExposedPorts = exposedPorts
 | 
				
			||||||
 | 
							builder.hostConfig.PortBindings = portBindings
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// add endpoint settings to existing networks
 | 
				
			||||||
 | 
						networkExist := make(map[string]bool, 0)
 | 
				
			||||||
 | 
						for networkName := range builder.networks {
 | 
				
			||||||
 | 
							networkExist[networkName] = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						networks, err := builder.client.NetworkList(ctx, types.NetworkListOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("Failed to list networks: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, nw := range networks {
 | 
				
			||||||
 | 
							if aliases, present := builder.networks[nw.Name]; present {
 | 
				
			||||||
 | 
								networkExist[nw.Name] = true
 | 
				
			||||||
 | 
								builder.networkConfig.EndpointsConfig[nw.Name] = &network.EndpointSettings{
 | 
				
			||||||
 | 
									Aliases:   aliases,
 | 
				
			||||||
 | 
									NetworkID: nw.ID,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for nw, found := range networkExist {
 | 
				
			||||||
 | 
							if !found {
 | 
				
			||||||
 | 
								return "", fmt.Errorf("Failed to add endpoint settings for network %v. It does not exist", nw)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// create container
 | 
				
			||||||
 | 
						resp, err := builder.client.ContainerCreate(ctx, builder.containerConfig, builder.hostConfig, builder.networkConfig, builder.containerName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("Failed to create container %v: %v", builder.containerName, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// start container
 | 
				
			||||||
 | 
						err = builder.client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							stopError := builder.client.ContainerStopByIDs(ctx, time.Second, resp.ID)
 | 
				
			||||||
 | 
							if stopError != nil {
 | 
				
			||||||
 | 
								return "", fmt.Errorf("Failed to start container %v: %v\nUnable to remove container %v. Manual cleanup necessary", builder.containerName, err, builder.containerName)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return "", fmt.Errorf("Failed to start container %v: %v", builder.containerName, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// wait for healthy
 | 
				
			||||||
 | 
						if builder.waitForHealthy {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							watcher := builder.client.GetWatcher()
 | 
				
			||||||
 | 
							errorChannel := make(chan error, 1)
 | 
				
			||||||
 | 
							doneChannel := make(chan struct{})
 | 
				
			||||||
 | 
							err = watcher.AddListener(resp.ID, errorChannel, doneChannel)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								containerRemoveError := builder.client.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
 | 
				
			||||||
 | 
								if containerRemoveError != nil {
 | 
				
			||||||
 | 
									return "", fmt.Errorf("Failed while watching status for container %v: %v\nUnable to remove container %v: %v", resp.ID, err, resp.ID, containerRemoveError)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return "", fmt.Errorf("Failed while watching status for container %v: %v", resp.ID, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case err := <-errorChannel:
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									containerRemoveError := builder.client.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
 | 
				
			||||||
 | 
									if containerRemoveError != nil {
 | 
				
			||||||
 | 
										return "", fmt.Errorf("Unable to remove container %v: %v", resp.ID, containerRemoveError)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return "", err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case <-doneChannel:
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return resp.ID, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (builder *Builder) WaitForHealthy() *Builder {
 | 
				
			||||||
 | 
						builder.waitForHealthy = true
 | 
				
			||||||
 | 
						return builder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										220
									
								
								pkg/testutils/dockerutils/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								pkg/testutils/dockerutils/client.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,220 @@
 | 
				
			|||||||
 | 
					package dockerutils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker/api/types"
 | 
				
			||||||
 | 
						"github.com/docker/docker/api/types/container"
 | 
				
			||||||
 | 
						"github.com/docker/docker/api/types/filters"
 | 
				
			||||||
 | 
						"github.com/docker/docker/api/types/network"
 | 
				
			||||||
 | 
						"github.com/docker/docker/client"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Client struct {
 | 
				
			||||||
 | 
						*client.Client
 | 
				
			||||||
 | 
						watcher *Watcher
 | 
				
			||||||
 | 
						mutex   *sync.Mutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Close docker connection
 | 
				
			||||||
 | 
					func (client *Client) Close() error {
 | 
				
			||||||
 | 
						client.mutex.Lock()
 | 
				
			||||||
 | 
						defer client.mutex.Unlock()
 | 
				
			||||||
 | 
						if client.watcher != nil {
 | 
				
			||||||
 | 
							client.watcher.stop()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return client.Close()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ContainerListByLabels returns only containers which match by given labels
 | 
				
			||||||
 | 
					func (client *Client) ContainerListByLabels(ctx context.Context, all bool, labels map[string]string) ([]types.Container, error) {
 | 
				
			||||||
 | 
						filterArgs := filters.NewArgs()
 | 
				
			||||||
 | 
						for key, value := range labels {
 | 
				
			||||||
 | 
							filterArgs.Add("label", fmt.Sprintf("%v=%v", key, value))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						containers, err := client.ContainerList(ctx, types.ContainerListOptions{
 | 
				
			||||||
 | 
							All:     all,
 | 
				
			||||||
 | 
							Filters: filterArgs,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if containers == nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("No containers found by given labels")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return containers, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ContainerListByNames returns only containers which match by given labels
 | 
				
			||||||
 | 
					func (client *Client) ContainerListByNames(ctx context.Context, all bool, names ...string) ([]types.Container, error) {
 | 
				
			||||||
 | 
						filterArgs := filters.NewArgs()
 | 
				
			||||||
 | 
						for _, name := range names {
 | 
				
			||||||
 | 
							filterArgs.Add("name", name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						containers, err := client.ContainerList(ctx, types.ContainerListOptions{
 | 
				
			||||||
 | 
							All:     all,
 | 
				
			||||||
 | 
							Filters: filterArgs,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if containers == nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("No containers found by given names")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return containers, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ContainerRemoveByIDs deletes all containers which match by their container ids
 | 
				
			||||||
 | 
					func (client *Client) ContainerRemoveByIDs(ctx context.Context, containerIDs ...string) error {
 | 
				
			||||||
 | 
						for _, containerID := range containerIDs {
 | 
				
			||||||
 | 
							err := client.ContainerRemove(ctx, containerID, types.ContainerRemoveOptions{Force: true})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ContainerRemoveByLabels deletes all containers which match by given labels
 | 
				
			||||||
 | 
					func (client *Client) ContainerRemoveByLabels(ctx context.Context, labels map[string]string) error {
 | 
				
			||||||
 | 
						containers, err := client.ContainerListByLabels(ctx, true, labels)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, container := range containers {
 | 
				
			||||||
 | 
							err := client.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{Force: true})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ContainerRemoveByNames deletes all containers which match by their names
 | 
				
			||||||
 | 
					func (client *Client) ContainerRemoveByNames(ctx context.Context, names ...string) error {
 | 
				
			||||||
 | 
						containers, err := client.ContainerListByNames(ctx, true, names...)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, container := range containers {
 | 
				
			||||||
 | 
							err := client.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{Force: true})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ContainerStopByIDs deletes all containers which match by their container ids
 | 
				
			||||||
 | 
					func (client *Client) ContainerStopByIDs(ctx context.Context, timeout time.Duration, containerIDs ...string) error {
 | 
				
			||||||
 | 
						for _, containerID := range containerIDs {
 | 
				
			||||||
 | 
							err := client.ContainerStop(ctx, containerID, &timeout)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ContainerStopByLabels shutdown containters which match by given labels
 | 
				
			||||||
 | 
					func (client *Client) ContainerStopByLabels(ctx context.Context, timeout time.Duration, labels map[string]string) error {
 | 
				
			||||||
 | 
						containers, err := client.ContainerListByLabels(ctx, true, labels)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, container := range containers {
 | 
				
			||||||
 | 
							err := client.ContainerStop(ctx, container.ID, &timeout)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetWatcher returns a watcher for container health states
 | 
				
			||||||
 | 
					func (client *Client) GetWatcher() *Watcher {
 | 
				
			||||||
 | 
						if client.watcher != nil {
 | 
				
			||||||
 | 
							return client.watcher
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client.mutex.Lock()
 | 
				
			||||||
 | 
						defer client.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client.watcher = &Watcher{
 | 
				
			||||||
 | 
							client:        client,
 | 
				
			||||||
 | 
							errorChannels: make(map[string]chan<- error),
 | 
				
			||||||
 | 
							doneChannels:  make(map[string]chan<- struct{}),
 | 
				
			||||||
 | 
							errorMapper:   make(map[string]ErrorMapper),
 | 
				
			||||||
 | 
							mutex:         new(sync.RWMutex),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client.watcher.start()
 | 
				
			||||||
 | 
						return client.watcher
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewBuilder returns a new builder for containers
 | 
				
			||||||
 | 
					func (client *Client) NewBuilder(image string) *Builder {
 | 
				
			||||||
 | 
						return &Builder{
 | 
				
			||||||
 | 
							client: client,
 | 
				
			||||||
 | 
							containerConfig: &container.Config{
 | 
				
			||||||
 | 
								Image: image,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							hostConfig:     new(container.HostConfig),
 | 
				
			||||||
 | 
							networkConfig:  new(network.NetworkingConfig),
 | 
				
			||||||
 | 
							networks:       make(map[string][]string, 0),
 | 
				
			||||||
 | 
							ports:          make([]string, 0),
 | 
				
			||||||
 | 
							pull:           false,
 | 
				
			||||||
 | 
							waitForHealthy: false,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Pull image
 | 
				
			||||||
 | 
					func (client *Client) Pull(ctx context.Context, image string, w io.Writer) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parts := strings.Split(image, "/")
 | 
				
			||||||
 | 
						switch len(parts) {
 | 
				
			||||||
 | 
						case 1:
 | 
				
			||||||
 | 
							image = fmt.Sprintf("docker.io/library/%v", parts[0])
 | 
				
			||||||
 | 
						case 2:
 | 
				
			||||||
 | 
							if strings.Compare(parts[0], "library") == 0 ||
 | 
				
			||||||
 | 
								strings.Compare(parts[0], "docker.io") == 0 {
 | 
				
			||||||
 | 
								image = fmt.Sprintf("docker.io/library/%v", parts[1])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						readCloser, err := client.ImagePull(ctx, image, types.ImagePullOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = io.Copy(w, readCloser)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PullQuiet image
 | 
				
			||||||
 | 
					func (client *Client) PullQuiet(ctx context.Context, image string) error {
 | 
				
			||||||
 | 
						return client.Pull(ctx, image, ioutil.Discard)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// New returns a new dockerutil client
 | 
				
			||||||
 | 
					func New() (*Client, error) {
 | 
				
			||||||
 | 
						dockerClient, err := client.NewEnvClient()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &Client{
 | 
				
			||||||
 | 
							dockerClient,
 | 
				
			||||||
 | 
							nil,
 | 
				
			||||||
 | 
							new(sync.Mutex),
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										164
									
								
								pkg/testutils/dockerutils/watcher.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								pkg/testutils/dockerutils/watcher.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,164 @@
 | 
				
			|||||||
 | 
					package dockerutils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker/api/types"
 | 
				
			||||||
 | 
						"github.com/docker/docker/api/types/events"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						ErrDied      = errors.New("died")
 | 
				
			||||||
 | 
						ErrUnhealthy = errors.New("went unhealthy")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Watcher struct {
 | 
				
			||||||
 | 
						client        *Client
 | 
				
			||||||
 | 
						errorChannels map[string]chan<- error
 | 
				
			||||||
 | 
						doneChannels  map[string]chan<- struct{}
 | 
				
			||||||
 | 
						errorMapper   map[string]ErrorMapper
 | 
				
			||||||
 | 
						mutex         *sync.RWMutex
 | 
				
			||||||
 | 
						lastError     error
 | 
				
			||||||
 | 
						cancelFunc    context.CancelFunc
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (watcher *Watcher) AddListenerWithErrorMapper(containerID string, errorChannel chan<- error, doneChannel chan<- struct{}, errorMapper ErrorMapper) error {
 | 
				
			||||||
 | 
						watcher.mutex.Lock()
 | 
				
			||||||
 | 
						defer watcher.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if watcher.lastError != nil {
 | 
				
			||||||
 | 
							return watcher.lastError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(20*time.Second))
 | 
				
			||||||
 | 
						defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						containerJSON, err := watcher.client.ContainerInspect(ctx, containerID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("Unable to check if container is already unhealthy: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if containerJSON.State.Dead || !containerJSON.State.Running {
 | 
				
			||||||
 | 
							go func() {
 | 
				
			||||||
 | 
								errorChannel <- errorMapper(fmt.Errorf("Container %v: %w", containerID, ErrDied))
 | 
				
			||||||
 | 
								doneChannel <- struct{}{}
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if containerJSON.State.Health == nil {
 | 
				
			||||||
 | 
							go func() {
 | 
				
			||||||
 | 
								doneChannel <- struct{}{}
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch containerJSON.State.Health.Status {
 | 
				
			||||||
 | 
						case "starting":
 | 
				
			||||||
 | 
							watcher.errorChannels[containerID] = errorChannel
 | 
				
			||||||
 | 
							watcher.doneChannels[containerID] = doneChannel
 | 
				
			||||||
 | 
							watcher.errorMapper[containerID] = errorMapper
 | 
				
			||||||
 | 
						case "healthy":
 | 
				
			||||||
 | 
							go func() {
 | 
				
			||||||
 | 
								doneChannel <- struct{}{}
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
						case "unhealthy":
 | 
				
			||||||
 | 
							go func() {
 | 
				
			||||||
 | 
								errorChannel <- errorMapper(fmt.Errorf("Container %v: %w", containerID, ErrUnhealthy))
 | 
				
			||||||
 | 
								doneChannel <- struct{}{}
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							go func() {
 | 
				
			||||||
 | 
								errorChannel <- errorMapper(fmt.Errorf("Container %v went in an unknown state during startup: %s", containerID, containerJSON.State.Health.Status))
 | 
				
			||||||
 | 
								doneChannel <- struct{}{}
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (watcher *Watcher) AddListener(containerID string, errorChannel chan<- error, doneChannel chan<- struct{}) error {
 | 
				
			||||||
 | 
						return watcher.AddListenerWithErrorMapper(containerID, errorChannel, doneChannel, defaultErrorMapper)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (watcher *Watcher) removeListener(containerID string) {
 | 
				
			||||||
 | 
						watcher.mutex.Lock()
 | 
				
			||||||
 | 
						defer watcher.mutex.Unlock()
 | 
				
			||||||
 | 
						delete(watcher.doneChannels, containerID)
 | 
				
			||||||
 | 
						delete(watcher.errorChannels, containerID)
 | 
				
			||||||
 | 
						delete(watcher.errorMapper, containerID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (watcher *Watcher) start() {
 | 
				
			||||||
 | 
						ctx, cancel := context.WithCancel(context.Background())
 | 
				
			||||||
 | 
						watcher.cancelFunc = cancel
 | 
				
			||||||
 | 
						dockerEvents, errors := watcher.client.Events(ctx, types.EventsOptions{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sendErrorFunc := func() {
 | 
				
			||||||
 | 
							watcher.mutex.Lock()
 | 
				
			||||||
 | 
							for containerID, errorChannel := range watcher.errorChannels {
 | 
				
			||||||
 | 
								go func(errorChannel chan<- error, doneChannel chan<- struct{}, err error, errorMapper ErrorMapper) {
 | 
				
			||||||
 | 
									errorChannel <- errorMapper(err)
 | 
				
			||||||
 | 
									doneChannel <- struct{}{}
 | 
				
			||||||
 | 
								}(errorChannel, watcher.doneChannels[containerID], watcher.lastError, watcher.errorMapper[containerID])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							watcher.mutex.Unlock()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							for {
 | 
				
			||||||
 | 
								select {
 | 
				
			||||||
 | 
								case event := <-dockerEvents:
 | 
				
			||||||
 | 
									watcher.mutex.RLock()
 | 
				
			||||||
 | 
									errorChannel, present := watcher.errorChannels[event.Actor.ID]
 | 
				
			||||||
 | 
									if !present || event.Type != events.ContainerEventType {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									doneChannel := watcher.doneChannels[event.Actor.ID]
 | 
				
			||||||
 | 
									errorMapper := watcher.errorMapper[event.Actor.ID]
 | 
				
			||||||
 | 
									watcher.mutex.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									switch event.Action {
 | 
				
			||||||
 | 
									case "health_status: healthy":
 | 
				
			||||||
 | 
										go func() {
 | 
				
			||||||
 | 
											doneChannel <- struct{}{}
 | 
				
			||||||
 | 
										}()
 | 
				
			||||||
 | 
										watcher.removeListener(event.Actor.ID)
 | 
				
			||||||
 | 
									case "health_status: unhealthy":
 | 
				
			||||||
 | 
										go func() {
 | 
				
			||||||
 | 
											errorChannel <- errorMapper(fmt.Errorf("Container %v: %w", event.Actor.ID, ErrUnhealthy))
 | 
				
			||||||
 | 
											doneChannel <- struct{}{}
 | 
				
			||||||
 | 
										}()
 | 
				
			||||||
 | 
										watcher.removeListener(event.Actor.ID)
 | 
				
			||||||
 | 
									case "die":
 | 
				
			||||||
 | 
										go func() {
 | 
				
			||||||
 | 
											errorChannel <- errorMapper(fmt.Errorf("Container %v: %w", event.Actor.ID, ErrDied))
 | 
				
			||||||
 | 
											doneChannel <- struct{}{}
 | 
				
			||||||
 | 
										}()
 | 
				
			||||||
 | 
										watcher.removeListener(event.Actor.ID)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								case err := <-errors:
 | 
				
			||||||
 | 
									watcher.lastError = err
 | 
				
			||||||
 | 
									sendErrorFunc()
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								case <-ctx.Done():
 | 
				
			||||||
 | 
									watcher.lastError = fmt.Errorf("Watcher was canceled")
 | 
				
			||||||
 | 
									sendErrorFunc()
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (watcher *Watcher) stop() {
 | 
				
			||||||
 | 
						watcher.cancelFunc()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ErrorMapper func(error) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func defaultErrorMapper(err error) error {
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,9 +4,8 @@ import "time"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Device represent a device with all his settings.
 | 
					// Device represent a device with all his settings.
 | 
				
			||||||
type Device struct {
 | 
					type Device struct {
 | 
				
			||||||
	ID           string     `json:"id" xml:"id"`
 | 
						ID           string    `json:"id" xml:"id"`
 | 
				
			||||||
	Name         string     `json:"name" xml:"name"`
 | 
						Name         string    `json:"name" xml:"name"`
 | 
				
			||||||
	Location     *string    `json:"location" xml:"location"`
 | 
						Location     *string   `json:"location" xml:"location"`
 | 
				
			||||||
	LastContact  *time.Time `json:"last_contact" xml:"last_contact"`
 | 
						CreationDate time.Time `json:"creation_date" xml:"creation_date"`
 | 
				
			||||||
	CreationDate time.Time  `json:"creation_date" xml:"creation_date"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,8 +11,7 @@ type MeasuredValue struct {
 | 
				
			|||||||
	ID           string     `json:"id" xml:"id"`
 | 
						ID           string     `json:"id" xml:"id"`
 | 
				
			||||||
	Value        float64    `json:"value,string" xml:"value,string"`
 | 
						Value        float64    `json:"value,string" xml:"value,string"`
 | 
				
			||||||
	ValueType    string     `json:"value_type" xml:"value_type"`
 | 
						ValueType    string     `json:"value_type" xml:"value_type"`
 | 
				
			||||||
	FromDate     time.Time  `json:"from_date" xml:"from_date"`
 | 
						Date         time.Time  `json:"date" xml:"date"`
 | 
				
			||||||
	TillDate     time.Time  `json:"till_date" xml:"till_date"`
 | 
					 | 
				
			||||||
	SensorID     string     `json:"sensor_id" xml:"sensor_id"`
 | 
						SensorID     string     `json:"sensor_id" xml:"sensor_id"`
 | 
				
			||||||
	CreationDate *time.Time `json:"creation_date" xml:"creation_date"`
 | 
						CreationDate *time.Time `json:"creation_date" xml:"creation_date"`
 | 
				
			||||||
	UpdateDate   *time.Time `json:"update_date" xml:"update_date"`
 | 
						UpdateDate   *time.Time `json:"update_date" xml:"update_date"`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,19 +7,18 @@ import (
 | 
				
			|||||||
// Sensor represents a sensor with all his settings. The struct does not
 | 
					// Sensor represents a sensor with all his settings. The struct does not
 | 
				
			||||||
// contains any read method.
 | 
					// contains any read method.
 | 
				
			||||||
type Sensor struct {
 | 
					type Sensor struct {
 | 
				
			||||||
	ID           string     `json:"id" xml:"id"`
 | 
						ID           string    `json:"id" xml:"id"`
 | 
				
			||||||
	Name         string     `json:"name" xml:"name"`
 | 
						Name         string    `json:"name" xml:"name"`
 | 
				
			||||||
	Location     string     `json:"location" xml:"location"`
 | 
						Location     string    `json:"location" xml:"location"`
 | 
				
			||||||
	WireID       *string    `json:"wire_id" xml:"wire_id"`
 | 
						WireID       *string   `json:"wire_id" xml:"wire_id"`
 | 
				
			||||||
	I2CBus       *int       `json:"i2c_bus" xml:"i2c_bus"`
 | 
						I2CBus       *int      `json:"i2c_bus" xml:"i2c_bus"`
 | 
				
			||||||
	I2CAddress   *uint8     `json:"i2c_address" xml:"i2c_address"`
 | 
						I2CAddress   *uint8    `json:"i2c_address" xml:"i2c_address"`
 | 
				
			||||||
	GPIONumber   string     `json:"gpio_number" xml:"gpio_number"`
 | 
						GPIONumber   string    `json:"gpio_number" xml:"gpio_number"`
 | 
				
			||||||
	Model        string     `json:"model" xml:"model"`
 | 
						Model        string    `json:"model" xml:"model"`
 | 
				
			||||||
	Enabled      bool       `json:"enabled" xml:"enabled"`
 | 
						Enabled      bool      `json:"enabled" xml:"enabled"`
 | 
				
			||||||
	LastContact  *time.Time `json:"last_contact" xml:"last_contact"`
 | 
						TickDuration string    `json:"tick_duration" xml:"tick_duration"`
 | 
				
			||||||
	TickDuration string     `json:"tick_duration" xml:"tick_duration"`
 | 
						DeviceID     string    `json:"device_id" xml:"device_id"`
 | 
				
			||||||
	DeviceID     string     `json:"device_id" xml:"device_id"`
 | 
						CreationDate time.Time `json:"creation_date" xml:"creation_date"`
 | 
				
			||||||
	CreationDate time.Time  `json:"creation_date" xml:"creation_date"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetID returns the UUID of the sensor.
 | 
					// GetID returns the UUID of the sensor.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user