Skip to content

Commit

Permalink
fix: camera mgmt docker build, secret names, and go mods (#229)
Browse files Browse the repository at this point in the history
* fix: camera mgmt docker build, secret names, and go mods

- Fixed the docker build for Camera Management App and make it work with
  docker compose.
- Fixed the naming inconsistencies with secrets in the yaml file and
  code. Now also matches rtspauth from the device-usb-camera service.
- Removed errors package from go.mod, to use EdgeX errors package, to
  match the device services.

Signed-off-by: Anthony Casagrande <[email protected]>
  • Loading branch information
Anthony Casagrande authored Jun 5, 2023
1 parent fa11870 commit 04d6a19
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 78 deletions.
12 changes: 6 additions & 6 deletions application-services/custom/camera-management/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
#

#build stage
ARG BASE=golang:1.18-alpine3.16
ARG BASE=golang:1.20-alpine3.17
FROM ${BASE} AS builder

ARG ALPINE_PKG_BASE="make git gcc libc-dev libsodium-dev zeromq-dev"
ARG ALPINE_PKG_BASE="make git gcc libc-dev libsodium-dev"
ARG ALPINE_PKG_EXTRA=""

LABEL license='SPDX-License-Identifier: Apache-2.0' \
Expand All @@ -37,13 +37,13 @@ ARG MAKE="make build-app"
RUN $MAKE

#final stage
FROM alpine:3.16
FROM alpine:3.17
LABEL license='SPDX-License-Identifier: Apache-2.0' \
copyright='Copyright (c) 2022: Intel'
copyright='Copyright (c) 2023: Intel'
LABEL Name=app-camera-management Version=${VERSION}

# dumb-init is required as security-bootstrapper uses it in the entrypoint script
RUN apk add --update --no-cache ca-certificates zeromq dumb-init
RUN apk add --update --no-cache ca-certificates dumb-init

COPY --from=builder /app/LICENSE /LICENSE
COPY --from=builder /app/res/ /res/
Expand All @@ -53,4 +53,4 @@ COPY --from=builder /app/web-ui/dist /web-ui/dist
EXPOSE 59750

ENTRYPOINT ["/app-camera-management"]
CMD ["-cp=consul.http://edgex-core-consul:8500", "--registry", "--confdir=/res"]
CMD ["--cp=consul.http://edgex-core-consul:8500", "--registry"]
26 changes: 10 additions & 16 deletions application-services/custom/camera-management/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,8 @@ cd edgex-examples/application-services/custom/camera-management
# Run this once to download edge-video-analytics into the edge-video-analytics sub-folder,
# download models, and patch pipelines
make install-edge-video-analytics
# Run the EVAM services (in another terminal)
make run-edge-video-analytics
# ...
# Leave this running
```

> **Note**: If you press `Ctrl-C` it will stop the EVAM services. If you then run `make stop-edge-video-analytics`,
> it will also remove the containers and free up the port mappings.

### 3. Build and run the example application service

#### 3.1 (Optional) Configure Onvif Camera Credentials.
Expand All @@ -161,8 +153,8 @@ make run-edge-video-analytics

```yaml
InsecureSecrets:
onvifCredentials:
SecretName: onvifAuth
onvifauth:
SecretName: onvifauth
SecretData:
username: "<username>"
password: "<password>"
Expand All @@ -183,8 +175,8 @@ Option 1: Modify the [res/configuration.yaml](res/configuration.yaml) file

```yaml
InsecureSecrets:
usbCredentials:
SecretName: rtspAuth
rtspauth:
SecretName: rtspauth
SecretData:
username: "<username>"
password: "<password>"
Expand Down Expand Up @@ -215,13 +207,15 @@ cd edgex-examples/application-services/custom/camera-management
```

```shell
# Build the app.
make build-app
# Build the docker image.
make docker
# Run the app.
make run-app
# Start the docker compose services in the background for both EVAM and Camera Management App
docker compose up -d
```

> **Note**: If you would like to view the logs for these services, you can use `docker compose logs -f`. To stop the services, use `docker compose down`.

## Using the App

1. Visit https://localhost:59750 to access the app.
Expand Down
37 changes: 25 additions & 12 deletions application-services/custom/camera-management/appcamera/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ package appcamera
import (
"net/http"
"sync"
"time"

"github.com/edgexfoundry/app-functions-sdk-go/v3/pkg/interfaces"
"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger"
"github.com/pkg/errors"
"github.com/edgexfoundry/go-mod-core-contracts/v3/errors"
)

const (
maxRetries = 10
)

type CameraManagementApp struct {
Expand All @@ -37,23 +42,31 @@ func NewCameraManagementApp(service interfaces.ApplicationService) *CameraManage

func (app *CameraManagementApp) Run() error {
if err := app.service.LoadCustomConfig(app.config, "AppCustom"); err != nil {
return errors.Wrap(err, "failed to load custom configuration")
return errors.NewCommonEdgeX(errors.KindServerError, "failed to load custom configuration", err)
}

var err error
for i := 0; i < maxRetries; i++ {
app.lc.Infof("Querying EVAM pipeline statuses.")
if err = app.queryAllPipelineStatuses(); err != nil {
app.lc.Errorf("Unable to query EVAM pipeline statuses. Is EVAM running? %s", err.Error())
time.Sleep(time.Second)
} else {
break // no error, so lets continue
}
}
if err != nil {
app.lc.Errorf("Unable to query EVAM pipeline statuses after %d tries.", maxRetries)
return err // exit. we do not want to run if evam is not accessible
}

if err := app.addRoutes(); err != nil {
return err
}

// Subscribe to events.
err := app.service.SetDefaultFunctionsPipeline(
app.processEdgeXDeviceSystemEvent)
if err != nil {
return errors.Wrap(err, "failed to set default pipeline to processEdgeXEvent")
}

if err = app.queryAllPipelineStatuses(); err != nil {
// do not exit, just log
app.lc.Errorf("Unable to query EVAM pipeline statuses. Is EVAM running? %s", err.Error())
if err := app.service.SetDefaultFunctionsPipeline(app.processEdgeXDeviceSystemEvent); err != nil {
return errors.NewCommonEdgeX(errors.KindServerError, "failed to set default pipeline to processEdgeXEvent", err)
}

devices, err := app.getAllDevices()
Expand All @@ -68,7 +81,7 @@ func (app *CameraManagementApp) Run() error {
}

if err = app.service.Run(); err != nil {
return errors.Wrap(err, "failed to run pipeline")
return errors.NewCommonEdgeX(errors.KindServerError, "failed to run pipeline", err)
}

return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ package appcamera
import (
"context"
"fmt"
"github.com/edgexfoundry/go-mod-core-contracts/v3/errors"

"github.com/IOTechSystems/onvif/device"
"github.com/IOTechSystems/onvif/media"
"github.com/IOTechSystems/onvif/ptz"
"github.com/IOTechSystems/onvif/xsd/onvif"
"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos"
dtosCommon "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/common"
"github.com/pkg/errors"
)

const (
Expand All @@ -36,7 +36,7 @@ const (
func (app *CameraManagementApp) getImageFormats(deviceName string) (interface{}, error) {
resp, err := app.issueGetCommand(context.Background(), deviceName, usbImageFormatsCommand)
if err != nil {
return nil, errors.Wrapf(err, "failed to issue get ImageFormats command")
return nil, errors.NewCommonEdgeX(errors.KindServerError, "failed to issue get ImageFormats command", err)
}
return resp.Event.Readings[0].ObjectValue, nil
}
Expand Down Expand Up @@ -80,7 +80,7 @@ func (app *CameraManagementApp) getCameraFeatures(deviceName string) (CameraFeat
features.CameraType = Onvif
caps, err := app.getCapabilities(deviceName)
if err != nil {
return CameraFeatures{}, errors.Wrapf(err, "unable to get device capabilities")
return CameraFeatures{}, errors.NewCommonEdgeX(errors.KindServerError, "unable to get device capabilities", err)
}
if caps.Capabilities.PTZ.XAddr != "" {
features.PTZ = true
Expand Down Expand Up @@ -193,8 +193,8 @@ func (app *CameraManagementApp) getAllDevices() ([]dtos.Device, error) {
}

if len(devices) <= 0 {
return nil, errors.Errorf("no devices registered yet for the device services %s or %s",
app.config.AppCustom.OnvifDeviceServiceName, app.config.AppCustom.USBDeviceServiceName)
return nil, errors.NewCommonEdgeX(errors.KindServerError, fmt.Sprintf("no devices registered yet for the device services %s or %s",
app.config.AppCustom.OnvifDeviceServiceName, app.config.AppCustom.USBDeviceServiceName), nil)
}

return devices, nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (
)

const (
rtspAuth = "rtspAuth"
onvifAuth = "onvifAuth"
rtspauth = "rtspauth"
onvifauth = "onvifauth"
)

// tryGetCredentials will attempt one time to get the camera credentials from the
Expand Down
32 changes: 16 additions & 16 deletions application-services/custom/camera-management/appcamera/evam.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"github.com/IOTechSystems/onvif/media"
"github.com/edgexfoundry/app-functions-sdk-go/v3/pkg/interfaces"
"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos"
"github.com/pkg/errors"
"github.com/edgexfoundry/go-mod-core-contracts/v3/errors"
)

const (
Expand Down Expand Up @@ -53,7 +53,7 @@ func (app *CameraManagementApp) addPipelineInfo(camera string, info PipelineInfo
app.pipelinesMap[camera] = info
return nil
}
return errors.Errorf("pipeline already running for device %v", camera)
return errors.NewCommonEdgeX(errors.KindServerError, fmt.Sprintf("pipeline already running for device %v", camera), nil)
}

func (app *CameraManagementApp) deletePipelineInfo(camera string) {
Expand Down Expand Up @@ -83,7 +83,7 @@ func (app *CameraManagementApp) queryStreamUri(deviceName string, sr StartPipeli
} else if sr.Onvif != nil {
return app.getOnvifStreamUri(deviceName, sr.Onvif.ProfileToken)
}
return "", errors.New("missing required stream configuration")
return "", errors.NewCommonEdgeX(errors.KindServerError, "missing required stream configuration", nil)
}

func (app *CameraManagementApp) getOnvifStreamUri(deviceName string, profileToken string) (string, error) {
Expand All @@ -99,7 +99,7 @@ func (app *CameraManagementApp) getOnvifStreamUri(deviceName string, profileToke
func (app *CameraManagementApp) getUSBStreamUri(deviceName string) (string, error) {
cmdResponse, err := app.issueGetCommand(context.Background(), deviceName, usbStreamUriCommand)
if err != nil {
return "", errors.Wrapf(err, "failed to issue get StreamUri command")
return "", errors.NewCommonEdgeX(errors.KindServerError, fmt.Sprintf("failed to issue get StreamUri command"), err)
}
return cmdResponse.Event.Readings[0].Value, nil
}
Expand All @@ -112,20 +112,20 @@ func (app *CameraManagementApp) startPipeline(deviceName string, sr StartPipelin
app.lc.Infof("Received stream uri for the device %s: %s", deviceName, streamUri)

// set the secret name to be the onvif one by default
secretName := onvifAuth
secretName := onvifauth
// if device is usb camera, start streaming first
if sr.USB != nil {
_, err := app.startStreaming(deviceName, *sr.USB)
if err != nil {
return errors.Wrapf(err, "failed to start streaming usb camera %s", deviceName)
return errors.NewCommonEdgeX(errors.KindServerError, fmt.Sprintf("failed to start streaming usb camera %s", deviceName), err)
}
// for usb cameras, use the rtspAuth instead
secretName = rtspAuth
// for usb cameras, use the rtspauth instead
secretName = rtspauth
}

body, err := app.createPipelineRequestBody(streamUri, deviceName, secretName)
if err != nil {
return errors.Wrapf(err, "failed to create DLStreamer pipeline request body")
return errors.NewCommonEdgeX(errors.KindServerError, "failed to create DLStreamer pipeline request body", err)
}

info := PipelineInfo{
Expand All @@ -140,11 +140,11 @@ func (app *CameraManagementApp) startPipeline(deviceName string, sr StartPipelin
reqPath := path.Join("/pipelines", info.Name, info.Version)

if err = issuePostRequest(context.Background(), &res, baseUrl.String(), reqPath, body); err != nil {
err = errors.Wrap(err, "POST request to start EVAM pipeline failed")
err = errors.NewCommonEdgeX(errors.KindServerError, "POST request to start EVAM pipeline failed", err)
// if we started the streaming on usb camera, we need to stop it
if sr.USB != nil {
if _, err2 := app.stopStreaming(deviceName); err2 != nil {
err = errors.Wrapf(err, "failed to stop streaming usb camera %s", deviceName)
err = errors.NewCommonEdgeX(errors.KindServerError, fmt.Sprintf("failed to stop streaming usb camera %s", deviceName), err)
}
}
return err
Expand All @@ -165,7 +165,7 @@ func (app *CameraManagementApp) stopPipeline(deviceName string, id string) error
var res interface{}

if err := issueDeleteRequest(context.Background(), &res, app.config.AppCustom.EvamBaseUrl, path.Join("/pipelines", id)); err != nil {
return errors.Wrap(err, "DELETE request to stop EVAM pipeline failed")
return errors.NewCommonEdgeX(errors.KindServerError, "DELETE request to stop EVAM pipeline failed", err)
}
app.lc.Infof("Successfully stopped EVAM pipeline for the device %s", deviceName)

Expand Down Expand Up @@ -219,7 +219,7 @@ func (app *CameraManagementApp) getPipelineStatus(deviceName string) (interface{
if info, found := app.getPipelineInfo(deviceName); found {
var res interface{}
if err := issueGetRequest(context.Background(), &res, app.config.AppCustom.EvamBaseUrl, path.Join("/pipelines", "status", info.Id)); err != nil {
return nil, errors.Wrap(err, "GET request to query EVAM pipeline status failed")
return nil, errors.NewCommonEdgeX(errors.KindServerError, "GET request to query EVAM pipeline status failed", err)
}
return res, nil
}
Expand Down Expand Up @@ -319,7 +319,7 @@ func (app *CameraManagementApp) startDefaultPipeline(device dtos.Device) error {
func (app *CameraManagementApp) queryAllPipelineStatuses() error {
var statuses []PipelineStatus
if err := issueGetRequest(context.Background(), &statuses, app.config.AppCustom.EvamBaseUrl, path.Join("/pipelines", "status")); err != nil {
return errors.Wrap(err, "GET request to query EVAM pipeline statuses failed")
return errors.NewCommonEdgeX(errors.KindServerError, "GET request to query EVAM pipeline statuses failed", err)
}

for _, status := range statuses {
Expand Down Expand Up @@ -370,7 +370,7 @@ func (app *CameraManagementApp) getAllPipelineStatuses() (map[string]PipelineInf
// loop through the partially filled response map to fill in the missing data. we do not need to hold the lock here.
for camera, data := range response {
if err := issueGetRequest(context.Background(), &data.Status, app.config.AppCustom.EvamBaseUrl, path.Join("/pipelines", "status", data.Info.Id)); err != nil {
return nil, errors.Wrap(err, "GET request to query EVAM pipeline failed")
return nil, errors.NewCommonEdgeX(errors.KindServerError, "GET request to query EVAM pipeline failed", err)
}
// overwrite the changed result in the map
response[camera] = data
Expand All @@ -382,7 +382,7 @@ func (app *CameraManagementApp) getAllPipelineStatuses() (map[string]PipelineInf
func (app *CameraManagementApp) getPipelines() (interface{}, error) {
var res interface{}
if err := issueGetRequest(context.Background(), &res, app.config.AppCustom.EvamBaseUrl, "/pipelines"); err != nil {
return nil, errors.Wrap(err, "GET request to query all EVAM pipelines failed")
return nil, errors.NewCommonEdgeX(errors.KindServerError, "GET request to query all EVAM pipelines failed", err)
}
return res, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ package appcamera

import (
"fmt"
"github.com/edgexfoundry/go-mod-core-contracts/v3/errors"
"net/http"
"path"

dtosCommon "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/common"
"github.com/gorilla/mux"

"github.com/edgexfoundry/go-mod-core-contracts/v3/common"
"github.com/pkg/errors"
)

const (
Expand Down Expand Up @@ -117,7 +117,7 @@ func (app *CameraManagementApp) addRoutes() error {

func (app *CameraManagementApp) addRoute(path, method string, f http.HandlerFunc) error {
if err := app.service.AddRoute(path, f, method); err != nil {
return errors.Wrapf(err, "failed to add route, path=%s, method=%s", path, method)
return errors.NewCommonEdgeX(errors.KindServerError, fmt.Sprintf("failed to add route, path=%s, method=%s", path, method), err)
}
return nil
}
Expand Down
Loading

0 comments on commit 04d6a19

Please sign in to comment.