Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

fix: camera mgmt docker build, secret names, and go mods #229

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since all other commands are make commands it would be nice to also have docker compose ones under make.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normally I would agree, but in this case I think it is fine to keep as docker compose, as the user has more control over it

```

> **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