Add depot, attendant, jetfire, optimus, ota services with kustomize overlays
This commit is contained in:
53
Dockerfile
Normal file
53
Dockerfile
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
# Shared Dockerfile for all Go services
|
||||||
|
# Usage: docker build --build-arg SERVICE=gateway -t gateway .
|
||||||
|
|
||||||
|
ARG SERVICE=gateway
|
||||||
|
|
||||||
|
# Build stage
|
||||||
|
FROM golang:1.25-alpine AS builder
|
||||||
|
|
||||||
|
ARG SERVICE
|
||||||
|
|
||||||
|
RUN apk add --no-cache git ca-certificates tzdata
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy module files for dependency caching (don't use go.work in container)
|
||||||
|
COPY pkg/go.mod pkg/go.sum ./pkg/
|
||||||
|
COPY pkg/can-go/go.mod pkg/can-go/go.sum ./pkg/can-go/
|
||||||
|
COPY services/${SERVICE}/go.mod services/${SERVICE}/go.sum ./services/${SERVICE}/
|
||||||
|
|
||||||
|
# Download dependencies (cached layer)
|
||||||
|
WORKDIR /app/services/${SERVICE}
|
||||||
|
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||||
|
go mod download -x
|
||||||
|
|
||||||
|
# Copy source
|
||||||
|
WORKDIR /app
|
||||||
|
COPY pkg/ ./pkg/
|
||||||
|
COPY services/${SERVICE}/ ./services/${SERVICE}/
|
||||||
|
|
||||||
|
# Build static binary
|
||||||
|
WORKDIR /app/services/${SERVICE}
|
||||||
|
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build \
|
||||||
|
CGO_ENABLED=0 GOOS=linux \
|
||||||
|
go build -ldflags="-s -w" -trimpath -o /app-binary .
|
||||||
|
|
||||||
|
# Runtime stage - distroless for minimal attack surface
|
||||||
|
FROM gcr.io/distroless/static-debian12:nonroot
|
||||||
|
|
||||||
|
ARG SERVICE
|
||||||
|
|
||||||
|
COPY --from=builder /app-binary /app
|
||||||
|
COPY --from=builder /app/pkg/logger/log_config /log_config
|
||||||
|
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
|
||||||
|
|
||||||
|
# Copy docs if they exist (optional)
|
||||||
|
COPY --from=builder /app/services/${SERVICE}/docs* /docs/
|
||||||
|
|
||||||
|
ENV LOG_CONFIG=/log_config
|
||||||
|
ENV TZ=UTC
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app"]
|
||||||
@@ -7,6 +7,11 @@ resources:
|
|||||||
- ../../base
|
- ../../base
|
||||||
- secrets.yaml
|
- secrets.yaml
|
||||||
- services/gateway/
|
- services/gateway/
|
||||||
|
- services/depot/
|
||||||
|
- services/attendant/
|
||||||
|
- services/jetfire/
|
||||||
|
- services/optimus/
|
||||||
|
- services/ota/
|
||||||
|
|
||||||
labels:
|
labels:
|
||||||
- pairs:
|
- pairs:
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: attendant
|
||||||
|
namespace: cloud-services
|
||||||
|
labels:
|
||||||
|
app: attendant
|
||||||
|
annotations:
|
||||||
|
reloader.stakater.com/auto: "true"
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: attendant
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: attendant
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: attendant
|
||||||
|
image: localhost:32000/attendant:v1
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 8077
|
||||||
|
name: http
|
||||||
|
- containerPort: 11011
|
||||||
|
name: health
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: cloud-common-config
|
||||||
|
- secretRef:
|
||||||
|
name: cloud-db-credentials
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
limits:
|
||||||
|
memory: 512Mi
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /liveness
|
||||||
|
port: 11011
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 30
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /readiness
|
||||||
|
port: 11011
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namespace: cloud-services
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
51
deploy/overlays/development/services/depot/deployment.yaml
Normal file
51
deploy/overlays/development/services/depot/deployment.yaml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: depot
|
||||||
|
namespace: cloud-services
|
||||||
|
labels:
|
||||||
|
app: depot
|
||||||
|
annotations:
|
||||||
|
reloader.stakater.com/auto: "true"
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: depot
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: depot
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: depot
|
||||||
|
image: localhost:32000/depot:v1
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 8077
|
||||||
|
name: http
|
||||||
|
- containerPort: 11011
|
||||||
|
name: health
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: cloud-common-config
|
||||||
|
- secretRef:
|
||||||
|
name: cloud-db-credentials
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
limits:
|
||||||
|
memory: 512Mi
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /liveness
|
||||||
|
port: 11011
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 30
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /readiness
|
||||||
|
port: 11011
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namespace: cloud-services
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
62
deploy/overlays/development/services/jetfire/deployment.yaml
Normal file
62
deploy/overlays/development/services/jetfire/deployment.yaml
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: jetfire
|
||||||
|
namespace: cloud-services
|
||||||
|
labels:
|
||||||
|
app: jetfire
|
||||||
|
annotations:
|
||||||
|
reloader.stakater.com/auto: "true"
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: jetfire
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: jetfire
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: jetfire
|
||||||
|
image: localhost:32000/jetfire:v1
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 8077
|
||||||
|
name: http
|
||||||
|
- containerPort: 11011
|
||||||
|
name: health
|
||||||
|
env:
|
||||||
|
- name: CLICKHOUSE_HOST
|
||||||
|
value: clickhouse.clickhouse.svc.cluster.local
|
||||||
|
- name: CLICKHOUSE_PORT
|
||||||
|
value: "9000"
|
||||||
|
- name: CLICKHOUSE_USER
|
||||||
|
value: default
|
||||||
|
- name: CLICKHOUSE_FEATURE_TABLE
|
||||||
|
value: feature_table
|
||||||
|
- name: CLICKHOUSE_VEHICLE_SIGNAL_TABLE
|
||||||
|
value: vehicle_signal
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: cloud-common-config
|
||||||
|
- secretRef:
|
||||||
|
name: cloud-db-credentials
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
memory: 2Gi
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /liveness
|
||||||
|
port: 11011
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 30
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /readiness
|
||||||
|
port: 11011
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namespace: cloud-services
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
58
deploy/overlays/development/services/optimus/deployment.yaml
Normal file
58
deploy/overlays/development/services/optimus/deployment.yaml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: optimus
|
||||||
|
namespace: cloud-services
|
||||||
|
labels:
|
||||||
|
app: optimus
|
||||||
|
annotations:
|
||||||
|
reloader.stakater.com/auto: "true"
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: optimus
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: optimus
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: optimus
|
||||||
|
image: localhost:32000/optimus:v1
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 8077
|
||||||
|
name: http
|
||||||
|
- containerPort: 11011
|
||||||
|
name: health
|
||||||
|
env:
|
||||||
|
- name: CLICKHOUSE_HOST
|
||||||
|
value: clickhouse.clickhouse.svc.cluster.local
|
||||||
|
- name: CLICKHOUSE_PORT
|
||||||
|
value: "9000"
|
||||||
|
- name: CLICKHOUSE_USER
|
||||||
|
value: default
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: cloud-common-config
|
||||||
|
- secretRef:
|
||||||
|
name: cloud-db-credentials
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
memory: 2Gi
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /liveness
|
||||||
|
port: 11011
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 30
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /readiness
|
||||||
|
port: 11011
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namespace: cloud-services
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
67
deploy/overlays/development/services/ota/deployment.yaml
Normal file
67
deploy/overlays/development/services/ota/deployment.yaml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: ota
|
||||||
|
namespace: cloud-services
|
||||||
|
labels:
|
||||||
|
app: ota
|
||||||
|
annotations:
|
||||||
|
reloader.stakater.com/auto: "true"
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: ota
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: ota
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: ota
|
||||||
|
image: localhost:32000/ota_update_go:v1
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 8077
|
||||||
|
name: http
|
||||||
|
- containerPort: 11011
|
||||||
|
name: health
|
||||||
|
env:
|
||||||
|
- name: SERVICE_BASE_URL
|
||||||
|
value: /ota_update
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: cloud-common-config
|
||||||
|
- secretRef:
|
||||||
|
name: cloud-db-credentials
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
memory: 512Mi
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /liveness
|
||||||
|
port: 11011
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 30
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /readiness
|
||||||
|
port: 11011
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: ota
|
||||||
|
namespace: cloud-services
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: ota
|
||||||
|
ports:
|
||||||
|
- port: 8077
|
||||||
|
targetPort: 8077
|
||||||
|
name: http
|
||||||
25
deploy/overlays/development/services/ota/ingress.yaml
Normal file
25
deploy/overlays/development/services/ota/ingress.yaml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: ota
|
||||||
|
namespace: cloud-services
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||||
|
spec:
|
||||||
|
ingressClassName: traefik
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- gw.mini.cloud.fiskerinc.com
|
||||||
|
secretName: cloud-services-tls
|
||||||
|
rules:
|
||||||
|
- host: gw.mini.cloud.fiskerinc.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /ota_update
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: ota
|
||||||
|
port:
|
||||||
|
number: 8077
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namespace: cloud-services
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
- ingress.yaml
|
||||||
5
go.work
5
go.work
@@ -3,5 +3,10 @@ go 1.25
|
|||||||
use (
|
use (
|
||||||
./pkg
|
./pkg
|
||||||
./pkg/can-go
|
./pkg/can-go
|
||||||
|
./services/attendant
|
||||||
|
./services/depot
|
||||||
./services/gateway
|
./services/gateway
|
||||||
|
./services/jetfire
|
||||||
|
./services/optimus
|
||||||
|
./services/ota_update_go
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ type ConsumerInterface interface {
|
|||||||
ConsumeToChannel(topics []string, events chan *Message) error
|
ConsumeToChannel(topics []string, events chan *Message) error
|
||||||
ConsumeToChannelJson(topics []string, events chan common.EventRawJSON) error
|
ConsumeToChannelJson(topics []string, events chan common.EventRawJSON) error
|
||||||
ConsumePartitionsToChannel(partitions []TopicPartition, events chan *Message) error
|
ConsumePartitionsToChannel(partitions []TopicPartition, events chan *Message) error
|
||||||
|
ConsumeOrRebalancedCatch(topics []string, events chan *Message, rebalance chan struct{}) error
|
||||||
|
Subscribe(topics []string)
|
||||||
GetMetadata(topic string) (*Metadata, error)
|
GetMetadata(topic string) (*Metadata, error)
|
||||||
Check(ctx context.Context) error
|
Check(ctx context.Context) error
|
||||||
Stop()
|
Stop()
|
||||||
@@ -275,6 +277,48 @@ func (c *Consumer) Stop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Subscribe adds topics to consume (for compatibility with old API)
|
||||||
|
func (c *Consumer) Subscribe(topics []string) {
|
||||||
|
c.client.AddConsumeTopics(topics...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsumeOrRebalancedCatch consumes messages and notifies on rebalance events
|
||||||
|
func (c *Consumer) ConsumeOrRebalancedCatch(topics []string, events chan *Message, rebalance chan struct{}) error {
|
||||||
|
c.client.AddConsumeTopics(topics...)
|
||||||
|
c.setConnected(true)
|
||||||
|
defer c.setConnected(false)
|
||||||
|
|
||||||
|
c.running = true
|
||||||
|
for c.running {
|
||||||
|
fetches := c.client.PollFetches(c.ctx)
|
||||||
|
if errs := fetches.Errors(); len(errs) > 0 {
|
||||||
|
for _, e := range errs {
|
||||||
|
if e.Err == context.Canceled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Check for rebalance-related errors
|
||||||
|
if e.Err.Error() == "REBALANCE_IN_PROGRESS" || e.Err.Error() == "NOT_COORDINATOR" {
|
||||||
|
select {
|
||||||
|
case rebalance <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
logger.Error().Err(e.Err).Msgf("fetch error on %s", e.Topic)
|
||||||
|
c.setConnected(false)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c.setConnected(true)
|
||||||
|
fetches.EachRecord(func(r *kgo.Record) {
|
||||||
|
events <- recordToMessage(r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Check verifies consumer connectivity
|
// Check verifies consumer connectivity
|
||||||
func (c *Consumer) Check(ctx context.Context) error {
|
func (c *Consumer) Check(ctx context.Context) error {
|
||||||
if !c.isConnected() {
|
if !c.isConnected() {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
|
|
||||||
"github.com/fiskerinc/cloud-services/pkg/logger"
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
"github.com/fiskerinc/cloud-services/pkg/utils"
|
"github.com/fiskerinc/cloud-services/pkg/utils"
|
||||||
cKafka "github.com/confluentinc/confluent-kafka-go/v2/kafka"
|
|
||||||
"github.com/go-pg/pg/v10"
|
"github.com/go-pg/pg/v10"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
@@ -106,10 +105,6 @@ func badDataErrorResp(w http.ResponseWriter, err error, defaultStatus int, errFu
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if handleKafkaErrors(err, errFunc, w) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
logError(err, err.Error(), defaultStatus, errFunc, w)
|
logError(err, err.Error(), defaultStatus, errFunc, w)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@@ -169,17 +164,6 @@ func handleSAPErrors(err error, errFunc respErr, w http.ResponseWriter) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleKafkaErrors(err error, errFunc respErr, w http.ResponseWriter) bool {
|
|
||||||
if kafkaErr, ok := err.(cKafka.Error); ok {
|
|
||||||
if kafkaErr.IsFatal() {
|
|
||||||
logError(kafkaErr, kafkaErr.Error(), http.StatusServiceUnavailable, errFunc, w)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func logError(err error, errMessage string, status int, errFunc respErr, w http.ResponseWriter) {
|
func logError(err error, errMessage string, status int, errFunc respErr, w http.ResponseWriter) {
|
||||||
switch status {
|
switch status {
|
||||||
case http.StatusServiceUnavailable, http.StatusInternalServerError:
|
case http.StatusServiceUnavailable, http.StatusInternalServerError:
|
||||||
|
|||||||
36
scripts/build.sh
Executable file
36
scripts/build.sh
Executable file
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Build and optionally deploy a service
|
||||||
|
# Usage: ./scripts/build.sh <service> [--deploy] [version]
|
||||||
|
# Example: ./scripts/build.sh depot --deploy v1
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SERVICE=${1:-gateway}
|
||||||
|
DEPLOY=${2:-}
|
||||||
|
VERSION=${3:-v1}
|
||||||
|
REGISTRY="localhost:32000"
|
||||||
|
TAG="$VERSION"
|
||||||
|
|
||||||
|
echo "Building $SERVICE..."
|
||||||
|
docker build --platform linux/arm64 \
|
||||||
|
--build-arg SERVICE=$SERVICE \
|
||||||
|
-t $REGISTRY/$SERVICE:$TAG \
|
||||||
|
-f Dockerfile .
|
||||||
|
|
||||||
|
if [ "$DEPLOY" == "--deploy" ]; then
|
||||||
|
echo "Saving image..."
|
||||||
|
docker save $REGISTRY/$SERVICE:$TAG -o /tmp/$SERVICE.tar
|
||||||
|
|
||||||
|
echo "Transferring to cluster..."
|
||||||
|
scp /tmp/$SERVICE.tar admin@control-plane.local:/tmp/
|
||||||
|
|
||||||
|
echo "Importing to microk8s..."
|
||||||
|
ssh admin@control-plane.local "/usr/local/bin/multipass transfer /tmp/$SERVICE.tar microk8s-vm:/tmp/"
|
||||||
|
ssh admin@control-plane.local "/usr/local/bin/multipass exec microk8s-vm -- microk8s ctr images rm $REGISTRY/$SERVICE:$TAG 2>/dev/null || true"
|
||||||
|
ssh admin@control-plane.local "/usr/local/bin/multipass exec microk8s-vm -- microk8s ctr images import /tmp/$SERVICE.tar"
|
||||||
|
|
||||||
|
echo "Restarting deployment..."
|
||||||
|
ssh admin@control-plane.local "/usr/local/bin/multipass exec microk8s-vm -- microk8s kubectl rollout restart deployment/$SERVICE -n cloud-services"
|
||||||
|
|
||||||
|
echo "Done!"
|
||||||
|
fi
|
||||||
25
services/attendant/Dockerfile
Normal file
25
services/attendant/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
ARG BASE_IMAGE=cloud_base_go
|
||||||
|
FROM ${BASE_IMAGE} as builder-go
|
||||||
|
|
||||||
|
WORKDIR /build/attendant
|
||||||
|
COPY ./attendant/go.mod ./attendant/go.sum ./
|
||||||
|
RUN go mod edit -replace fiskerinc.com/modules=../fiskerinc.com/modules \
|
||||||
|
&& go mod download
|
||||||
|
|
||||||
|
COPY ./attendant ./
|
||||||
|
RUN go mod edit -replace fiskerinc.com/modules=../fiskerinc.com/modules \
|
||||||
|
&& go build -tags musl
|
||||||
|
|
||||||
|
|
||||||
|
FROM alpine:3.17
|
||||||
|
|
||||||
|
RUN apk add --no-cache librdkafka --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community \
|
||||||
|
&& apk add --no-cache ca-certificates
|
||||||
|
|
||||||
|
COPY ./modules_go/logger/log_config .
|
||||||
|
COPY --from=builder-go /build/attendant/attendant .
|
||||||
|
|
||||||
|
ENV LOG_CONFIG=log_config
|
||||||
|
EXPOSE 8077
|
||||||
|
|
||||||
|
CMD ./attendant
|
||||||
518
services/attendant/controllers/car_update_progress.go
Normal file
518
services/attendant/controllers/car_update_progress.go
Normal file
@@ -0,0 +1,518 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/cache"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
s "github.com/fiskerinc/cloud-services/pkg/common/carupdatestatus"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/db/queries"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/manifestsender"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/hwversion"
|
||||||
|
"github.com/go-pg/pg/v10"
|
||||||
|
r "github.com/gomodule/redigo/redis"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const redisObjectExpire = 3600
|
||||||
|
|
||||||
|
const (
|
||||||
|
PackageDownloadStart = "package_download_start"
|
||||||
|
PackageDownloadComplete = "package_download_complete"
|
||||||
|
PackageInstallStart = "package_install_start"
|
||||||
|
PackageInstallComplete = "package_install_complete"
|
||||||
|
InstallError = "install_error"
|
||||||
|
)
|
||||||
|
|
||||||
|
var RepeatedStatus = errors.New("RepeatedStatus")
|
||||||
|
|
||||||
|
// CarUpdateProgress takes in a car update message and saves it to our database
|
||||||
|
// This includes setting the status of a car update, and telling the car and SAP that the update is done
|
||||||
|
func NewCarUpdateProgress(clientPool redis.ClientPoolInterface, ka *services.KeepAwake, db *services.DB, device common.Device) CarUpdateProgressInterface {
|
||||||
|
if device == common.TRex {
|
||||||
|
return &CarUpdateProgress{
|
||||||
|
RedisClientPool: clientPool,
|
||||||
|
DB: db,
|
||||||
|
ka: ka,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if device == common.HMI {
|
||||||
|
return &HMICarUpdateProgress{
|
||||||
|
conf: services.GetVehicleConfig(),
|
||||||
|
sms: services.GetSMSClient(),
|
||||||
|
ka: ka,
|
||||||
|
CarUpdateProgress: CarUpdateProgress{
|
||||||
|
RedisClientPool: clientPool,
|
||||||
|
DB: db,
|
||||||
|
ka: ka,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CarUpdateProgressInterface interface {
|
||||||
|
Process(vin string, data []byte) error
|
||||||
|
ProcessStatus(vin string, status common.CarUpdateProgress) error
|
||||||
|
Dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
type CarUpdateProgress struct {
|
||||||
|
RedisClientPool redis.ClientPoolInterface
|
||||||
|
DB *services.DB
|
||||||
|
ka *services.KeepAwake
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) Process(vin string, data []byte) error {
|
||||||
|
var status common.CarUpdateProgress
|
||||||
|
|
||||||
|
err := json.Unmarshal(data, &status)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cu.ProcessStatus(vin, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) ProcessStatus(vin string, status common.CarUpdateProgress) (err error) {
|
||||||
|
if cu.transformDBCarUpdateProgress(&status) {
|
||||||
|
err = cu.logStatusDB(status)
|
||||||
|
// If the error is the repeated, we can just exit early
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, queries.RepeatedStatus) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cu.cancelTheCANAwake(vin, status)
|
||||||
|
|
||||||
|
batch := redis.NewRedisBatchCommands()
|
||||||
|
|
||||||
|
cu.transformRedisCarUpdateProgress(&status)
|
||||||
|
cu.BatchCacheRedis(batch, redis.CarUpdateStatusHashKey(status.CarUpdateID), &status)
|
||||||
|
|
||||||
|
client := cu.RedisClientPool.GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
_, err = client.ExecuteBatch(batch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not send car update status for internal cloud statuses
|
||||||
|
if cu.isInternalStatus(status) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := cu.getMessage(&status)
|
||||||
|
err = cu.publishStatusHMI(vin, &msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
msgMobile := cu.getMessageForMobile(&status, vin)
|
||||||
|
err = cu.publishStatusMobile(vin, &msgMobile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cu.onUpdateManifestComplete(&status, vin)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will try and cancel sending CAN status stuff
|
||||||
|
func (cu *CarUpdateProgress) cancelTheCANAwake(vin string, status common.CarUpdateProgress) {
|
||||||
|
switch status.Status {
|
||||||
|
case s.DownloadFailed, s.InstallFailed, s.ManifestCancelAccepted, s.ManifestCancelRejected,
|
||||||
|
s.ManifestError, s.ManifestRejected, s.ManifestValidationFailed, s.RequirementsFailed, s.ManifestCanceled:
|
||||||
|
logger.Info().Msgf("canceling CAN Awake for %s because %s", vin, status.Status)
|
||||||
|
cu.ka.RemoveKeepAwakeMessage(vin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) isInternalStatus(status common.CarUpdateProgress) bool {
|
||||||
|
return status.Status == s.Sent || status.Status == s.Pending
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) transformDBCarUpdateProgress(status *common.CarUpdateProgress) bool {
|
||||||
|
switch status.Status {
|
||||||
|
case s.DownloadStarted:
|
||||||
|
if status.PackageCurrent == 0 {
|
||||||
|
status.Status = PackageDownloadStart
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case s.DownloadCompleted:
|
||||||
|
if status.PackageCurrent == status.PackageTotal {
|
||||||
|
status.Status = PackageDownloadComplete
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case s.InstallStarted:
|
||||||
|
if status.InstalledFiles == 0 && status.TotalFiles > 0 {
|
||||||
|
status.Status = PackageInstallStart
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case s.InstallSucceeded:
|
||||||
|
if status.InstalledFiles == status.TotalFiles && status.TotalFiles > 0 {
|
||||||
|
status.Status = PackageInstallComplete
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case InstallError:
|
||||||
|
status.Status = s.InstallFailed
|
||||||
|
return true
|
||||||
|
case s.Installing:
|
||||||
|
return false
|
||||||
|
case s.Downloading:
|
||||||
|
// these status updates do not need to be saved in the database
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) transformRedisCarUpdateProgress(status *common.CarUpdateProgress) {
|
||||||
|
switch status.Status {
|
||||||
|
case s.DownloadStarted, s.DownloadCompleted, PackageDownloadStart:
|
||||||
|
status.Status = s.Downloading
|
||||||
|
case s.InstallStarted, s.InstallSucceeded, PackageInstallStart:
|
||||||
|
status.Status = s.Installing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) logStatusDB(status common.CarUpdateProgress) (err error) {
|
||||||
|
// If we are one of these status's we want to ignore, then we need to do some extra database steps, otherwise insert normally
|
||||||
|
carUpdate := common.CarUpdate{
|
||||||
|
ID: status.CarUpdateID,
|
||||||
|
Status: status.Status,
|
||||||
|
ErrorCode: status.ErrorCode,
|
||||||
|
Info: strings.TrimSpace(fmt.Sprintf("%s %s", status.ECU, status.Info)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := s.NoRepeatUpdateStatus[status.Status]; ok {
|
||||||
|
_, err = cu.DB.GetCarUpdates().UpdateStatusIfNotRepeat(&carUpdate)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = cu.DB.GetCarUpdates().UpdateStatus(&carUpdate)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) GetCache(key string) (*common.CarUpdateProgress, error) {
|
||||||
|
client := cu.RedisClientPool.GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
status := common.CarUpdateProgress{}
|
||||||
|
err := client.GetObject(key, &status)
|
||||||
|
return &status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) BatchCacheRedis(batch *redis.RedisBatchCommands, key string, status *common.CarUpdateProgress) {
|
||||||
|
batch.Add(r.Args{}.Add("HSET").Add(key).AddFlat(status)...)
|
||||||
|
batch.Add("EXPIRE", key, redisObjectExpire)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) getMessage(status *common.CarUpdateProgress) common.Message {
|
||||||
|
return common.Message{
|
||||||
|
Handler: "car_update_status",
|
||||||
|
Data: status,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) getMessageForMobile(status *common.CarUpdateProgress, vin string) common.Message {
|
||||||
|
type mobileData struct {
|
||||||
|
VIN string `json:"vin"`
|
||||||
|
*common.CarUpdateProgress
|
||||||
|
}
|
||||||
|
|
||||||
|
return common.Message{
|
||||||
|
Handler: "car_update_status",
|
||||||
|
Data: mobileData{vin, status},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) publishStatusHMI(vin string, msg *common.Message) error {
|
||||||
|
client := cu.RedisClientPool.GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
// redis publish to HMI
|
||||||
|
hmiKey := common.HMI.Key(vin)
|
||||||
|
// Add VIN
|
||||||
|
err := client.SafePublishMessage(hmiKey, msg)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) publishStatusMobile(vin string, msg *common.Message) error {
|
||||||
|
drivers := cache.NewDriversCache(cu.RedisClientPool, cu.DB.GetCars())
|
||||||
|
|
||||||
|
// redis publish to mobile devices
|
||||||
|
driverIDs, err := drivers.RetrieveDriverIDs(vin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change thos for loop to isntead create a batch and execute it all at once
|
||||||
|
client := cu.RedisClientPool.GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
for _, d := range driverIDs {
|
||||||
|
mobileKey := common.Mobile.Key(d)
|
||||||
|
err = client.SafePublishMessage(mobileKey, msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) Dispose() {
|
||||||
|
cu.DB = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type HMICarUpdateProgress struct {
|
||||||
|
conf vconfig.ConfigServiceInterface
|
||||||
|
sms sms.SMSServiceClient
|
||||||
|
ka *services.KeepAwake
|
||||||
|
CarUpdateProgress
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HMICarUpdateProgress) Process(vin string, data []byte) error {
|
||||||
|
var status common.CarUpdateProgress
|
||||||
|
|
||||||
|
err := json.Unmarshal(data, &status)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.downloadComplete(&status) {
|
||||||
|
// stop calling the sendKeepAwakeMessage
|
||||||
|
h.ka.RemoveKeepAwakeMessage(vin)
|
||||||
|
_, err = h.sendManifestToTRex(vin, &status)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
h.logStatusDB(common.CarUpdateProgress{
|
||||||
|
CarUpdateID: status.CarUpdateID,
|
||||||
|
Status: s.Sent,
|
||||||
|
Info: "TBOX",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.ProcessStatus(vin, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HMICarUpdateProgress) downloadComplete(status *common.CarUpdateProgress) bool {
|
||||||
|
return status.Status == s.DownloadCompleted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HMICarUpdateProgress) getManifest(status *common.CarUpdateProgress) (*common.UpdateManifest, error) {
|
||||||
|
update := common.CarUpdate{ID: status.CarUpdateID}
|
||||||
|
err := h.DB.GetCarUpdates().Load(&update)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
update.UpdateManifest.CarUpdateID = status.CarUpdateID
|
||||||
|
|
||||||
|
return update.UpdateManifest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HMICarUpdateProgress) sendManifestToTRex(vin string, status *common.CarUpdateProgress) (msgID string, err error) {
|
||||||
|
logger.Info().Msgf("HMICarUpdateProgress sendManifestToTRex car_update_id %d", status.CarUpdateID)
|
||||||
|
manifest, err := h.getManifest(status)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !manifest.HasSelfDownload() {
|
||||||
|
logger.Error().Msgf("%s download_completed for non-self-download manifest", vin)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = hwversion.SetHWVersion(manifest, vin, services.GetDB().GetCars())
|
||||||
|
if err != nil {
|
||||||
|
// An error here is very unexpected. The hw versioning should have been confirmed earlier before ICC was updated
|
||||||
|
err = errors.WithStack(err)
|
||||||
|
logger.Err(err).Str("VIN", vin).Int64("UpdateID", status.CarUpdateID).Msg("failed to set hw versions for a manifest after ICC complete update")
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
manifest.SortECUs()
|
||||||
|
manifest.FilterCompatibleECUs(vin)
|
||||||
|
|
||||||
|
// This code is going to be removed by mny other PR so not going to mess with it for now
|
||||||
|
client := h.RedisClientPool.GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
trex := manifestsender.NewTBOXManifestSender(client, h.conf, h.DB, h.sms, nil)
|
||||||
|
defer trex.Close()
|
||||||
|
|
||||||
|
msgID, err = trex.ProcessSoftwareUpdate(vin, manifest, services.GetDB().GetCarConfigData())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HMICarUpdateProgress) GetRedisHashKey(status *common.CarUpdateProgress) string {
|
||||||
|
return redis.CarUpdateStatusHMIHashKey(status.CarUpdateID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Car Update Done
|
||||||
|
func (cu *CarUpdateProgress) onUpdateManifestComplete(status *common.CarUpdateProgress, vin string) (err error) {
|
||||||
|
success := false
|
||||||
|
final := false
|
||||||
|
submitSAP := false
|
||||||
|
|
||||||
|
switch status.Status {
|
||||||
|
case s.ManifestSucceeded:
|
||||||
|
success = true
|
||||||
|
submitSAP = true
|
||||||
|
final = true
|
||||||
|
case s.ManifestCanceled, s.ManifestError, s.ManifestRejected:
|
||||||
|
success = false
|
||||||
|
submitSAP = true
|
||||||
|
final = true
|
||||||
|
case s.DownloadFailed, s.ManifestCancelPending, s.RollbackSucceeded, s.RollbackFailed, s.CleanupSucceeded:
|
||||||
|
final = true
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
carUpdatesDB := cu.DB.GetCarUpdates()
|
||||||
|
carUpdate, err := carUpdatesDB.SelectByID(status.CarUpdateID)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.WithStack(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if carUpdate != nil {
|
||||||
|
// Notify car user of in progress update through FOA API
|
||||||
|
fs := services.GetFoaService()
|
||||||
|
foaResp, err := fs.OtaUpdateStatus(vin, carUpdate, status)
|
||||||
|
if err != nil || (foaResp != nil && foaResp.StatusCode != http.StatusOK) {
|
||||||
|
bodyBytes, _ := io.ReadAll(foaResp.Body)
|
||||||
|
bodyString := string(bodyBytes)
|
||||||
|
logger.Err(err).Msgf("notify FOA for update manifest %d final state %s for %s failed with http status %d and message %s", carUpdate.UpdateManifestID, status.Status, vin, foaResp.StatusCode, bodyString)
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info().Msgf("Manifest update completed for %s with status of %s", vin, status.Status)
|
||||||
|
|
||||||
|
if submitSAP {
|
||||||
|
logger.Info().Msg("SAP: No Longer Submit Updates")
|
||||||
|
// sap := services.GetSapService()
|
||||||
|
// err = sap.SubmitResult(vin, success)
|
||||||
|
// if err != nil {
|
||||||
|
// requestBody := struct {
|
||||||
|
// VIN string
|
||||||
|
// Success bool
|
||||||
|
// CarUpdateProgress common.CarUpdateProgress
|
||||||
|
// }{VIN: vin, Success: success, CarUpdateProgress: *status}
|
||||||
|
// logger.Err(err).Interface("body", requestBody).Msgf("failed to call sap submit result")
|
||||||
|
// err = nil
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
if success {
|
||||||
|
// If we are successful, we want to possibly update the cars sums version
|
||||||
|
// Need to pull the manifest to check it has a sums version, and then update the car
|
||||||
|
err = cu.updateCarsSUMSVersion(status)
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Msgf("failed to update car sums version for manifest with CarUpdateID %d", status.CarUpdateID)
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the read_ecu_versions remote command so that the ECU data is updated in postgres ASAP
|
||||||
|
client := services.RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
err = client.SafePublishMessage(
|
||||||
|
common.TRex.Key(vin),
|
||||||
|
common.Message{
|
||||||
|
Handler: "read_ecu_versions",
|
||||||
|
Data: common.RemoteReadVersionsCommandArgs{
|
||||||
|
ECUName: "*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Msgf("failed to send read_ecu_versions command to vin %s", vin)
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if final {
|
||||||
|
// if the manifest is in a final state
|
||||||
|
// then delete the redundant requirements_await rows from car_update_statuses, to avoid overcrowding the table
|
||||||
|
err = cu.truncateRequirementsAwaitForUpdate(status)
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Msgf("failed to delete redundant requirements_await rows from car_update_statuses for manifest with CarUpdateID %d", status.CarUpdateID)
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cu *CarUpdateProgress) truncateRequirementsAwaitForUpdate(status *common.CarUpdateProgress) error {
|
||||||
|
logger.Info().Msgf("Manifest with CarUpdateID %d successful with status %s. Deleting redundant requirements_await rows from car_update_statuses", status.CarUpdateID, status.Status)
|
||||||
|
|
||||||
|
_, err := cu.DB.GetCarUpdates().TruncateRequirementsAwaitForUpdate(status.CarUpdateID)
|
||||||
|
if err != nil && !errors.Is(err, pg.ErrNoRows) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the car update, and it gives the update manifest
|
||||||
|
// If the manifest has a sums version, apply it to the car
|
||||||
|
func (cu *CarUpdateProgress) updateCarsSUMSVersion(status *common.CarUpdateProgress) (err error) {
|
||||||
|
carUpdatesDB := cu.DB.GetCarUpdates()
|
||||||
|
carUpdate, err := carUpdatesDB.SelectByID(status.CarUpdateID)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.WithStack(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if carUpdate.UpdateManifest == nil {
|
||||||
|
err = errors.New("failed to pull car updates update manifest")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
um := carUpdate.UpdateManifest
|
||||||
|
// So if we have have a sums version we want to update
|
||||||
|
if um.SUMS == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
carsDB := cu.DB.GetCars()
|
||||||
|
filter := common.Car{
|
||||||
|
VIN: carUpdate.VIN,
|
||||||
|
}
|
||||||
|
|
||||||
|
cars, err := carsDB.Select(&filter, nil)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.WithStack(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cars) != 1 {
|
||||||
|
err = fmt.Errorf("did not receive only one car, received: %d", len(cars))
|
||||||
|
err = errors.WithStack(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
car := cars[0]
|
||||||
|
car.SUMSVersion = um.SUMS
|
||||||
|
_, err = carsDB.Update(&car)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.WithStack(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
184
services/attendant/controllers/dtc_request.go
Normal file
184
services/attendant/controllers/dtc_request.go
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DTCEntry struct {
|
||||||
|
CreatedAt time.Time `bson:"created_at"`
|
||||||
|
VIN string `bson:"vin"`
|
||||||
|
ECU string `json:"ecu" bson:"ecu"`
|
||||||
|
DTC uint64 `json:"dtc" bson:"dtc"`
|
||||||
|
Status uint8 `json:"status" bson:"status"`
|
||||||
|
Timestamp time.Time `json:"timestamp" bson:"timestamp"`
|
||||||
|
Speed uint16 `json:"speed" bson:"speed"`
|
||||||
|
Mileage uint32 `json:"mileage" bson:"mileage"`
|
||||||
|
Voltage uint16 `json:"voltage" bson:"voltage"`
|
||||||
|
|
||||||
|
SnapshotBase64 string `json:"snapshot,omitempty" bson:"snapshot,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GRPCToDTCEntry(payload *kafka_grpc.GRPC_AttendantPayload) []byte {
|
||||||
|
|
||||||
|
if payload.Data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
data := payload.Data.(*kafka_grpc.GRPC_AttendantPayload_DtcEntry)
|
||||||
|
if data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dtc := &DTCEntry{
|
||||||
|
VIN: data.DtcEntry.Vin,
|
||||||
|
ECU: data.DtcEntry.Ecu,
|
||||||
|
CreatedAt: milliToDate(data.DtcEntry.CreatedAt),
|
||||||
|
DTC: data.DtcEntry.Dtc,
|
||||||
|
Status: uint8(data.DtcEntry.Status),
|
||||||
|
Timestamp: milliToDate(data.DtcEntry.Timestamp),
|
||||||
|
Speed: uint16(data.DtcEntry.Speed),
|
||||||
|
Mileage: data.DtcEntry.Mileage,
|
||||||
|
Voltage: uint16(data.DtcEntry.Volt),
|
||||||
|
SnapshotBase64: data.DtcEntry.SnapshotBase64,
|
||||||
|
}
|
||||||
|
bytes, _ := json.Marshal(dtc)
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
func milliToDate(timestamp int64) time.Time {
|
||||||
|
seconds := timestamp / 1000
|
||||||
|
nanoseconds := (timestamp % 1000) * int64(time.Millisecond)
|
||||||
|
return time.Unix(seconds, nanoseconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
const SNAPSHOT_LEN = 21
|
||||||
|
|
||||||
|
func (entry *DTCEntry) ParseSnapshot() error {
|
||||||
|
|
||||||
|
data, errc := base64.StdEncoding.DecodeString(entry.SnapshotBase64)
|
||||||
|
if errc != nil {
|
||||||
|
return errc
|
||||||
|
}
|
||||||
|
payloadLen := len(data) - 2
|
||||||
|
|
||||||
|
if payloadLen < SNAPSHOT_LEN {
|
||||||
|
logger.Debug().Msgf("DTC snapshot payload is too small. Required length=%d, actual=%d, ECU=%s\n", SNAPSHOT_LEN, len(data), entry.ECU)
|
||||||
|
return errors.New("Snapshot too small")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our "Basic Diagnostic" spec defines 4 mandatory DIDs that must be
|
||||||
|
// stored inside DTCStapshotRecord:
|
||||||
|
// 1. EF F6 - Timestamp (6 bytes).
|
||||||
|
// 2. EF F7 - Vehicle Speed (2 bytes).
|
||||||
|
// 3. EF F8 - Milage (idk, our spec doesn't specify exact number.
|
||||||
|
// Empirically it's 4 bytes (same as "ICC_0x531::TotMilg_ODO").
|
||||||
|
// 4. EF F9 - Battery Voltage (2 bytes).
|
||||||
|
///entry.Speed = int16(speed.ToPhysical(float64(uint16(decodedBytes[0])<<8 | uint16(decodedBytes[1]))))
|
||||||
|
//entry.Voltage = int16(voltage.ToPhysical(float64(uint16(decodedBytes[0])<<8 | uint16(decodedBytes[1]))))
|
||||||
|
|
||||||
|
for idx := 2; idx < len(data)-1; {
|
||||||
|
if data[idx] != 0xEF {
|
||||||
|
idx++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if data[idx+1] == 0xF6 && idx+7 < len(data) {
|
||||||
|
idx += 2
|
||||||
|
entry.Timestamp, idx = consumeTimestamp(data, idx)
|
||||||
|
} else if data[idx+1] == 0xF7 && idx+3 < len(data) {
|
||||||
|
idx += 2
|
||||||
|
entry.Speed, idx = consumeSpeed(data, idx)
|
||||||
|
} else if data[idx+1] == 0xF8 && idx+5 < len(data) {
|
||||||
|
idx += 2
|
||||||
|
entry.Mileage, idx = consumeMileage(data, idx)
|
||||||
|
} else if data[idx+1] == 0xF9 && idx+3 < len(data) {
|
||||||
|
idx += 2
|
||||||
|
entry.Voltage, idx = consumeVoltage(data, idx)
|
||||||
|
} else {
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var onceTimestamp sync.Once
|
||||||
|
var (
|
||||||
|
day *descriptor.Signal
|
||||||
|
hr *descriptor.Signal
|
||||||
|
mins *descriptor.Signal
|
||||||
|
yr *descriptor.Signal
|
||||||
|
month *descriptor.Signal
|
||||||
|
sec *descriptor.Signal
|
||||||
|
)
|
||||||
|
|
||||||
|
func consumeTimestamp(data []byte, idx int) (res time.Time, idx_ret int) {
|
||||||
|
|
||||||
|
onceTimestamp.Do(func() {
|
||||||
|
day, _ = services.GetDBC().Signal(0x62F, "TBOX_CrtTi_Day")
|
||||||
|
hr, _ = services.GetDBC().Signal(0x62F, "TBOX_CrtTi_Hr")
|
||||||
|
mins, _ = services.GetDBC().Signal(0x62F, "TBOX_CrtTi_Mins")
|
||||||
|
yr, _ = services.GetDBC().Signal(0x62F, "TBOX_CrtTi_Yr")
|
||||||
|
month, _ = services.GetDBC().Signal(0x62F, "TBOX_CrtTi_Mth")
|
||||||
|
sec, _ = services.GetDBC().Signal(0x62F, "TBOX_CrtTi_Sec")
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
res = time.Date(int(yr.ToPhysical(float64(data[idx]))),
|
||||||
|
time.Month(month.ToPhysical(float64(data[idx+1]))),
|
||||||
|
int(day.ToPhysical(float64(data[idx+2]))),
|
||||||
|
int(hr.ToPhysical(float64(data[idx+3]))),
|
||||||
|
int(mins.ToPhysical(float64(data[idx+4]))),
|
||||||
|
int(sec.ToPhysical(float64(data[idx+5]))), 0, time.UTC)
|
||||||
|
idx_ret = idx + 6
|
||||||
|
return res, idx_ret
|
||||||
|
}
|
||||||
|
|
||||||
|
var onceSpeed sync.Once
|
||||||
|
var speed *descriptor.Signal
|
||||||
|
|
||||||
|
func consumeSpeed(data []byte, idx int) (res uint16, idx_ret int) {
|
||||||
|
|
||||||
|
onceSpeed.Do(func() {
|
||||||
|
speed, _ = services.GetDBC().Signal(0x318, "ESP_VehSpd")
|
||||||
|
})
|
||||||
|
res = uint16(speed.ToPhysical(float64(uint16(data[idx])<<8 | uint16(data[idx+1]))))
|
||||||
|
idx_ret = idx + 2
|
||||||
|
return res, idx_ret
|
||||||
|
}
|
||||||
|
|
||||||
|
var onceMileage sync.Once
|
||||||
|
var mileage *descriptor.Signal
|
||||||
|
|
||||||
|
func consumeMileage(data []byte, idx int) (res uint32, idx_ret int) {
|
||||||
|
onceMileage.Do(func() {
|
||||||
|
mileage, _ = services.GetDBC().Signal(0x531, "ICC_TotMilg_ODO")
|
||||||
|
})
|
||||||
|
res = uint32(mileage.ToPhysical(
|
||||||
|
float64(
|
||||||
|
uint64(data[idx])<<24 |
|
||||||
|
uint64(data[idx+1])<<16 |
|
||||||
|
uint64(data[idx+2])<<8 | uint64(data[idx+3]))))
|
||||||
|
idx_ret = idx + 4
|
||||||
|
return res, idx_ret
|
||||||
|
}
|
||||||
|
|
||||||
|
var onceVoltage sync.Once
|
||||||
|
var voltage *descriptor.Signal
|
||||||
|
|
||||||
|
func consumeVoltage(data []byte, idx int) (res uint16, idx_ret int) {
|
||||||
|
onceVoltage.Do(func() {
|
||||||
|
voltage, _ = services.GetDBC().Signal(0x507, "VCU_BattVolt")
|
||||||
|
})
|
||||||
|
|
||||||
|
res = uint16(voltage.ToPhysical(float64(uint16(data[idx])<<8 | uint16(data[idx+1]))))
|
||||||
|
idx_ret = idx + 2
|
||||||
|
return res, idx_ret
|
||||||
|
|
||||||
|
}
|
||||||
60
services/attendant/controllers/dtc_request_test.go
Normal file
60
services/attendant/controllers/dtc_request_test.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package controllers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/controllers"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParsing(t *testing.T) {
|
||||||
|
ecc := []byte(`{"ecu":"ECC","dtc":1719200,"status":9,"snapshot":"AQTv9ucIARIKNO/3AADv+AAAAADv+TLI"}`)
|
||||||
|
icc := []byte(`{"ecu":"ICC","dtc":14302599,"status":9,"snapshot":"AQUFAAHv+AAAAADv+S2W7/cAAO/2B+cIChAB"}`)
|
||||||
|
gw := []byte(`{"ecu":"GW","dtc":10718486,"status":8,"snapshot":"AQTv+QLA7/cAAO/2CAcHFAMs7/gAAAAA"}`)
|
||||||
|
mcu := []byte(`{"ecu":"MCU","dtc":14123795,"status":47,"snapshot":"AQTv9ggHCRcBD+/3AADv+AAAAADv+TLI"}`)
|
||||||
|
|
||||||
|
check := func(ecu []byte) {
|
||||||
|
var entry controllers.DTCEntry
|
||||||
|
err := json.Unmarshal(ecu, &entry)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
entry.ParseSnapshot()
|
||||||
|
if entry.Mileage != 0 {
|
||||||
|
t.Errorf("Incorrect Mileage %d", entry.Mileage)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(entry.Timestamp.Unix())
|
||||||
|
switch entry.ECU {
|
||||||
|
case "ECC":
|
||||||
|
if entry.Timestamp.Unix() != 8730871852 {
|
||||||
|
t.Errorf("Incorrect Timestamp %d", entry.Timestamp.Unix())
|
||||||
|
}
|
||||||
|
if entry.Voltage != 13 {
|
||||||
|
t.Errorf("Incorrect Voltage %d", entry.Voltage)
|
||||||
|
}
|
||||||
|
case "ICC":
|
||||||
|
if entry.Timestamp.Unix() != 1670580961 {
|
||||||
|
t.Errorf("Incorrect Timestamp %d", entry.Timestamp.Unix())
|
||||||
|
}
|
||||||
|
if entry.Voltage != 11 {
|
||||||
|
t.Errorf("Incorrect Voltage %d", entry.Voltage)
|
||||||
|
}
|
||||||
|
case "GW":
|
||||||
|
if entry.Timestamp.Unix() != 1691525024 {
|
||||||
|
t.Errorf("Incorrect Timestamp %d", entry.Timestamp.Unix())
|
||||||
|
}
|
||||||
|
case "MCU":
|
||||||
|
if entry.Timestamp.Unix() != 1691708475 {
|
||||||
|
t.Errorf("Incorrect Timestamp %d", entry.Timestamp.Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check(ecc)
|
||||||
|
check(icc)
|
||||||
|
check(gw)
|
||||||
|
check(mcu)
|
||||||
|
|
||||||
|
}
|
||||||
81
services/attendant/controllers/get_filekeys.go
Normal file
81
services/attendant/controllers/get_filekeys.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/cache"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/validator"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mega ducky repeat of /handlers/get_file_keys. Need a code re-org
|
||||||
|
func GetFileKeys(db *services.DB, device common.Device, id string, data []byte) error {
|
||||||
|
logger.Debug().Msgf("GetFileKeys %v %s", device, id)
|
||||||
|
var err error
|
||||||
|
var req *common.FileKeysRequest
|
||||||
|
|
||||||
|
client := services.RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
req, err = parseGetFileKeysRequest(data)
|
||||||
|
if err != nil {
|
||||||
|
notifyFileKeysGeneralError(client, device, id, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, err := cache.RetrieveFileEncryptionParams(client, db.GetFileKeys(), req.FileIDs)
|
||||||
|
if err != nil {
|
||||||
|
notifyFileKeysGeneralError(client, device, id, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.SafePublishMessage(device.Key(id), common.Message{
|
||||||
|
Handler: "filekeys",
|
||||||
|
Data: keys,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug().Msgf("GetFileKeys sent %v %s", device, id)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGetFileKeysRequest(data []byte) (*common.FileKeysRequest, error) {
|
||||||
|
var status common.FileKeysRequest
|
||||||
|
|
||||||
|
err := json.Unmarshal(data, &status)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = validator.ValidateStruct(status)
|
||||||
|
if err != nil {
|
||||||
|
return &status, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func notifyFileKeysGeneralError(client redis.Client, device common.Device, id string, err error) {
|
||||||
|
e := client.SafePublishMessage(device.Key(id), common.Message{
|
||||||
|
Handler: "filekeys",
|
||||||
|
Data: []common.FileKeyResponse{
|
||||||
|
{
|
||||||
|
FileID: "0",
|
||||||
|
Error: err.Error(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if e != nil {
|
||||||
|
logger.Error().Err(errors.WithStack(e)).Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
59
services/attendant/controllers/health_check.go
Normal file
59
services/attendant/controllers/health_check.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/health"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mismatchTypeError = errors.New("mismatch type error")
|
||||||
|
|
||||||
|
func HealthCheck() {
|
||||||
|
redis := health.NewRedisHealth(services.RedisClientPool())
|
||||||
|
server := health.HealthCheckServer{}
|
||||||
|
err := server.Serve([]health.Config{
|
||||||
|
{
|
||||||
|
Name: "db",
|
||||||
|
Check: health.NewPostgresCheck(services.GetDB().GetDBClient().GetConn()),
|
||||||
|
Timeout: time.Second * 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "redis",
|
||||||
|
Check: redis.Check,
|
||||||
|
Timeout: time.Second * 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "kafka",
|
||||||
|
Check: health.NewKafkaMultiCheck(getKafkaConsumer),
|
||||||
|
Timeout: time.Second * 1,
|
||||||
|
Vital: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getKafkaConsumer() (connections []health.KafkaConnCheckInterface, err error) {
|
||||||
|
client, oldClient, err := services.GetKafkaConsumer()
|
||||||
|
if err != nil {
|
||||||
|
return connections, err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, ok := client.(health.KafkaConnCheckInterface)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.WithStack(mismatchTypeError)
|
||||||
|
}
|
||||||
|
connections = append(connections, conn)
|
||||||
|
oldConn, ok := oldClient.(health.KafkaConnCheckInterface)
|
||||||
|
if !ok {
|
||||||
|
return connections, errors.WithStack(mismatchTypeError)
|
||||||
|
}
|
||||||
|
connections = append(connections, oldConn)
|
||||||
|
return connections, nil
|
||||||
|
}
|
||||||
403
services/attendant/controllers/send_manifest.go
Normal file
403
services/attendant/controllers/send_manifest.go
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/carcommand"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common/carupdatestatus"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common/manifestfingerprintparams"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/hwversion"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/kafka"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/manifestsender"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/tmobile"
|
||||||
|
uhelpers "github.com/fiskerinc/cloud-services/pkg/usecase_helpers"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/randomvalues"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/whereami"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/validator"
|
||||||
|
vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 0100084000000101012200010101010001010101000000000000000000ff7eff7f000101010101000101010100010001010101000101010100000000000000000000000000010101000100000100010101000201010101000101020101000101010200010101010101010101000101000100010001010101010101010101010000000000000100010101020101010101010000000000000000ffffff00010102010102020001010000000000000000000000000000000000000000000000000000000000000000000100202310010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6
|
||||||
|
var defaultVOD string = envtool.GetEnv("DEFAULT_VOD", "00FF111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111114A")
|
||||||
|
|
||||||
|
var fileKeyBypass map[int64]string
|
||||||
|
var FileKeyBypassManifest_US map[int64]string = map[int64]string{
|
||||||
|
1189: "fe4f19fbc940ed58",
|
||||||
|
1380: "6a44fd71c940716d",
|
||||||
|
}
|
||||||
|
|
||||||
|
var FileKeyBypassManifest_EU map[int64]string = map[int64]string{
|
||||||
|
893: "fe4f19fbc940ed58",
|
||||||
|
1002: "6a44fd71c940716d",
|
||||||
|
1012: "d70e1d32c940a221",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if whereami.Environment == whereami.PRODUCTION_EU {
|
||||||
|
fileKeyBypass = FileKeyBypassManifest_EU
|
||||||
|
} else {
|
||||||
|
fileKeyBypass = FileKeyBypassManifest_US
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManifestSender(
|
||||||
|
r redis.ClientPoolInterface,
|
||||||
|
db *services.DB,
|
||||||
|
conf vconfig.ConfigServiceInterface,
|
||||||
|
device common.Device,
|
||||||
|
ka *services.KeepAwake,
|
||||||
|
seed int64, //This seed is used only for testing
|
||||||
|
) *ManifestSender {
|
||||||
|
randomGenerator := randomvalues.NewNonCryptoGenerator("ABCDEFGHIJKLMNOPQRSTUVWXYZ", seed)
|
||||||
|
return &ManifestSender{
|
||||||
|
Redis: r,
|
||||||
|
db: db,
|
||||||
|
conf: conf,
|
||||||
|
Device: device,
|
||||||
|
sms: services.GetSMSClient(),
|
||||||
|
ka: ka,
|
||||||
|
gen: randomGenerator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ManifestSender struct {
|
||||||
|
Redis redis.ClientPoolInterface
|
||||||
|
db *services.DB
|
||||||
|
conf vconfig.ConfigServiceInterface
|
||||||
|
sms sms.SMSServiceClient
|
||||||
|
Device common.Device
|
||||||
|
ka *services.KeepAwake
|
||||||
|
gen randomvalues.NonCryptoGenerator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ManifestSender) Process(id string, data []byte) error {
|
||||||
|
// id string parameter is unused except in the following log
|
||||||
|
// the data might not be used either, just for its single carUpdateID
|
||||||
|
u, err := m.parseRequest(data)
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Msgf("Manifest sender, unable to process update %s", id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
carUpdate := common.CarUpdate{ID: u.CarUpdateID}
|
||||||
|
err = m.loadCarUpdate(&carUpdate)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info().Msgf("ManifestSender car_update_id %d", carUpdate.ID)
|
||||||
|
|
||||||
|
carUpdate.UpdateManifest.CarUpdateID = carUpdate.ID
|
||||||
|
helper := uhelpers.NewECUKeys(m.db.GetECCKeys())
|
||||||
|
err = helper.AddECUECCKeys(carUpdate.UpdateManifest)
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Msgf("Unable to decrypt keys for %d", carUpdate.ID)
|
||||||
|
return err //comment out for local debugging
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we fail to deliver the sms, then we don't believe the car has awoken
|
||||||
|
// then set the manifest as failed
|
||||||
|
err = m.send(carUpdate.VIN, carUpdate.UpdateManifest)
|
||||||
|
if err != nil {
|
||||||
|
update := NewCarUpdateProgress(m.Redis, m.ka, m.db, common.TRex)
|
||||||
|
err = update.ProcessStatus(carUpdate.VIN, common.CarUpdateProgress{
|
||||||
|
CarUpdateID: u.CarUpdateID,
|
||||||
|
Status: carupdatestatus.ManifestCanceled,
|
||||||
|
Info: " Underlying error: " + err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ManifestSender) destination(manifest *common.UpdateManifest) string {
|
||||||
|
if manifest.HasSelfDownload() {
|
||||||
|
return "ICC"
|
||||||
|
} else {
|
||||||
|
return "ICC/TBOX"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ManifestSender) parseRequest(data []byte) (*common.UpdateManifest, error) {
|
||||||
|
var req common.UpdateManifest
|
||||||
|
|
||||||
|
err := json.Unmarshal(data, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = validator.ValidateIDField(req.CarUpdateID)
|
||||||
|
if err != nil {
|
||||||
|
return &req, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ManifestSender) loadCarUpdate(cu *common.CarUpdate) error {
|
||||||
|
err := m.db.GetCarUpdates().Load(cu)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cu.UpdateManifest == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ManifestSender) queueManifest(vin string, manifest *common.UpdateManifest) error {
|
||||||
|
loggedManifest := manifest.Copy()
|
||||||
|
loggedManifest.RemoveECCKeysFromECUs()
|
||||||
|
logger.At(logger.Info(), common.HMI.Key(vin), "update").Interface("manifest.json", loggedManifest).Msgf("HMI ManifestSender sent %v %s %d", common.HMI, vin, manifest.CarUpdateID)
|
||||||
|
client := m.Redis.GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
err := client.SafeQueueMessage(common.HMI.Key(vin), common.Message{
|
||||||
|
Handler: "update_manifest",
|
||||||
|
Data: manifest,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Msgf("failed to queue manifest in redis vin %s ", vin)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ManifestSender) send(vin string, manifest *common.UpdateManifest) error {
|
||||||
|
updateManifestID := manifest.ID
|
||||||
|
manifest.TransformECUNames()
|
||||||
|
err := hwversion.SetHWVersion(manifest, vin, m.db.GetCars())
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Msgf("manifest sender failed at SetHwVersion for vin %s", vin)
|
||||||
|
return err //comment out for local debugging
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest.SortECUs()
|
||||||
|
manifest.RemoveOriginalS19HexFiles()
|
||||||
|
manifest.RemoveOriginalS19HexFilesRollbacks()
|
||||||
|
|
||||||
|
manifest.FilterCompatibleECUs(vin)
|
||||||
|
|
||||||
|
err = uhelpers.PopulateECUsCurrentVersion(m.db.GetCars(), vin, manifest.ECUs)
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Msgf("manifest sender failed at PupulateECUsCurrentVersion for vin %s", vin)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fpparams := manifestfingerprintparams.GetFPParams()
|
||||||
|
manifest.GenerateFingerprint(fpparams.CurTime(), fpparams.ManifestSerial())
|
||||||
|
|
||||||
|
hmiManifest := manifest.Copy()
|
||||||
|
|
||||||
|
cds, err := m.addVOD(hmiManifest, vin)
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Msgf("manifest sender failed at adding the vod for vin %s", vin)
|
||||||
|
return err // comment out for local debugging
|
||||||
|
}
|
||||||
|
|
||||||
|
err = validator.ValidateField(hmiManifest.SUMS, "sums_version")
|
||||||
|
if err == nil {
|
||||||
|
err = hmiManifest.AddSUMSToVOD()
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Msgf("manifest sender failed at AddSUMSToVOD for vin %s", vin)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.Err(err).Msgf("manifest sender failed at Validation for vin %s", vin)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hmiManifest.Scrub(common.HMI)
|
||||||
|
|
||||||
|
client := m.Redis.GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
// If HasSelfDownload, we are not yet ready to send to t box, so we wake the car if the update is forced
|
||||||
|
if manifest.HasSelfDownload() && m.sms != nil {
|
||||||
|
// The function that calls this one handles canceling the manifest
|
||||||
|
var msgID string
|
||||||
|
res, err := carcommand.QueueSMSWakeUp(vin, true, client, m.db.GetCars(), m.sms)
|
||||||
|
if err != nil || res == nil || !res.SentSuccessful {
|
||||||
|
logger.Err(err).Msgf("Attendant:manifest sender failed at sendSMSWakeUp for vin %s", vin)
|
||||||
|
msgID = m.gen.GetString(10)
|
||||||
|
defer m.selfKafkaCallback(msgID)
|
||||||
|
} else {
|
||||||
|
msgID = res.SmsMsgID
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.ka.SendFirstKeepAwakeMessage(vin)
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Msgf("failed to SendFirstKeepAwakeMessage on msgID %s", msgID)
|
||||||
|
}
|
||||||
|
m.queueManifest(vin, hmiManifest)
|
||||||
|
|
||||||
|
fileID, ok := fileKeyBypass[updateManifestID]
|
||||||
|
if ok {
|
||||||
|
logger.Info().Str("VIN", vin).Msg("Queueing SendFileKeys")
|
||||||
|
time.AfterFunc(time.Second*3, func() { sendFileKeys(vin, fileID) })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not send manifest to TBOX, wait until ICC has finished downloading
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
trex := manifestsender.NewTBOXManifestSender(client, m.conf, m.db, services.GetSMSClient(), cds)
|
||||||
|
defer trex.Close()
|
||||||
|
//smsID, err := trex.ProcessSoftwareUpdate(vin, manifest)
|
||||||
|
_, err = trex.ProcessSoftwareUpdate(vin, manifest, services.GetDB().GetCarConfigData())
|
||||||
|
logger.Debug().Msgf("Send HMI manifest to %s ", vin)
|
||||||
|
m.queueManifest(vin, hmiManifest)
|
||||||
|
|
||||||
|
// We managed to send an sms, so we want to continue with an sms delivered check
|
||||||
|
// if smsID != "" {
|
||||||
|
// err = m.setManifestCache(smsID, ManifestCachedPoint{
|
||||||
|
// VIN: vin,
|
||||||
|
// ManifestID: manifest.ID,
|
||||||
|
// Destination: m.destination(manifest),
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// So this flow of continue will be activated at two different times, possible multiple times.
|
||||||
|
// 1) When a new car_update comes through and the .hasdownload is true, and then again when the manifest is being sent to the tbox
|
||||||
|
func (m *ManifestSender) ContinueTBOXSend(id string, data []byte) (err error) {
|
||||||
|
// After a text has been succesfully delivered, we can now continue the update
|
||||||
|
// Check the status and make sure that is is success, otherwise fail and set the update status as failed
|
||||||
|
var msgStatus tmobile.MessageStatus
|
||||||
|
err = json.Unmarshal(data, &msgStatus)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ManifestSender) setManifestCache(msgID string, cache ManifestCachedPoint) (err error) {
|
||||||
|
client := m.Redis.GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
return client.Set(msgIDToRedisKey(msgID), cache)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ManifestCachedPoint struct {
|
||||||
|
VIN string
|
||||||
|
ManifestID int64
|
||||||
|
Destination string
|
||||||
|
}
|
||||||
|
|
||||||
|
func msgIDToRedisKey(msgID string) (redisKey string) {
|
||||||
|
return fmt.Sprintf("manifest_tbox_send_cache:%s", msgID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ManifestSender) addVOD(manifest *common.UpdateManifest, vin string) (map[string]string, error) {
|
||||||
|
cds, err := uhelpers.GetCDS(m.conf, services.GetDB().GetCarConfigData(), vin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if vod, ok := cds["VOD"]; ok && vod != "" {
|
||||||
|
manifest.VOD = vod
|
||||||
|
} else {
|
||||||
|
manifest.VOD = defaultVOD
|
||||||
|
}
|
||||||
|
|
||||||
|
return cds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// When car is a virtual trex, we are going to produce to the kafka message queue as if the sms service did it
|
||||||
|
func (m *ManifestSender) selfKafkaCallback(messageID string) {
|
||||||
|
producer, err := services.GetKafkaProducer()
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Send()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
messageStatus := &kafka_grpc.GRPC_AttendantPayload_MessageStatus{
|
||||||
|
MessageStatus: &kafka_grpc.MessageStatus{
|
||||||
|
MessageId: messageID,
|
||||||
|
Status: kafka_grpc.EmumStatus_DELIVERED,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
kafkaMSG := kafka_grpc.GRPC_AttendantPayload{
|
||||||
|
Handler: "sms_delivery_status_manifest",
|
||||||
|
Data: messageStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
binaryPayload, _ := proto.Marshal(&kafkaMSG)
|
||||||
|
err = producer.ProduceBinary(kafka.AttendantServiceGRPCKafka, "4:Service", binaryPayload, nil)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.WithStack(err)
|
||||||
|
logger.Err(err).Msgf("failed to produce kafka message for sms id %s", messageID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ManifestSender) Release() {
|
||||||
|
m.Redis = nil
|
||||||
|
m.db = nil
|
||||||
|
m.conf = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var fpParams FingerprintParamer
|
||||||
|
var fpParamsOnce sync.Once
|
||||||
|
|
||||||
|
func SetFPParams(fpp FingerprintParamer) {
|
||||||
|
fpParams = fpp
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFPParams() FingerprintParamer {
|
||||||
|
fpParamsOnce.Do(func() {
|
||||||
|
if fpParams == nil {
|
||||||
|
fpParams = &fingerprintParams{
|
||||||
|
serialNum: envtool.GetEnv("OTA_MANIFEST_SERIAL", "00000000000000000"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fpParams
|
||||||
|
}
|
||||||
|
|
||||||
|
type fingerprintParams struct {
|
||||||
|
serialNum string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *fingerprintParams) ManifestSerial() string {
|
||||||
|
return p.serialNum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *fingerprintParams) CurTime() time.Time {
|
||||||
|
return time.Now().UTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
type FingerprintParamer interface {
|
||||||
|
ManifestSerial() string
|
||||||
|
CurTime() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mega hack mode
|
||||||
|
|
||||||
|
func sendFileKeys(vin string, fileID string) {
|
||||||
|
logger.Info().Str("VIN", vin).Msg("Sending SendFileKeys")
|
||||||
|
db := services.GetDB()
|
||||||
|
err := GetFileKeys(db, common.HMI, vin, []byte(`{"file_ids": ["`+fileID+`"]}`))
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Str("VIN", vin).Str("file ID", fileID).Msg("Failed to SendFileKeys")
|
||||||
|
}
|
||||||
|
}
|
||||||
115
services/attendant/go.mod
Normal file
115
services/attendant/go.mod
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
module github.com/fiskerinc/cloud-services/services/attendant
|
||||||
|
|
||||||
|
go 1.25
|
||||||
|
|
||||||
|
toolchain go1.25.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fiskerinc/cloud-services/pkg v0.0.0-00010101000000-000000000000
|
||||||
|
github.com/fiskerinc/cloud-services/pkg/can-go v0.0.0-00010101000000-000000000000
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/go-pg/pg/v10 v10.11.1
|
||||||
|
github.com/gomodule/redigo v1.8.9
|
||||||
|
github.com/jinzhu/copier v0.3.5
|
||||||
|
github.com/stretchr/testify v1.10.0
|
||||||
|
google.golang.org/grpc v1.67.3
|
||||||
|
google.golang.org/protobuf v1.36.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/DataDog/appsec-internal-go v1.4.0 // indirect
|
||||||
|
github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 // indirect
|
||||||
|
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1 // indirect
|
||||||
|
github.com/DataDog/datadog-go/v5 v5.3.0 // indirect
|
||||||
|
github.com/DataDog/go-libddwaf/v2 v2.2.3 // indirect
|
||||||
|
github.com/DataDog/go-tuf v1.0.2-0.5.2 // indirect
|
||||||
|
github.com/DataDog/sketches-go v1.4.2 // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||||
|
github.com/ReneKroon/ttlcache/v2 v2.11.0 // indirect
|
||||||
|
github.com/albenik/bcd v0.0.0-20170831201648-635201416bc7 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/confluentinc/confluent-kafka-go/v2 v2.3.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/ebitengine/purego v0.5.2 // indirect
|
||||||
|
github.com/elliotchance/orderedmap/v2 v2.2.0 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
|
github.com/go-jose/go-jose/v4 v4.1.0 // indirect
|
||||||
|
github.com/go-pg/zerochecker v0.2.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.15.1 // indirect
|
||||||
|
github.com/golang/mock v1.7.0-rc.1 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/schema v1.2.0 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
|
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||||
|
github.com/jeremywohl/flatten v1.0.1 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.2 // indirect
|
||||||
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||||
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
|
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||||
|
github.com/onsi/gomega v1.25.0 // indirect
|
||||||
|
github.com/outcaste-io/ristretto v0.2.3 // indirect
|
||||||
|
github.com/philhofer/fwd v1.1.2 // indirect
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
|
github.com/redis/go-redis/v9 v9.5.1 // indirect
|
||||||
|
github.com/rs/zerolog v1.29.1 // indirect
|
||||||
|
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
|
||||||
|
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f // indirect
|
||||||
|
github.com/tinylib/msgp v1.1.8 // indirect
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
|
github.com/twmb/franz-go v1.20.6 // indirect
|
||||||
|
github.com/twmb/franz-go/pkg/kadm v1.17.2 // indirect
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.12.0 // indirect
|
||||||
|
github.com/vmihailenco/bufpool v0.1.11 // indirect
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||||
|
github.com/vmihailenco/tagparser v0.1.2 // indirect
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
|
github.com/xdg-go/scram v1.1.2 // indirect
|
||||||
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||||
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
|
go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
||||||
|
golang.org/x/crypto v0.45.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||||
|
golang.org/x/mod v0.29.0 // indirect
|
||||||
|
golang.org/x/net v0.47.0 // indirect
|
||||||
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
|
golang.org/x/text v0.31.0 // indirect
|
||||||
|
golang.org/x/time v0.8.0 // indirect
|
||||||
|
golang.org/x/tools v0.38.0 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
|
||||||
|
gopkg.in/DataDog/dd-trace-go.v1 v1.60.1 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect
|
||||||
|
mellium.im/sasl v0.3.1 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace (
|
||||||
|
github.com/fiskerinc/cloud-services/pkg => ../../pkg
|
||||||
|
github.com/fiskerinc/cloud-services/pkg/can-go => ../../pkg/can-go
|
||||||
|
)
|
||||||
483
services/attendant/go.sum
Normal file
483
services/attendant/go.sum
Normal file
@@ -0,0 +1,483 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/DataDog/appsec-internal-go v1.4.0 h1:KFI8ElxkJOgpw+cUm9TXK/jh5EZvRaWM07sXlxGg9Ck=
|
||||||
|
github.com/DataDog/appsec-internal-go v1.4.0/go.mod h1:ONW8aV6R7Thgb4g0bB9ZQCm+oRgyz5eWiW7XoQ19wIc=
|
||||||
|
github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 h1:bUMSNsw1iofWiju9yc1f+kBd33E3hMJtq9GuU602Iy8=
|
||||||
|
github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0/go.mod h1:HzySONXnAgSmIQfL6gOv9hWprKJkx8CicuXuUbmgWfo=
|
||||||
|
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1 h1:5nE6N3JSs2IG3xzMthNFhXfOaXlrsdgqmJ73lndFf8c=
|
||||||
|
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1/go.mod h1:Vc+snp0Bey4MrrJyiV2tVxxJb6BmLomPvN1RgAvjGaQ=
|
||||||
|
github.com/DataDog/datadog-go/v5 v5.3.0 h1:2q2qjFOb3RwAZNU+ez27ZVDwErJv5/VpbBPprz7Z+s8=
|
||||||
|
github.com/DataDog/datadog-go/v5 v5.3.0/go.mod h1:XRDJk1pTc00gm+ZDiBKsjh7oOOtJfYfglVCmFb8C2+Q=
|
||||||
|
github.com/DataDog/go-libddwaf/v2 v2.2.3 h1:LpKE8AYhVrEhlmlw6FGD41udtDf7zW/aMdLNbCXpegQ=
|
||||||
|
github.com/DataDog/go-libddwaf/v2 v2.2.3/go.mod h1:8nX0SYJMB62+fbwYmx5J7zuCGEjiC/RxAo3+AuYJuFE=
|
||||||
|
github.com/DataDog/go-tuf v1.0.2-0.5.2 h1:EeZr937eKAWPxJ26IykAdWA4A0jQXJgkhUjqEI/w7+I=
|
||||||
|
github.com/DataDog/go-tuf v1.0.2-0.5.2/go.mod h1:zBcq6f654iVqmkk8n2Cx81E1JnNTMOAx1UEO/wZR+P0=
|
||||||
|
github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/IGQh4=
|
||||||
|
github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM=
|
||||||
|
github.com/DataDog/sketches-go v1.4.2 h1:gppNudE9d19cQ98RYABOetxIhpTCl4m7CnbRZjvVA/o=
|
||||||
|
github.com/DataDog/sketches-go v1.4.2/go.mod h1:xJIXldczJyyjnbDop7ZZcLxJdV3+7Kra7H1KMgpgkLk=
|
||||||
|
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||||
|
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||||
|
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||||
|
github.com/ReneKroon/ttlcache/v2 v2.11.0 h1:OvlcYFYi941SBN3v9dsDcC2N8vRxyHcCmJb3Vl4QMoM=
|
||||||
|
github.com/ReneKroon/ttlcache/v2 v2.11.0/go.mod h1:mBxvsNY+BT8qLLd6CuAJubbKo6r0jh3nb5et22bbfGY=
|
||||||
|
github.com/albenik/bcd v0.0.0-20170831201648-635201416bc7 h1:m3Ayfs5OcAlIMEdLIQKubBsVLGee4YMUr14+d1256WE=
|
||||||
|
github.com/albenik/bcd v0.0.0-20170831201648-635201416bc7/go.mod h1:QIAMbrwsnQZ2ES3G26RubSrDB5SPyzsp9Hts5NJdTrI=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
|
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/confluentinc/confluent-kafka-go/v2 v2.3.0 h1:icCHutJouWlQREayFwCc7lxDAhws08td+W3/gdqgZts=
|
||||||
|
github.com/confluentinc/confluent-kafka-go/v2 v2.3.0/go.mod h1:/VTy8iEpe6mD9pkCH5BhijlUl8ulUXymKv1Qig5Rgb8=
|
||||||
|
github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg=
|
||||||
|
github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||||
|
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||||
|
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
|
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
|
||||||
|
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
|
github.com/docker/docker v23.0.4+incompatible h1:Kd3Bh9V/rO+XpTP/BLqM+gx8z7+Yb0AA2Ibj+nNo4ek=
|
||||||
|
github.com/docker/docker v23.0.4+incompatible/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.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||||
|
github.com/ebitengine/purego v0.5.2 h1:r2MQEtkGzZ4LRtFZVAg5bjYKnUbxxloaeuGxH0t7qfs=
|
||||||
|
github.com/ebitengine/purego v0.5.2/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||||
|
github.com/elliotchance/orderedmap/v2 v2.2.0 h1:7/2iwO98kYT4XkOjA9mBEIwvi4KpGB4cyHeOFOnj4Vk=
|
||||||
|
github.com/elliotchance/orderedmap/v2 v2.2.0/go.mod h1:85lZyVbpGaGvHvnKa7Qhx7zncAdBIBq6u56Hb1PRU5Q=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.1.0 h1:cYSYxd3pw5zd2FSXk2vGdn9igQU2PS8MuxrCOCl0FdY=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.1.0/go.mod h1:GG/vqmYm3Von2nYiB2vGTXzdoNKE5tix5tuc6iAd+sw=
|
||||||
|
github.com/go-pg/pg/v10 v10.11.1 h1:vYwbFpqoMpTDphnzIPshPPepdy3VpzD8qo29OFKp4vo=
|
||||||
|
github.com/go-pg/pg/v10 v10.11.1/go.mod h1:ExJWndhDNNftBdw1Ow83xqpSf4WMSJK8urmXD5VXS1I=
|
||||||
|
github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU=
|
||||||
|
github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.15.1 h1:BSe8uhN+xQ4r5guV/ywQI4gO59C2raYcGffYWZEjZzM=
|
||||||
|
github.com/go-playground/validator/v10 v10.15.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
|
github.com/go-redis/redismock/v9 v9.2.0 h1:ZrMYQeKPECZPjOj5u9eyOjg8Nnb0BS9lkVIZ6IpsKLw=
|
||||||
|
github.com/go-redis/redismock/v9 v9.2.0/go.mod h1:18KHfGDK4Y6c2R0H38EUGWAdc7ZQS9gfYxc94k7rWT0=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
|
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
|
||||||
|
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
|
||||||
|
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo=
|
||||||
|
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
|
||||||
|
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
|
||||||
|
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||||
|
github.com/jeremywohl/flatten v1.0.1 h1:LrsxmB3hfwJuE+ptGOijix1PIfOoKLJ3Uee/mzbgtrs=
|
||||||
|
github.com/jeremywohl/flatten v1.0.1/go.mod h1:4AmD/VxjWcI5SRB0n6szE2A6s2fsNHDLO0nAlMHgfLQ=
|
||||||
|
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
|
||||||
|
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
|
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||||
|
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||||
|
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||||
|
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||||
|
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||||
|
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo=
|
||||||
|
github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||||
|
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||||
|
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||||
|
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
|
||||||
|
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||||
|
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||||
|
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||||
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
|
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||||
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
|
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||||
|
github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
|
||||||
|
github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
|
||||||
|
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/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8=
|
||||||
|
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
|
||||||
|
github.com/opencontainers/runc v1.1.6 h1:XbhB8IfG/EsnhNvZtNdLB0GBw92GYEFvKlhaJk9jUgA=
|
||||||
|
github.com/opencontainers/runc v1.1.6/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50=
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||||
|
github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0=
|
||||||
|
github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac=
|
||||||
|
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
|
||||||
|
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
|
||||||
|
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||||
|
github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052 h1:Qp27Idfgi6ACvFQat5+VJvlYToylpM/hcyLBI3WaKPA=
|
||||||
|
github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052/go.mod h1:uvX/8buq8uVeiZiFht+0lqSLBHF+uGV8BrTv8W/SIwk=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
|
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
|
github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
|
||||||
|
github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
|
||||||
|
github.com/secure-systems-lab/go-securesystemslib v0.7.0 h1:OwvJ5jQf9LnIAS83waAjPbcMsODrTQUpJ02eNLUoxBg=
|
||||||
|
github.com/secure-systems-lab/go-securesystemslib v0.7.0/go.mod h1:/2gYnlnHVQ6xeGtfIqFy7Do03K4cdCY0A/GlJLDKLHI=
|
||||||
|
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f h1:1R9KdKjCNSd7F8iGTxIpoID9prlYH8nuNYKt0XvweHA=
|
||||||
|
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f/go.mod h1:vQhwQ4meQEDfahT5kd61wLAF5AAeh5ZPLVI4JJ/tYo8=
|
||||||
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/testcontainers/testcontainers-go v0.14.0 h1:h0D5GaYG9mhOWr2qHdEKDXpkce/VlvaYOCzTRi6UBi8=
|
||||||
|
github.com/testcontainers/testcontainers-go v0.14.0/go.mod h1:hSRGJ1G8Q5Bw2gXgPulJOLlEBaYJHeBSOkQM5JLG+JQ=
|
||||||
|
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
|
||||||
|
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||||
|
github.com/twmb/franz-go v1.20.6 h1:TpQTt4QcixJ1cHEmQGPOERvTzo99s8jAutmS7rbSD6w=
|
||||||
|
github.com/twmb/franz-go v1.20.6/go.mod h1:u+FzH2sInp7b9HNVv2cZN8AxdXy6y/AQ1Bkptu4c0FM=
|
||||||
|
github.com/twmb/franz-go/pkg/kadm v1.17.2 h1:g5f1sAxnTkYC6G96pV5u715HWhxd66hWaDZUAQ8xHY8=
|
||||||
|
github.com/twmb/franz-go/pkg/kadm v1.17.2/go.mod h1:ST55zUB+sUS+0y+GcKY/Tf1XxgVilaFpB9I19UubLmU=
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.12.0 h1:CbatD7ers1KzDNgJqPbKOq0Bz/WLBdsTH75wgzeVaPc=
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.12.0/go.mod h1:+DPt4NC8RmI6hqb8G09+3giKObE6uD2Eya6CfqBpeJY=
|
||||||
|
github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94=
|
||||||
|
github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||||
|
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
|
||||||
|
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||||
|
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
|
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
|
||||||
|
go4.org/intern v0.0.0-20230525184215-6c62f75575cb h1:ae7kzL5Cfdmcecbh22ll7lYP3iuUdnfnhiPcSaDgH/8=
|
||||||
|
go4.org/intern v0.0.0-20230525184215-6c62f75575cb/go.mod h1:Ycrt6raEcnF5FTsLiLKkhBTO6DPX3RCUCUVnks3gFJU=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 h1:lGdhQUN/cnWdSH3291CUuxSEqc+AsGTiDxPP3r2J0l4=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||||
|
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||||
|
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||||
|
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
|
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
|
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||||
|
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||||
|
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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||||
|
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||||
|
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||||
|
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||||
|
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||||
|
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||||
|
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
|
||||||
|
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.67.3 h1:OgPcDAFKHnH8X3O4WcO4XUc8GRDeKsKReqbQtiCj7N8=
|
||||||
|
google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||||
|
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
|
gopkg.in/DataDog/dd-trace-go.v1 v1.60.1 h1:Sqkq62MxQW/RD+sgZsQuUdHWHyXI4JS5x0lxlxrv2Hk=
|
||||||
|
gopkg.in/DataDog/dd-trace-go.v1 v1.60.1/go.mod h1:6aArYrAHjnuaofJ3lKuSRQbhrBx1LcSpiEYCIScJE5Y=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||||
|
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||||
|
honnef.co/go/gotraceui v0.2.0 h1:dmNsfQ9Vl3GwbiVD7Z8d/osC6WtGGrasyrC2suc4ZIQ=
|
||||||
|
honnef.co/go/gotraceui v0.2.0/go.mod h1:qHo4/W75cA3bX0QQoSvDjbJa4R8mAyyFjbWAj63XElc=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a h1:1XCVEdxrvL6c0TGOhecLuB7U9zYNdxZEjvOqJreKZiM=
|
||||||
|
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a/go.mod h1:e83i32mAQOW1LAqEIweALsuK2Uw4mhQadA5r7b0Wobo=
|
||||||
|
mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo=
|
||||||
|
mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
|
||||||
26
services/attendant/handlers/car_update_progress.go
Normal file
26
services/attendant/handlers/car_update_progress.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/controllers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CarUpdateProgressStatus(db *services.DB, ka *services.KeepAwake, device common.Device, id string, data []byte) error {
|
||||||
|
logger.Debug().Msgf("CarUpdateProgressStatus %v %s", device, id)
|
||||||
|
|
||||||
|
clientPool := services.RedisClientPool()
|
||||||
|
|
||||||
|
handler := controllers.NewCarUpdateProgress(clientPool, ka, db, device)
|
||||||
|
if handler == nil {
|
||||||
|
return errors.Errorf("NewCarUpdateProgress cannot handle device %v", device)
|
||||||
|
}
|
||||||
|
defer handler.Dispose()
|
||||||
|
|
||||||
|
err := handler.Process(id, data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
389
services/attendant/handlers/car_update_progress_func_test.go
Normal file
389
services/attendant/handlers/car_update_progress_func_test.go
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/handlers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis/tester"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/testhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCarUpdateProgressFunctional(t *testing.T) {
|
||||||
|
t.Skip()
|
||||||
|
testVIN := "WBSEH93466B798124"
|
||||||
|
conn := tester.NewRedisMock()
|
||||||
|
db := services.GetDB()
|
||||||
|
manifest, carUpdateID, err := setupCarUpdateProgressFunc(db, testVIN)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if carUpdateID > 0 {
|
||||||
|
db.GetCarUpdates().Delete(&common.CarUpdate{ID: carUpdateID})
|
||||||
|
conn.Delete(redis.CarUpdateStatusTBOXHashKey(carUpdateID), redis.CarUpdateStatusHMIHashKey(carUpdateID))
|
||||||
|
}
|
||||||
|
if manifest != nil && manifest.ID > 0 {
|
||||||
|
db.GetUpdateManifests().Delete(manifest)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
ka := services.NewKeepAwakeService()
|
||||||
|
ka.SetService(&services.MockKeepAwakeImplementation{})
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
Name string
|
||||||
|
Device common.Device
|
||||||
|
Payload string
|
||||||
|
ExpectedMsg string
|
||||||
|
ExpectInstalled int
|
||||||
|
ExpectInstallTotal int
|
||||||
|
ExpectDBStatus string
|
||||||
|
ExpectDownloadCurrent uint64
|
||||||
|
ExpectDownloadTotal uint64
|
||||||
|
ExpectErrorCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []testCase{
|
||||||
|
{
|
||||||
|
Name: "manifest_received",
|
||||||
|
Device: common.TRex,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id": %d,
|
||||||
|
"msg": "manifest_received"
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "manifest_received",
|
||||||
|
ExpectDBStatus: "manifest_received",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "install_approval_await",
|
||||||
|
Device: common.TRex,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id": %d,
|
||||||
|
"msg": "install_approval_await"
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "install_approval_await",
|
||||||
|
ExpectDBStatus: "install_approval_await",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "other error",
|
||||||
|
Device: common.TRex,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id": %d,
|
||||||
|
"ecu": "TEST",
|
||||||
|
"installed": 3,
|
||||||
|
"total_files": 10,
|
||||||
|
"msg": "other error",
|
||||||
|
"err": -100
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "other error",
|
||||||
|
ExpectDBStatus: "other error",
|
||||||
|
ExpectInstalled: 3,
|
||||||
|
ExpectInstallTotal: 10,
|
||||||
|
ExpectErrorCode: -100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "download_start",
|
||||||
|
Device: common.TRex,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id": %d,
|
||||||
|
"ecu": "TEST",
|
||||||
|
"file_current": 0,
|
||||||
|
"file_total": 100,
|
||||||
|
"package_current": 0,
|
||||||
|
"package_total": 100,
|
||||||
|
"msg": "download_start",
|
||||||
|
"err": 0
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "downloading",
|
||||||
|
ExpectDBStatus: "package_download_start",
|
||||||
|
ExpectDownloadCurrent: 0,
|
||||||
|
ExpectDownloadTotal: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "downloading",
|
||||||
|
Device: common.TRex,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id": %d,
|
||||||
|
"ecu": "TEST",
|
||||||
|
"file_current": 0,
|
||||||
|
"file_total": 100,
|
||||||
|
"package_current": 30,
|
||||||
|
"package_total": 100,
|
||||||
|
"msg": "downloading",
|
||||||
|
"err": 0
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "downloading",
|
||||||
|
ExpectDBStatus: "package_download_start",
|
||||||
|
ExpectDownloadCurrent: 30,
|
||||||
|
ExpectDownloadTotal: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "download_complete",
|
||||||
|
Device: common.TRex,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id": %d,
|
||||||
|
"ecu": "TEST",
|
||||||
|
"file_current": 100,
|
||||||
|
"file_total": 100,
|
||||||
|
"package_current": 900,
|
||||||
|
"package_total": 1000,
|
||||||
|
"msg": "download_complete",
|
||||||
|
"err": 0
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "downloading",
|
||||||
|
ExpectDBStatus: "package_download_start",
|
||||||
|
ExpectDownloadCurrent: 900,
|
||||||
|
ExpectDownloadTotal: 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "package_download_complete",
|
||||||
|
Device: common.TRex,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id": %d,
|
||||||
|
"ecu": "TEST",
|
||||||
|
"file_current": 100,
|
||||||
|
"file_total": 100,
|
||||||
|
"package_current": 1000,
|
||||||
|
"package_total": 1000,
|
||||||
|
"msg": "download_complete",
|
||||||
|
"err": 0
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "package_download_complete",
|
||||||
|
ExpectDBStatus: "package_download_complete",
|
||||||
|
ExpectDownloadCurrent: 1000,
|
||||||
|
ExpectDownloadTotal: 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "download_error",
|
||||||
|
Device: common.TRex,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id": %d,
|
||||||
|
"ecu": "TEST",
|
||||||
|
"file_current": 0,
|
||||||
|
"file_total": 100,
|
||||||
|
"package_current": 0,
|
||||||
|
"package_total": 1000,
|
||||||
|
"msg": "download_error",
|
||||||
|
"err": 0
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "download_error",
|
||||||
|
ExpectDBStatus: "download_error",
|
||||||
|
ExpectDownloadCurrent: 0,
|
||||||
|
ExpectDownloadTotal: 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "install_start",
|
||||||
|
Device: common.TRex,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id": %d,
|
||||||
|
"ecu": "TEST",
|
||||||
|
"installed": 0,
|
||||||
|
"total_files": 10,
|
||||||
|
"msg": "install_start",
|
||||||
|
"err": 0
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "installing",
|
||||||
|
ExpectDBStatus: "package_install_start",
|
||||||
|
ExpectInstalled: 0,
|
||||||
|
ExpectInstallTotal: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "installing",
|
||||||
|
Device: common.TRex,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id": %d,
|
||||||
|
"ecu": "TEST",
|
||||||
|
"installed": 2,
|
||||||
|
"total_files": 10,
|
||||||
|
"msg": "installing",
|
||||||
|
"err": 0
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "installing",
|
||||||
|
ExpectDBStatus: "package_install_start",
|
||||||
|
ExpectInstalled: 2,
|
||||||
|
ExpectInstallTotal: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "install_complete",
|
||||||
|
Device: common.TRex,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id": %d,
|
||||||
|
"ecu": "TEST",
|
||||||
|
"installed": 9,
|
||||||
|
"total_files": 10,
|
||||||
|
"msg": "install_complete",
|
||||||
|
"err": 0
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "installing",
|
||||||
|
ExpectDBStatus: "package_install_start",
|
||||||
|
ExpectInstalled: 9,
|
||||||
|
ExpectInstallTotal: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "package_install_complete",
|
||||||
|
Device: common.TRex,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id": %d,
|
||||||
|
"ecu": "TEST",
|
||||||
|
"installed": 10,
|
||||||
|
"total_files": 10,
|
||||||
|
"msg": "install_complete",
|
||||||
|
"err": 0
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "package_install_complete",
|
||||||
|
ExpectDBStatus: "package_install_complete",
|
||||||
|
ExpectInstalled: 10,
|
||||||
|
ExpectInstallTotal: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "install_error",
|
||||||
|
Device: common.TRex,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id": %d,
|
||||||
|
"ecu": "TEST",
|
||||||
|
"installed": 3,
|
||||||
|
"total_files": 10,
|
||||||
|
"msg": "install_error",
|
||||||
|
"err": 0
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "install_error",
|
||||||
|
ExpectDBStatus: "install_error",
|
||||||
|
ExpectInstalled: 3,
|
||||||
|
ExpectInstallTotal: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "package_download_complete",
|
||||||
|
Device: common.HMI,
|
||||||
|
Payload: fmt.Sprintf(`{
|
||||||
|
"car_update_id":%d,
|
||||||
|
"ecu":"ICC",
|
||||||
|
"file_current":null,
|
||||||
|
"file_total":null,
|
||||||
|
"package_current":920639485,
|
||||||
|
"package_total":920639485,
|
||||||
|
"installed":null,
|
||||||
|
"total_files":null,
|
||||||
|
"msg":"package_download_complete",
|
||||||
|
"err":null
|
||||||
|
}`, carUpdateID),
|
||||||
|
ExpectedMsg: "package_download_complete",
|
||||||
|
ExpectDBStatus: "package_download_complete",
|
||||||
|
ExpectInstalled: 3,
|
||||||
|
ExpectInstallTotal: 10,
|
||||||
|
ExpectDownloadCurrent: 920639485,
|
||||||
|
ExpectDownloadTotal: 920639485,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]string, 1)
|
||||||
|
statuses := make([]interface{}, 1)
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
err := handlers.CarUpdateProgressStatus(db, ka, test.Device, testVIN, []byte(test.Payload))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s output error", test.Device, test.Name), nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
status := common.CarUpdateProgress{}
|
||||||
|
keys[0] = redis.CarUpdateStatusHashKey(carUpdateID)
|
||||||
|
statuses[0] = &status
|
||||||
|
|
||||||
|
err = conn.GetObjectsMulti(keys, statuses)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, "GetObjectsMulti", nil, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if status.Status != test.ExpectedMsg {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s Status", test.Device, test.Name), test.ExpectedMsg, status.Status)
|
||||||
|
}
|
||||||
|
if status.InstalledFiles != test.ExpectInstalled {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s InstalledFiles", test.Device, test.Name), test.ExpectInstalled, status.InstalledFiles)
|
||||||
|
}
|
||||||
|
if status.TotalFiles != test.ExpectInstallTotal {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s TotalFiles", test.Device, test.Name), test.ExpectInstallTotal, status.TotalFiles)
|
||||||
|
}
|
||||||
|
if status.PackageCurrent != test.ExpectDownloadCurrent {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s PackageCurrent", test.Device, test.Name), test.ExpectDownloadCurrent, status.PackageCurrent)
|
||||||
|
}
|
||||||
|
if status.PackageTotal != test.ExpectDownloadTotal {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s PackageTotal", test.Device, test.Name), test.ExpectDownloadTotal, status.PackageTotal)
|
||||||
|
}
|
||||||
|
if status.ErrorCode != test.ExpectErrorCode {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s ErrorCode", test.Device, test.Name), test.ExpectErrorCode, status.ErrorCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
cu, err := db.GetCarUpdates().SelectByID(carUpdateID)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, "Get from DB", nil, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cu.Status != test.ExpectDBStatus {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s DB Status", test.Device, test.Name), test.ExpectDBStatus, cu.Status)
|
||||||
|
}
|
||||||
|
if cu.ErrorCode != test.ExpectErrorCode {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s DB ErrorCode", test.Device, test.Name), test.ExpectErrorCode, cu.ErrorCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupCarUpdateProgressFunc(db *services.DB, vin string) (*common.UpdateManifest, int64, error) {
|
||||||
|
_, err := db.GetCars().SelectOrInsert(&common.Car{
|
||||||
|
VIN: vin,
|
||||||
|
Model: "Ocean",
|
||||||
|
Year: 2022,
|
||||||
|
Trim: "Sport",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest := common.UpdateManifest{
|
||||||
|
Name: "TestCarUpdateProgressFunctional",
|
||||||
|
Version: "1000",
|
||||||
|
Description: "For TestCarUpdateProgressFunctional",
|
||||||
|
ReleaseNotes: "http://releasenotes.com",
|
||||||
|
Country: "US",
|
||||||
|
PowerTrain: "MD23",
|
||||||
|
Restraint: "None",
|
||||||
|
Model: "Ocean",
|
||||||
|
Trim: "Sport",
|
||||||
|
Year: 2022,
|
||||||
|
BodyType: "truck",
|
||||||
|
}
|
||||||
|
_, err = db.GetUpdateManifests().Insert(&manifest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
_, err = db.GetUpdateManifests().ECUInsert(&common.UpdateManifestECU{
|
||||||
|
UpdateManifestID: manifest.ID,
|
||||||
|
ECU: "ICC",
|
||||||
|
Version: "ICCVERSION",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
_, err = db.GetUpdateManifests().ECUInsert(&common.UpdateManifestECU{
|
||||||
|
UpdateManifestID: manifest.ID,
|
||||||
|
ECU: "ADAS",
|
||||||
|
Version: "ADASVERSION",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
carupdate := common.CarUpdate{
|
||||||
|
VIN: vin,
|
||||||
|
UpdateManifestID: manifest.ID,
|
||||||
|
}
|
||||||
|
_, err = db.GetCarUpdates().Insert(&carupdate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &manifest, carupdate.ID, nil
|
||||||
|
}
|
||||||
776
services/attendant/handlers/car_update_progress_test.go
Normal file
776
services/attendant/handlers/car_update_progress_test.go
Normal file
@@ -0,0 +1,776 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/controllers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common/manifestfingerprintparams"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis/tester"
|
||||||
|
th "github.com/fiskerinc/cloud-services/pkg/testhelper"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/elptr"
|
||||||
|
vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
schemaToTRex = "file://" + th.GetSchemaDirPath() + "/trex/RXMessage.json"
|
||||||
|
schemaToHMI = "file://" + th.GetSchemaDirPath() + "/hmi/RXMessage.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCarUpdateProgress(t *testing.T) {
|
||||||
|
testGetSetResult := `["valid-cognito-id-1","valid-cognito-id-2"]`
|
||||||
|
testVIN := "JH4KA7680RC01"
|
||||||
|
mobile1Key := "3:valid-cognito-id-1"
|
||||||
|
mobile2Key := "3:valid-cognito-id-2"
|
||||||
|
hmiKey := "2:JH4KA7680RC01"
|
||||||
|
trexKey := common.TRex.Key(testVIN)
|
||||||
|
carupdateKey := "carupdate:297"
|
||||||
|
var bhex common.BinaryHex
|
||||||
|
expectedExpire := 3600
|
||||||
|
bhex = []byte("test")
|
||||||
|
|
||||||
|
fingerprintTime, _ := time.Parse("02/01/06", "19/01/24")
|
||||||
|
fpp := manifestfingerprintparams.MockFingerprintParamer{
|
||||||
|
ManifestSerialValue: "00000000000000000",
|
||||||
|
Time: fingerprintTime,
|
||||||
|
}
|
||||||
|
manifestfingerprintparams.SetFPParams(&fpp)
|
||||||
|
|
||||||
|
manifest := common.UpdateManifest{
|
||||||
|
ID: 1,
|
||||||
|
Name: "test",
|
||||||
|
Version: "MANIFEST_VERSION",
|
||||||
|
SUMS: "2023.10.01.00.E",
|
||||||
|
Description: "description",
|
||||||
|
ReleaseNotes: "http://releasenotes.com",
|
||||||
|
Country: "US",
|
||||||
|
PowerTrain: "MD23",
|
||||||
|
Restraint: "None",
|
||||||
|
Model: "Ocean",
|
||||||
|
Trim: "Sport",
|
||||||
|
Year: 2022,
|
||||||
|
BodyType: "truck",
|
||||||
|
RollbackEnabled: true,
|
||||||
|
Type: "standard",
|
||||||
|
ECUs: []*common.UpdateManifestECU{
|
||||||
|
{
|
||||||
|
ECU: "ICC",
|
||||||
|
Version: "version",
|
||||||
|
HWVersions: []string{"hardware_version"},
|
||||||
|
Mode: "D",
|
||||||
|
SelfDownload: true,
|
||||||
|
Files: []*common.UpdateManifestFile{
|
||||||
|
{
|
||||||
|
FileID: "fileid",
|
||||||
|
URL: "http://download.com",
|
||||||
|
Filename: "filename.bin",
|
||||||
|
FileSize: 10000,
|
||||||
|
FileType: common.Software,
|
||||||
|
WriteRegionID: 2222,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
ID: 2000,
|
||||||
|
Offset: 10000,
|
||||||
|
Length: 20,
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ECU: "ADAS",
|
||||||
|
Version: "version",
|
||||||
|
HWVersions: []string{"hardware_version"},
|
||||||
|
Mode: "A",
|
||||||
|
InstallPriority: 10,
|
||||||
|
Files: []*common.UpdateManifestFile{
|
||||||
|
{
|
||||||
|
FileID: "fileid",
|
||||||
|
URL: "http://download.com",
|
||||||
|
Filename: "adas.bin",
|
||||||
|
FileSize: 9999,
|
||||||
|
FileType: common.Software,
|
||||||
|
WriteRegionID: 9999,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
ID: 8888,
|
||||||
|
Offset: 8888,
|
||||||
|
Length: 8888,
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ECCKeys: &common.ECCKeys{
|
||||||
|
ECU: "ADAS",
|
||||||
|
PrivKey1: &bhex,
|
||||||
|
PrivKey2: &bhex,
|
||||||
|
PrivKey3: &bhex,
|
||||||
|
PubKey1: &bhex,
|
||||||
|
PubKey2: &bhex,
|
||||||
|
PubKey3: &bhex,
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ECU: "ECUA",
|
||||||
|
Version: "version",
|
||||||
|
HWVersions: []string{"hardware_version"},
|
||||||
|
Mode: "A",
|
||||||
|
InstallPriority: 5,
|
||||||
|
Files: []*common.UpdateManifestFile{
|
||||||
|
{
|
||||||
|
FileID: "fileid",
|
||||||
|
URL: "http://download.com",
|
||||||
|
Filename: "adas.bin",
|
||||||
|
FileSize: 9999,
|
||||||
|
FileType: common.Software,
|
||||||
|
WriteRegionID: 9999,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
ID: 8888,
|
||||||
|
Offset: 8888,
|
||||||
|
Length: 8888,
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileID: "SHOULD_NOT_BE_IN_UPDATE",
|
||||||
|
URL: "http://download.com/SHOULD_NOT_BE_IN_UPDATE.bin",
|
||||||
|
FileSize: 1000,
|
||||||
|
Checksum: "AAAAAAA",
|
||||||
|
FileType: common.Calibration,
|
||||||
|
EraseRegionID: 200,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 201,
|
||||||
|
Length: 202,
|
||||||
|
},
|
||||||
|
Parsed: elptr.ElPtr(false),
|
||||||
|
WriteRegionID: 100,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 101,
|
||||||
|
Length: 102,
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileID: "MUST_BE_IN_UPDATE",
|
||||||
|
URL: "http://download.com/MUST_BE_IN_UPDATE.bin",
|
||||||
|
FileSize: 1000,
|
||||||
|
Checksum: "AAAAAAA",
|
||||||
|
FileType: common.Calibration,
|
||||||
|
EraseRegionID: 200,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 201,
|
||||||
|
Length: 202,
|
||||||
|
},
|
||||||
|
Parsed: elptr.ElPtr(true),
|
||||||
|
WriteRegionID: 100,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 101,
|
||||||
|
Length: 102,
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ECCKeys: &common.ECCKeys{
|
||||||
|
ECU: "ECUA",
|
||||||
|
PrivKey1: &bhex,
|
||||||
|
PrivKey2: &bhex,
|
||||||
|
PrivKey3: &bhex,
|
||||||
|
PubKey1: &bhex,
|
||||||
|
PubKey2: &bhex,
|
||||||
|
PubKey3: &bhex,
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
}
|
||||||
|
ecuaRollback := []*common.UpdateManifestECU{
|
||||||
|
{
|
||||||
|
ID: 100,
|
||||||
|
UpdateManifestID: 200,
|
||||||
|
ECU: "ECUA",
|
||||||
|
Version: "VERSIONOLD",
|
||||||
|
HWVersions: []string{"hardware_version"},
|
||||||
|
Mode: "A",
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
Files: []*common.UpdateManifestFile{
|
||||||
|
{
|
||||||
|
FileID: "FILEIDOLD",
|
||||||
|
UpdateManifestECUID: 1001,
|
||||||
|
Filename: "FILENAMEOLD",
|
||||||
|
URL: "URLOLD",
|
||||||
|
FileType: common.Software,
|
||||||
|
WriteRegionID: 700,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 701,
|
||||||
|
Length: 702,
|
||||||
|
},
|
||||||
|
FileSize: 240,
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ECCKeys: &common.ECCKeys{
|
||||||
|
ECU: "ECUA",
|
||||||
|
PrivKey1: &bhex,
|
||||||
|
PrivKey2: &bhex,
|
||||||
|
PrivKey3: &bhex,
|
||||||
|
PubKey1: &bhex,
|
||||||
|
PubKey2: &bhex,
|
||||||
|
PubKey3: &bhex,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mockDB := &services.DB{}
|
||||||
|
mockCars := &mocks.MockCars{}
|
||||||
|
mockCarUpdates := &mocks.MockCarUpdates{
|
||||||
|
SelectCarUpdateResponse: &common.CarUpdate{
|
||||||
|
UpdateManifestID: 816,
|
||||||
|
UpdateManifest: &common.UpdateManifest{
|
||||||
|
ID: 816,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mockManifests := &mocks.MockUpdateManifests{
|
||||||
|
ECUUpdatesMock: func(man *common.UpdateManifestECU, vin string) ([]*common.UpdateManifestECU, error) {
|
||||||
|
if man.ECU == "ECUA" {
|
||||||
|
return ecuaRollback, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mockKeys := &mocks.MockEccKeys{
|
||||||
|
MockListResponse: []common.ECCKeys{
|
||||||
|
{
|
||||||
|
ECU: "PDU",
|
||||||
|
PrivKey1: &bhex,
|
||||||
|
PrivKey2: &bhex,
|
||||||
|
PrivKey3: &bhex,
|
||||||
|
PubKey1: &bhex,
|
||||||
|
PubKey2: &bhex,
|
||||||
|
PubKey3: &bhex,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ECU: "TBOX",
|
||||||
|
PrivKey1: &bhex,
|
||||||
|
PrivKey2: &bhex,
|
||||||
|
PrivKey3: &bhex,
|
||||||
|
PubKey1: &bhex,
|
||||||
|
PubKey2: &bhex,
|
||||||
|
PubKey3: &bhex,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mockDB.SetCars(mockCars)
|
||||||
|
mockDB.SetCarUpdates(mockCarUpdates)
|
||||||
|
mockDB.SetECCKeys(mockKeys)
|
||||||
|
mockDB.SetManifests(mockManifests)
|
||||||
|
|
||||||
|
mockFoa := FoaServiceMock{}
|
||||||
|
services.SetFoaService(&mockFoa)
|
||||||
|
|
||||||
|
mockRedis := tester.NewRedisMock()
|
||||||
|
mockKeepAwake := services.NewKeepAwakeService()
|
||||||
|
services.SetRedisClientPool(tester.NewMockClientPool(mockRedis))
|
||||||
|
mockSap := vconfig.SAPServiceMock{GetConfigurationMock: func(vin string) (common.SAPResponse, error) {
|
||||||
|
return common.SAPResponse{
|
||||||
|
ModelYear: 2023,
|
||||||
|
ModelType: "Ocean",
|
||||||
|
VersionDuringModelYear: "1",
|
||||||
|
Features: []common.SAPFeature{
|
||||||
|
{
|
||||||
|
FamilyCode: "FamilyCode1",
|
||||||
|
FeatureCode: "FeatureCode1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FamilyCode: "FamilyCode2",
|
||||||
|
FeatureCode: "FeatureCode2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}}
|
||||||
|
mockConf := vconfig.ConfigMock{GetVODCDSCodingDataMock: func(request common.VODCDSRequest) (map[string]string, error) {
|
||||||
|
return map[string]string{
|
||||||
|
"ECUA": "config",
|
||||||
|
"VOD": "00a62299027600000101012200010100010001010101000000000000000000fffeffff000101010101010101010100010001010101000101010100000000000000000000000000010101000100000100010101000201010101000101020101000101010200010101010101010101000101010100010001010101010101010201010000000000000100000101ff00000001010200000000000003ffffffff0000000201010200000100000000000000000000000000000000000000000000000000000000000000000001202310010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, nil
|
||||||
|
}}
|
||||||
|
|
||||||
|
services.SetSapService(mockSap)
|
||||||
|
services.SetVehicleConfig(mockConf)
|
||||||
|
schemaTesterHMI := th.NewSchemaTestHelper(t, schemaToHMI)
|
||||||
|
schemaTesterTRex := th.NewSchemaTestHelper(t, schemaToTRex)
|
||||||
|
|
||||||
|
tests := []AttendentRouteTestCase{
|
||||||
|
{
|
||||||
|
Name: "[HMI] install_error",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.HMI,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"TEST","installed":5,"total_files":10,"msg":"install_error","err":0}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"TEST","errorcode":0,"file_size":0,"file_total":0,"id":297,"installed":5,"status":"install_failed","total_files":10,"total_size":0}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":5,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"install_failed","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":5,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"install_failed","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":5,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"install_failed","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[HMI] download_completed",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.HMI,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"ICC","file_current":null,"file_total":null,"package_current":920639485,"package_total":920639485,"installed":null,"total_files":null,"msg":"download_completed","err":null}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":920639485,"ecu":"ICC","errorcode":0,"file_size":0,"file_total":0,"id":297,"installed":0,"status":"package_download_complete","total_files":0,"total_size":920639485}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
trexKey: `{"handler":"update_manifest","data":{"ecu_updates":[{"name":"ICC","version":"version","hw_version":"hardware_version","self_download":true},{"name":"ECUA","version":"version","hw_version":"hardware_version","configuration":"config","files":[{"file_id":"fileid","url":"http://download.com","file_size":9999,"type":"software","write_region":{"offset":8888,"length":8888}},{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}}],"rollback":[{"version":"VERSIONOLD","files":[{"file_id":"FILEIDOLD","url":"URLOLD","file_size":240,"type":"software","write_region":{"offset":701,"length":702}}]}],"ecc_keys":{"level_1":"74657374","level_2":"74657374","level_3":"74657374"}},{"name":"ADAS","version":"version","hw_version":"hardware_version","files":[{"file_id":"fileid","url":"http://download.com","file_size":9999,"type":"software","write_region":{"offset":8888,"length":8888}}],"ecc_keys":{"level_1":"74657374","level_2":"74657374","level_3":"74657374"}}],"fingerprint":"240119FISKER00000000000000000","car_update_id":297,"rollback":true,"type":"standard","vod":"01012299027600000101012200010100010001010101000000000000000000fffeffff000101010101010101010100010001010101000101010100000000000000000000000000010101000100000100010101000201010101000101020101000101010200010101010101010101000101010100010001010101010101010201010000000000000100000101ff00000001010200000000000003ffffffff00000002010102000001000000000000000000000000000000000000000000000000000000000000000000012023100100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021","update_duration":30}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":920639485,"package_total":920639485,"installed":0,"total_files":0,"car_update_id":297,"ecu":"ICC","msg":"package_download_complete","err":0}}`,
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":920639485,"package_total":920639485,"installed":0,"total_files":0,"car_update_id":297,"ecu":"ICC","msg":"package_download_complete","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":920639485,"package_total":920639485,"installed":0,"total_files":0,"car_update_id":297,"ecu":"ICC","msg":"package_download_complete","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
MockLoadManifest: &manifest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[HMI] manifest_succeeded",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.HMI,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"ICC","file_current":null,"file_total":null,"package_current":920639485,"package_total":920639485,"installed":null,"total_files":null,"msg":"manifest_succeeded","err":null}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":920639485,"ecu":"ICC","errorcode":0,"file_size":0,"file_total":0,"id":297,"installed":0,"status":"manifest_succeeded","total_files":0,"total_size":920639485}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":920639485,"package_total":920639485,"installed":0,"total_files":0,"car_update_id":297,"ecu":"ICC","msg":"manifest_succeeded","err":0}}`,
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":920639485,"package_total":920639485,"installed":0,"total_files":0,"car_update_id":297,"ecu":"ICC","msg":"manifest_succeeded","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":920639485,"package_total":920639485,"installed":0,"total_files":0,"car_update_id":297,"ecu":"ICC","msg":"manifest_succeeded","err":0}}`,
|
||||||
|
"1:JH4KA7680RC01": `{"handler":"read_ecu_versions","data":{"ecu_name":"*"}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
SelectCarUpdate: &common.CarUpdate{
|
||||||
|
UpdateManifest: &validUpdateManifest,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] manifest_received",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"msg":"manifest_received","err":-6,"extra_info":""}`,
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"manifest_received","err":-6}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"manifest_received","err":-6}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"manifest_received","err":-6}}`,
|
||||||
|
},
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"","errorcode":-6,"file_size":0,"file_total":0,"id":297,"installed":0,"status":"manifest_received","total_files":0,"total_size":0}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] manifest_accepted",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"msg":"manifest_accepted","err":-7,"extra_info":""}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"","errorcode":-7,"file_size":0,"file_total":0,"id":297,"installed":0,"status":"manifest_accepted","total_files":0,"total_size":0}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"manifest_accepted","err":-7}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"manifest_accepted","err":-7}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"manifest_accepted","err":-7}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] download_started",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"msg":"download_started","err":-14,"extra_info":""}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"","errorcode":-14,"file_size":0,"file_total":0,"id":297,"installed":0,"status":"downloading","total_files":0,"total_size":0}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"downloading","err":-14}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"downloading","err":-14}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"downloading","err":-14}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] downloading",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"ADAS","file_current":1048576,"file_total":1264672,"package_current":1048576,"package_total":2529856,"msg":"downloading","err":0}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":1048576,"ecu":"ADAS","errorcode":0,"file_size":1048576,"file_total":1264672,"id":297,"installed":0,"status":"downloading","total_files":0,"total_size":2529856}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":1048576,"file_total":1264672,"package_current":1048576,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"ADAS","msg":"downloading","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":1048576,"file_total":1264672,"package_current":1048576,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"ADAS","msg":"downloading","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":1048576,"file_total":1264672,"package_current":1048576,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"ADAS","msg":"downloading","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] download_completed ECU 1",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"ADAS","file_current":1264672,"file_total":1264672,"package_current":1264672,"package_total":2529856,"msg":"download_completed","err":0}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":1264672,"ecu":"ADAS","errorcode":0,"file_size":1264672,"file_total":1264672,"id":297,"installed":0,"status":"downloading","total_files":0,"total_size":2529856}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":1264672,"file_total":1264672,"package_current":1264672,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"ADAS","msg":"downloading","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":1264672,"file_total":1264672,"package_current":1264672,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"ADAS","msg":"downloading","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":1264672,"file_total":1264672,"package_current":1264672,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"ADAS","msg":"downloading","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] download_started ECU 2",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"EKS","file_current":0,"file_total":1265184,"package_current":1264672,"package_total":2529856,"msg":"download_started","err":0}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":1264672,"ecu":"EKS","errorcode":0,"file_size":0,"file_total":1265184,"id":297,"installed":0,"status":"downloading","total_files":0,"total_size":2529856}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":1265184,"package_current":1264672,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"EKS","msg":"downloading","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":1265184,"package_current":1264672,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"EKS","msg":"downloading","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":1265184,"package_current":1264672,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"EKS","msg":"downloading","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] downloading ECU 2",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"EKS","file_current":1048576,"file_total":1265184,"package_current":2313248,"package_total":2529856,"msg":"downloading","err":0}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":2313248,"ecu":"EKS","errorcode":0,"file_size":1048576,"file_total":1265184,"id":297,"installed":0,"status":"downloading","total_files":0,"total_size":2529856}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":1048576,"file_total":1265184,"package_current":2313248,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"EKS","msg":"downloading","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":1048576,"file_total":1265184,"package_current":2313248,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"EKS","msg":"downloading","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":1048576,"file_total":1265184,"package_current":2313248,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"EKS","msg":"downloading","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] download_completed ECU 2",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"EKS","file_current":1265184,"file_total":1265184,"package_current":2529856,"package_total":2529856,"msg":"download_completed","err":0}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":2529856,"ecu":"EKS","errorcode":0,"file_size":1265184,"file_total":1265184,"id":297,"installed":0,"status":"package_download_complete","total_files":0,"total_size":2529856}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":1265184,"file_total":1265184,"package_current":2529856,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"EKS","msg":"package_download_complete","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":1265184,"file_total":1265184,"package_current":2529856,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"EKS","msg":"package_download_complete","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":1265184,"file_total":1265184,"package_current":2529856,"package_total":2529856,"installed":0,"total_files":0,"car_update_id":297,"ecu":"EKS","msg":"package_download_complete","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] package_download_complete",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"msg":"download_completed","err":-15,"extra_info":""}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"","errorcode":-15,"file_size":0,"file_total":0,"id":297,"installed":0,"status":"package_download_complete","total_files":0,"total_size":0}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"package_download_complete","err":-15}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"package_download_complete","err":-15}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"package_download_complete","err":-15}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] download_failed",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"TEST","file_current":0,"file_total":100,"package_current":0,"package_total":1000,"msg":"download_failed","err":0}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"TEST","errorcode":0,"file_size":0,"file_total":100,"id":297,"installed":0,"status":"download_failed","total_files":0,"total_size":1000}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":100,"package_current":0,"package_total":1000,"installed":0,"total_files":0,"car_update_id":297,"ecu":"TEST","msg":"download_failed","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":100,"package_current":0,"package_total":1000,"installed":0,"total_files":0,"car_update_id":297,"ecu":"TEST","msg":"download_failed","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":100,"package_current":0,"package_total":1000,"installed":0,"total_files":0,"car_update_id":297,"ecu":"TEST","msg":"download_failed","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] install_started",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"TEST","installed":0,"total_files":10,"msg":"install_started","err":0}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"TEST","errorcode":0,"file_size":0,"file_total":0,"id":297,"installed":0,"status":"installing","total_files":10,"total_size":0}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"installing","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"installing","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"installing","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] installing",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"TEST","installed":5,"total_files":10,"msg":"installing","err":0}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"TEST","errorcode":0,"file_size":0,"file_total":0,"id":297,"installed":5,"status":"installing","total_files":10,"total_size":0}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":5,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"installing","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":5,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"installing","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":5,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"installing","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] install_succeeded ECU",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"TEST","installed":10,"total_files":10,"msg":"install_succeeded"}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"TEST","errorcode":0,"file_size":0,"file_total":0,"id":297,"installed":10,"status":"package_install_complete","total_files":10,"total_size":0}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":10,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"package_install_complete","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":10,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"package_install_complete","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":10,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"package_install_complete","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] package_install_complete",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"msg":"install_succeeded","err":0}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"","errorcode":0,"file_size":0,"file_total":0,"id":297,"installed":0,"status":"installing","total_files":0,"total_size":0}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"installing","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"installing","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":0,"total_files":0,"car_update_id":297,"ecu":"","msg":"installing","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] install_failed",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"TEST","installed":5,"total_files":10,"msg":"install_failed","err":0}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"TEST","errorcode":0,"file_size":0,"file_total":0,"id":297,"installed":5,"status":"install_failed","total_files":10,"total_size":0}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":5,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"install_failed","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":5,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"install_failed","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":5,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"install_failed","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] requirements_failed",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"TEST","installed":5,"total_files":10,"msg":"requirements_failed","err":0}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"TEST","errorcode":0,"file_size":0,"file_total":0,"id":297,"installed":5,"status":"requirements_failed","total_files":10,"total_size":0}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":5,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"requirements_failed","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":5,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"requirements_failed","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":5,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"requirements_failed","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] install_scheduled ECU",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"TEST","installed":10,"total_files":10,"msg":"install_scheduled"}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"TEST","errorcode":0,"file_size":0,"file_total":0,"id":297,"installed":10,"status":"install_scheduled","total_files":10,"total_size":0}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":10,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"install_scheduled","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":10,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"install_scheduled","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":10,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"install_scheduled","err":0}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] manifest_succeeded",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":297,"ecu":"TEST","installed":10,"total_files":10,"msg":"manifest_succeeded"}`,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
carupdateKey: {
|
||||||
|
Value: `{"current_size":0,"ecu":"TEST","errorcode":0,"file_size":0,"file_total":0,"id":297,"installed":10,"status":"manifest_succeeded","total_files":10,"total_size":0}`,
|
||||||
|
Expires: expectedExpire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobile1Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":10,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"manifest_succeeded","err":0}}`,
|
||||||
|
mobile2Key: `{"handler":"car_update_status","data":{"vin":"JH4KA7680RC01","file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":10,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"manifest_succeeded","err":0}}`,
|
||||||
|
hmiKey: `{"handler":"car_update_status","data":{"file_current":0,"file_total":0,"package_current":0,"package_total":0,"installed":10,"total_files":10,"car_update_id":297,"ecu":"TEST","msg":"manifest_succeeded","err":0}}`,
|
||||||
|
"1:JH4KA7680RC01": `{"handler":"read_ecu_versions","data":{"ecu_name":"*"}}`,
|
||||||
|
},
|
||||||
|
MockRedisGetSet: testGetSetResult,
|
||||||
|
},
|
||||||
|
SelectCarUpdate: &common.CarUpdate{
|
||||||
|
UpdateManifest: &validUpdateManifest,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range tests {
|
||||||
|
mockRedis.Reset()
|
||||||
|
test := &tests[i]
|
||||||
|
test.SetupRedis(mockRedis)
|
||||||
|
test.SetupDB(mockCars, mockCarUpdates, test)
|
||||||
|
|
||||||
|
redisPool := tester.NewMockClientPool(mockRedis)
|
||||||
|
handler := controllers.NewCarUpdateProgress(redisPool, mockKeepAwake, mockDB, test.Device)
|
||||||
|
if handler == nil {
|
||||||
|
t.Error(errors.New("NewCarUpdateProgress cannot handle device %v"))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := handler.Process(test.DeviceKey, []byte(test.PayloadData))
|
||||||
|
|
||||||
|
test.CheckHandlerError(t, test.Name, err)
|
||||||
|
test.Validate(t, test.Name, mockRedis)
|
||||||
|
|
||||||
|
for key, m := range test.RedisTestCase.ExpectedMessages {
|
||||||
|
name := fmt.Sprintf("%s %s", test.Name, key)
|
||||||
|
if strings.Contains(key, "1:") {
|
||||||
|
schemaTesterTRex.ValidateSchemaObject(name, []byte(m))
|
||||||
|
} else if strings.Contains(key, "2:") {
|
||||||
|
schemaTesterHMI.ValidateSchemaObject(name, []byte(m))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type FoaServiceMock struct{}
|
||||||
|
|
||||||
|
func (f *FoaServiceMock) OtaUpdateStatus(vin string, carUpdate *common.CarUpdate, status *common.CarUpdateProgress) (*http.Response, error) {
|
||||||
|
return &http.Response{StatusCode: 200}, nil
|
||||||
|
}
|
||||||
74
services/attendant/handlers/car_update_state.go
Normal file
74
services/attendant/handlers/car_update_state.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
fv "github.com/fiskerinc/cloud-services/pkg/flashpackversion"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UpdateCarState(db *services.DB, vin string, data []byte) error {
|
||||||
|
logger.Debug().Msgf("UpdateCarState %s", vin)
|
||||||
|
var update common.CarStateUpdate
|
||||||
|
|
||||||
|
err := json.Unmarshal(data, &update)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = validator.ValidateStruct(&update)
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Interface("CarStateUpdate", update).Str("CarStateUpdateByteValue as string", string(data)).Send()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return processUpdateCarStateECUs(db, vin, update.ECUs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processUpdateCarStateECUs(db *services.DB, vin string, ecus map[string]common.CarECU) error {
|
||||||
|
cache := services.GetCarEcuCache()
|
||||||
|
insert := []common.CarECU{}
|
||||||
|
errs := []error{}
|
||||||
|
|
||||||
|
for name, ecu := range ecus {
|
||||||
|
ecu.VIN = vin
|
||||||
|
ecu.ECU = name
|
||||||
|
err := validator.ValidateStruct(&ecu)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Msgf("invalid CarECU %v", err)
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cache.Exists(ecu.CacheKey(), ecu.HashValues()) {
|
||||||
|
insert = append(insert, ecu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(insert) == 0 {
|
||||||
|
return combineErrors(errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := fv.InsertCarECUsAndUpdateFlashpackVersion(db.GetCars(), db.GetCarVersionsLog(), vin, insert)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return combineErrors(errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func combineErrors(errs []error) error {
|
||||||
|
if len(errs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
errString := errs[0].Error()
|
||||||
|
for i := 1; i < len(errs); i++ {
|
||||||
|
errString += " " + errs[i].Error()
|
||||||
|
}
|
||||||
|
return errors.New(errString)
|
||||||
|
}
|
||||||
113
services/attendant/handlers/car_update_state_test.go
Normal file
113
services/attendant/handlers/car_update_state_test.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/handlers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/testhelper"
|
||||||
|
vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUpdateCarState(t *testing.T) {
|
||||||
|
setupDBMock()
|
||||||
|
setupRedisMock()
|
||||||
|
|
||||||
|
mockSap := vconfig.SAPServiceMock{}
|
||||||
|
services.SetSapService(mockSap)
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
Name string
|
||||||
|
VIN string
|
||||||
|
Payload string
|
||||||
|
ExpectedErr string
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []testCase{
|
||||||
|
{
|
||||||
|
Name: "Empty ECU",
|
||||||
|
VIN: "JH4KA7680RC011845",
|
||||||
|
Payload: `{"ecus": {}}`,
|
||||||
|
ExpectedErr: "Key: 'CarStateUpdate.ECUs' Error:Field validation for 'ECUs' failed on the 'min' tag",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Bad VIN and ECU",
|
||||||
|
VIN: "JH4KA7680RC01",
|
||||||
|
Payload: `{
|
||||||
|
"ecus": {
|
||||||
|
"ECU1": {
|
||||||
|
"serial_number": "AAAAA",
|
||||||
|
"hw_version": "2000",
|
||||||
|
"boot_loader_version": "3000",
|
||||||
|
"fingerprint": "0xffffffffffffffffff",
|
||||||
|
"config": "0x024000941f9fffbfffe2dd9bff5860000007dfff091fff7f",
|
||||||
|
"vendor": "A021E00029"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
ExpectedErr: "Key: 'CarECU.VIN' Error:Field validation for 'VIN' failed on the 'vin' tag",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Good ECUs with/without software version",
|
||||||
|
VIN: "JH4KA7680RC011845",
|
||||||
|
Payload: `{
|
||||||
|
"ecus": {
|
||||||
|
"ECU1": {
|
||||||
|
"sw_version": "1000",
|
||||||
|
"serial_number": "AAAAA",
|
||||||
|
"hw_version": "2000",
|
||||||
|
"boot_loader_version": "3000",
|
||||||
|
"fingerprint": "0xffffffffffffffffff",
|
||||||
|
"config": "0x024000941f9fffbfffe2dd9bff5860000007dfff091fff7f",
|
||||||
|
"vendor": "A021E00029"
|
||||||
|
},
|
||||||
|
"ECU2": {
|
||||||
|
"serial_number": "BBBBBBB",
|
||||||
|
"hw_version": "2001",
|
||||||
|
"boot_loader_version": "3001",
|
||||||
|
"fingerprint": "0xffffffffffffffffff",
|
||||||
|
"config": "0x024000941f9fffbfffe2dd9bff5860000007dfff091fff7f",
|
||||||
|
"vendor": "A021E00029"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
ExpectedErr: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "bad ECU followed by a Good ECU",
|
||||||
|
VIN: "JH4KA7680RC011845",
|
||||||
|
Payload: `{
|
||||||
|
"ecus": {
|
||||||
|
"ECU1": {
|
||||||
|
"serial_number": "AAAAA",
|
||||||
|
"hw_version": "2000",
|
||||||
|
"boot_loader_version": "3000",
|
||||||
|
"fingerprint": "0xffffffffffffffffff",
|
||||||
|
"config": "0x024000941f9fffbfffe2dd9bff5860000007dfff091fff7f",
|
||||||
|
"vendor": "A021E00029",
|
||||||
|
"epoch_usec": "ABC"
|
||||||
|
},
|
||||||
|
"ECU2": {
|
||||||
|
"serial_number": "BBBBBBB",
|
||||||
|
"hw_version": "2001",
|
||||||
|
"boot_loader_version": "3001",
|
||||||
|
"fingerprint": "0xffffffffffffffffff",
|
||||||
|
"config": "0x024000941f9fffbfffe2dd9bff5860000007dfff091fff7f",
|
||||||
|
"vendor": "A021E00029"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
ExpectedErr: "json: cannot unmarshal string into Go struct field CarECU.ecus.epoch_usec of type int64",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
err := handlers.UpdateCarState(mockDB, test.VIN, []byte(test.Payload))
|
||||||
|
if err != nil && test.ExpectedErr != err.Error() {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, test.Name, test.ExpectedErr, err.Error())
|
||||||
|
} else if err == nil && test.ExpectedErr != "" {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, test.Name, test.ExpectedErr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
94
services/attendant/handlers/get_ecc_keys.go
Normal file
94
services/attendant/handlers/get_ecc_keys.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetAllEccKeys(db *services.DB, id string, data []byte) error {
|
||||||
|
logger.Debug().Msgf("GetAllEccKeys %v %s", common.TRex, id)
|
||||||
|
var err error
|
||||||
|
var eccKeys []common.ECCKeys
|
||||||
|
|
||||||
|
req, err := parseGetAllEccKeysRequest(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.CarUpdateID == 0 {
|
||||||
|
eccKeys, err = db.GetECCKeys().SelectAllPrivateKeysByVIN(id)
|
||||||
|
} else {
|
||||||
|
eccKeys, err = db.GetECCKeys().SelectAllPrivateKeysByCarUpdateID(req.CarUpdateID)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if eccKeys == nil {
|
||||||
|
eccKeys = make([]common.ECCKeys, 0)
|
||||||
|
}
|
||||||
|
eccKeys = replaceECU(eccKeys)
|
||||||
|
|
||||||
|
client := services.RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
err = client.SafePublishMessage(common.TRex.Key(id), common.Message{
|
||||||
|
Handler: "ecc_keys",
|
||||||
|
Data: eccKeys,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug().Msgf("GetAllEccKeys sent %v %s", common.TRex, id)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGetAllEccKeysRequest(data []byte) (common.CarUpdateRequest, error) {
|
||||||
|
var req common.CarUpdateRequest
|
||||||
|
|
||||||
|
if len(data) == 0 {
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := json.Unmarshal(data, &req)
|
||||||
|
if err != nil {
|
||||||
|
return req, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func replaceECU(eccKeys []common.ECCKeys) []common.ECCKeys {
|
||||||
|
|
||||||
|
ecuReplacements := common.ECUReplacement()
|
||||||
|
for i := 0; i < len(eccKeys); i++ {
|
||||||
|
ecu := eccKeys[i].ECU
|
||||||
|
if replacement, exist := ecuReplacements[ecu]; exist {
|
||||||
|
eccKeys[i].ECU = replacement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return eccKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func notifyEccKeysGeneralError(client redis.Client, device common.Device, id string, err error) {
|
||||||
|
e := client.PublishMessage(device.Key(id), common.Message{
|
||||||
|
Handler: "ecc_keys",
|
||||||
|
Data: []struct{ Error string }{
|
||||||
|
{
|
||||||
|
Error: err.Error(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if e != nil {
|
||||||
|
logger.Error().Err(errors.WithStack(e)).Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
111
services/attendant/handlers/get_ecc_keys_test.go
Normal file
111
services/attendant/handlers/get_ecc_keys_test.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/handlers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis/tester"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/testhelper"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetAllEccKeys(t *testing.T) {
|
||||||
|
testVIN := "JH4KA7680RC01"
|
||||||
|
trexKey := "1:JH4KA7680RC01"
|
||||||
|
|
||||||
|
pk11 := common.NewBinaryHex([]byte("testprivkey11"))
|
||||||
|
pk12 := common.NewBinaryHex([]byte("testprivkey12"))
|
||||||
|
pk13 := common.NewBinaryHex([]byte("testprivkey13"))
|
||||||
|
pk21 := common.NewBinaryHex([]byte("testprivkey21"))
|
||||||
|
pk22 := common.NewBinaryHex([]byte("testprivkey22"))
|
||||||
|
pk23 := common.NewBinaryHex([]byte("testprivkey23"))
|
||||||
|
|
||||||
|
testDBQuery := []common.ECCKeys{
|
||||||
|
{
|
||||||
|
ECU: "testecu1",
|
||||||
|
PrivKey1: &pk11,
|
||||||
|
PrivKey2: &pk12,
|
||||||
|
PrivKey3: &pk13,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ECU: "testecu2",
|
||||||
|
PrivKey1: &pk21,
|
||||||
|
PrivKey2: &pk22,
|
||||||
|
PrivKey3: &pk23,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ECU: "PDU",
|
||||||
|
PrivKey1: &pk21,
|
||||||
|
PrivKey2: &pk22,
|
||||||
|
PrivKey3: &pk23,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockRedis := tester.NewRedisMock()
|
||||||
|
services.SetRedisClientPool(tester.NewMockClientPool(mockRedis))
|
||||||
|
mockEccKeys := &mocks.MockEccKeys{}
|
||||||
|
mockDB := &services.DB{}
|
||||||
|
mockDB.SetECCKeys(mockEccKeys)
|
||||||
|
|
||||||
|
tests := []AttendentRouteTestCase{
|
||||||
|
{
|
||||||
|
Name: "[TREX] From DB, no car update id",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
trexKey: `{"handler":"ecc_keys","data":[{"ecu":"testecu1","level_1":"74657374707269766b65793131","level_2":"74657374707269766b65793132","level_3":"74657374707269766b65793133"},{"ecu":"testecu2","level_1":"74657374707269766b65793231","level_2":"74657374707269766b65793232","level_3":"74657374707269766b65793233"},{"ecu":"OBC","level_1":"74657374707269766b65793231","level_2":"74657374707269766b65793232","level_3":"74657374707269766b65793233"}]}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MockEccKeysSelect: testDBQuery,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[TREX] From DB, with car update id",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
trexKey: `{"handler":"ecc_keys","data":[{"ecu":"testecu1","level_1":"74657374707269766b65793131","level_2":"74657374707269766b65793132","level_3":"74657374707269766b65793133"},{"ecu":"testecu2","level_1":"74657374707269766b65793231","level_2":"74657374707269766b65793232","level_3":"74657374707269766b65793233"},{"ecu":"OBC","level_1":"74657374707269766b65793231","level_2":"74657374707269766b65793232","level_3":"74657374707269766b65793233"}]}`,
|
||||||
|
},
|
||||||
|
PayloadData: `{"car_update_id":10000}`,
|
||||||
|
},
|
||||||
|
MockEccKeysSelect: testDBQuery,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Error",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
ExpectedError: "something went wrong",
|
||||||
|
},
|
||||||
|
MockEccKeysSelect: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
schemaTester := testhelper.NewSchemaTestHelper(t, schemaToTRex)
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
|
mockRedis.Reset()
|
||||||
|
test.SetupRedis(mockRedis)
|
||||||
|
mockEccKeys.MockListResponse = test.MockEccKeysSelect
|
||||||
|
|
||||||
|
if test.Name == "Error" {
|
||||||
|
mockEccKeys.Error = errors.New("something went wrong")
|
||||||
|
} else {
|
||||||
|
mockEccKeys.Error = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := handlers.GetAllEccKeys(mockDB, test.DeviceKey, []byte(test.PayloadData))
|
||||||
|
|
||||||
|
test.CheckHandlerError(t, test.Name, err)
|
||||||
|
test.Validate(t, test.Name, mockRedis)
|
||||||
|
|
||||||
|
for _, m := range test.ExpectedMessages {
|
||||||
|
schemaTester.ValidateSchemaObject(test.Name, []byte(m))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
80
services/attendant/handlers/get_filekeys.go
Normal file
80
services/attendant/handlers/get_filekeys.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/cache"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/validator"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetFileKeys(db *services.DB, device common.Device, id string, data []byte) error {
|
||||||
|
logger.Debug().Msgf("GetFileKeys %v %s", device, id)
|
||||||
|
var err error
|
||||||
|
var req *common.FileKeysRequest
|
||||||
|
|
||||||
|
client := services.RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
req, err = parseGetFileKeysRequest(data)
|
||||||
|
if err != nil {
|
||||||
|
notifyFileKeysGeneralError(client, device, id, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, err := cache.RetrieveFileEncryptionParams(client, db.GetFileKeys(), req.FileIDs)
|
||||||
|
if err != nil {
|
||||||
|
notifyFileKeysGeneralError(client, device, id, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.SafeQueueMessage(device.Key(id), common.Message{
|
||||||
|
Handler: "filekeys",
|
||||||
|
Data: keys,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug().Msgf("GetFileKeys sent %v %s", device, id)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGetFileKeysRequest(data []byte) (*common.FileKeysRequest, error) {
|
||||||
|
var status common.FileKeysRequest
|
||||||
|
|
||||||
|
err := json.Unmarshal(data, &status)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = validator.ValidateStruct(status)
|
||||||
|
if err != nil {
|
||||||
|
return &status, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func notifyFileKeysGeneralError(client redis.Client, device common.Device, id string, err error) {
|
||||||
|
e := client.SafePublishMessage(device.Key(id), common.Message{
|
||||||
|
Handler: "filekeys",
|
||||||
|
Data: []common.FileKeyResponse{
|
||||||
|
{
|
||||||
|
FileID: "0",
|
||||||
|
Error: err.Error(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if e != nil {
|
||||||
|
logger.Error().Err(errors.WithStack(e)).Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
124
services/attendant/handlers/get_filekeys_test.go
Normal file
124
services/attendant/handlers/get_filekeys_test.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/testhelper"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/handlers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis/tester"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetFileKey(t *testing.T) {
|
||||||
|
testVIN := "JH4KA7680RC01"
|
||||||
|
trexKey := "1:JH4KA7680RC01"
|
||||||
|
hmiKey := "2:JH4KA7680RC01"
|
||||||
|
fileCache1 := "fileid:b7d94be8c94062cf"
|
||||||
|
fileCache2 := "fileid:83165a80c940e8b3"
|
||||||
|
testPayload := `{"file_ids": ["b7d94be8c94062cf","83165a80c940e8b3"]}`
|
||||||
|
testDBQuery := []common.FileKey{
|
||||||
|
{
|
||||||
|
FileID: "b7d94be8c94062cf",
|
||||||
|
Auth: []byte("AuthValue"),
|
||||||
|
Key: []byte("KeyValue"),
|
||||||
|
Nonce: []byte("NonceValue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileID: "83165a80c940e8b3",
|
||||||
|
Auth: []byte("AuthValue2"),
|
||||||
|
Key: []byte("KeyValue2"),
|
||||||
|
Nonce: []byte("NonceValue2"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockRedis := tester.NewRedisMock()
|
||||||
|
services.SetRedisClientPool(tester.NewMockClientPool(mockRedis))
|
||||||
|
mockFileKeys := &mocks.MockFileKeys{}
|
||||||
|
mockDB := &services.DB{}
|
||||||
|
mockDB.SetFileKeys(mockFileKeys)
|
||||||
|
|
||||||
|
tests := []AttendentRouteTestCase{
|
||||||
|
{
|
||||||
|
Name: "[TREX] From DB",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.TRex,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: testPayload,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
fileCache1: {
|
||||||
|
Value: `{"file_id":"b7d94be8c94062cf","key":"S2V5VmFsdWU=","auth":"QXV0aFZhbHVl","nonce":"Tm9uY2VWYWx1ZQ=="}`,
|
||||||
|
Expires: 86400,
|
||||||
|
},
|
||||||
|
fileCache2: {
|
||||||
|
Value: `{"file_id":"83165a80c940e8b3","key":"S2V5VmFsdWUy","auth":"QXV0aFZhbHVlMg==","nonce":"Tm9uY2VWYWx1ZTI="}`,
|
||||||
|
Expires: 86400,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
trexKey: `{"handler":"filekeys","data":[{"file_id":"b7d94be8c94062cf","key":"S2V5VmFsdWU=","auth":"QXV0aFZhbHVl","nonce":"Tm9uY2VWYWx1ZQ=="},{"file_id":"83165a80c940e8b3","key":"S2V5VmFsdWUy","auth":"QXV0aFZhbHVlMg==","nonce":"Tm9uY2VWYWx1ZTI="}]}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MockFileKeysSelect: testDBQuery,
|
||||||
|
MockFileKeysError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "[HMI] From DB",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.HMI,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: testPayload,
|
||||||
|
ExpectedCaches: map[string]tester.ExpiringCacheResult{
|
||||||
|
fileCache1: {
|
||||||
|
Value: `{"file_id":"b7d94be8c94062cf","key":"S2V5VmFsdWU=","auth":"QXV0aFZhbHVl","nonce":"Tm9uY2VWYWx1ZQ=="}`,
|
||||||
|
Expires: 86400,
|
||||||
|
},
|
||||||
|
fileCache2: {
|
||||||
|
Value: `{"file_id":"83165a80c940e8b3","key":"S2V5VmFsdWUy","auth":"QXV0aFZhbHVlMg==","nonce":"Tm9uY2VWYWx1ZTI="}`,
|
||||||
|
Expires: 86400,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
hmiKey: `{"handler":"filekeys","data":[{"file_id":"b7d94be8c94062cf","key":"S2V5VmFsdWU=","auth":"QXV0aFZhbHVl","nonce":"Tm9uY2VWYWx1ZQ=="},{"file_id":"83165a80c940e8b3","key":"S2V5VmFsdWUy","auth":"QXV0aFZhbHVlMg==","nonce":"Tm9uY2VWYWx1ZTI="}]}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MockFileKeysSelect: testDBQuery,
|
||||||
|
MockFileKeysError: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
schemaTester := testhelper.NewSchemaTestHelper(t, schemaToTRex)
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
|
mockRedis.Reset()
|
||||||
|
test.SetupRedis(mockRedis)
|
||||||
|
mockFileKeys.GetMultiResponse = test.MockFileKeysSelect
|
||||||
|
mockFileKeys.Error = test.MockFileKeysError
|
||||||
|
|
||||||
|
err := handlers.GetFileKeys(mockDB, test.Device, test.DeviceKey, []byte(test.PayloadData))
|
||||||
|
|
||||||
|
test.CheckHandlerError(t, test.Name, err)
|
||||||
|
test.Validate(t, test.Name, mockRedis)
|
||||||
|
|
||||||
|
for _, m := range test.ExpectedMessages {
|
||||||
|
schemaTester.ValidateSchemaObject(test.Name, []byte(m))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkGetFileKey(b *testing.B) {
|
||||||
|
db := services.GetDB()
|
||||||
|
vin := "1F15K3R45N1234567"
|
||||||
|
id := []byte(`{"file_ids": ["b7d94be8c94062cf","83165a80c940e8b3"]}`)
|
||||||
|
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
err := handlers.GetFileKeys(db, common.TRex, vin, id)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
services/attendant/handlers/mock_test.go
Normal file
113
services/attendant/handlers/mock_test.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis/tester"
|
||||||
|
"github.com/go-pg/pg/v10/orm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mockRedis *redis.Connection
|
||||||
|
var mockDB *services.DB
|
||||||
|
var testDateTime time.Time = time.Date(2022, 1, 2, 3, 4, 5, 6, time.UTC)
|
||||||
|
|
||||||
|
func setupRedisMock() {
|
||||||
|
redis.MockRedisConnection()
|
||||||
|
mockRedis = &redis.Connection{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupDBMock() {
|
||||||
|
db := services.DB{}
|
||||||
|
db.SetCarUpdates(&mocks.MockCarUpdates{
|
||||||
|
SelectCarUpdateResponse: &common.CarUpdate{
|
||||||
|
ID: 1,
|
||||||
|
VIN: "FISKER123",
|
||||||
|
UpdateManifestID: 2,
|
||||||
|
UpdateManifest: &common.UpdateManifest{
|
||||||
|
ID: 2,
|
||||||
|
Name: "TEST_PACKAGE",
|
||||||
|
Version: "1.0.0",
|
||||||
|
ReleaseNotes: "http://releasenotes.com",
|
||||||
|
Country: "US",
|
||||||
|
PowerTrain: "MD23",
|
||||||
|
Restraint: "None",
|
||||||
|
Model: "Ocean",
|
||||||
|
Trim: "Sport",
|
||||||
|
Year: 2022,
|
||||||
|
BodyType: "truck",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
db.SetECU(&mocks.MockEcuDtc{})
|
||||||
|
db.SetCars(&mocks.MockCars{
|
||||||
|
SelectResponse: &common.Car{},
|
||||||
|
})
|
||||||
|
carVersionLogMock := mocks.MockCarVersionsLog{MockLogVersionChange: func(log *common.CarVersionLogs) (orm.Result, error) {
|
||||||
|
return nil, nil
|
||||||
|
}}
|
||||||
|
db.SetCarVersionsLog(&carVersionLogMock)
|
||||||
|
mockDB = &db
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockRedisCache struct {
|
||||||
|
redis.Connection
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockRedisCache) GetSet(id string, data interface{}) error {
|
||||||
|
driverIDs := []string{"valid-cognito-id-1", "valid-cognito-id-2"}
|
||||||
|
|
||||||
|
dataBytes, err := json.Marshal(driverIDs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(dataBytes, data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRedisMock() *tester.MockRedis {
|
||||||
|
redis.MockRedisConnection()
|
||||||
|
return &tester.MockRedis{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttendentRouteTestCase struct {
|
||||||
|
Name string
|
||||||
|
SelectCarUpdate *common.CarUpdate
|
||||||
|
SelectCarUpdates []common.CarUpdate
|
||||||
|
CarUpdateError error
|
||||||
|
SelectCarToDrivers []common.CarToDriver
|
||||||
|
CarToDriversError error
|
||||||
|
MockLoadManifest *common.UpdateManifest
|
||||||
|
MockFileKeysSelect []common.FileKey
|
||||||
|
MockFileKeysError error
|
||||||
|
MockEccKeysSelect []common.ECCKeys
|
||||||
|
tester.RedisTestCase
|
||||||
|
}
|
||||||
|
|
||||||
|
func (at *AttendentRouteTestCase) SetupDB(mockCars *mocks.MockCars, mockCarUpdates *mocks.MockCarUpdates, test *AttendentRouteTestCase) {
|
||||||
|
if test == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mockCars != nil {
|
||||||
|
mockCars.SelectCarsForDrivers = test.SelectCarToDrivers
|
||||||
|
mockCars.Error = test.CarToDriversError
|
||||||
|
}
|
||||||
|
|
||||||
|
if mockCarUpdates != nil {
|
||||||
|
mockCarUpdates.SelectCarUpdateResponse = test.SelectCarUpdate
|
||||||
|
mockCarUpdates.SelectCarUpdatesResponse = test.SelectCarUpdates
|
||||||
|
mockCarUpdates.LoadManifest = test.MockLoadManifest
|
||||||
|
mockCarUpdates.Error = test.CarUpdateError
|
||||||
|
}
|
||||||
|
}
|
||||||
160
services/attendant/handlers/order_updated.go
Normal file
160
services/attendant/handlers/order_updated.go
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/manifestsender"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/tmobile"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ENABLE_ORDERUPDATE_SENDS = envtool.GetEnvBool("ENABLE_ORDERUPDATE_SENDS", false)
|
||||||
|
|
||||||
|
// Take the feature codes from VehicleOrder message to generate the VOD and CDSs
|
||||||
|
// Create a new common.UpdateManifest with the VOD and CDS and save it as a common.ConfigUpdateType
|
||||||
|
// Create a new common.CarUpdate for the VIN and the manifest in step 2 and save it to the database
|
||||||
|
// Copy the car update id into the UpdateManifest
|
||||||
|
// Send the UpdateManifest to both trex and hmi
|
||||||
|
func OrderUpdated(db *services.DB, smsClient sms.SMSServiceClient, vin string, data []byte) error {
|
||||||
|
logger.Debug().Msgf("Order updated %s", vin)
|
||||||
|
|
||||||
|
var order common.VehicleOrder
|
||||||
|
err := json.Unmarshal(data, &order)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setCarSettings(db, vin, order)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = changeRatePlan(db, smsClient, vin, order.VehicleSpecification.DestinationCountry)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Msgf("Failed to change rate plan for %s, %s", vin, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ENABLE_ORDERUPDATE_SENDS {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := services.GetVehicleConfig()
|
||||||
|
r := services.RedisClientPool().GetFromPool()
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
trex := manifestsender.NewTBOXManifestSender(r, cs, services.GetDB(), nil, nil)
|
||||||
|
defer trex.Close()
|
||||||
|
|
||||||
|
// I don't think this has ever been called on production, or dev !
|
||||||
|
input := manifestsender.ProcessConfigUpdateStruct{
|
||||||
|
VIN: vin,
|
||||||
|
Name: "SAP order change update",
|
||||||
|
Username: "unidentified attendant sap user",
|
||||||
|
SendToCar: true,
|
||||||
|
DontCreateDatabaseEntry: false,
|
||||||
|
Forced: false,
|
||||||
|
}
|
||||||
|
_, err = trex.ProcessConfigUpdate(input, services.GetDB().GetCarConfigData())
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCarSettings(db *services.DB, vin string, order common.VehicleOrder) (err error) {
|
||||||
|
// If the sequence number is missing, go will fill it in as 0, which is the case where the car has no sequence number
|
||||||
|
if order.VehicleSpecification.SequenceNumber != "" {
|
||||||
|
_, err = db.GetCars().SetSetting(&common.CarSetting{
|
||||||
|
VIN: vin,
|
||||||
|
Name: common.SEQUENCE_NUMBER,
|
||||||
|
Value: fmt.Sprint(order.VehicleSpecification.SequenceNumber),
|
||||||
|
Type: "string",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.GetCars().SetSetting(&common.CarSetting{
|
||||||
|
VIN: vin,
|
||||||
|
Name: common.BODY_COLOR,
|
||||||
|
Value: common.FeatureCodeToBodyColor(order.VehicleSpecification.VehicleFeatures),
|
||||||
|
Type: "string",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Incase SAP hasn't sent this value yet
|
||||||
|
if order.VehicleSpecification.DestinationCountry != "" {
|
||||||
|
_, err = db.GetCars().SetSetting(&common.CarSetting{
|
||||||
|
VIN: vin,
|
||||||
|
Name: common.DELIVERY_DESTINATION,
|
||||||
|
Value: order.VehicleSpecification.DestinationCountry,
|
||||||
|
Type: "string",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func changeRatePlan(db *services.DB, smsClient sms.SMSServiceClient, vin, destinationCountry string) error {
|
||||||
|
car, err := services.GetDB().GetCars().SelectByVIN(vin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(car.ICCID) == 0 {
|
||||||
|
return fmt.Errorf("no iccid found for vehicle %s", vin)
|
||||||
|
}
|
||||||
|
iccid := strings.TrimSuffix(strings.ToLower(car.ICCID), "f")
|
||||||
|
|
||||||
|
customAtributeReq := sms.CustomAtributesRequest{
|
||||||
|
ICCID: iccid,
|
||||||
|
AccountCustom1: destinationCountry,
|
||||||
|
}
|
||||||
|
_, err = smsClient.HandleCustomAttributes(context.Background(), &customAtributeReq)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ratePlan, err := db.GetRatePlan().Select(destinationCountry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
changeRatePlanRequest := sms.ChangeRatePlanRequest{
|
||||||
|
ICCID: iccid,
|
||||||
|
ProductId: ratePlan.ProductID,
|
||||||
|
AccountId: tmobile.FISKER_TMOBILE_ACCOUNT_ID,
|
||||||
|
}
|
||||||
|
_, err = smsClient.HandleChangeRatePlan(context.Background(), &changeRatePlanRequest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go verifyRatePlan(smsClient, iccid, destinationCountry)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyRatePlan(smsClient sms.SMSServiceClient, iccid, destinationCountry string) error {
|
||||||
|
deviceDetailsRequest := sms.DeviceDetailsRequest{
|
||||||
|
ICCID: iccid,
|
||||||
|
}
|
||||||
|
|
||||||
|
details, err := smsClient.HandleDeviceDetails(context.Background(), &deviceDetailsRequest)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Msgf("failed to check device details for iccid %s", iccid)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info().Msgf("device details for iccid %s: %+v", iccid, details)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
224
services/attendant/handlers/order_updated_test.go
Normal file
224
services/attendant/handlers/order_updated_test.go
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/handlers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
q "github.com/fiskerinc/cloud-services/pkg/db/queries"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis/tester"
|
||||||
|
vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
someErr = errors.New("some error")
|
||||||
|
vinMock = "FISKER123"
|
||||||
|
vodMock = "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
confMock = "000000000000000000000000000000000000000"
|
||||||
|
validOrder = common.VehicleOrder{
|
||||||
|
SpecID: 800010200,
|
||||||
|
OrderNumber: 8000102,
|
||||||
|
MessageIdentifier: "VEHICLEORDERSUBMISSION",
|
||||||
|
VehicleSpecification: common.VehicleSpecification{
|
||||||
|
OrderIndicator: "S",
|
||||||
|
FleetOrderIndicator: "N",
|
||||||
|
ProductionPhaseIndicator: "01",
|
||||||
|
VehicleIndicator: "000",
|
||||||
|
ManufacturingPlant: "G",
|
||||||
|
ExpectedReferenceDate: common.ExpectedReferenceDate{
|
||||||
|
Time: time.Date(2022, 5, 26, 0, 0, 0, 0, time.UTC),
|
||||||
|
},
|
||||||
|
ModelType: "FM29",
|
||||||
|
ModelYearIndicator: 2023,
|
||||||
|
VehicleModel: "F29",
|
||||||
|
VinPrefix: "VCF1ZBU2_PG",
|
||||||
|
VehicleFeatures: []common.FeatureCodes{
|
||||||
|
{
|
||||||
|
FamilyCode: "2801",
|
||||||
|
FeatureCode: "280102",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FamilyCode: "2804",
|
||||||
|
FeatureCode: "280401",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FamilyCode: "2805",
|
||||||
|
FeatureCode: "280501",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
validUpdateManifest = common.UpdateManifest{
|
||||||
|
ID: 1,
|
||||||
|
CarUpdateID: 1,
|
||||||
|
Version: fmt.Sprint(validOrder.OrderNumber),
|
||||||
|
Description: fmt.Sprintf("configuration %s %s", vinMock, validOrder.MessageIdentifier),
|
||||||
|
ManifestType: common.ConfigUpdateType,
|
||||||
|
VOD: "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
|
||||||
|
}
|
||||||
|
validRespECUs = []*common.UpdateManifestECU{
|
||||||
|
{
|
||||||
|
UpdateManifestID: 1,
|
||||||
|
ECU: "ICC",
|
||||||
|
Configuration: confMock,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
smsMock = sms.NewSMSMockSuccess()
|
||||||
|
mockCars = mocks.MockCars{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOrderUpdated(t *testing.T) {
|
||||||
|
handlers.ENABLE_ORDERUPDATE_SENDS = true
|
||||||
|
validUpdateManifestResp := validUpdateManifest
|
||||||
|
validUpdateManifestResp.ID = 1
|
||||||
|
validUpdateManifestResp.ECUs = validRespECUs
|
||||||
|
|
||||||
|
scrubbedValidUpdateManifest := validUpdateManifestResp.ToUpdateConfigManifest()
|
||||||
|
scrubbedValidUpdateManifest.Type = "standard"
|
||||||
|
|
||||||
|
successPayload, _ := json.Marshal(validOrder)
|
||||||
|
redisMock := tester.NewRedisMock()
|
||||||
|
services.SetRedisClientPool(tester.NewMockClientPool(redisMock))
|
||||||
|
mockSap := vconfig.SAPServiceMock{GetConfigurationMock: func(vin string) (common.SAPResponse, error) {
|
||||||
|
return common.SAPResponse{
|
||||||
|
ModelYear: 2023,
|
||||||
|
ModelType: "Ocean",
|
||||||
|
VersionDuringModelYear: "1",
|
||||||
|
Features: []common.SAPFeature{
|
||||||
|
{
|
||||||
|
FamilyCode: "FamilyCode1",
|
||||||
|
FeatureCode: "FeatureCode1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FamilyCode: "FamilyCode2",
|
||||||
|
FeatureCode: "FeatureCode2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FamilyCode: "VOD",
|
||||||
|
FeatureCode: vodMock,
|
||||||
|
},
|
||||||
|
}}, nil
|
||||||
|
}}
|
||||||
|
services.SetSapService(mockSap)
|
||||||
|
mockCars.SetLoadResp(&common.Car{
|
||||||
|
VIN: vinMock,
|
||||||
|
ICCID: "1234567890",
|
||||||
|
SoldStatus: common.CarSoldStatusRetailed,
|
||||||
|
})
|
||||||
|
|
||||||
|
tests := map[string]struct {
|
||||||
|
updateManifest q.UpdateManifestsInterface
|
||||||
|
cars q.CarsInterface
|
||||||
|
carsUpdate q.CarUpdatesInterface
|
||||||
|
ratePlan q.RatePlanInterface
|
||||||
|
vConfig vconfig.ConfigServiceInterface
|
||||||
|
payload []byte
|
||||||
|
expRedisMsgs map[string]interface{}
|
||||||
|
expErr error
|
||||||
|
}{
|
||||||
|
"success": {
|
||||||
|
updateManifest: &mocks.MockUpdateManifests{SelectResponse: []common.UpdateManifest{{ID: 1234}}},
|
||||||
|
cars: &mockCars,
|
||||||
|
carsUpdate: &mocks.MockCarUpdates{},
|
||||||
|
ratePlan: &mocks.MockRatePlan{},
|
||||||
|
vConfig: vconfig.ConfigMock{GetVODCDSCodingDataMock: SuccessGetCDSMock},
|
||||||
|
payload: successPayload,
|
||||||
|
expRedisMsgs: map[string]interface{}{
|
||||||
|
"2:" + vinMock: common.Message{
|
||||||
|
Handler: "car_update",
|
||||||
|
Data: common.CarUpdate{
|
||||||
|
ID: 1,
|
||||||
|
VIN: vinMock,
|
||||||
|
UpdateManifestID: 1,
|
||||||
|
UpdateManifest: &validUpdateManifestResp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"1:" + vinMock: common.Message{
|
||||||
|
Handler: "config_update",
|
||||||
|
Data: scrubbedValidUpdateManifest,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expErr: nil,
|
||||||
|
},
|
||||||
|
"json_parse_err": {
|
||||||
|
payload: []byte(`12`),
|
||||||
|
expErr: errors.New("json: cannot unmarshal number into Go value of type common.VehicleOrder"),
|
||||||
|
},
|
||||||
|
"failed_cars_db": {
|
||||||
|
updateManifest: &mocks.MockUpdateManifests{SelectResponse: []common.UpdateManifest{{ID: 1234}}},
|
||||||
|
cars: &mocks.MockCars{
|
||||||
|
DBMockHelper: mocks.DBMockHelper{
|
||||||
|
Error: someErr,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
carsUpdate: &mocks.MockCarUpdates{},
|
||||||
|
ratePlan: &mocks.MockRatePlan{},
|
||||||
|
vConfig: vconfig.ConfigMock{GetVODCDSCodingDataMock: SuccessGetCDSMock},
|
||||||
|
payload: successPayload,
|
||||||
|
expErr: someErr,
|
||||||
|
},
|
||||||
|
"failed_cds": {
|
||||||
|
updateManifest: &mocks.MockUpdateManifests{SelectResponse: []common.UpdateManifest{{ID: 1234}}},
|
||||||
|
carsUpdate: &mocks.MockCarUpdates{},
|
||||||
|
cars: &mockCars,
|
||||||
|
ratePlan: &mocks.MockRatePlan{},
|
||||||
|
vConfig: vconfig.ConfigMock{GetVODCDSCodingDataMock: FailedGetCDSMock},
|
||||||
|
payload: successPayload,
|
||||||
|
expErr: someErr,
|
||||||
|
},
|
||||||
|
"failed_db": {
|
||||||
|
updateManifest: &mocks.MockUpdateManifests{
|
||||||
|
DBMockHelper: mocks.DBMockHelper{
|
||||||
|
Error: someErr,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cars: &mockCars,
|
||||||
|
carsUpdate: &mocks.MockCarUpdates{},
|
||||||
|
ratePlan: &mocks.MockRatePlan{},
|
||||||
|
vConfig: vconfig.ConfigMock{GetVODCDSCodingDataMock: SuccessGetCDSMock},
|
||||||
|
payload: successPayload,
|
||||||
|
expErr: someErr,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for tname, tt := range tests {
|
||||||
|
t.Run(tname, func(t *testing.T) {
|
||||||
|
redisMock.Reset()
|
||||||
|
db := services.GetDB()
|
||||||
|
db.SetManifests(tt.updateManifest)
|
||||||
|
db.SetCars(tt.cars)
|
||||||
|
db.SetCarUpdates(tt.carsUpdate)
|
||||||
|
db.SetRatePlan(tt.ratePlan)
|
||||||
|
services.SetVehicleConfig(tt.vConfig)
|
||||||
|
err := handlers.OrderUpdated(db, &smsMock, vinMock, tt.payload)
|
||||||
|
if err != nil && tt.expErr != nil {
|
||||||
|
assert.Equal(t, tt.expErr.Error(), err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.expErr, err)
|
||||||
|
// assert.Equal(t, tt.expRedisMsgs, redisMock.PublishedMessages)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SuccessGetCDSMock(request common.VODCDSRequest) (map[string]string, error) {
|
||||||
|
ecus := map[string]string{
|
||||||
|
"VOD": vodMock,
|
||||||
|
"ICC": confMock,
|
||||||
|
}
|
||||||
|
|
||||||
|
return ecus, nil
|
||||||
|
}
|
||||||
|
func FailedGetCDSMock(request common.VODCDSRequest) (map[string]string, error) {
|
||||||
|
return nil, someErr
|
||||||
|
}
|
||||||
24
services/attendant/handlers/send_manifest.go
Normal file
24
services/attendant/handlers/send_manifest.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/controllers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var PrivateSeed int64 // Used only for testing. Do not change otherwise
|
||||||
|
|
||||||
|
func SendManifest(d *services.DB, ka *services.KeepAwake, device common.Device, id string, data []byte) error {
|
||||||
|
logger.Debug().Msgf("SendManifest %v %s", device, id)
|
||||||
|
|
||||||
|
clientPool := services.RedisClientPool()
|
||||||
|
|
||||||
|
configService := services.GetVehicleConfig()
|
||||||
|
|
||||||
|
generator := controllers.NewManifestSender(clientPool, d, configService, device, ka, PrivateSeed)
|
||||||
|
defer generator.Release()
|
||||||
|
|
||||||
|
return generator.Process(id, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// If the update is not finished installing on the car, we send out an sms to wake the car up.
|
||||||
|
// If this message is failed to deliver, we need to cancel the car update and set its status as failed
|
||||||
|
func CarSendManifestSMSWakeUpCallback(d *services.DB, ka *services.KeepAwake, device common.Device, id string, data []byte) (err error) {
|
||||||
|
logger.Info().Msgf("SMS delivered but not sending TBox manifest %v %s", device, id)
|
||||||
|
return nil
|
||||||
|
/*
|
||||||
|
clientPool := services.RedisClientPool()
|
||||||
|
|
||||||
|
sap := services.GetSapService()
|
||||||
|
configService := services.GetVehicleConfig()
|
||||||
|
|
||||||
|
generator := controllers.NewManifestSender(clientPool, d, sap, configService, device, ka, PrivateSeed)
|
||||||
|
defer generator.Release()
|
||||||
|
|
||||||
|
return generator.ContinueTBOXSend(id, data) */
|
||||||
|
}
|
||||||
962
services/attendant/handlers/send_manifest_test.go
Normal file
962
services/attendant/handlers/send_manifest_test.go
Normal file
@@ -0,0 +1,962 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/handlers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common/manifestfingerprintparams"
|
||||||
|
dbm "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/kafka"
|
||||||
|
kafkaMock "github.com/fiskerinc/cloud-services/pkg/kafka/mock"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
rm "github.com/fiskerinc/cloud-services/pkg/redis/tester"
|
||||||
|
th "github.com/fiskerinc/cloud-services/pkg/testhelper"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/testrunner"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/elptr"
|
||||||
|
vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig"
|
||||||
|
"github.com/jinzhu/copier"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSendManifest(t *testing.T) {
|
||||||
|
os.Setenv("APP_SERVICE_NAME", "ATTENDANT")
|
||||||
|
handlers.PrivateSeed = 123456789
|
||||||
|
|
||||||
|
ka := services.NewKeepAwakeService()
|
||||||
|
ka.SetService(&services.MockKeepAwakeImplementation{})
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
testVIN := "JH4KA7680RC01"
|
||||||
|
tboxKey := common.TRex.Key(testVIN)
|
||||||
|
hmiKey := common.HMI.Key(testVIN)
|
||||||
|
|
||||||
|
smsKey := "manifest_tbox_send_cache:100"
|
||||||
|
|
||||||
|
var bhex common.BinaryHex = []byte("test")
|
||||||
|
redis.MockRedisConnection()
|
||||||
|
falsePtrValue := elptr.ElPtr(false)
|
||||||
|
truePtrValue := elptr.ElPtr(true)
|
||||||
|
ecuaRollback := []*common.UpdateManifestECU{
|
||||||
|
{
|
||||||
|
ID: 100,
|
||||||
|
UpdateManifestID: 200,
|
||||||
|
ECU: "ECUA",
|
||||||
|
Version: "VERSIONOLD",
|
||||||
|
Mode: "A",
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
Files: []*common.UpdateManifestFile{
|
||||||
|
{
|
||||||
|
FileID: "FILEIDOLD",
|
||||||
|
UpdateManifestECUID: 1001,
|
||||||
|
Filename: "FILENAMEOLD",
|
||||||
|
URL: "URLOLD",
|
||||||
|
FileType: common.Software,
|
||||||
|
WriteRegionID: 700,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 701,
|
||||||
|
Length: 702,
|
||||||
|
},
|
||||||
|
FileSize: 240,
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ecus := []*common.UpdateManifestECU{
|
||||||
|
{
|
||||||
|
ECU: "ICC",
|
||||||
|
Version: "SWVERSION",
|
||||||
|
HWVersions: []string{"HWVERSION"},
|
||||||
|
Mode: "D",
|
||||||
|
SelfDownload: true,
|
||||||
|
InstallPriority: 13,
|
||||||
|
Files: []*common.UpdateManifestFile{
|
||||||
|
{
|
||||||
|
FileID: "AAAAAAA",
|
||||||
|
URL: "http://download.com/file1.bin",
|
||||||
|
FileSize: 1000,
|
||||||
|
Checksum: "AAAAAAA",
|
||||||
|
WriteRegionID: 100,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 101,
|
||||||
|
Length: 102,
|
||||||
|
},
|
||||||
|
FileType: common.Calibration,
|
||||||
|
EraseRegionID: 200,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 201,
|
||||||
|
Length: 202,
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileID: "SHOULD_NOT_BE_IN_UPDATE",
|
||||||
|
URL: "http://download.com/SHOULD_NOT_BE_IN_UPDATE.bin",
|
||||||
|
FileSize: 1000,
|
||||||
|
Checksum: "AAAAAAA",
|
||||||
|
WriteRegionID: 100,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 101,
|
||||||
|
Length: 102,
|
||||||
|
},
|
||||||
|
FileType: common.Calibration,
|
||||||
|
EraseRegionID: 200,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 201,
|
||||||
|
Length: 202,
|
||||||
|
},
|
||||||
|
Parsed: falsePtrValue,
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileID: "MUST_BE_IN_UPDATE",
|
||||||
|
URL: "http://download.com/MUST_BE_IN_UPDATE.bin",
|
||||||
|
FileSize: 1000,
|
||||||
|
Checksum: "AAAAAAA",
|
||||||
|
WriteRegionID: 100,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 101,
|
||||||
|
Length: 102,
|
||||||
|
},
|
||||||
|
FileType: common.Calibration,
|
||||||
|
EraseRegionID: 200,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 201,
|
||||||
|
Length: 202,
|
||||||
|
},
|
||||||
|
Parsed: truePtrValue,
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ECU: "ECUD",
|
||||||
|
Version: "SWVERSION",
|
||||||
|
HWVersions: []string{"HWVERSION"},
|
||||||
|
Mode: "D",
|
||||||
|
InstallPriority: 4,
|
||||||
|
Files: []*common.UpdateManifestFile{
|
||||||
|
{
|
||||||
|
FileID: "SHOULD_NOT_BE_IN_UPDATE",
|
||||||
|
URL: "http://download.com/SHOULD_NOT_BE_IN_UPDATE.bin",
|
||||||
|
FileSize: 1000,
|
||||||
|
Checksum: "AAAAAAA",
|
||||||
|
WriteRegionID: 100,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 101,
|
||||||
|
Length: 102,
|
||||||
|
},
|
||||||
|
FileType: common.Calibration,
|
||||||
|
EraseRegionID: 200,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 201,
|
||||||
|
Length: 202,
|
||||||
|
},
|
||||||
|
Parsed: falsePtrValue,
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileID: "MUST_BE_IN_UPDATE",
|
||||||
|
URL: "http://download.com/MUST_BE_IN_UPDATE.bin",
|
||||||
|
FileSize: 1000,
|
||||||
|
Checksum: "AAAAAAA",
|
||||||
|
WriteRegionID: 100,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 101,
|
||||||
|
Length: 102,
|
||||||
|
},
|
||||||
|
FileType: common.Calibration,
|
||||||
|
EraseRegionID: 200,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 201,
|
||||||
|
Length: 202,
|
||||||
|
},
|
||||||
|
Parsed: truePtrValue,
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileID: "BBBBBBB",
|
||||||
|
URL: "http://download.com/file2.bin",
|
||||||
|
FileSize: 2000,
|
||||||
|
Checksum: "BBBBBBB",
|
||||||
|
FileType: common.Calibration,
|
||||||
|
WriteRegionID: 300,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 301,
|
||||||
|
Length: 302,
|
||||||
|
},
|
||||||
|
EraseRegionID: 400,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 401,
|
||||||
|
Length: 402,
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 100,
|
||||||
|
UpdateManifestID: 200,
|
||||||
|
ECU: "PDU",
|
||||||
|
Version: "PDU-VERS",
|
||||||
|
HWVersions: []string{"PDU-VERS"},
|
||||||
|
Mode: "PDU",
|
||||||
|
ConfigurationMask: "AAAAAAAA",
|
||||||
|
InstallPriority: 7,
|
||||||
|
Files: []*common.UpdateManifestFile{
|
||||||
|
{
|
||||||
|
FileID: "NOT_BE_IN_UPDATE",
|
||||||
|
URL: "http://download.com/NOT_BE_IN_UPDATE.bin",
|
||||||
|
FileSize: 1000,
|
||||||
|
Checksum: "AAAAAAA",
|
||||||
|
WriteRegionID: 100,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 101,
|
||||||
|
Length: 102,
|
||||||
|
},
|
||||||
|
FileType: common.Calibration,
|
||||||
|
EraseRegionID: 200,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 201,
|
||||||
|
Length: 202,
|
||||||
|
},
|
||||||
|
Parsed: falsePtrValue,
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileID: "MUST_BE_IN_UPDATE",
|
||||||
|
URL: "http://download.com/MUST_BE_IN_UPDATE.bin",
|
||||||
|
FileSize: 1000,
|
||||||
|
Checksum: "AAAAAAA",
|
||||||
|
WriteRegionID: 100,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 101,
|
||||||
|
Length: 102,
|
||||||
|
},
|
||||||
|
FileType: common.Calibration,
|
||||||
|
EraseRegionID: 200,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 201,
|
||||||
|
Length: 202,
|
||||||
|
},
|
||||||
|
Parsed: truePtrValue,
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileID: "AAAAAAAA",
|
||||||
|
URL: "http://download.com/filea.bin",
|
||||||
|
FileSize: 2000,
|
||||||
|
Checksum: "AAAAAAAA",
|
||||||
|
WriteRegionID: 500,
|
||||||
|
FileType: common.Calibration,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 501,
|
||||||
|
Length: 502,
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 100,
|
||||||
|
UpdateManifestID: 200,
|
||||||
|
ECU: "ECUA",
|
||||||
|
Version: "A-VERS",
|
||||||
|
HWVersions: []string{"A-VERS"},
|
||||||
|
Mode: "A",
|
||||||
|
ConfigurationMask: "AAAAAAAA",
|
||||||
|
InstallPriority: 1,
|
||||||
|
Files: []*common.UpdateManifestFile{
|
||||||
|
{
|
||||||
|
FileID: "FILEID",
|
||||||
|
UpdateManifestECUID: 100,
|
||||||
|
Filename: "FILENAME",
|
||||||
|
URL: "URL",
|
||||||
|
FileType: common.Calibration,
|
||||||
|
WriteRegionID: 600,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 601,
|
||||||
|
Length: 602,
|
||||||
|
},
|
||||||
|
FileSize: 240,
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fingerprint := getTodaysFingerprint()
|
||||||
|
|
||||||
|
standardManifest := common.UpdateManifest{
|
||||||
|
ID: 100,
|
||||||
|
CarUpdateID: 297,
|
||||||
|
Name: "TEST",
|
||||||
|
Version: "MANIFEST_VERSION",
|
||||||
|
SUMS: "2023.10.01.00",
|
||||||
|
Description: "TESTDESC",
|
||||||
|
ReleaseNotes: "http://fiskerinc.com/release_notes",
|
||||||
|
Type: common.ManifestTypeForced,
|
||||||
|
Active: truePtrValue,
|
||||||
|
Country: "US",
|
||||||
|
PowerTrain: "MD23",
|
||||||
|
Restraint: "None",
|
||||||
|
Model: "Ocean",
|
||||||
|
Trim: "Sport",
|
||||||
|
Year: 2022,
|
||||||
|
BodyType: "truck",
|
||||||
|
RollbackEnabled: true,
|
||||||
|
ECUs: ecus,
|
||||||
|
UpdateDuration: 30,
|
||||||
|
}
|
||||||
|
|
||||||
|
rollbackDisabledManifest := common.UpdateManifest{
|
||||||
|
ID: 100,
|
||||||
|
CarUpdateID: 297,
|
||||||
|
Name: "TEST",
|
||||||
|
Version: "MANIFEST_VERSION",
|
||||||
|
SUMS: "2023.10.01.00.E",
|
||||||
|
Description: "TESTDESC",
|
||||||
|
ReleaseNotes: "http://fiskerinc.com/release_notes",
|
||||||
|
Type: "standard",
|
||||||
|
Active: truePtrValue,
|
||||||
|
Country: "US",
|
||||||
|
PowerTrain: "MD23",
|
||||||
|
Restraint: "None",
|
||||||
|
Model: "Ocean",
|
||||||
|
Trim: "Sport",
|
||||||
|
Year: 2022,
|
||||||
|
BodyType: "truck",
|
||||||
|
ECUs: ecus,
|
||||||
|
UpdateDuration: 30,
|
||||||
|
}
|
||||||
|
standardManifestNoICC := common.UpdateManifest{
|
||||||
|
ID: 100,
|
||||||
|
CarUpdateID: 297,
|
||||||
|
Name: "TEST",
|
||||||
|
Version: "MANIFEST_VERSION",
|
||||||
|
SUMS: "2023.10.01.00.E",
|
||||||
|
Description: "TESTDESC",
|
||||||
|
ReleaseNotes: "http://fiskerinc.com/release_notes",
|
||||||
|
Type: "standard",
|
||||||
|
Country: "US",
|
||||||
|
PowerTrain: "MD23",
|
||||||
|
Restraint: "None",
|
||||||
|
Model: "Ocean",
|
||||||
|
Trim: "Sport",
|
||||||
|
Year: 2022,
|
||||||
|
RollbackEnabled: true,
|
||||||
|
BodyType: "truck",
|
||||||
|
ECUs: []*common.UpdateManifestECU{
|
||||||
|
{
|
||||||
|
ECU: "ECUD",
|
||||||
|
HWVersions: []string{"HWVERSION"},
|
||||||
|
Version: "SWVERSION",
|
||||||
|
Mode: "D",
|
||||||
|
InstallPriority: 7,
|
||||||
|
Files: []*common.UpdateManifestFile{
|
||||||
|
{
|
||||||
|
FileID: "BBBBBBB",
|
||||||
|
URL: "http://download.com/file2.bin",
|
||||||
|
FileSize: 2000,
|
||||||
|
Checksum: "BBBBBBB",
|
||||||
|
FileType: common.Calibration,
|
||||||
|
WriteRegionID: 700,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 701,
|
||||||
|
Length: 702,
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 100,
|
||||||
|
UpdateManifestID: 200,
|
||||||
|
ECU: "PDU",
|
||||||
|
Version: "PDU-VERS",
|
||||||
|
HWVersions: []string{"PDU-VERS"},
|
||||||
|
Mode: "PDU",
|
||||||
|
ConfigurationMask: "AAAAAAAA",
|
||||||
|
InstallPriority: 10,
|
||||||
|
Files: []*common.UpdateManifestFile{
|
||||||
|
{
|
||||||
|
FileID: "AAAAAAAA",
|
||||||
|
URL: "http://download.com/filea.bin",
|
||||||
|
FileSize: 2000,
|
||||||
|
FileType: common.Calibration,
|
||||||
|
Checksum: "AAAAAAAA",
|
||||||
|
WriteRegionID: 800,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 801,
|
||||||
|
Length: 802,
|
||||||
|
},
|
||||||
|
EraseRegionID: 900,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 901,
|
||||||
|
Length: 902,
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 100,
|
||||||
|
UpdateManifestID: 200,
|
||||||
|
ECU: "ECUA",
|
||||||
|
Version: "A-VERS",
|
||||||
|
HWVersions: []string{"A-VERS"},
|
||||||
|
Mode: "A",
|
||||||
|
ConfigurationMask: "AAAAAAAA",
|
||||||
|
InstallPriority: 4,
|
||||||
|
Files: []*common.UpdateManifestFile{
|
||||||
|
{
|
||||||
|
FileID: "FILEID",
|
||||||
|
UpdateManifestECUID: 100,
|
||||||
|
Filename: "FILENAME",
|
||||||
|
URL: "URL",
|
||||||
|
FileType: common.Calibration,
|
||||||
|
WriteRegionID: 600,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 601,
|
||||||
|
Length: 602,
|
||||||
|
},
|
||||||
|
FileSize: 240,
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UpdateDuration: 30,
|
||||||
|
}
|
||||||
|
mockCarUpdate := &common.CarUpdate{
|
||||||
|
ID: 297,
|
||||||
|
VIN: testVIN,
|
||||||
|
UpdateManifestID: 100,
|
||||||
|
}
|
||||||
|
mockCarECUs := []common.CarECU{
|
||||||
|
{ECU: "ECUD", HWVersion: "HWVERSION"},
|
||||||
|
{ECU: "ECUA", HWVersion: "A-VERS", Version: "TEST122"},
|
||||||
|
{ECU: "OBC", HWVersion: "PDU-VERS", Version: "TEST121"},
|
||||||
|
{ECU: "BCM", HWVersion: ";#A;#"},
|
||||||
|
{ECU: "ICC", HWVersion: "HWVERSION", Version: "TEST123"},
|
||||||
|
}
|
||||||
|
mockRedis := rm.MockRedis{}
|
||||||
|
mockCUDB := dbm.MockCarUpdates{}
|
||||||
|
mockManDB := dbm.MockUpdateManifests{}
|
||||||
|
mockKeys := dbm.MockEccKeys{
|
||||||
|
MockListResponse: []common.ECCKeys{
|
||||||
|
{
|
||||||
|
ECU: "PDU",
|
||||||
|
PrivKey1: &bhex,
|
||||||
|
PrivKey2: &bhex,
|
||||||
|
PrivKey3: &bhex,
|
||||||
|
PubKey1: &bhex,
|
||||||
|
PubKey2: &bhex,
|
||||||
|
PubKey3: &bhex,
|
||||||
|
Env: "current",
|
||||||
|
DBModelBase: dbbasemodel.DBModelBase{
|
||||||
|
CreatedAt: &now,
|
||||||
|
UpdatedAt: &now,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ECU: "ECUA",
|
||||||
|
PrivKey1: &bhex,
|
||||||
|
PrivKey2: &bhex,
|
||||||
|
PrivKey3: &bhex,
|
||||||
|
PubKey1: &bhex,
|
||||||
|
PubKey2: &bhex,
|
||||||
|
PubKey3: &bhex,
|
||||||
|
Env: "current",
|
||||||
|
DBModelBase: dbbasemodel.DBModelBase{
|
||||||
|
CreatedAt: &now,
|
||||||
|
UpdatedAt: &now,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ECU: "ECUD",
|
||||||
|
PrivKey1: &bhex,
|
||||||
|
PrivKey2: &bhex,
|
||||||
|
PrivKey3: &bhex,
|
||||||
|
PubKey1: &bhex,
|
||||||
|
PubKey2: &bhex,
|
||||||
|
PubKey3: &bhex,
|
||||||
|
Env: "current",
|
||||||
|
DBModelBase: dbbasemodel.DBModelBase{
|
||||||
|
CreatedAt: &now,
|
||||||
|
UpdatedAt: &now,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
services.GetDB().SetCarUpdates(&mockCUDB)
|
||||||
|
services.GetDB().SetCars(&dbm.MockCars{SelectCarECUs: mockCarECUs, SelectResponse: &common.Car{ICCID: "8000000000000000000"}})
|
||||||
|
services.GetDB().SetManifests(&mockManDB)
|
||||||
|
services.GetDB().SetECCKeys(&mockKeys)
|
||||||
|
services.SetRedisClientPool(rm.NewMockClientPool(&mockRedis))
|
||||||
|
mockSap := vconfig.SAPServiceMock{GetConfigurationMock: func(vin string) (common.SAPResponse, error) {
|
||||||
|
return common.SAPResponse{
|
||||||
|
ModelYear: 2023,
|
||||||
|
ModelType: "Ocean",
|
||||||
|
VersionDuringModelYear: "1",
|
||||||
|
Features: []common.SAPFeature{
|
||||||
|
{
|
||||||
|
FamilyCode: "FamilyCode1",
|
||||||
|
FeatureCode: "FeatureCode1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FamilyCode: "FamilyCode2",
|
||||||
|
FeatureCode: "FeatureCode2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}}
|
||||||
|
mockConf := vconfig.ConfigMock{GetVODCDSCodingDataMock: func(request common.VODCDSRequest) (map[string]string, error) {
|
||||||
|
return map[string]string{
|
||||||
|
"EPS": "000147303031313632011D",
|
||||||
|
"ESP": "000F47303031313632000001012301027600000101000002B0",
|
||||||
|
"ECUA": "config",
|
||||||
|
"VOD": "",
|
||||||
|
"ADAS": "000147303031313632011D",
|
||||||
|
}, nil
|
||||||
|
}}
|
||||||
|
mockSMS := &sms.SMSMock{}
|
||||||
|
mockSMS.SetHandleSMSQueueResponse(&sms.SMSQueueResponse{
|
||||||
|
SmsMsgID: "100",
|
||||||
|
SentSuccessful: true,
|
||||||
|
}, nil)
|
||||||
|
|
||||||
|
services.SetSapService(mockSap)
|
||||||
|
services.SetVehicleConfig(mockConf)
|
||||||
|
services.SetSmsClient(mockSMS)
|
||||||
|
|
||||||
|
mockKafkaProducer := kafkaMock.GetKafkaMock(nil)
|
||||||
|
services.SetKafkaProducer(mockKafkaProducer)
|
||||||
|
|
||||||
|
clonedStandardManifest := common.UpdateManifest{}
|
||||||
|
clonedStandardManifestNoICC := common.UpdateManifest{}
|
||||||
|
err := copier.CopyWithOption(&clonedStandardManifest, &standardManifest, copier.Option{DeepCopy: true})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = copier.CopyWithOption(&clonedStandardManifestNoICC, &standardManifestNoICC, copier.Option{DeepCopy: true})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []testrunner.TestCase{
|
||||||
|
{
|
||||||
|
Name: "Send manifest w Self Download",
|
||||||
|
DBTestCase: &dbm.DBTestCase{
|
||||||
|
MockLoadResponse: mockCarUpdate,
|
||||||
|
SetupMockResponse: func() {
|
||||||
|
mockCUDB.LoadManifest = &clonedStandardManifest
|
||||||
|
mockManDB.ECUUpdatesMock = func(man *common.UpdateManifestECU, vin string) ([]*common.UpdateManifestECU, error) {
|
||||||
|
if man.ECU == "ECUA" {
|
||||||
|
return ecuaRollback, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RedisTestCase: &rm.RedisTestCase{
|
||||||
|
Device: common.Service,
|
||||||
|
DeviceKey: kafka.OTAUpdateService,
|
||||||
|
PayloadData: `{"car_update_id":297}`,
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
hmiKey: `{"handler":"update_manifest","data":{"name":"TEST","version":"MANIFEST_VERSION","description":"TESTDESC","release_notes":"http://fiskerinc.com/release_notes","ecu_updates":[{"name":"ECUA","version":"A-VERS","current_version":"TEST122","hw_version":"A-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"FILEID","url":"URL","file_size":240,"type":"calibration","write_region":{"offset":601,"length":602}}]},{"name":"ECUD","version":"SWVERSION","hw_version":"HWVERSION","files":[{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"BBBBBBB","url":"http://download.com/file2.bin","file_size":2000,"checksum":"BBBBBBB","type":"calibration","write_region":{"offset":301,"length":302},"erase_region":{"offset":401,"length":402}}]},{"name":"OBC","version":"PDU-VERS","current_version":"TEST121","hw_version":"PDU-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"AAAAAAAA","url":"http://download.com/filea.bin","file_size":2000,"checksum":"AAAAAAAA","type":"calibration","write_region":{"offset":501,"length":502}}]},{"name":"ICC","version":"SWVERSION","current_version":"TEST123","hw_version":"HWVERSION","self_download":true,"files":[{"file_id":"AAAAAAA","url":"http://download.com/file1.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}}]}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":true,"type":"forced","vod":"01011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110100202310010011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100001e","update_duration":30}}`,
|
||||||
|
},
|
||||||
|
ExpectedCaches: map[string]rm.ExpiringCacheResult{
|
||||||
|
// carupdateKey: {
|
||||||
|
// Value: `{"current_size":0,"ecu":"","errorcode":0,"file_size":0,"file_total":0,"id":297,"info":"ICC","installed":0,"status":"sent","total_files":0,"total_size":0}`,
|
||||||
|
// Expires: expectedExpire,
|
||||||
|
// },
|
||||||
|
smsKey: {
|
||||||
|
Value: `{"VIN":"JH4KA7680RC01","ManifestID":100,"Destination":"ICC"}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Send manifest w Self Download rollback disabled",
|
||||||
|
DBTestCase: &dbm.DBTestCase{
|
||||||
|
MockLoadResponse: mockCarUpdate,
|
||||||
|
SetupMockResponse: func() {
|
||||||
|
mockCUDB.LoadManifest = &rollbackDisabledManifest
|
||||||
|
mockManDB.ECUUpdatesMock = func(man *common.UpdateManifestECU, vin string) ([]*common.UpdateManifestECU, error) {
|
||||||
|
if man.ECU == "ECUA" {
|
||||||
|
return ecuaRollback, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RedisTestCase: &rm.RedisTestCase{
|
||||||
|
Device: common.Service,
|
||||||
|
DeviceKey: kafka.OTAUpdateService,
|
||||||
|
PayloadData: `{"car_update_id":297}`,
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
hmiKey: `{"handler":"update_manifest","data":{"name":"TEST","version":"MANIFEST_VERSION","description":"TESTDESC","release_notes":"http://fiskerinc.com/release_notes","ecu_updates":[{"name":"ECUA","version":"A-VERS","current_version":"TEST122","hw_version":"A-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"FILEID","url":"URL","file_size":240,"type":"calibration","write_region":{"offset":601,"length":602}}]},{"name":"ECUD","version":"SWVERSION","hw_version":"HWVERSION","files":[{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"BBBBBBB","url":"http://download.com/file2.bin","file_size":2000,"checksum":"BBBBBBB","type":"calibration","write_region":{"offset":301,"length":302},"erase_region":{"offset":401,"length":402}}]},{"name":"OBC","version":"PDU-VERS","current_version":"TEST121","hw_version":"PDU-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"AAAAAAAA","url":"http://download.com/filea.bin","file_size":2000,"checksum":"AAAAAAAA","type":"calibration","write_region":{"offset":501,"length":502}}]},{"name":"ICC","version":"SWVERSION","current_version":"TEST123","hw_version":"HWVERSION","self_download":true,"files":[{"file_id":"AAAAAAA","url":"http://download.com/file1.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}}]}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":false,"type":"standard","vod":"0101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111010020231001000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000ba","update_duration":30}}`,
|
||||||
|
},
|
||||||
|
ExpectedCaches: map[string]rm.ExpiringCacheResult{
|
||||||
|
smsKey: {
|
||||||
|
Value: `{"VIN":"JH4KA7680RC01","ManifestID":100,"Destination":"ICC"}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Bad message",
|
||||||
|
RedisTestCase: &rm.RedisTestCase{
|
||||||
|
Device: common.HMI,
|
||||||
|
DeviceKey: testVIN,
|
||||||
|
PayloadData: `{"car_update_id":`,
|
||||||
|
ExpectedError: "unexpected end of JSON input",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Send manifest w No Self Download",
|
||||||
|
DBTestCase: &dbm.DBTestCase{
|
||||||
|
MockLoadResponse: mockCarUpdate,
|
||||||
|
SetupMockResponse: func() {
|
||||||
|
mockCUDB.LoadManifest = &clonedStandardManifestNoICC
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RedisTestCase: &rm.RedisTestCase{
|
||||||
|
Device: common.Service,
|
||||||
|
DeviceKey: kafka.OTAUpdateService,
|
||||||
|
PayloadData: `{"car_update_id":297}`,
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
hmiKey: `{"handler":"update_manifest","data":{"name":"TEST","version":"MANIFEST_VERSION","description":"TESTDESC","release_notes":"http://fiskerinc.com/release_notes","ecu_updates":[{"name":"ECUA","version":"A-VERS","current_version":"TEST122","hw_version":"A-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"FILEID","url":"URL","file_size":240,"type":"calibration","write_region":{"offset":601,"length":602}}]},{"name":"ECUD","version":"SWVERSION","hw_version":"HWVERSION","files":[{"file_id":"BBBBBBB","url":"http://download.com/file2.bin","file_size":2000,"checksum":"BBBBBBB","type":"calibration","write_region":{"offset":701,"length":702}}]},{"name":"OBC","version":"PDU-VERS","current_version":"TEST121","hw_version":"PDU-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"AAAAAAAA","url":"http://download.com/filea.bin","file_size":2000,"checksum":"AAAAAAAA","type":"calibration","write_region":{"offset":801,"length":802},"erase_region":{"offset":901,"length":902}}]}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":true,"type":"standard","vod":"0101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111010020231001000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000ba","update_duration":30}}`,
|
||||||
|
tboxKey: `{"handler":"update_manifest","data":{"ecu_updates":[{"name":"ECUA","version":"A-VERS","current_version":"TEST122","hw_version":"A-VERS","configuration_mask":"AAAAAAAA","configuration":"config","files":[{"file_id":"FILEID","url":"URL","file_size":240,"type":"calibration","write_region":{"offset":601,"length":602}}],"rollback":[{"version":"VERSIONOLD","files":[{"file_id":"FILEIDOLD","url":"URLOLD","file_size":240,"type":"software","write_region":{"offset":701,"length":702}}]}],"ecc_keys":{"level_1":"74657374","level_2":"74657374","level_3":"74657374"}},{"name":"ECUD","version":"SWVERSION","hw_version":"HWVERSION","files":[{"file_id":"BBBBBBB","url":"http://download.com/file2.bin","file_size":2000,"checksum":"BBBBBBB","type":"calibration","write_region":{"offset":701,"length":702}}],"ecc_keys":{"level_1":"74657374","level_2":"74657374","level_3":"74657374"}},{"name":"OBC","version":"PDU-VERS","current_version":"TEST121","hw_version":"PDU-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"AAAAAAAA","url":"http://download.com/filea.bin","file_size":2000,"checksum":"AAAAAAAA","type":"calibration","write_region":{"offset":801,"length":802},"erase_region":{"offset":901,"length":902}}],"ecc_keys":{"level_1":"74657374","level_2":"74657374","level_3":"74657374"}}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":true,"type":"standard","vod":"0101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111010020231001000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000ba","update_duration":30}}`,
|
||||||
|
},
|
||||||
|
ExpectedCaches: map[string]rm.ExpiringCacheResult{
|
||||||
|
"manifest_tbox_send_cache:UQIDEPFQUH": {
|
||||||
|
Value: `{"VIN":"JH4KA7680RC01","ManifestID":0,"Destination":"ICC/TBOX"}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
schemaTesterHMI := th.NewSchemaTestHelper(t, schemaToHMI)
|
||||||
|
schemaTesterTRex := th.NewSchemaTestHelper(t, schemaToTRex)
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
mockRedis.Reset()
|
||||||
|
for _, ecu := range ecus {
|
||||||
|
ecu.Rollback = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.DBTestCase != nil {
|
||||||
|
test.DBTestCase.SetupDB(&mockCUDB)
|
||||||
|
}
|
||||||
|
if test.RedisTestCase != nil {
|
||||||
|
test.RedisTestCase.SetupRedis(&mockRedis)
|
||||||
|
|
||||||
|
err := handlers.SendManifest(services.GetDB(), ka, test.RedisTestCase.Device, test.RedisTestCase.DeviceKey, []byte(test.RedisTestCase.PayloadData))
|
||||||
|
|
||||||
|
test.RedisTestCase.CheckHandlerError(t, test.Name, err)
|
||||||
|
test.RedisTestCase.Validate(t, test.Name, &mockRedis)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.DBTestCase != nil {
|
||||||
|
test.DBTestCase.Validate(t, test.Name, &mockCUDB)
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, m := range test.RedisTestCase.ExpectedMessages {
|
||||||
|
name := fmt.Sprintf("%s %s", test.Name, key)
|
||||||
|
if strings.Contains(key, "2:") {
|
||||||
|
schemaTesterHMI.ValidateSchemaObject(name, []byte(m))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
schemaTesterTRex.ValidateSchemaObject(name, []byte(m))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSendManifestMultiFile(t *testing.T) {
|
||||||
|
os.Setenv("APP_SERVICE_NAME", "ATTENDANT")
|
||||||
|
testVIN := "JH4KA7680RC01"
|
||||||
|
now := time.Now()
|
||||||
|
hmiKey := common.HMI.Key(testVIN)
|
||||||
|
falsePtrValue := elptr.ElPtr(false)
|
||||||
|
truePtrValue := elptr.ElPtr(true)
|
||||||
|
var bhex common.BinaryHex = []byte("test")
|
||||||
|
redis.MockRedisConnection()
|
||||||
|
ka := services.NewKeepAwakeService()
|
||||||
|
ka.SetService(&services.MockKeepAwakeImplementation{})
|
||||||
|
|
||||||
|
ecus := []*common.UpdateManifestECU{
|
||||||
|
{
|
||||||
|
ECU: "BCM",
|
||||||
|
Version: "DB22121A",
|
||||||
|
HWVersions: []string{";#A;#"},
|
||||||
|
Mode: "D",
|
||||||
|
SelfDownload: true,
|
||||||
|
Files: []*common.UpdateManifestFile{
|
||||||
|
{
|
||||||
|
FileID: "ShouldNotSee",
|
||||||
|
Filename: "notparsed.s19",
|
||||||
|
URL: "fakeurl.com",
|
||||||
|
Checksum: "shouldnotsee",
|
||||||
|
Parsed: falsePtrValue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileID: "3ee5fd6bc9402d67",
|
||||||
|
Filename: "MAGNA_BCM_FBL_driver.s19_0.bin",
|
||||||
|
URL: "https://upload-dev.fiskerdps.com/4466d78a-50e1-4f1d-a4b8-5f78076758ed/MAGNA_BCM_FBL_driver.s19_0.bin",
|
||||||
|
FileSize: 1000,
|
||||||
|
Checksum: "shouldsee",
|
||||||
|
WriteRegionID: 100,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 101,
|
||||||
|
Length: 102,
|
||||||
|
},
|
||||||
|
FileType: common.Bootloader,
|
||||||
|
EraseRegionID: 200,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 201,
|
||||||
|
Length: 202,
|
||||||
|
},
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
Parsed: truePtrValue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileID: "7878ede9c9407779",
|
||||||
|
Filename: "FM29_Application_FR40_220111.s19_0.bin",
|
||||||
|
URL: "ttps://upload-dev.fiskerdps.com/b5c3c8c8-86cd-422e-aa11-a248697edfa3/FM29_Application_FR40_220111.s19_0.bin",
|
||||||
|
FileSize: 1000,
|
||||||
|
Checksum: "SHOULD NOT SEE",
|
||||||
|
WriteRegionID: 100,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 101,
|
||||||
|
Length: 102,
|
||||||
|
},
|
||||||
|
FileType: common.Software,
|
||||||
|
EraseRegionID: 200,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 201,
|
||||||
|
Length: 202,
|
||||||
|
},
|
||||||
|
Parsed: truePtrValue,
|
||||||
|
Signature: "ShouldNOtSee",
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileID: "7bb5083dc9407ae9",
|
||||||
|
Filename: "FM29_Application_FR40_220111.s19_1.bin",
|
||||||
|
URL: "https://upload-dev.fiskerdps.com/2cdeb566-cb00-42cd-93a8-69769b19269a/FM29_Application_FR40_220111.s19_1.bin",
|
||||||
|
FileSize: 1000,
|
||||||
|
Checksum: "shantsee",
|
||||||
|
WriteRegionID: 100,
|
||||||
|
WriteRegion: common.MemoryRegion{
|
||||||
|
Offset: 101,
|
||||||
|
Length: 102,
|
||||||
|
},
|
||||||
|
FileType: common.Software,
|
||||||
|
EraseRegionID: 200,
|
||||||
|
EraseRegion: &common.MemoryRegion{
|
||||||
|
Offset: 201,
|
||||||
|
Length: 202,
|
||||||
|
},
|
||||||
|
Parsed: truePtrValue,
|
||||||
|
DBModelBase: th.Timestamp,
|
||||||
|
Signature: "SHOULD NOT SEE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileID: "7ebb4fadc9409b91",
|
||||||
|
Filename: "FM29_Application_FR40_220111.s19_2.bin",
|
||||||
|
URL: "https://upload-dev.fiskerdps.com/ec9f8a84-61fb-4a9b-9735-ac6f91b07a83/FM29_Application_FR40_220111.s19_2.bin",
|
||||||
|
FileType: common.Software,
|
||||||
|
Parsed: truePtrValue,
|
||||||
|
Signature: "SomeSignatureYouShouldSee",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fingerprint := getTodaysFingerprint()
|
||||||
|
|
||||||
|
standardManifest := common.UpdateManifest{
|
||||||
|
ID: 100,
|
||||||
|
CarUpdateID: 297,
|
||||||
|
Name: "TEST",
|
||||||
|
Version: "MANIFEST_VERSION",
|
||||||
|
SUMS: "2023.10.01.00",
|
||||||
|
Description: "TESTDESC",
|
||||||
|
ReleaseNotes: "http://fiskerinc.com/release_notes",
|
||||||
|
Type: "standard",
|
||||||
|
Active: truePtrValue,
|
||||||
|
Country: "US",
|
||||||
|
PowerTrain: "MD23",
|
||||||
|
Restraint: "None",
|
||||||
|
Model: "Ocean",
|
||||||
|
Trim: "Sport",
|
||||||
|
Year: 2022,
|
||||||
|
BodyType: "truck",
|
||||||
|
RollbackEnabled: false,
|
||||||
|
ECUs: ecus,
|
||||||
|
UpdateDuration: 30,
|
||||||
|
}
|
||||||
|
|
||||||
|
mockCarUpdate := &common.CarUpdate{
|
||||||
|
ID: 297,
|
||||||
|
VIN: testVIN,
|
||||||
|
UpdateManifestID: 100,
|
||||||
|
}
|
||||||
|
mockCarECUs := []common.CarECU{
|
||||||
|
{ECU: "ECUD", HWVersion: "HWVERSION"},
|
||||||
|
{ECU: "ECUA", HWVersion: "A-VERS"},
|
||||||
|
{ECU: "OBC", HWVersion: "PDU-VERS"},
|
||||||
|
{ECU: "BCM", HWVersion: ";#A;#"},
|
||||||
|
{ECU: "ICC", HWVersion: "HWVERSION"},
|
||||||
|
}
|
||||||
|
mockRedis := rm.MockRedis{}
|
||||||
|
mockCUDB := dbm.MockCarUpdates{}
|
||||||
|
mockManDB := dbm.MockUpdateManifests{}
|
||||||
|
mockKeys := dbm.MockEccKeys{
|
||||||
|
MockListResponse: []common.ECCKeys{
|
||||||
|
{
|
||||||
|
ECU: "BCM",
|
||||||
|
PrivKey1: &bhex,
|
||||||
|
PrivKey2: &bhex,
|
||||||
|
PrivKey3: &bhex,
|
||||||
|
PubKey1: &bhex,
|
||||||
|
PubKey2: &bhex,
|
||||||
|
PubKey3: &bhex,
|
||||||
|
Env: "current",
|
||||||
|
DBModelBase: dbbasemodel.DBModelBase{
|
||||||
|
CreatedAt: &now,
|
||||||
|
UpdatedAt: &now,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
services.GetDB().SetCarUpdates(&mockCUDB)
|
||||||
|
services.GetDB().SetCars(&dbm.MockCars{SelectCarECUs: mockCarECUs, SelectResponse: &common.Car{ICCID: ""}})
|
||||||
|
services.GetDB().SetManifests(&mockManDB)
|
||||||
|
services.GetDB().SetECCKeys(&mockKeys)
|
||||||
|
services.SetRedisClientPool(rm.NewMockClientPool(&mockRedis))
|
||||||
|
|
||||||
|
mockSap := vconfig.SAPServiceMock{GetConfigurationMock: func(vin string) (common.SAPResponse, error) {
|
||||||
|
return common.SAPResponse{
|
||||||
|
ModelYear: 2023,
|
||||||
|
ModelType: "Ocean",
|
||||||
|
VersionDuringModelYear: "1",
|
||||||
|
Features: []common.SAPFeature{
|
||||||
|
{
|
||||||
|
FamilyCode: "FamilyCode1",
|
||||||
|
FeatureCode: "FeatureCode1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FamilyCode: "FamilyCode2",
|
||||||
|
FeatureCode: "FeatureCode2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}}
|
||||||
|
mockConf := vconfig.ConfigMock{GetVODCDSCodingDataMock: func(request common.VODCDSRequest) (map[string]string, error) {
|
||||||
|
return map[string]string{
|
||||||
|
"ECUA": "config",
|
||||||
|
"VOD": "",
|
||||||
|
}, nil
|
||||||
|
}}
|
||||||
|
mockSMS := &sms.SMSMock{}
|
||||||
|
mockSMS.SetHandleSMSQueueResponse(&sms.SMSQueueResponse{
|
||||||
|
SmsMsgID: "100",
|
||||||
|
SentSuccessful: true,
|
||||||
|
}, nil)
|
||||||
|
services.SetSmsClient(mockSMS)
|
||||||
|
services.SetSapService(mockSap)
|
||||||
|
services.SetVehicleConfig(mockConf)
|
||||||
|
smsKey := "manifest_tbox_send_cache:100"
|
||||||
|
tests := []testrunner.TestCase{
|
||||||
|
{
|
||||||
|
Name: "MultiFile right order",
|
||||||
|
DBTestCase: &dbm.DBTestCase{
|
||||||
|
MockLoadResponse: mockCarUpdate,
|
||||||
|
SetupMockResponse: func() {
|
||||||
|
mockCUDB.LoadManifest = &standardManifest
|
||||||
|
mockManDB.ECUUpdatesMock = func(man *common.UpdateManifestECU, vin string) ([]*common.UpdateManifestECU, error) {
|
||||||
|
if man.ECU == "ECUA" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RedisTestCase: &rm.RedisTestCase{
|
||||||
|
Device: common.Service,
|
||||||
|
DeviceKey: kafka.OTAUpdateService,
|
||||||
|
PayloadData: `{"car_update_id":297}`,
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
hmiKey: `{"handler":"update_manifest","data":{"name":"TEST","version":"MANIFEST_VERSION","description":"TESTDESC","release_notes":"http://fiskerinc.com/release_notes","ecu_updates":[{"name":"BCM","version":"DB22121A","hw_version":";#A;#","self_download":true,"files":[{"file_id":"3ee5fd6bc9402d67","url":"https://upload-dev.fiskerdps.com/4466d78a-50e1-4f1d-a4b8-5f78076758ed/MAGNA_BCM_FBL_driver.s19_0.bin","file_size":1000,"checksum":"shouldsee","type":"bootloader","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"7878ede9c9407779","url":"ttps://upload-dev.fiskerdps.com/b5c3c8c8-86cd-422e-aa11-a248697edfa3/FM29_Application_FR40_220111.s19_0.bin","file_size":1000,"type":"software","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"7bb5083dc9407ae9","url":"https://upload-dev.fiskerdps.com/2cdeb566-cb00-42cd-93a8-69769b19269a/FM29_Application_FR40_220111.s19_1.bin","file_size":1000,"type":"software","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"7ebb4fadc9409b91","url":"https://upload-dev.fiskerdps.com/ec9f8a84-61fb-4a9b-9735-ac6f91b07a83/FM29_Application_FR40_220111.s19_2.bin","type":"software","write_region":{"offset":0,"length":0},"signature":"SomeSignatureYouShouldSee"}]}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":false,"type":"standard","vod":"01011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110100202310010011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100001e","update_duration":30}}`,
|
||||||
|
},
|
||||||
|
ExpectedCaches: map[string]rm.ExpiringCacheResult{smsKey: {
|
||||||
|
Value: `{"VIN":"JH4KA7680RC01","ManifestID":100,"Destination":"ICC"}`,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
mockRedis.Reset()
|
||||||
|
for _, ecu := range ecus {
|
||||||
|
ecu.Rollback = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.DBTestCase != nil {
|
||||||
|
test.DBTestCase.SetupDB(&mockCUDB)
|
||||||
|
}
|
||||||
|
if test.RedisTestCase != nil {
|
||||||
|
test.RedisTestCase.SetupRedis(&mockRedis)
|
||||||
|
|
||||||
|
err := handlers.SendManifest(services.GetDB(), ka, test.RedisTestCase.Device, test.RedisTestCase.DeviceKey, []byte(test.RedisTestCase.PayloadData))
|
||||||
|
|
||||||
|
test.RedisTestCase.CheckHandlerError(t, test.Name, err)
|
||||||
|
test.RedisTestCase.Validate(t, test.Name, &mockRedis)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.DBTestCase != nil {
|
||||||
|
test.DBTestCase.Validate(t, test.Name, &mockCUDB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSendManifestIntegration(t *testing.T) {
|
||||||
|
os.Setenv("APP_SERVICE_NAME", "ATTENDANT")
|
||||||
|
ka := services.NewKeepAwakeService()
|
||||||
|
ka.SetService(&services.MockKeepAwakeImplementation{})
|
||||||
|
err := handlers.SendManifest(services.GetDB(), ka, common.Service, "", []byte(`{"car_update_id":297}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTodaysFingerprint() string {
|
||||||
|
// generate today's fingerprint
|
||||||
|
fpparams := manifestfingerprintparams.GetFPParams()
|
||||||
|
var fm = common.UpdateManifest{}
|
||||||
|
fm.GenerateFingerprint(fpparams.CurTime(), fpparams.ManifestSerial())
|
||||||
|
return fm.Fingerprint
|
||||||
|
}
|
||||||
55
services/attendant/handlers/update_approve.go
Normal file
55
services/attendant/handlers/update_approve.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdateData extracts update ID from byte slice
|
||||||
|
type UpdateData struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApproveUpdate updates DB update field and
|
||||||
|
//
|
||||||
|
// sends redis message to vehicle to initialize download
|
||||||
|
func ApproveUpdate(db *services.DB, id string, data []byte) error {
|
||||||
|
logger.Debug().Msgf("ApproveUpdate %s", id)
|
||||||
|
|
||||||
|
client := services.RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
// TODO: NEEDS VALIDATION THAT INCOMING ID CAN APPROVE
|
||||||
|
// CAN COME FROM MOBILE OR HMI - NEEDS TO HANDLE BOTH CASES
|
||||||
|
|
||||||
|
var u UpdateData
|
||||||
|
err := json.Unmarshal(data, &u)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
update, err := db.ModifyUpdateStatus(u.ID, "approved")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Debug().Msgf("Sending redis queue- %s, key- %s, hander- %s, data- %v", "attendant", update.VIN, "update_download", u)
|
||||||
|
|
||||||
|
err = client.SafeQueueMessage(
|
||||||
|
common.TRex.Key(update.VIN),
|
||||||
|
common.Message{
|
||||||
|
Handler: "update_download",
|
||||||
|
Data: u,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
28
services/attendant/handlers/update_approve_test.go
Normal file
28
services/attendant/handlers/update_approve_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/handlers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis/tester"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/testhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestApproveUpdate(t *testing.T) {
|
||||||
|
mockRedis := &tester.MockRedis{}
|
||||||
|
services.SetRedisClientPool(tester.NewMockClientPool(mockRedis))
|
||||||
|
|
||||||
|
data := []byte(`{"id": 1}`)
|
||||||
|
|
||||||
|
err := handlers.ApproveUpdate(mockDB, "FISKER123", data)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, "ApproveUpdate", "no error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handlers.ApproveUpdate(mockDB, "FISKER123", nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, "ApproveUpdate", "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
70
services/attendant/handlers/updates_get.go
Normal file
70
services/attendant/handlers/updates_get.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/cache"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
s "github.com/fiskerinc/cloud-services/pkg/common/carupdatestatus"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/validator"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VehicleData extracts VIN from byte slice
|
||||||
|
type VehicleData struct {
|
||||||
|
VIN string `json:"vin" validate:"required,vin"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUpdates queries DB for updates based off VIN and
|
||||||
|
//
|
||||||
|
// sends redis message back to requester
|
||||||
|
func GetUpdates(db *services.DB, id string, data []byte) error {
|
||||||
|
clientPool := services.RedisClientPool()
|
||||||
|
|
||||||
|
var v VehicleData
|
||||||
|
err := json.Unmarshal(data, &v)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = validator.ValidateStruct(&v)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := cache.VerifyCarToDriver(clientPool, db.GetCars(), v.VIN, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if !ok {
|
||||||
|
return cache.ErrInvalidCarToDriverAssociation(v.VIN, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
cu := common.CarUpdate{
|
||||||
|
VIN: v.VIN,
|
||||||
|
Status: s.InstallApprovalAwait,
|
||||||
|
}
|
||||||
|
updates, err := db.GetCarUpdates().Select(&cu, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]common.ApprovalUpdate, len(updates))
|
||||||
|
for i := range updates {
|
||||||
|
result[i] = common.NewApprovalUpdates(&updates[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
client := clientPool.GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
logger.Debug().Msgf("Sending redis queue- %s, key- %s, hander- %s, data- %v", "attendant", id, "updates", result)
|
||||||
|
return client.SafeQueueMessage(
|
||||||
|
common.Mobile.Key(id),
|
||||||
|
common.Message{
|
||||||
|
Handler: "updates",
|
||||||
|
Data: result,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
148
services/attendant/handlers/updates_get_test.go
Normal file
148
services/attendant/handlers/updates_get_test.go
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/handlers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
s "github.com/fiskerinc/cloud-services/pkg/common/carupdatestatus"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis/tester"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUpdatesGet(t *testing.T) {
|
||||||
|
mockRedis := &tester.MockRedis{}
|
||||||
|
services.SetRedisClientPool(tester.NewMockClientPool(mockRedis))
|
||||||
|
mockCars := &mocks.MockCars{}
|
||||||
|
mockCarUpdates := &mocks.MockCarUpdates{}
|
||||||
|
mockDB = &services.DB{}
|
||||||
|
mockDB.SetCars(mockCars)
|
||||||
|
mockDB.SetCarUpdates(mockCarUpdates)
|
||||||
|
|
||||||
|
cognitoID := "valid-cognito-id-1"
|
||||||
|
mobileKey := common.Mobile.Key(cognitoID)
|
||||||
|
vin := "JM1BG2241R0797923"
|
||||||
|
data := fmt.Sprintf(`{"vin":"%s"}`, vin)
|
||||||
|
now := time.Now()
|
||||||
|
selectList := []common.CarUpdate{
|
||||||
|
{
|
||||||
|
ID: 1234,
|
||||||
|
VIN: vin,
|
||||||
|
UpdateManifestID: 4321,
|
||||||
|
Status: s.InstallApprovalAwait,
|
||||||
|
UpdateManifest: &common.UpdateManifest{
|
||||||
|
Name: "TEST",
|
||||||
|
Description: "TEST",
|
||||||
|
ReleaseNotes: "http://releasenotes.com",
|
||||||
|
Country: "US",
|
||||||
|
PowerTrain: "MD23",
|
||||||
|
Restraint: "None",
|
||||||
|
Model: "Ocean",
|
||||||
|
Trim: "Sport",
|
||||||
|
Year: 2022,
|
||||||
|
BodyType: "truck",
|
||||||
|
ECUs: []*common.UpdateManifestECU{
|
||||||
|
{
|
||||||
|
ECU: "ADAS",
|
||||||
|
Version: "VERSION",
|
||||||
|
Mode: "A",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DBModelBase: dbbasemodel.DBModelBase{
|
||||||
|
CreatedAt: &now,
|
||||||
|
UpdatedAt: &now,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
carToDrivers := []common.CarToDriver{
|
||||||
|
{
|
||||||
|
ID: 2000,
|
||||||
|
VIN: vin,
|
||||||
|
DriverID: cognitoID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []AttendentRouteTestCase{
|
||||||
|
{
|
||||||
|
Name: "No data",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.Mobile,
|
||||||
|
DeviceKey: cognitoID,
|
||||||
|
PayloadData: "",
|
||||||
|
ExpectedError: "unexpected end of JSON input",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Bad request",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.Mobile,
|
||||||
|
DeviceKey: cognitoID,
|
||||||
|
PayloadData: "{}",
|
||||||
|
ExpectedError: "Key: 'VehicleData.VIN' Error:Field validation for 'VIN' failed on the 'required' tag",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Bad association",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.Mobile,
|
||||||
|
DeviceKey: cognitoID,
|
||||||
|
PayloadData: data,
|
||||||
|
ExpectedError: "no relationship found between vin JM1BG2241R0797923 and driver valid-cognito-id-1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Cache error",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.Mobile,
|
||||||
|
DeviceKey: cognitoID,
|
||||||
|
PayloadData: data,
|
||||||
|
ExpectedError: "cache error",
|
||||||
|
},
|
||||||
|
CarToDriversError: errors.New("cache error"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Good request",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.Mobile,
|
||||||
|
DeviceKey: cognitoID,
|
||||||
|
PayloadData: data,
|
||||||
|
ExpectedMessages: map[string]string{
|
||||||
|
mobileKey: `{"handler":"updates","data":[{"id":1234,"vin":"JM1BG2241R0797923","name":"TEST","description":"TEST","release_notes":"http://releasenotes.com"}]}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SelectCarToDrivers: carToDrivers,
|
||||||
|
SelectCarUpdates: selectList,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "DB error",
|
||||||
|
RedisTestCase: tester.RedisTestCase{
|
||||||
|
Device: common.Mobile,
|
||||||
|
DeviceKey: cognitoID,
|
||||||
|
PayloadData: data,
|
||||||
|
ExpectedError: "database error",
|
||||||
|
},
|
||||||
|
SelectCarToDrivers: carToDrivers,
|
||||||
|
SelectCarUpdates: nil,
|
||||||
|
CarUpdateError: errors.New("database error"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range tests {
|
||||||
|
mockRedis.Reset()
|
||||||
|
test := &tests[i]
|
||||||
|
test.SetupRedis(mockRedis)
|
||||||
|
test.SetupDB(mockCars, mockCarUpdates, test)
|
||||||
|
|
||||||
|
err := handlers.GetUpdates(mockDB, test.DeviceKey, []byte(test.PayloadData))
|
||||||
|
|
||||||
|
test.CheckHandlerError(t, test.Name, err)
|
||||||
|
test.Validate(t, test.Name, mockRedis)
|
||||||
|
}
|
||||||
|
}
|
||||||
45
services/attendant/handlers/upload_dtc.go
Normal file
45
services/attendant/handlers/upload_dtc.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/controllers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/mongo"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/validator"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UploadDtc(id string, data []byte) error {
|
||||||
|
// ID is a vin in this case
|
||||||
|
logger.Debug().Msgf("Upload DTC for %s", id)
|
||||||
|
var DTCEntry controllers.DTCEntry
|
||||||
|
DTCEntry.VIN = id
|
||||||
|
|
||||||
|
err := json.Unmarshal(data, &DTCEntry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = validator.GetValidator().Struct(&DTCEntry); err != nil {
|
||||||
|
return errors.WithMessage(err, "failed structure validation")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = DTCEntry.ParseSnapshot()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Msg(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
client, err := services.GetMongoClient()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Str("id", id).Err(err).Send()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dtcs := mongo.NewCollection(client.Collection("dtcs"))
|
||||||
|
DTCEntry.CreatedAt = time.Now()
|
||||||
|
_, err = dtcs.InsertOne(DTCEntry)
|
||||||
|
return err
|
||||||
|
}
|
||||||
27
services/attendant/main.go
Normal file
27
services/attendant/main.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/controllers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/server"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/kafka"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/app"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
app.Setup("attendant", cleanup)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
go controllers.HealthCheck()
|
||||||
|
go server.StartConsumer(kafka.AttendantServiceGRPCKafka, kafka.AttendantService)
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanup() {
|
||||||
|
logger.Close()
|
||||||
|
}
|
||||||
7
services/attendant/server/errors.go
Normal file
7
services/attendant/server/errors.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrInvalidDevice = errors.New("invalid device associated to message")
|
||||||
221
services/attendant/server/server_consumer.go
Normal file
221
services/attendant/server/server_consumer.go
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/controllers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/handlers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/attendant/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/kafka"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/loggerdataresp"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/tmobile"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StartConsumer runs consumer and puts events into a channel for router
|
||||||
|
func StartConsumer(topic, oldTopic string) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
logger.Error().Msgf("PanicConsumer %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
events := make(chan *kafka.Message)
|
||||||
|
eventsJSON := make(chan common.EventRawJSON)
|
||||||
|
|
||||||
|
go routeEvents(events)
|
||||||
|
go routeOldEvents(eventsJSON)
|
||||||
|
|
||||||
|
logger.Info().Msgf("consumer intialized for topic: %v", topic)
|
||||||
|
consumer, oldConsumer, err := services.GetKafkaConsumer()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
err = oldConsumer.ConsumeToChannelJson([]string{oldTopic}, eventsJSON)
|
||||||
|
loggerdataresp.BadDataError(err, loggerdataresp.EofErrorCheck)
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = consumer.ConsumeToChannel([]string{topic}, events)
|
||||||
|
loggerdataresp.BadDataError(err, loggerdataresp.EofErrorCheck)
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeEvents(events chan *kafka.Message) {
|
||||||
|
db := services.GetDB()
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
sms := services.GetSMSClient()
|
||||||
|
ka := services.NewKeepAwakeService()
|
||||||
|
|
||||||
|
for {
|
||||||
|
event := <-events
|
||||||
|
var err error
|
||||||
|
payload := &kafka_grpc.GRPC_AttendantPayload{}
|
||||||
|
err = proto.Unmarshal(event.Value, payload)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Err(err).Send()
|
||||||
|
}
|
||||||
|
device, key := common.ParseDeviceKey(string(event.Key))
|
||||||
|
logger.Debug().Str("id", key).Msgf("source: %s, type: %s, handler: %s", key, device, payload.GetHandler())
|
||||||
|
|
||||||
|
switch device {
|
||||||
|
case common.TRex:
|
||||||
|
if payload.GetHandler() == "dtcs" {
|
||||||
|
d := &common.ConsumerPayload{
|
||||||
|
Handler: payload.GetHandler(),
|
||||||
|
Data: controllers.GRPCToDTCEntry(payload),
|
||||||
|
}
|
||||||
|
routeService(db, ka, sms, key, d)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d, _ := common.AttendantRouteTRexPayload(payload)
|
||||||
|
routeTRex(db, ka, key, d)
|
||||||
|
case common.HMI:
|
||||||
|
d, _ := common.AttendantRouteHMIPayload(payload)
|
||||||
|
routeHMI(db, ka, key, d)
|
||||||
|
case common.Mobile:
|
||||||
|
d, _ := common.AttendantRouteMobilePayload(payload)
|
||||||
|
routeMobile(db, key, d)
|
||||||
|
case common.Service:
|
||||||
|
if payload.GetHandler() == "sms_delivery_status_manifest" {
|
||||||
|
d := &common.ConsumerPayload{
|
||||||
|
Handler: payload.GetHandler(),
|
||||||
|
Data: tmobile.GRPCToTMessage(payload),
|
||||||
|
}
|
||||||
|
routeService(db, ka, sms, key, d)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d, _ := common.AttendantRouteServicePayload(payload)
|
||||||
|
routeService(db, ka, sms, key, d)
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
logger.Error().Str("id", key).Msgf("Unknown device: %s, type: %s, handler: %s", device, key, payload.GetHandler())
|
||||||
|
|
||||||
|
loggerdataresp.BadDataError(ErrInvalidDevice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loggerdataresp.BadDataError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeOldEvents(events chan common.EventRawJSON) {
|
||||||
|
db := services.GetDB()
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
sms := services.GetSMSClient()
|
||||||
|
ka := services.NewKeepAwakeService()
|
||||||
|
|
||||||
|
for {
|
||||||
|
event := <-events
|
||||||
|
var p common.Payload
|
||||||
|
err := p.Unmarshal(event.Payload)
|
||||||
|
device, key := common.ParseDeviceKey(event.Key)
|
||||||
|
payload := &common.ConsumerPayload{
|
||||||
|
Handler: p.Handler,
|
||||||
|
Data: p.Data,
|
||||||
|
}
|
||||||
|
logger.Debug().Str("id", key).Msgf("source: %s, type: %s, handler: %s", key, device, payload.GetHandler())
|
||||||
|
switch device {
|
||||||
|
case common.TRex:
|
||||||
|
routeTRex(db, ka, key, payload)
|
||||||
|
case common.HMI:
|
||||||
|
routeHMI(db, ka, key, payload)
|
||||||
|
case common.Mobile:
|
||||||
|
routeMobile(db, key, payload)
|
||||||
|
case common.Service:
|
||||||
|
routeService(db, ka, sms, key, payload)
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
logger.Error().Str("id", key).Msgf("Unknown device: %s, type: %s, handler: %s", device, key, p.Handler)
|
||||||
|
|
||||||
|
loggerdataresp.BadDataError(ErrInvalidDevice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loggerdataresp.BadDataError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeTRex(db *services.DB, ka *services.KeepAwake, id string, p common.ConsumerPayloadInterface) {
|
||||||
|
// route TRex messages. Also update cloud/modules_go/kafka/topics.go
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch p.GetHandler() {
|
||||||
|
case "car_state":
|
||||||
|
err = handlers.UpdateCarState(db, id, p.GetData())
|
||||||
|
loggerdataresp.BadDataError(err)
|
||||||
|
case "car_update_status", "car_update_download", "car_update_install":
|
||||||
|
err = handlers.CarUpdateProgressStatus(db, ka, common.TRex, id, p.GetData())
|
||||||
|
loggerdataresp.BadDataError(err)
|
||||||
|
case "get_filekeys":
|
||||||
|
err = handlers.GetFileKeys(db, common.TRex, id, p.GetData())
|
||||||
|
case "dtcs":
|
||||||
|
err = handlers.UploadDtc(id, p.GetData())
|
||||||
|
case "ecc_keys":
|
||||||
|
err = handlers.GetAllEccKeys(db, id, p.GetData())
|
||||||
|
default:
|
||||||
|
err = kafka.ErrUnhandledMessage(common.TRex, id, p.GetHandler(), string(p.GetData()))
|
||||||
|
}
|
||||||
|
loggerdataresp.BadDataError(err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeHMI(db *services.DB, ka *services.KeepAwake, id string, p common.ConsumerPayloadInterface) {
|
||||||
|
// route HMI messages. Also update cloud/modules_go/kafka/topics.go
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch p.GetHandler() {
|
||||||
|
case "update_approve":
|
||||||
|
err = handlers.ApproveUpdate(db, id, p.GetData())
|
||||||
|
case "car_update_status", "car_update_download", "car_update_install":
|
||||||
|
go func() {
|
||||||
|
err = handlers.CarUpdateProgressStatus(db, ka, common.HMI, id, p.GetData())
|
||||||
|
loggerdataresp.BadDataError(err)
|
||||||
|
}()
|
||||||
|
err = handlers.CarUpdateProgressStatus(db, ka, common.HMI, id, p.GetData())
|
||||||
|
loggerdataresp.BadDataError(err)
|
||||||
|
case "get_filekeys":
|
||||||
|
err = handlers.GetFileKeys(db, common.HMI, id, p.GetData())
|
||||||
|
default:
|
||||||
|
err = kafka.ErrUnhandledMessage(common.HMI, id, p.GetHandler(), string(p.GetData()))
|
||||||
|
}
|
||||||
|
|
||||||
|
loggerdataresp.BadDataError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeMobile(db *services.DB, id string, p common.ConsumerPayloadInterface) {
|
||||||
|
// route mobile messages. Also update cloud/modules_go/kafka/topics.go
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch p.GetHandler() {
|
||||||
|
case "update_approve":
|
||||||
|
err = handlers.ApproveUpdate(db, id, p.GetData())
|
||||||
|
case "updates_get":
|
||||||
|
err = handlers.GetUpdates(db, id, p.GetData())
|
||||||
|
default:
|
||||||
|
err = kafka.ErrUnhandledMessage(common.Mobile, id, p.GetHandler(), string(p.GetData()))
|
||||||
|
}
|
||||||
|
loggerdataresp.BadDataError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeService(db *services.DB, ka *services.KeepAwake, sms sms.SMSServiceClient, id string, p common.ConsumerPayloadInterface) {
|
||||||
|
// route cloud service messages. Also update cloud/modules_go/kafka/topics.go
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// We should switch out these strings for constants in the kafka library
|
||||||
|
switch p.GetHandler() {
|
||||||
|
case "send_manifest":
|
||||||
|
err = handlers.SendManifest(db, ka, common.Service, id, p.GetData())
|
||||||
|
case "order_updated":
|
||||||
|
err = handlers.OrderUpdated(db, sms, id, p.GetData())
|
||||||
|
case "sms_delivery_status_manifest":
|
||||||
|
err = handlers.CarSendManifestSMSWakeUpCallback(db, ka, common.Service, id, p.GetData())
|
||||||
|
default:
|
||||||
|
err = kafka.ErrUnhandledMessage(common.Service, id, p.GetHandler(), string(p.GetData()))
|
||||||
|
}
|
||||||
|
|
||||||
|
loggerdataresp.BadDataError(err)
|
||||||
|
}
|
||||||
29
services/attendant/services/config.go
Normal file
29
services/attendant/services/config.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
|
||||||
|
vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
configOnce sync.Once
|
||||||
|
configInstance vconfig.ConfigServiceInterface
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetVehicleConfig() vconfig.ConfigServiceInterface {
|
||||||
|
configOnce.Do(func() {
|
||||||
|
if configInstance != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info().Msg("init vehicle config instance")
|
||||||
|
configInstance = vconfig.NewConfigService()
|
||||||
|
})
|
||||||
|
return configInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetVehicleConfig(c vconfig.ConfigServiceInterface) {
|
||||||
|
configInstance = c
|
||||||
|
}
|
||||||
278
services/attendant/services/db.go
Normal file
278
services/attendant/services/db.go
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/db"
|
||||||
|
q "github.com/fiskerinc/cloud-services/pkg/db/queries"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dbOnce sync.Once
|
||||||
|
dbInstance *DB
|
||||||
|
)
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
client *db.DBClient
|
||||||
|
cars q.CarsInterface
|
||||||
|
carVersionsLog q.CarVersionsLogInterface
|
||||||
|
carupdates q.CarUpdatesInterface
|
||||||
|
filekeys q.FileKeysInterface
|
||||||
|
manifests q.UpdateManifestsInterface
|
||||||
|
ecu q.ECUInterface
|
||||||
|
eccKeys q.EccKeysInterface
|
||||||
|
ratePlan q.RatePlanInterface
|
||||||
|
updateManifestSUMSVersions q.SUMSVersionsInterface
|
||||||
|
carConfigData q.CarConfigDataInterface
|
||||||
|
|
||||||
|
onceEcu sync.Once
|
||||||
|
onceClient sync.Once
|
||||||
|
onceCars sync.Once
|
||||||
|
onceCarVersionsLog sync.Once
|
||||||
|
onceCarUpdates sync.Once
|
||||||
|
onceFileKeys sync.Once
|
||||||
|
onceManifests sync.Once
|
||||||
|
onceEccKeys sync.Once
|
||||||
|
onceRatePlan sync.Once
|
||||||
|
onceUpdateManifestSUMSVersions sync.Once
|
||||||
|
onceCarConfigData sync.Once
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDB() *DB {
|
||||||
|
dbOnce.Do(func() {
|
||||||
|
if dbInstance != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info().Msg("init DB instance")
|
||||||
|
dbInstance = &DB{}
|
||||||
|
})
|
||||||
|
return dbInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetDB(db *DB) {
|
||||||
|
if dbInstance != nil {
|
||||||
|
dbInstance.Close()
|
||||||
|
}
|
||||||
|
dbInstance = db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetDBClient() *db.DBClient {
|
||||||
|
d.onceClient.Do(func() {
|
||||||
|
if d.client != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info().Msg("init DBClient instance")
|
||||||
|
client := &db.DBClient{}
|
||||||
|
client.RegisterManyToManyRel([]interface{}{
|
||||||
|
(*common.CarToDriver)(nil),
|
||||||
|
})
|
||||||
|
err := client.InitSchema([]interface{}{
|
||||||
|
(*common.UpdateManifest)(nil),
|
||||||
|
(*common.Car)(nil),
|
||||||
|
(*common.CarToDriver)(nil),
|
||||||
|
(*common.CarUpdateStatus)(nil),
|
||||||
|
(*common.CarUpdate)(nil),
|
||||||
|
(*common.FileKey)(nil),
|
||||||
|
(*common.RatePlanTMobile)(nil),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
}
|
||||||
|
// Uncomment below to show generated SQL queries
|
||||||
|
// client.GetConn().AddQueryHook(db.SQLLogger{})
|
||||||
|
d.client = client
|
||||||
|
})
|
||||||
|
return d.client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetDBClient(client *db.DBClient) {
|
||||||
|
if d.client != nil {
|
||||||
|
d.client.Close()
|
||||||
|
}
|
||||||
|
d.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Close() {
|
||||||
|
if d.client == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.client.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetCarUpdates() q.CarUpdatesInterface {
|
||||||
|
d.onceCarUpdates.Do(func() {
|
||||||
|
if d.carupdates != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance := &q.CarUpdates{}
|
||||||
|
instance.SetClient(d.GetDBClient())
|
||||||
|
d.carupdates = instance
|
||||||
|
})
|
||||||
|
return d.carupdates
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetCarUpdates(carupdates q.CarUpdatesInterface) {
|
||||||
|
d.carupdates = carupdates
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetCars() q.CarsInterface {
|
||||||
|
d.onceCars.Do(func() {
|
||||||
|
if d.cars != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance := &q.Cars{}
|
||||||
|
instance.SetClient(d.GetDBClient())
|
||||||
|
d.cars = instance
|
||||||
|
})
|
||||||
|
return d.cars
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetCars(cars q.CarsInterface) {
|
||||||
|
d.cars = cars
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetCarVersionsLog() q.CarVersionsLogInterface {
|
||||||
|
d.onceCarVersionsLog.Do(func() {
|
||||||
|
if d.carVersionsLog != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance := &q.CarVersionsLog{}
|
||||||
|
instance.SetClient(d.GetDBClient())
|
||||||
|
d.carVersionsLog = instance
|
||||||
|
})
|
||||||
|
return d.carVersionsLog
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetCarVersionsLog(log q.CarVersionsLogInterface) {
|
||||||
|
d.carVersionsLog = log
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetFileKeys() q.FileKeysInterface {
|
||||||
|
d.onceFileKeys.Do(func() {
|
||||||
|
if d.filekeys != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance := &q.FileKeys{}
|
||||||
|
instance.SetClient(d.GetDBClient())
|
||||||
|
d.filekeys = instance
|
||||||
|
})
|
||||||
|
return d.filekeys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetFileKeys(filekeys q.FileKeysInterface) {
|
||||||
|
d.filekeys = filekeys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetUpdateManifests() q.UpdateManifestsInterface {
|
||||||
|
d.onceManifests.Do(func() {
|
||||||
|
if d.manifests != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance := q.NewUpdateManifest(nil)
|
||||||
|
instance.SetClient(d.GetDBClient())
|
||||||
|
d.manifests = instance
|
||||||
|
})
|
||||||
|
return d.manifests
|
||||||
|
}
|
||||||
|
func (d *DB) GetECU() q.ECUInterface {
|
||||||
|
|
||||||
|
d.onceEcu.Do(func() {
|
||||||
|
if d.ecu != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance := &q.ECU{}
|
||||||
|
instance.SetClient(d.GetDBClient())
|
||||||
|
d.ecu = instance
|
||||||
|
})
|
||||||
|
return d.ecu
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetECU(instance q.ECUInterface) {
|
||||||
|
d.ecu = instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetManifests(manifests q.UpdateManifestsInterface) {
|
||||||
|
d.manifests = manifests
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) ModifyUpdateStatus(id int, status string) (*common.CarUpdate, error) {
|
||||||
|
cu := d.GetCarUpdates()
|
||||||
|
updates, err := cu.SelectByID(int64(id))
|
||||||
|
if err != nil {
|
||||||
|
return updates, err
|
||||||
|
}
|
||||||
|
|
||||||
|
updates.Status = status
|
||||||
|
_, err = cu.UpdateStatus(updates)
|
||||||
|
|
||||||
|
return updates, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetECCKeys() q.EccKeysInterface {
|
||||||
|
d.onceEccKeys.Do(func() {
|
||||||
|
if d.eccKeys != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
eccKeys := &q.EccKeys{}
|
||||||
|
eccKeys.SetClient(d.GetDBClient())
|
||||||
|
d.eccKeys = eccKeys
|
||||||
|
})
|
||||||
|
return d.eccKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetECCKeys(eccKeys q.EccKeysInterface) {
|
||||||
|
d.eccKeys = eccKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetRatePlan() q.RatePlanInterface {
|
||||||
|
d.onceRatePlan.Do(func() {
|
||||||
|
if d.ratePlan != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance := &q.RatePlanTmobile{}
|
||||||
|
instance.SetClient(d.GetDBClient())
|
||||||
|
d.ratePlan = instance
|
||||||
|
})
|
||||||
|
return d.ratePlan
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetRatePlan(ratePlan q.RatePlanInterface) {
|
||||||
|
d.ratePlan = ratePlan
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetUpdateManifestSUMSVersions() q.SUMSVersionsInterface {
|
||||||
|
d.onceUpdateManifestSUMSVersions.Do(func() {
|
||||||
|
if d.updateManifestSUMSVersions != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance := &q.SUMSVersions{}
|
||||||
|
instance.SetClient(d.GetDBClient())
|
||||||
|
d.updateManifestSUMSVersions = instance
|
||||||
|
})
|
||||||
|
return d.updateManifestSUMSVersions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetUpdateManifestVersions(umv q.SUMSVersionsInterface) {
|
||||||
|
d.updateManifestSUMSVersions = umv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetCarConfigData() q.CarConfigDataInterface {
|
||||||
|
d.onceCarConfigData.Do(func() {
|
||||||
|
if d.carConfigData != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debug().Msg("Init CarConfigData instance")
|
||||||
|
carConfigData := &q.CarConfigData{}
|
||||||
|
carConfigData.SetClient(d.GetDBClient())
|
||||||
|
d.carConfigData = carConfigData
|
||||||
|
})
|
||||||
|
return d.carConfigData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetCarConfigData(carConfigData q.CarConfigDataInterface) {
|
||||||
|
d.carConfigData = carConfigData
|
||||||
|
}
|
||||||
20
services/attendant/services/dbc.go
Normal file
20
services/attendant/services/dbc.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/dbc"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/dbc/models"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor"
|
||||||
|
)
|
||||||
|
|
||||||
|
var model models.DBCVersionInterface
|
||||||
|
var collectionOnce sync.Once
|
||||||
|
|
||||||
|
// GetDBCCollection returns singleton instance of collection of DBCs
|
||||||
|
func GetDBC() *descriptor.Database {
|
||||||
|
collectionOnce.Do(func() {
|
||||||
|
model = dbc.NewFM29_FRSD390_DBC()
|
||||||
|
})
|
||||||
|
return model.GetDatabase()
|
||||||
|
}
|
||||||
24
services/attendant/services/dtc_cache.go
Normal file
24
services/attendant/services/dtc_cache.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/cache"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
MAX_KEY_CACHE int = envtool.GetEnvInt("MAX_KEY_CACHE", 10000)
|
||||||
|
carDtcsCache cache.CarDTCsCacheInterface
|
||||||
|
onceDTCCache sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetCarDtcCache() cache.CarDTCsCacheInterface {
|
||||||
|
onceDTCCache.Do(func() {
|
||||||
|
if carDtcsCache == nil {
|
||||||
|
carDtcsCache = cache.NewCarDTCsCache(MAX_KEY_CACHE)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return carDtcsCache
|
||||||
|
}
|
||||||
28
services/attendant/services/ecu_cache.go
Normal file
28
services/attendant/services/ecu_cache.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/cache"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
MAX_ECU_KEY_CACHE int = envtool.GetEnvInt("MAX_ECU_KEY_CACHE", 10000)
|
||||||
|
ring cache.RingMapInterface
|
||||||
|
onceECUCache sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetCarEcuCache() cache.RingMapInterface {
|
||||||
|
onceECUCache.Do(func() {
|
||||||
|
if ring == nil {
|
||||||
|
ring = cache.NewRingMap(MAX_ECU_KEY_CACHE)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return ring
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetCarEcuCache(value cache.RingMapInterface) {
|
||||||
|
ring = value
|
||||||
|
}
|
||||||
88
services/attendant/services/foa.go
Normal file
88
services/attendant/services/foa.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"slices"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
s "github.com/fiskerinc/cloud-services/pkg/common/carupdatestatus"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/foa"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/httpclient"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
|
||||||
|
)
|
||||||
|
|
||||||
|
var UPDATE_MANIFEST_IDS_TO_NOTIFY_FOA = []int64{816, 817, 818, 819, 820}
|
||||||
|
|
||||||
|
var (
|
||||||
|
foaService FoaServiceInterface
|
||||||
|
foaOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetFoaService() FoaServiceInterface {
|
||||||
|
foaOnce.Do(func() {
|
||||||
|
if foaService != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
foaService = NewFoaService()
|
||||||
|
})
|
||||||
|
|
||||||
|
return foaService
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetFoaService(foa FoaServiceInterface) {
|
||||||
|
foaService = foa
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFoaService() FoaServiceInterface {
|
||||||
|
return &FoaService{
|
||||||
|
foaURL: envtool.GetEnv("FOA_URL", "REPLACE_ME"),
|
||||||
|
foaAPIToken: envtool.GetEnv("FOA_API_KEY", "REPLACE_ME"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type FoaServiceInterface interface {
|
||||||
|
OtaUpdateStatus(vin string, carUpdate *common.CarUpdate, status *common.CarUpdateProgress) (*http.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FoaService struct {
|
||||||
|
foaURL string
|
||||||
|
foaAPIToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FoaService) OtaUpdateStatus(vin string, carUpdate *common.CarUpdate, status *common.CarUpdateProgress) (*http.Response, error) {
|
||||||
|
if !slices.Contains(UPDATE_MANIFEST_IDS_TO_NOTIFY_FOA, carUpdate.UpdateManifestID) {
|
||||||
|
// Nothing to send if the manifest is not one of the specified IDs
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var body interface{} = nil
|
||||||
|
|
||||||
|
switch status.Status {
|
||||||
|
case s.ManifestSucceeded:
|
||||||
|
body = foa.BuildOtaUpdateStatusSuccessRequest(vin, carUpdate.UpdateManifestID)
|
||||||
|
case s.ManifestError:
|
||||||
|
body = foa.BuildOtaUpdateStatusFailedRequest(vin, carUpdate.UpdateManifestID, status.Info)
|
||||||
|
case s.ManifestCanceled:
|
||||||
|
body = foa.BuildOtaUpdateStatusCanceledRequest(vin, carUpdate.UpdateManifestID, status.Info)
|
||||||
|
}
|
||||||
|
|
||||||
|
if body == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info().Msgf("Notifying FOA for %s of update %d status %s", vin, carUpdate.UpdateManifestID, status.Status)
|
||||||
|
|
||||||
|
urlString, err := url.JoinPath(f.foaURL, "ota/update_status")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
postHeader := http.Header{}
|
||||||
|
postHeader.Add("Authorization", "Bearer "+f.foaAPIToken)
|
||||||
|
postHeader.Add("Content-Type", "application/json")
|
||||||
|
|
||||||
|
return httpclient.Post(urlString, body, postHeader)
|
||||||
|
}
|
||||||
62
services/attendant/services/kafka.go
Normal file
62
services/attendant/services/kafka.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/kafka"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
const serviceName = "attendant"
|
||||||
|
const oldServiceName = "old-attendant"
|
||||||
|
|
||||||
|
var consumer, oldConsumer kafka.ConsumerInterface
|
||||||
|
var consumerOnce sync.Once
|
||||||
|
|
||||||
|
// GetKafkaConsumer returns singleton instance of kafka consumer
|
||||||
|
func GetKafkaConsumer() (kafka.ConsumerInterface, kafka.ConsumerInterface, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
consumerOnce.Do(func() {
|
||||||
|
consumer, err = kafka.NewConsumer(serviceName)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
}
|
||||||
|
oldConsumer, err = kafka.NewConsumer(oldServiceName)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumer, oldConsumer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var producer kafka.ProducerInterface
|
||||||
|
var producerOnce sync.Once
|
||||||
|
|
||||||
|
func GetKafkaProducer() (kafka.ProducerInterface, error) {
|
||||||
|
var err error
|
||||||
|
producerOnce.Do(func() {
|
||||||
|
if producer == nil {
|
||||||
|
var producerTemp kafka.ProducerInterface
|
||||||
|
producerTemp, err = kafka.NewProducer(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Send()
|
||||||
|
}
|
||||||
|
producer = producerTemp
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return producer, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetKafkaProducer(k kafka.ProducerInterface) {
|
||||||
|
producer = k
|
||||||
|
}
|
||||||
215
services/attendant/services/keep_awake.go
Normal file
215
services/attendant/services/keep_awake.go
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This can probably be moved into a better spot, but we are on a time limit
|
||||||
|
var ADD_TIME = time.Minute
|
||||||
|
|
||||||
|
type KeepAwake struct {
|
||||||
|
tick *time.Ticker
|
||||||
|
KAI KeepAwakeInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeepAwakeInterface interface {
|
||||||
|
CheckKeepAwakeMessages()
|
||||||
|
// addToKeepAwakeMessages(vin string) (err error)
|
||||||
|
RemoveKeepAwakeMessage(vin string) (err error)
|
||||||
|
// getKeepAwakeMessages() (vins []string, err error)
|
||||||
|
SendFirstKeepAwakeMessage(vin string) (err error)
|
||||||
|
// sendKeepAwakeMessage(vin string) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeepAwakeImplementation struct {
|
||||||
|
Cars map[string]time.Time // Map of car vins to the time it was last ran
|
||||||
|
oneTime sync.Once
|
||||||
|
mapLock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeepAwakeService() (ka *KeepAwake) {
|
||||||
|
ka = &KeepAwake{}
|
||||||
|
|
||||||
|
ka.tick = time.NewTicker(time.Minute)
|
||||||
|
ka.KAI = &KeepAwakeImplementation{}
|
||||||
|
|
||||||
|
// Start our timer
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ka.tick.C:
|
||||||
|
ka.KAI.CheckKeepAwakeMessages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ka *KeepAwake) SetService(inf KeepAwakeInterface) {
|
||||||
|
ka.KAI = inf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ka *KeepAwake) RemoveKeepAwakeMessage(vin string) (err error) {
|
||||||
|
return ka.KAI.RemoveKeepAwakeMessage(vin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ka *KeepAwake) SendFirstKeepAwakeMessage(vin string) (err error) {
|
||||||
|
return ka.KAI.SendFirstKeepAwakeMessage(vin)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to move this out of here, as this manifest sender is created every time a manifest is to be sent
|
||||||
|
// On a timeout, check if the keep awake messages need to be sent
|
||||||
|
func (k *KeepAwakeImplementation) CheckKeepAwakeMessages() {
|
||||||
|
k.oneTime.Do(func() {
|
||||||
|
k.Cars = make(map[string]time.Time)
|
||||||
|
})
|
||||||
|
|
||||||
|
k.mapLock.Lock()
|
||||||
|
vins, _ := k.getKeepAwakeMessages()
|
||||||
|
|
||||||
|
for _, vin := range vins {
|
||||||
|
k.sendKeepAwakeMessage(vin)
|
||||||
|
|
||||||
|
k.addToKeepAwakeMessages(vin)
|
||||||
|
}
|
||||||
|
k.mapLock.Unlock()
|
||||||
|
// Call this function again in some amount of time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KeepAwakeImplementation) addToKeepAwakeMessages(vin string) (err error) {
|
||||||
|
k.Cars[vin] = time.Now().Add(ADD_TIME)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KeepAwakeImplementation) RemoveKeepAwakeMessage(vin string) (err error) {
|
||||||
|
k.oneTime.Do(func() {
|
||||||
|
k.Cars = make(map[string]time.Time)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Remove from Redis
|
||||||
|
err = k.removeRedisCarEntry(vin)
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Msg("Unable to remove keeep awake message from redis")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info().Msgf("Ended keep awake messaging for %s", vin)
|
||||||
|
k.mapLock.Lock()
|
||||||
|
defer k.mapLock.Unlock()
|
||||||
|
_, ok := k.Cars[vin]
|
||||||
|
if !ok {
|
||||||
|
logger.Debug().Msgf("Attempted to end keep awake for %s, but not in list", vin)
|
||||||
|
}
|
||||||
|
delete(k.Cars, vin)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a list of vins that need sendKeepAwakeMessage
|
||||||
|
func (k *KeepAwakeImplementation) getKeepAwakeMessages() (vins []string, err error) {
|
||||||
|
for vin, t := range k.Cars {
|
||||||
|
// Parse out the time and check if its before the time it is now
|
||||||
|
if t.Before(time.Now()) {
|
||||||
|
// This needs processing
|
||||||
|
vins = append(vins, vin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KeepAwakeImplementation) SendFirstKeepAwakeMessage(vin string) (err error) {
|
||||||
|
k.oneTime.Do(func() {
|
||||||
|
k.Cars = make(map[string]time.Time)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Additionally add to the list of redis
|
||||||
|
err = k.addRedisCarEntry(vin)
|
||||||
|
logger.Err(err).Msg("")
|
||||||
|
|
||||||
|
logger.Info().Msgf("Started keep awake messaging for %s", vin)
|
||||||
|
k.mapLock.Lock()
|
||||||
|
defer k.mapLock.Unlock()
|
||||||
|
err = k.sendKeepAwakeMessage(vin)
|
||||||
|
k.addToKeepAwakeMessages(vin)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a special message to keep the tbox awake to download the update
|
||||||
|
// We want to continue to call this until the download is complete or if we fail
|
||||||
|
func (k *KeepAwakeImplementation) sendKeepAwakeMessage(vin string) (err error) {
|
||||||
|
client := RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
// check if in redis
|
||||||
|
found, err := k.checkRedisForCarEntry(vin, client)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// If we did not find the redis entry, then another service has removed it from the keep alive set
|
||||||
|
// Don't call k.Delete as it creates a lock on the map, and then we will be deadlocked
|
||||||
|
if !found {
|
||||||
|
delete(k.Cars, vin)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info().Msgf("sending keep awake message for %s", vin)
|
||||||
|
|
||||||
|
type Action struct {
|
||||||
|
Action string `json:"action"`
|
||||||
|
Timeout int32 `json:"timeout"`
|
||||||
|
}
|
||||||
|
logger.Debug().Msgf("Sending redis queue- %s, key- %s, hander- %s, data- %v", "attendant", vin, "can_network", Action{Action: "on"})
|
||||||
|
err = client.SafeQueueMessage(common.TRex.Key(vin), common.Message{
|
||||||
|
Handler: "can_network",
|
||||||
|
Data: Action{Action: "on", Timeout: 120},
|
||||||
|
})
|
||||||
|
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KeepAwakeImplementation) checkRedisForCarEntry(vin string, client redis.Client) (found bool, err error) {
|
||||||
|
line, err := client.Execute("SISMEMBER", "can_keep_awake", vin)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
check, ok := line.(int64)
|
||||||
|
if !ok {
|
||||||
|
err = fmt.Errorf("received wrong type from redis for the can_keep_awake for %s, %v, %s", vin, line, reflect.TypeOf(line))
|
||||||
|
}
|
||||||
|
return check > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KeepAwakeImplementation) removeRedisCarEntry(vin string) (err error) {
|
||||||
|
client := RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
_, err = client.Execute("SREM", "can_keep_awake", vin)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KeepAwakeImplementation) addRedisCarEntry(vin string) (err error) {
|
||||||
|
client := RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
_, err = client.Execute("SADD", "can_keep_awake", vin)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockKeepAwakeImplementation struct{}
|
||||||
|
|
||||||
|
func (k *MockKeepAwakeImplementation) CheckKeepAwakeMessages() {}
|
||||||
|
|
||||||
|
func (k *MockKeepAwakeImplementation) RemoveKeepAwakeMessage(vin string) (err error) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *MockKeepAwakeImplementation) SendFirstKeepAwakeMessage(vin string) (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
68
services/attendant/services/keep_awake_test.go
Normal file
68
services/attendant/services/keep_awake_test.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestKeepAwake(t *testing.T){
|
||||||
|
t.Skip()
|
||||||
|
ADD_TIME = 0
|
||||||
|
ka := KeepAwake{}
|
||||||
|
impl := &KeepAwakeImplementation{}
|
||||||
|
ka.SetService(impl)
|
||||||
|
|
||||||
|
testVin := "JH4KA7680RC01"
|
||||||
|
// Check that trying to delete a non-existing set is okay
|
||||||
|
err := impl.RemoveKeepAwakeMessage(testVin)
|
||||||
|
if err != nil{
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
client := RedisClientPool().GetFromPool()
|
||||||
|
found, err := impl.checkRedisForCarEntry(testVin, client)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if found{
|
||||||
|
t.Error("did find expected vin when not supposed to")
|
||||||
|
}
|
||||||
|
client.Close()
|
||||||
|
|
||||||
|
// Check we can successfully add
|
||||||
|
err = impl.SendFirstKeepAwakeMessage(testVin)
|
||||||
|
if err != nil{
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client = RedisClientPool().GetFromPool()
|
||||||
|
found, err = impl.checkRedisForCarEntry(testVin, client)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if !found{
|
||||||
|
t.Error("did not find expected vin")
|
||||||
|
}
|
||||||
|
client.Close()
|
||||||
|
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
vins, err := impl.getKeepAwakeMessages()
|
||||||
|
if err != nil{
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if len(vins) != 1{
|
||||||
|
t.Error("len of vins wrong ", len(vins))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = impl.RemoveKeepAwakeMessage(testVin)
|
||||||
|
if err != nil{
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vins, err = impl.getKeepAwakeMessages()
|
||||||
|
if err != nil{
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if len(vins) != 0{
|
||||||
|
t.Error("len of vins wrong ", len(vins))
|
||||||
|
}
|
||||||
|
}
|
||||||
32
services/attendant/services/mongo.go
Normal file
32
services/attendant/services/mongo.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
clientOnce sync.Once
|
||||||
|
client mongo.Client
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetMongoClient returns singleton instance of mongo client
|
||||||
|
func GetMongoClient() (mongo.Client, error) {
|
||||||
|
var err error
|
||||||
|
clientOnce.Do(func() {
|
||||||
|
client, err = initMongoClient()
|
||||||
|
})
|
||||||
|
|
||||||
|
return client, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func initMongoClient() (mongo.Client, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if client == nil {
|
||||||
|
client, err = mongo.NewClient(mongo.StandardDB)
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, err
|
||||||
|
}
|
||||||
28
services/attendant/services/redis.go
Normal file
28
services/attendant/services/redis.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
clientPoolOnce sync.Once
|
||||||
|
clientPool redis.ClientPoolInterface
|
||||||
|
)
|
||||||
|
|
||||||
|
func RedisClientPool() redis.ClientPoolInterface {
|
||||||
|
clientPoolOnce.Do(func() {
|
||||||
|
if clientPool != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clientPool = redis.NewClientPool()
|
||||||
|
})
|
||||||
|
|
||||||
|
return clientPool
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetRedisClientPool(cp redis.ClientPoolInterface) {
|
||||||
|
clientPool = cp
|
||||||
|
}
|
||||||
28
services/attendant/services/sap.go
Normal file
28
services/attendant/services/sap.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
sapService vconfig.SAPServiceInterface
|
||||||
|
sapOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetSapService() vconfig.SAPServiceInterface {
|
||||||
|
sapOnce.Do(func() {
|
||||||
|
if sapService != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sapService = vconfig.NewSAPService()
|
||||||
|
})
|
||||||
|
|
||||||
|
return sapService
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSapService is supposed t be used for testing.
|
||||||
|
func SetSapService(sap vconfig.SAPServiceInterface) {
|
||||||
|
sapService = sap
|
||||||
|
}
|
||||||
43
services/attendant/services/sms.go
Normal file
43
services/attendant/services/sms.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
)
|
||||||
|
|
||||||
|
var smsClient sms.SMSServiceClient
|
||||||
|
var smsClientOnce sync.Once
|
||||||
|
|
||||||
|
func newSmsClient() {
|
||||||
|
logger.Info().Msg("Init SMS client")
|
||||||
|
target := fmt.Sprintf("%s:%s",
|
||||||
|
envtool.GetEnv("SMS_HOST", "sms"),
|
||||||
|
envtool.GetEnv("SMS_PORT", "8077"))
|
||||||
|
c, err := grpc.Dial(target, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
smsClient = sms.NewSMSServiceClient(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSMSClient() sms.SMSServiceClient {
|
||||||
|
smsClientOnce.Do(func() {
|
||||||
|
if smsClient != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newSmsClient()
|
||||||
|
})
|
||||||
|
|
||||||
|
return smsClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetSmsClient(c sms.SMSServiceClient) {
|
||||||
|
smsClient = c
|
||||||
|
}
|
||||||
25
services/depot/Dockerfile
Normal file
25
services/depot/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
ARG BASE_IMAGE=cloud_base_go
|
||||||
|
FROM ${BASE_IMAGE} as builder-go
|
||||||
|
|
||||||
|
WORKDIR /build/depot
|
||||||
|
COPY ./depot/go.mod ./depot/go.sum ./
|
||||||
|
RUN go mod edit -replace fiskerinc.com/modules=../fiskerinc.com/modules \
|
||||||
|
&& go mod download
|
||||||
|
|
||||||
|
COPY ./depot ./
|
||||||
|
RUN go mod edit -replace fiskerinc.com/modules=../fiskerinc.com/modules \
|
||||||
|
&& go build -tags musl
|
||||||
|
|
||||||
|
|
||||||
|
FROM alpine:3.17
|
||||||
|
|
||||||
|
RUN apk add --no-cache librdkafka --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community \
|
||||||
|
&& apk add --no-cache ca-certificates
|
||||||
|
|
||||||
|
COPY ./modules_go/logger/log_config .
|
||||||
|
COPY --from=builder-go /build/depot/depot .
|
||||||
|
|
||||||
|
ENV LOG_CONFIG=log_config
|
||||||
|
EXPOSE 8077
|
||||||
|
|
||||||
|
CMD ./depot
|
||||||
81
services/depot/controllers/health_check.go
Normal file
81
services/depot/controllers/health_check.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/health"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mismatchTypeError = errors.New("mismatch type error")
|
||||||
|
|
||||||
|
func HealthCheck() {
|
||||||
|
redis := health.NewRedisHealth(services.RedisClientPool())
|
||||||
|
server := health.HealthCheckServer{}
|
||||||
|
err := server.Serve([]health.Config{
|
||||||
|
{
|
||||||
|
Name: "db",
|
||||||
|
Check: health.NewPostgresCheck(services.GetDB().GetDBClient().GetConn()),
|
||||||
|
Timeout: time.Second * 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "redis",
|
||||||
|
Check: redis.Check,
|
||||||
|
Timeout: time.Second * 1,
|
||||||
|
Info: redis.RedisStatus,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "mongodb",
|
||||||
|
Check: health.NewMongoDBCheck(getMongoClient),
|
||||||
|
Timeout: time.Second * 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "kafka",
|
||||||
|
Check: health.NewKafkaMultiCheck(getKafkaConsumer),
|
||||||
|
Timeout: time.Second * 1,
|
||||||
|
Vital: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMongoClient() (health.MongoConnCheckInterface, error) {
|
||||||
|
client, err := services.GetMongoClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, ok := client.(health.MongoConnCheckInterface)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.WithStack(mismatchTypeError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getKafkaConsumer() ([]health.KafkaConnCheckInterface, error) {
|
||||||
|
var connections []health.KafkaConnCheckInterface
|
||||||
|
client, oldClient, err := services.GetKafkaConsumer()
|
||||||
|
if err != nil {
|
||||||
|
return connections, err
|
||||||
|
}
|
||||||
|
conn, ok := client.(health.KafkaConnCheckInterface)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.WithStack(mismatchTypeError)
|
||||||
|
}
|
||||||
|
connections = append(connections, conn)
|
||||||
|
|
||||||
|
oldConn, ok := oldClient.(health.KafkaConnCheckInterface)
|
||||||
|
if !ok {
|
||||||
|
return connections, errors.WithStack(mismatchTypeError)
|
||||||
|
}
|
||||||
|
connections = append(connections, oldConn)
|
||||||
|
|
||||||
|
return connections, nil
|
||||||
|
}
|
||||||
109
services/depot/go.mod
Normal file
109
services/depot/go.mod
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
module github.com/fiskerinc/cloud-services/services/depot
|
||||||
|
|
||||||
|
go 1.25
|
||||||
|
|
||||||
|
toolchain go1.25.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fiskerinc/cloud-services/pkg v0.0.0-00010101000000-000000000000
|
||||||
|
github.com/fiskerinc/cloud-services/pkg/can-go v0.0.0-00010101000000-000000000000
|
||||||
|
github.com/go-pg/pg/v10 v10.11.1
|
||||||
|
github.com/gomodule/redigo v1.8.9
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/sony/gobreaker v0.5.0
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0
|
||||||
|
google.golang.org/grpc v1.67.3
|
||||||
|
google.golang.org/protobuf v1.36.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/DataDog/appsec-internal-go v1.4.0 // indirect
|
||||||
|
github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 // indirect
|
||||||
|
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1 // indirect
|
||||||
|
github.com/DataDog/datadog-go/v5 v5.3.0 // indirect
|
||||||
|
github.com/DataDog/go-libddwaf/v2 v2.2.3 // indirect
|
||||||
|
github.com/DataDog/go-tuf v1.0.2-0.5.2 // indirect
|
||||||
|
github.com/DataDog/sketches-go v1.4.2 // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||||
|
github.com/ReneKroon/ttlcache/v2 v2.11.0 // indirect
|
||||||
|
github.com/albenik/bcd v0.0.0-20170831201648-635201416bc7 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/confluentinc/confluent-kafka-go/v2 v2.3.0 // indirect
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/ebitengine/purego v0.5.2 // indirect
|
||||||
|
github.com/elliotchance/orderedmap/v2 v2.2.0 // indirect
|
||||||
|
github.com/fiskerinc/cloud-services/pkg/can-go v0.0.0-00010101000000-000000000000 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
|
github.com/go-pg/zerochecker v0.2.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.15.1 // indirect
|
||||||
|
github.com/golang/mock v1.7.0-rc.1 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/schema v1.2.0 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
|
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||||
|
github.com/jinzhu/copier v0.3.5 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.2 // indirect
|
||||||
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||||
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
|
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||||
|
github.com/onsi/gomega v1.25.0 // indirect
|
||||||
|
github.com/outcaste-io/ristretto v0.2.3 // indirect
|
||||||
|
github.com/philhofer/fwd v1.1.2 // indirect
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||||
|
github.com/redis/go-redis/v9 v9.5.1 // indirect
|
||||||
|
github.com/rs/zerolog v1.29.1 // indirect
|
||||||
|
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
|
||||||
|
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f // indirect
|
||||||
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
|
github.com/tinylib/msgp v1.1.8 // indirect
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
|
github.com/twmb/franz-go v1.20.6 // indirect
|
||||||
|
github.com/twmb/franz-go/pkg/kadm v1.17.2 // indirect
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.12.0 // indirect
|
||||||
|
github.com/vmihailenco/bufpool v0.1.11 // indirect
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||||
|
github.com/vmihailenco/tagparser v0.1.2 // indirect
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
|
github.com/xdg-go/scram v1.1.2 // indirect
|
||||||
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
|
||||||
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
|
go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
||||||
|
golang.org/x/crypto v0.45.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||||
|
golang.org/x/mod v0.29.0 // indirect
|
||||||
|
golang.org/x/net v0.47.0 // indirect
|
||||||
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
|
golang.org/x/text v0.31.0 // indirect
|
||||||
|
golang.org/x/time v0.8.0 // indirect
|
||||||
|
golang.org/x/tools v0.38.0 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
|
||||||
|
gopkg.in/DataDog/dd-trace-go.v1 v1.60.1 // indirect
|
||||||
|
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect
|
||||||
|
mellium.im/sasl v0.3.1 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace (
|
||||||
|
github.com/fiskerinc/cloud-services/pkg => ../../pkg
|
||||||
|
github.com/fiskerinc/cloud-services/pkg/can-go => ../../pkg/can-go
|
||||||
|
)
|
||||||
481
services/depot/go.sum
Normal file
481
services/depot/go.sum
Normal file
@@ -0,0 +1,481 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/DataDog/appsec-internal-go v1.4.0 h1:KFI8ElxkJOgpw+cUm9TXK/jh5EZvRaWM07sXlxGg9Ck=
|
||||||
|
github.com/DataDog/appsec-internal-go v1.4.0/go.mod h1:ONW8aV6R7Thgb4g0bB9ZQCm+oRgyz5eWiW7XoQ19wIc=
|
||||||
|
github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 h1:bUMSNsw1iofWiju9yc1f+kBd33E3hMJtq9GuU602Iy8=
|
||||||
|
github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0/go.mod h1:HzySONXnAgSmIQfL6gOv9hWprKJkx8CicuXuUbmgWfo=
|
||||||
|
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1 h1:5nE6N3JSs2IG3xzMthNFhXfOaXlrsdgqmJ73lndFf8c=
|
||||||
|
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1/go.mod h1:Vc+snp0Bey4MrrJyiV2tVxxJb6BmLomPvN1RgAvjGaQ=
|
||||||
|
github.com/DataDog/datadog-go/v5 v5.3.0 h1:2q2qjFOb3RwAZNU+ez27ZVDwErJv5/VpbBPprz7Z+s8=
|
||||||
|
github.com/DataDog/datadog-go/v5 v5.3.0/go.mod h1:XRDJk1pTc00gm+ZDiBKsjh7oOOtJfYfglVCmFb8C2+Q=
|
||||||
|
github.com/DataDog/go-libddwaf/v2 v2.2.3 h1:LpKE8AYhVrEhlmlw6FGD41udtDf7zW/aMdLNbCXpegQ=
|
||||||
|
github.com/DataDog/go-libddwaf/v2 v2.2.3/go.mod h1:8nX0SYJMB62+fbwYmx5J7zuCGEjiC/RxAo3+AuYJuFE=
|
||||||
|
github.com/DataDog/go-tuf v1.0.2-0.5.2 h1:EeZr937eKAWPxJ26IykAdWA4A0jQXJgkhUjqEI/w7+I=
|
||||||
|
github.com/DataDog/go-tuf v1.0.2-0.5.2/go.mod h1:zBcq6f654iVqmkk8n2Cx81E1JnNTMOAx1UEO/wZR+P0=
|
||||||
|
github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/IGQh4=
|
||||||
|
github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM=
|
||||||
|
github.com/DataDog/sketches-go v1.4.2 h1:gppNudE9d19cQ98RYABOetxIhpTCl4m7CnbRZjvVA/o=
|
||||||
|
github.com/DataDog/sketches-go v1.4.2/go.mod h1:xJIXldczJyyjnbDop7ZZcLxJdV3+7Kra7H1KMgpgkLk=
|
||||||
|
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||||
|
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||||
|
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||||
|
github.com/ReneKroon/ttlcache/v2 v2.11.0 h1:OvlcYFYi941SBN3v9dsDcC2N8vRxyHcCmJb3Vl4QMoM=
|
||||||
|
github.com/ReneKroon/ttlcache/v2 v2.11.0/go.mod h1:mBxvsNY+BT8qLLd6CuAJubbKo6r0jh3nb5et22bbfGY=
|
||||||
|
github.com/albenik/bcd v0.0.0-20170831201648-635201416bc7 h1:m3Ayfs5OcAlIMEdLIQKubBsVLGee4YMUr14+d1256WE=
|
||||||
|
github.com/albenik/bcd v0.0.0-20170831201648-635201416bc7/go.mod h1:QIAMbrwsnQZ2ES3G26RubSrDB5SPyzsp9Hts5NJdTrI=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
|
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/confluentinc/confluent-kafka-go/v2 v2.3.0 h1:icCHutJouWlQREayFwCc7lxDAhws08td+W3/gdqgZts=
|
||||||
|
github.com/confluentinc/confluent-kafka-go/v2 v2.3.0/go.mod h1:/VTy8iEpe6mD9pkCH5BhijlUl8ulUXymKv1Qig5Rgb8=
|
||||||
|
github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg=
|
||||||
|
github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||||
|
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||||
|
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
|
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
|
||||||
|
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
|
github.com/docker/docker v23.0.4+incompatible h1:Kd3Bh9V/rO+XpTP/BLqM+gx8z7+Yb0AA2Ibj+nNo4ek=
|
||||||
|
github.com/docker/docker v23.0.4+incompatible/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.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||||
|
github.com/ebitengine/purego v0.5.2 h1:r2MQEtkGzZ4LRtFZVAg5bjYKnUbxxloaeuGxH0t7qfs=
|
||||||
|
github.com/ebitengine/purego v0.5.2/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||||
|
github.com/elliotchance/orderedmap/v2 v2.2.0 h1:7/2iwO98kYT4XkOjA9mBEIwvi4KpGB4cyHeOFOnj4Vk=
|
||||||
|
github.com/elliotchance/orderedmap/v2 v2.2.0/go.mod h1:85lZyVbpGaGvHvnKa7Qhx7zncAdBIBq6u56Hb1PRU5Q=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
|
github.com/go-pg/pg/v10 v10.11.1 h1:vYwbFpqoMpTDphnzIPshPPepdy3VpzD8qo29OFKp4vo=
|
||||||
|
github.com/go-pg/pg/v10 v10.11.1/go.mod h1:ExJWndhDNNftBdw1Ow83xqpSf4WMSJK8urmXD5VXS1I=
|
||||||
|
github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU=
|
||||||
|
github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.15.1 h1:BSe8uhN+xQ4r5guV/ywQI4gO59C2raYcGffYWZEjZzM=
|
||||||
|
github.com/go-playground/validator/v10 v10.15.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
|
github.com/go-redis/redismock/v9 v9.2.0 h1:ZrMYQeKPECZPjOj5u9eyOjg8Nnb0BS9lkVIZ6IpsKLw=
|
||||||
|
github.com/go-redis/redismock/v9 v9.2.0/go.mod h1:18KHfGDK4Y6c2R0H38EUGWAdc7ZQS9gfYxc94k7rWT0=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
|
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
|
||||||
|
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
|
||||||
|
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo=
|
||||||
|
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
|
||||||
|
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
|
||||||
|
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||||
|
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
|
||||||
|
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
|
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||||
|
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||||
|
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||||
|
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||||
|
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||||
|
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo=
|
||||||
|
github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||||
|
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||||
|
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||||
|
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
|
||||||
|
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||||
|
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||||
|
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||||
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
|
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||||
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
|
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||||
|
github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
|
||||||
|
github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
|
||||||
|
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/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8=
|
||||||
|
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
|
||||||
|
github.com/opencontainers/runc v1.1.6 h1:XbhB8IfG/EsnhNvZtNdLB0GBw92GYEFvKlhaJk9jUgA=
|
||||||
|
github.com/opencontainers/runc v1.1.6/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50=
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||||
|
github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0=
|
||||||
|
github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac=
|
||||||
|
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
|
||||||
|
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
|
||||||
|
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||||
|
github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052 h1:Qp27Idfgi6ACvFQat5+VJvlYToylpM/hcyLBI3WaKPA=
|
||||||
|
github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052/go.mod h1:uvX/8buq8uVeiZiFht+0lqSLBHF+uGV8BrTv8W/SIwk=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
|
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
|
github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
|
||||||
|
github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
|
||||||
|
github.com/secure-systems-lab/go-securesystemslib v0.7.0 h1:OwvJ5jQf9LnIAS83waAjPbcMsODrTQUpJ02eNLUoxBg=
|
||||||
|
github.com/secure-systems-lab/go-securesystemslib v0.7.0/go.mod h1:/2gYnlnHVQ6xeGtfIqFy7Do03K4cdCY0A/GlJLDKLHI=
|
||||||
|
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f h1:1R9KdKjCNSd7F8iGTxIpoID9prlYH8nuNYKt0XvweHA=
|
||||||
|
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f/go.mod h1:vQhwQ4meQEDfahT5kd61wLAF5AAeh5ZPLVI4JJ/tYo8=
|
||||||
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
|
||||||
|
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/testcontainers/testcontainers-go v0.14.0 h1:h0D5GaYG9mhOWr2qHdEKDXpkce/VlvaYOCzTRi6UBi8=
|
||||||
|
github.com/testcontainers/testcontainers-go v0.14.0/go.mod h1:hSRGJ1G8Q5Bw2gXgPulJOLlEBaYJHeBSOkQM5JLG+JQ=
|
||||||
|
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
|
||||||
|
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||||
|
github.com/twmb/franz-go v1.20.6 h1:TpQTt4QcixJ1cHEmQGPOERvTzo99s8jAutmS7rbSD6w=
|
||||||
|
github.com/twmb/franz-go v1.20.6/go.mod h1:u+FzH2sInp7b9HNVv2cZN8AxdXy6y/AQ1Bkptu4c0FM=
|
||||||
|
github.com/twmb/franz-go/pkg/kadm v1.17.2 h1:g5f1sAxnTkYC6G96pV5u715HWhxd66hWaDZUAQ8xHY8=
|
||||||
|
github.com/twmb/franz-go/pkg/kadm v1.17.2/go.mod h1:ST55zUB+sUS+0y+GcKY/Tf1XxgVilaFpB9I19UubLmU=
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.12.0 h1:CbatD7ers1KzDNgJqPbKOq0Bz/WLBdsTH75wgzeVaPc=
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.12.0/go.mod h1:+DPt4NC8RmI6hqb8G09+3giKObE6uD2Eya6CfqBpeJY=
|
||||||
|
github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94=
|
||||||
|
github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||||
|
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
|
||||||
|
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||||
|
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
|
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
|
||||||
|
go4.org/intern v0.0.0-20230525184215-6c62f75575cb h1:ae7kzL5Cfdmcecbh22ll7lYP3iuUdnfnhiPcSaDgH/8=
|
||||||
|
go4.org/intern v0.0.0-20230525184215-6c62f75575cb/go.mod h1:Ycrt6raEcnF5FTsLiLKkhBTO6DPX3RCUCUVnks3gFJU=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 h1:lGdhQUN/cnWdSH3291CUuxSEqc+AsGTiDxPP3r2J0l4=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||||
|
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||||
|
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||||
|
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
|
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
|
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||||
|
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||||
|
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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||||
|
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||||
|
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||||
|
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||||
|
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||||
|
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||||
|
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
|
||||||
|
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.67.3 h1:OgPcDAFKHnH8X3O4WcO4XUc8GRDeKsKReqbQtiCj7N8=
|
||||||
|
google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||||
|
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
|
gopkg.in/DataDog/dd-trace-go.v1 v1.60.1 h1:Sqkq62MxQW/RD+sgZsQuUdHWHyXI4JS5x0lxlxrv2Hk=
|
||||||
|
gopkg.in/DataDog/dd-trace-go.v1 v1.60.1/go.mod h1:6aArYrAHjnuaofJ3lKuSRQbhrBx1LcSpiEYCIScJE5Y=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||||
|
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||||
|
honnef.co/go/gotraceui v0.2.0 h1:dmNsfQ9Vl3GwbiVD7Z8d/osC6WtGGrasyrC2suc4ZIQ=
|
||||||
|
honnef.co/go/gotraceui v0.2.0/go.mod h1:qHo4/W75cA3bX0QQoSvDjbJa4R8mAyyFjbWAj63XElc=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a h1:1XCVEdxrvL6c0TGOhecLuB7U9zYNdxZEjvOqJreKZiM=
|
||||||
|
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a/go.mod h1:e83i32mAQOW1LAqEIweALsuK2Uw4mhQadA5r7b0Wobo=
|
||||||
|
mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo=
|
||||||
|
mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
|
||||||
118
services/depot/handlers/common.go
Normal file
118
services/depot/handlers/common.go
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
fv "github.com/fiskerinc/cloud-services/pkg/flashpackversion"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// get vehicle settings
|
||||||
|
func getSettings(db *services.DB, vin string) ([]common.CarSetting, error) {
|
||||||
|
settings, err := db.GetCars().GetVehicleSpecificSettings(&common.CarToDriver{
|
||||||
|
VIN: vin,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
osVersionSetting, err := getOSVersion(vin)
|
||||||
|
if err == nil && osVersionSetting != nil {
|
||||||
|
settings = append(settings, *osVersionSetting)
|
||||||
|
} else {
|
||||||
|
logger.Error().Msgf("failed to get os version %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
flashpackNumber, err := getFlashpackNumber(vin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
if flashpackNumber != nil {
|
||||||
|
settings = append(settings, *flashpackNumber)
|
||||||
|
} else {
|
||||||
|
logger.Warn().Msgf("no flashpack number yet for vin %s because not enough ECUs have been updated", vin)
|
||||||
|
}
|
||||||
|
|
||||||
|
certNeedsRenewal, err := services.GetCertService().CheckCertificateNeedsRenewal(vin, common.CertICC)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Msgf("failed to check for certificate renewal %v", err)
|
||||||
|
} else if certNeedsRenewal {
|
||||||
|
logger.Debug().Msg("ICC cert for vin " + vin + " is out of date")
|
||||||
|
|
||||||
|
settings = append(settings, common.CarSetting{
|
||||||
|
Name: "certificates",
|
||||||
|
Value: "expired",
|
||||||
|
Type: "string",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSettings(settings)
|
||||||
|
return settings, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the OS version given a vin
|
||||||
|
func getOSVersion(vin string) (*common.CarSetting, error) {
|
||||||
|
sumsVersion, err := getSUMS(vin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sumsDB := services.GetDB().GetUpdateManifestSUMSVersions()
|
||||||
|
sums, err := sumsDB.Select(sumsVersion)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Msgf("can not load SUMS, %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := common.CarSetting{
|
||||||
|
Name: "os_version",
|
||||||
|
Value: sums.OSVersion,
|
||||||
|
Type: "string",
|
||||||
|
}
|
||||||
|
cs.CreatedAt = sums.CreatedAt
|
||||||
|
cs.UpdatedAt = sums.UpdatedAt
|
||||||
|
|
||||||
|
return &cs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the SUMS version from update manifest for a given vin
|
||||||
|
func getSUMS(vin string) (sumsVersion string, err error) {
|
||||||
|
carsDB := services.GetDB().GetCars()
|
||||||
|
car, err := carsDB.SelectByVIN(vin)
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Msgf("failed in getSUMS(vin string) for car %s to fetch car", vin)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return car.SUMSVersion, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the flash pack number
|
||||||
|
func getFlashpackNumber(vin string) (*common.CarSetting, error) {
|
||||||
|
cars := services.GetDB().GetCars()
|
||||||
|
|
||||||
|
car, err := cars.SelectByVIN(vin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
flashpackNumber, err := fv.FindCurrentFlashpackVersionForCar(cars, *car)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if flashpackNumber != "" {
|
||||||
|
cs := &common.CarSetting{
|
||||||
|
Name: "flashpack_number",
|
||||||
|
Value: flashpackNumber,
|
||||||
|
Type: "string",
|
||||||
|
}
|
||||||
|
|
||||||
|
return cs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
36
services/depot/handlers/hmi_del.go
Normal file
36
services/depot/handlers/hmi_del.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HMIDel(db *services.DB, id string) error {
|
||||||
|
client := services.RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
logger.Info().Msgf("Remove HMI session in Redis for %s", id)
|
||||||
|
|
||||||
|
err := removeHMISession(client, id)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Str("id", id).Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = removeHMISessionID(client, id)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Str("id", id).Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeHMISession(client redis.Client, id string) error {
|
||||||
|
_, err := client.Execute("SREM", redis.HMISessionsKey(), id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeHMISessionID(client redis.Client, id string) error {
|
||||||
|
return client.Delete(redis.HMISessionKey(id))
|
||||||
|
}
|
||||||
20
services/depot/handlers/hmi_del_test.go
Normal file
20
services/depot/handlers/hmi_del_test.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/handlers"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/testhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHMIDel(t *testing.T) {
|
||||||
|
setupRedisMock()
|
||||||
|
setupDBMock()
|
||||||
|
|
||||||
|
id := "FISKER123"
|
||||||
|
err := handlers.HMIDel(mockDB, id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, "TestHMIDel", nil, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
99
services/depot/handlers/hmi_init.go
Normal file
99
services/depot/handlers/hmi_init.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HMIInit(db *services.DB, id string, data []byte) error {
|
||||||
|
fmt.Printf("HMIInit()")
|
||||||
|
client := services.RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
var sessionPayload common.HMISessionData
|
||||||
|
err := json.Unmarshal(data, &sessionPayload)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info().Msgf("Initialize HMI session in Redis for %s with ID %s", id, sessionPayload.SessionID)
|
||||||
|
err = addHMISession(client, id)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Str("id", id).Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = addHMISessionID(client, id, sessionPayload.SessionID)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Str("id", id).Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
if sessionPayload.Salt != "" {
|
||||||
|
err = addHMISaltID(client, id, sessionPayload.Salt)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Str("id", id).Err(err).Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sendSettingsHMI(client, db, id)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Str("id", id).Err(err).Send()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addHMISession(client redis.Client, id string) error {
|
||||||
|
_, err := client.Execute("SADD", redis.HMISessionsKey(), id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func addHMISessionID(client redis.Client, id string, sessionID string) error {
|
||||||
|
_, err := client.Execute("SET", redis.HMISessionKey(id), sessionID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func addHMISaltID(client redis.Client, id string, salt string) error {
|
||||||
|
_, err := client.Execute("SET", redis.HMISaltKey(id), salt)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendSettingsHMI(client redis.Client, db *services.DB, vin string) error {
|
||||||
|
|
||||||
|
settings, err := getSettings(db, vin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings) == 0 {
|
||||||
|
logger.Info().Msgf("no settings to send for car %s", vin)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.SafePublishMessage(common.HMI.Key(vin),
|
||||||
|
common.Message{
|
||||||
|
Handler: "car_settings",
|
||||||
|
Data: settings,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearSettings(settings []common.CarSetting) {
|
||||||
|
cTime := time.Now()
|
||||||
|
for i, s := range settings {
|
||||||
|
s.CreatedAt = nil
|
||||||
|
if s.UpdatedAt == nil {
|
||||||
|
s.UpdatedAt = &cTime
|
||||||
|
}
|
||||||
|
settings[i] = s
|
||||||
|
}
|
||||||
|
}
|
||||||
30
services/depot/handlers/hmi_init_test.go
Normal file
30
services/depot/handlers/hmi_init_test.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/handlers"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/testhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHMIInit(t *testing.T) {
|
||||||
|
setupRedisMock()
|
||||||
|
setupDBMock()
|
||||||
|
|
||||||
|
id := "FISKER123"
|
||||||
|
sessionData := common.HMISessionData{
|
||||||
|
SessionID: "XXXXX",
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(sessionData)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, "TestHMIInit", nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handlers.HMIInit(mockDB, id, data)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, "TestHMIInit", nil, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
25
services/depot/handlers/mobile_del.go
Normal file
25
services/depot/handlers/mobile_del.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MobileDel(db *services.DB, id string) error {
|
||||||
|
client := services.RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
err := removeMobileSession(client, id)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Str("id", id).Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeMobileSession(client redis.Client, id string) error {
|
||||||
|
_, err := client.Execute("SREM", redis.MobileSessionsKey(), id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
20
services/depot/handlers/mobile_del_test.go
Normal file
20
services/depot/handlers/mobile_del_test.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/handlers"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/testhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMobileDel(t *testing.T) {
|
||||||
|
setupRedisMock()
|
||||||
|
setupDBMock()
|
||||||
|
|
||||||
|
id := "VALID-COGNITO-ID-1"
|
||||||
|
err := handlers.MobileDel(mockDB, id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, "TestMobileDel", nil, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
60
services/depot/handlers/mobile_init.go
Normal file
60
services/depot/handlers/mobile_init.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MobileInit(db *services.DB, id string) error {
|
||||||
|
client := services.RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
err := addMobileSession(client, id)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Str("id", id).Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
carDrivers, err := db.GetCars().GetCarsForDriver(id)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Msgf("can not load cars for driver %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, carDriver := range carDrivers {
|
||||||
|
sendSettingsMobile(client, db, carDriver.VIN, id)
|
||||||
|
// If we fail to send the digital twin, it is not a major problem
|
||||||
|
sendFullDigitalTwinToMobile(carDriver.VIN, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addMobileSession(client redis.Client, id string) error {
|
||||||
|
_, err := client.Execute("SADD", redis.MobileSessionsKey(), id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendSettingsMobile(client redis.Client, db *services.DB, vin, driverID string) error {
|
||||||
|
settings, err := getSettings(db, vin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.SafePublishMessage(common.Mobile.Key(driverID),
|
||||||
|
common.Message{
|
||||||
|
Handler: "car_settings",
|
||||||
|
Data: settings,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendFullDigitalTwinToMobile(vin string, driverID string) (err error) {
|
||||||
|
err = services.GetSendDigitalTwin().SendToDriver(vin, driverID)
|
||||||
|
if err != nil {
|
||||||
|
logger.Err(err).Str("VIN", vin).Str("Driver ID", driverID).Msg("Failed to Send Full Digital Twin to Mobile Connection on INIT")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
20
services/depot/handlers/mobile_init_test.go
Normal file
20
services/depot/handlers/mobile_init_test.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/handlers"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/testhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMobileInit(t *testing.T) {
|
||||||
|
setupRedisMock()
|
||||||
|
setupDBMock()
|
||||||
|
|
||||||
|
id := "VALID-COGNITO-ID-1"
|
||||||
|
err := handlers.MobileInit(mockDB, id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, "TestMobileInit", nil, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
51
services/depot/handlers/mock_test.go
Normal file
51
services/depot/handlers/mock_test.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/services"
|
||||||
|
|
||||||
|
"github.com/go-pg/pg/v10/orm"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis/tester"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mockRedis *redis.Connection
|
||||||
|
var mockDB *services.DB
|
||||||
|
|
||||||
|
func setupRedisMock() {
|
||||||
|
mockRedis := tester.NewRedisMock()
|
||||||
|
services.SetRedisClientPool(tester.NewMockClientPool(mockRedis))
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupDBMock() {
|
||||||
|
db := services.DB{}
|
||||||
|
db.SetCars(&mocks.MockCars{
|
||||||
|
SelectResponse: &common.Car{VIN: "FISKER123", ICCID: "1111111111111111111F"},
|
||||||
|
SelectCarSettings: []common.CarSetting{},
|
||||||
|
SelectCarECUs: []common.CarECU{
|
||||||
|
{
|
||||||
|
VIN: "FISKER123",
|
||||||
|
ECU: "ADAS",
|
||||||
|
SupplierSWVersion: "ADASVersion",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
VIN: "FISKER123",
|
||||||
|
ECU: "ACUN",
|
||||||
|
SupplierSWVersion: "ACUNVersion",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
VIN: "FISKER123",
|
||||||
|
ECU: "BCM",
|
||||||
|
SupplierSWVersion: "BCMVersion",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
db.SetCarVersionsLog(&mocks.MockCarVersionsLog{MockLogVersionChange: func(log *common.CarVersionLogs) (orm.Result, error) {
|
||||||
|
return nil, nil
|
||||||
|
}})
|
||||||
|
mockDB = &db
|
||||||
|
services.SetDB(&db)
|
||||||
|
}
|
||||||
24
services/depot/handlers/trex_del.go
Normal file
24
services/depot/handlers/trex_del.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TRexDel(id string) error {
|
||||||
|
client := services.RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
err := removeTRexSession(client, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeTRexSession(client redis.Client, id string) error {
|
||||||
|
_, err := client.Execute("SREM", redis.CarSessionsKey(), id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
20
services/depot/handlers/trex_del_test.go
Normal file
20
services/depot/handlers/trex_del_test.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/handlers"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/testhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTrexDel(t *testing.T) {
|
||||||
|
setupRedisMock()
|
||||||
|
setupDBMock()
|
||||||
|
|
||||||
|
id := "FISKER123"
|
||||||
|
err := handlers.TRexDel(id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, "TestTrexDel", nil, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
336
services/depot/handlers/trex_init.go
Normal file
336
services/depot/handlers/trex_init.go
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/cache"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/carcommand"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/db/queries"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/dbc/state"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/mongo"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/userconsent"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/loggerdataresp"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/elptr"
|
||||||
|
"github.com/go-pg/pg/v10"
|
||||||
|
redigo "github.com/gomodule/redigo/redis"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
mon "go.mongodb.org/mongo-driver/mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errInvalidICCID = errors.New("invalid iccid submitted")
|
||||||
|
var errBlocked = errors.New("unable to connect: car is blocked")
|
||||||
|
var logLevel = envtool.GetEnv("TREX_LOG_LEVEL", common.CriticalLabel)
|
||||||
|
var wakeUpVINS = envtool.GetEnv("WAKE_UP_VINS", "VCF1EBU2XPG001140")
|
||||||
|
var defaultFleet = envtool.GetEnv("DEFAULT_FLEET", "Default-Ocean")
|
||||||
|
|
||||||
|
// id is the vehicles VIN
|
||||||
|
func TRexInit(id string, vMap map[string]string) error {
|
||||||
|
carsDB := services.GetDB().GetCars()
|
||||||
|
blocked, err := insertIfNotExist(id, carsDB)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if blocked {
|
||||||
|
return errBlocked
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(wakeUpVINS, id) {
|
||||||
|
go wakeup(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := services.RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
err = addTRexSession(client, id)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Str("id", id).Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := services.GetMongoClient()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Str("id", id).Err(err).Send()
|
||||||
|
} else {
|
||||||
|
msg, err := retrieveTRexSettings(client, m, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log the config that will be sent to TREX
|
||||||
|
config, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Info().Msgf("TREX Config sent for vin %s: \n%s", id, config)
|
||||||
|
|
||||||
|
err = sendTRexSettings(client, id, msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setTRexVersion(client, id, vMap["version"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setDBCVersion(client, id, vMap["dbc_version"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setTRexIP(client, id, vMap["ip"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = addICCID(carsDB, id, vMap["iccid"])
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn().Err(err).Str("VIN", id).Str("ICCID", vMap["iccid"]).Msg("failed to add iccid to car")
|
||||||
|
}
|
||||||
|
|
||||||
|
uMsg, err := getUserConsentData(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sendTRexUserConsentData(client, id, uMsg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = checkAndRenewCertificate(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkAndRenewCertificate(id string) error {
|
||||||
|
logger.Debug().Msg("on trex init: checking TBOX cert for vin " + id)
|
||||||
|
|
||||||
|
cs := services.GetCertService()
|
||||||
|
certNeedsRenewal, err := cs.CheckCertificateNeedsRenewal(id, common.CertTBOX)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if certNeedsRenewal {
|
||||||
|
logger.Debug().Msg("TBOX cert for vin " + id + " is out of date")
|
||||||
|
|
||||||
|
cert, err := cs.RenewCertificate(id, common.CertTBOX)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := services.RedisClientPool().GetFromPool()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
err = client.SafeQueueMessage(
|
||||||
|
common.TRex.Key(id),
|
||||||
|
common.Message{
|
||||||
|
Handler: "update_cert",
|
||||||
|
Data: common.UpdateCert{
|
||||||
|
SSLCertBase64: cert.PublicKey,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug().Msg("TBOX cert for vin " + id + " is not out of date")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserConsentData(id string) ([]common.UserConsentDataTrexMsg, error) {
|
||||||
|
var ucdToSend = []common.UserConsentDataTrexMsg{}
|
||||||
|
|
||||||
|
resp, err := userconsent.GetUserConsentService().UserConsentByVehicleNumber(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, errors.New("call to user-consent/byVehicleNumber endpoint returned " + resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ucdResult := []common.UserConsentDataResponse{}
|
||||||
|
err = json.Unmarshal(respBody, &ucdResult)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
if len(ucdResult) < 1 {
|
||||||
|
return ucdToSend, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// only send to TREX the consents that have the userID of the first consent returned
|
||||||
|
var userId = ucdResult[0].UserID
|
||||||
|
for _, ucd := range ucdResult {
|
||||||
|
if ucd.UserID == userId {
|
||||||
|
ucdToSend = append(ucdToSend, common.BuildUserConsentTrexMessage(ucd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ucdToSend, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendTRexUserConsentData(r redis.Client, id string, msg []common.UserConsentDataTrexMsg) error {
|
||||||
|
|
||||||
|
if len(msg) > 0 {
|
||||||
|
return r.SafePublishMessage(
|
||||||
|
common.TRex.Key(id),
|
||||||
|
common.Message{
|
||||||
|
Handler: "consent",
|
||||||
|
Data: msg,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertIfNotExist(id string, carsDB queries.CarsInterface) (bool, error) {
|
||||||
|
car, err := carsDB.SelectByVIN(id)
|
||||||
|
if err == nil && car != nil {
|
||||||
|
return car.Blocked, nil
|
||||||
|
}
|
||||||
|
if errors.Is(err, pg.ErrNoRows) {
|
||||||
|
err = nil
|
||||||
|
|
||||||
|
c, err := utils.ParseVIN(id)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = carsDB.Insert(c)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := services.GetMongoClient()
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
v := &mongo.Vehicle{
|
||||||
|
VIN: id,
|
||||||
|
CANBus: common.CANBus{Enabled: true, DataLogger: true, DTCEnabled: elptr.ElPtr(false)},
|
||||||
|
LogLevel: common.UnmarshalLogLevelString(logLevel),
|
||||||
|
}
|
||||||
|
err = client.GetVehicles().AddVehicle(v)
|
||||||
|
if mon.IsDuplicateKeyError(err) {
|
||||||
|
err = client.GetVehicles().UpdateVehicle(v)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.GetFleets().AddVehiclesToFleet(defaultFleet, []string{id})
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addICCID(db queries.CarsInterface, id, iccid string) error {
|
||||||
|
// Going to only update where an iccid is valid
|
||||||
|
if len(iccid) != 20 {
|
||||||
|
return errInvalidICCID
|
||||||
|
}
|
||||||
|
|
||||||
|
car := &common.Car{ICCID: iccid, VIN: id}
|
||||||
|
|
||||||
|
if ct, err := db.UpdateICCID(car); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to update car's iccid %s", id)
|
||||||
|
} else if ct.RowsAffected() == 1 {
|
||||||
|
logger.Info().Msgf("car %s has been updated with iccid %s", id, iccid)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addTRexSession(client redis.Client, id string) error {
|
||||||
|
_, err := client.Execute("SADD", redis.CarSessionsKey(), id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func retrieveTRexSettings(r redis.Client, m mongo.Client, id string) (*common.TRexConfigResponse, error) {
|
||||||
|
return cache.RetrieveVehicleConfig(r, m, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendTRexSettings(r redis.Client, id string, msg *common.TRexConfigResponse) error {
|
||||||
|
err := r.SafePublishMessage(
|
||||||
|
common.TRex.Key(id),
|
||||||
|
common.Message{
|
||||||
|
Handler: "config",
|
||||||
|
Data: msg,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTRexIP(r redis.Client, vin, ip string) error {
|
||||||
|
elms := strings.Split(ip, ":")
|
||||||
|
|
||||||
|
return r.SetObjectField(redis.CarStateHashKey(vin), state.TREX_IP, elms[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDBCVersion(r redis.Client, vin, version string) error {
|
||||||
|
return setVersion(r, vin, version, state.DBC_VERSION, common.DBCVersionSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTRexVersion(r redis.Client, vin, version string) error {
|
||||||
|
return setVersion(r, vin, version, state.TREX_VERSION, common.TREXVersionSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setVersion(r redis.Client, vin, version, redisField string, versionSource common.VersionSource) error {
|
||||||
|
v, err := r.GetObjectField(redis.CarStateHashKey(vin), redisField)
|
||||||
|
if err != nil && !errors.Is(err, redigo.ErrNil) {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v == version {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
db := services.GetDB().GetCarVersionsLog()
|
||||||
|
err = r.SetObjectField(redis.CarStateHashKey(vin), redisField, version)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.LogVersionChange(&common.CarVersionLogs{
|
||||||
|
VIN: vin,
|
||||||
|
VersionSource: versionSource,
|
||||||
|
Version: version,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func wakeup(vin string) {
|
||||||
|
wake := carcommand.NewCarWakeUp(services.GetDB().GetCars(), services.GetSMSClient())
|
||||||
|
err := wake.WakeUp(vin, false)
|
||||||
|
loggerdataresp.BadDataError(err)
|
||||||
|
}
|
||||||
78
services/depot/handlers/trex_init_test.go
Normal file
78
services/depot/handlers/trex_init_test.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package handlers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/handlers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/mongo"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis/tester"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/testhelper"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/userconsent"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTrexInit(t *testing.T) {
|
||||||
|
rMock := tester.MockRedis{}
|
||||||
|
rMock.Reset()
|
||||||
|
|
||||||
|
mockUserConsent := userconsent.UserConsentServiceMock{}
|
||||||
|
userconsent.SetUserConsentService(&mockUserConsent)
|
||||||
|
|
||||||
|
mockCert := CertServiceMock{}
|
||||||
|
services.SetCertService(&mockCert)
|
||||||
|
|
||||||
|
services.SetRedisClientPool(tester.NewMockClientPool(&rMock))
|
||||||
|
id := "FISKER123"
|
||||||
|
|
||||||
|
setupDBMock()
|
||||||
|
services.SetMongoClient(mongo.NewMockClient())
|
||||||
|
|
||||||
|
mockCertificates := mocks.MockCertificates{}
|
||||||
|
mockCertificates.MockCertificate = &common.Certificate{
|
||||||
|
PublicKey: "test",
|
||||||
|
SerialNumber: "",
|
||||||
|
Type: common.CertTBOX,
|
||||||
|
}
|
||||||
|
services.GetDB().SetCertificates(&mockCertificates)
|
||||||
|
|
||||||
|
mocksms := sms.NewSMSMockSuccess()
|
||||||
|
services.SetSmsClient(&mocksms)
|
||||||
|
|
||||||
|
err := handlers.TRexInit(
|
||||||
|
id,
|
||||||
|
map[string]string{"version": "1.2.3", "iccid": "123456789123456789123456789", "ip": "172.20.0.17:49850"})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, "TestTRexInit", nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rez := rMock.SetValues[redis.CarStateHashKey(id)]
|
||||||
|
if rez.Value.(string) != `{"ip":"172.20.0.17","trex_version":"1.2.3"}` {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, "TestTRexInit_wrongVersion", `{"ip":"172.20.0.17","trex_version":"1.2.3"}`, rez.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
rez = rMock.SetValues[redis.CarConfigKey(id)]
|
||||||
|
if rez.Value.(string) != `{"canbus":{"enabled":false,"data_logger_enabled":false,"dtc_enabled":false},"log_level":"trace","log":{"matches":[{"channel":"cmd","level":"trace"}]}}` {
|
||||||
|
t.Errorf(testhelper.TestErrorTemplate, "TestTRexInit_wrongVersion", `{"canbus":{"enabled":false,"data_logger_enabled":false,"dtc_enabled":false},"log_level":"trace"}`, rez.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CertServiceMock struct{}
|
||||||
|
|
||||||
|
func (csm *CertServiceMock) CheckCertificateNeedsRenewal(vin string, certType string) (bool, error) {
|
||||||
|
return certType == common.CertTBOX, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csm *CertServiceMock) RenewCertificate(vin string, certType string) (*common.Certificate, error) {
|
||||||
|
cert := common.Certificate{
|
||||||
|
PublicKey: "test",
|
||||||
|
SerialNumber: "",
|
||||||
|
Type: common.CertTBOX,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cert, nil
|
||||||
|
}
|
||||||
31
services/depot/main.go
Normal file
31
services/depot/main.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/controllers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/server"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/kafka"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/tracer"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/app"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
app.Setup("depot", cleanup)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
tracer.Start()
|
||||||
|
defer tracer.Stop()
|
||||||
|
|
||||||
|
go controllers.HealthCheck()
|
||||||
|
go server.StartConsumer(kafka.DepotServiceGRPCKafka, kafka.DepotService)
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanup() {
|
||||||
|
logger.Close()
|
||||||
|
}
|
||||||
7
services/depot/server/errors.go
Normal file
7
services/depot/server/errors.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrInvalidDevice = errors.New("invalid device associated to message")
|
||||||
158
services/depot/server/server_consumer.go
Normal file
158
services/depot/server/server_consumer.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/handlers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/depot/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/kafka"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/loggerdataresp"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StartConsumer runs consumer and puts events into a channel for router
|
||||||
|
func StartConsumer(topic, oldTopic string) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
logger.Error().Msgf("PanicConsumer %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
eventsJSON := make(chan common.EventRawJSON)
|
||||||
|
events := make(chan *kafka.Message)
|
||||||
|
go routeEvents(events)
|
||||||
|
go routeOldEvents(eventsJSON)
|
||||||
|
|
||||||
|
logger.Info().Msgf("consumer initialized for topic: %v", topic)
|
||||||
|
consumer, oldConsumer, err := services.GetKafkaConsumer()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
err = oldConsumer.ConsumeToChannelJson([]string{oldTopic}, eventsJSON)
|
||||||
|
loggerdataresp.BadDataError(err, loggerdataresp.EofErrorCheck)
|
||||||
|
}()
|
||||||
|
err = consumer.ConsumeToChannel([]string{topic}, events)
|
||||||
|
loggerdataresp.BadDataError(err, loggerdataresp.EofErrorCheck)
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeOldEvents(events chan common.EventRawJSON) {
|
||||||
|
p := services.GetDB()
|
||||||
|
defer p.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
event := <-events
|
||||||
|
var err error
|
||||||
|
var dt common.Payload
|
||||||
|
err = dt.Unmarshal(event.Payload)
|
||||||
|
device, k := common.ParseDeviceKey(event.Key)
|
||||||
|
payload := &common.ConsumerPayload{
|
||||||
|
Handler: dt.Handler,
|
||||||
|
Data: dt.Data,
|
||||||
|
}
|
||||||
|
logger.Debug().Str("id", k).Msgf("source: %s, type: %s, handler: %s", k, device, payload.GetHandler())
|
||||||
|
|
||||||
|
switch device {
|
||||||
|
case common.TRex:
|
||||||
|
err = routeTRex(k, payload)
|
||||||
|
case common.HMI:
|
||||||
|
err = routeHMI(p, k, payload)
|
||||||
|
case common.Mobile:
|
||||||
|
err = routeMobile(p, k, payload)
|
||||||
|
default:
|
||||||
|
err = ErrInvalidDevice
|
||||||
|
}
|
||||||
|
|
||||||
|
loggerdataresp.BadDataError(err, loggerdataresp.EofErrorCheck)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeEvents(events chan *kafka.Message) {
|
||||||
|
p := services.GetDB()
|
||||||
|
defer p.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
event := <-events
|
||||||
|
|
||||||
|
var err error
|
||||||
|
payload := &kafka_grpc.GRPC_DepotPayload{}
|
||||||
|
err = proto.Unmarshal(event.Value, payload)
|
||||||
|
|
||||||
|
device, k := common.ParseDeviceKey(string(event.Key))
|
||||||
|
logger.Debug().Str("id", k).Msgf("source: %s, type: %s, handler: %s", k, device, payload.GetHandler())
|
||||||
|
|
||||||
|
switch device {
|
||||||
|
case common.TRex:
|
||||||
|
d, _ := common.DepotRouteTRexPayload(payload)
|
||||||
|
err = routeTRex(k, d)
|
||||||
|
case common.HMI:
|
||||||
|
d, _ := common.DepotRouteHMIPayload(payload)
|
||||||
|
err = routeHMI(p, k, d)
|
||||||
|
case common.Mobile:
|
||||||
|
d, _ := common.DepotRouteMobilePayload(payload)
|
||||||
|
err = routeMobile(p, k, d)
|
||||||
|
default:
|
||||||
|
err = ErrInvalidDevice
|
||||||
|
}
|
||||||
|
|
||||||
|
loggerdataresp.BadDataError(err, loggerdataresp.EofErrorCheck)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeTRex(id string, d common.ConsumerPayloadInterface) error {
|
||||||
|
// route TRex messages
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch d.GetHandler() {
|
||||||
|
case "init":
|
||||||
|
var vMap map[string]string
|
||||||
|
err = json.Unmarshal(d.GetData(), &vMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = handlers.TRexInit(id, vMap)
|
||||||
|
case "del":
|
||||||
|
err = handlers.TRexDel(id)
|
||||||
|
default:
|
||||||
|
|
||||||
|
err = kafka.ErrUnhandledMessage(common.TRex, id, d.GetHandler(), string(d.GetData()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeHMI(p *services.DB, id string, d common.ConsumerPayloadInterface) error {
|
||||||
|
// route HMI messages
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch d.GetHandler() {
|
||||||
|
case "init":
|
||||||
|
err = handlers.HMIInit(p, id, d.GetData())
|
||||||
|
case "del":
|
||||||
|
err = handlers.HMIDel(p, id)
|
||||||
|
default:
|
||||||
|
err = kafka.ErrUnhandledMessage(common.HMI, id, d.GetHandler(), string(d.GetData()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeMobile(p *services.DB, id string, d common.ConsumerPayloadInterface) error {
|
||||||
|
// route mobile messages
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch d.GetHandler() {
|
||||||
|
case "init":
|
||||||
|
err = handlers.MobileInit(p, id)
|
||||||
|
case "del":
|
||||||
|
err = handlers.MobileDel(p, id)
|
||||||
|
default:
|
||||||
|
err = kafka.ErrUnhandledMessage(common.Mobile, id, d.GetHandler(), string(d.GetData()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
113
services/depot/services/cert.go
Normal file
113
services/depot/services/cert.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
|
||||||
|
"github.com/go-pg/pg/v10"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
certService CertServiceInterface
|
||||||
|
certOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetCertService() CertServiceInterface {
|
||||||
|
certOnce.Do(func() {
|
||||||
|
if certService != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
certService = NewCertService()
|
||||||
|
})
|
||||||
|
|
||||||
|
return certService
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetCertService(cs CertServiceInterface) {
|
||||||
|
certService = cs
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCertService() CertServiceInterface {
|
||||||
|
return &CertService{
|
||||||
|
certURL: envtool.GetEnv("CERT_URL", "REPLACE_ME"),
|
||||||
|
certAPIToken: envtool.GetEnv("CERTIFICATE_API_KEY", "REPLACE_ME"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CertServiceInterface interface {
|
||||||
|
CheckCertificateNeedsRenewal(vin string, certType string) (bool, error)
|
||||||
|
RenewCertificate(vin string, certType string) (*common.Certificate, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CertService struct {
|
||||||
|
certURL string
|
||||||
|
certAPIToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *CertService) CheckCertificateNeedsRenewal(vin string, certType string) (bool, error) {
|
||||||
|
logger.Debug().Msg("checking " + certType + " cert for vin " + vin)
|
||||||
|
|
||||||
|
cert, err := GetDB().GetCertificates().SelectMostRecent(vin, certType)
|
||||||
|
if err != nil && !errors.Is(err, pg.ErrNoRows) {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if cert == nil {
|
||||||
|
logger.Debug().Msg("no existing " + certType + " cert to renew for vin " + vin)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var daysBeforeExp = 183 // six months
|
||||||
|
switch certType {
|
||||||
|
case common.CertTBOX:
|
||||||
|
daysBeforeExp = envtool.GetEnvInt("TBOX_CERT_RENEW_DAYS_BEFORE_EXP", daysBeforeExp)
|
||||||
|
case common.CertICC:
|
||||||
|
daysBeforeExp = envtool.GetEnvInt("ICC_CERT_RENEW_DAYS_BEFORE_EXP", daysBeforeExp)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug().Msg("checking validity of " + certType + " cert with serial number " + cert.SerialNumber + " for vin " + vin)
|
||||||
|
|
||||||
|
return cert.IsExpiredOrInvalidAtTime(time.Now(), daysBeforeExp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *CertService) RenewCertificate(vin string, certType string) (*common.Certificate, error) {
|
||||||
|
logger.Debug().Msg("renewing " + certType + " cert for vin " + vin)
|
||||||
|
|
||||||
|
jsonBytes, err := json.Marshal(common.CertificateRenewRequest{
|
||||||
|
Type: certType,
|
||||||
|
CommonName: vin,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
request, err := http.NewRequest(http.MethodPost, cs.certURL+"renew", bytes.NewReader(jsonBytes))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
request.Header.Add("Api-Key", cs.certAPIToken)
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, errors.New("renew " + certType + " certificate returned " + resp.Status + " for vin " + vin)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var cert common.Certificate
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&cert)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cert, err
|
||||||
|
}
|
||||||
170
services/depot/services/db.go
Normal file
170
services/depot/services/db.go
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/db"
|
||||||
|
q "github.com/fiskerinc/cloud-services/pkg/db/queries"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dbOnce sync.Once
|
||||||
|
dbInstance *DB
|
||||||
|
)
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
client *db.DBClient
|
||||||
|
cars q.CarsInterface
|
||||||
|
carVersionsLog q.CarVersionsLogInterface
|
||||||
|
certificates q.CertificatesInterface
|
||||||
|
updateManifest q.UpdateManifestsInterface
|
||||||
|
updateManifestSUMSVersions q.SUMSVersionsInterface
|
||||||
|
|
||||||
|
onceClient sync.Once
|
||||||
|
onceCars sync.Once
|
||||||
|
onceCarVersionsLog sync.Once
|
||||||
|
onceCertificates sync.Once
|
||||||
|
onceUpdateManifest sync.Once
|
||||||
|
onceUpdateManifestSUMSVersions sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDB() *DB {
|
||||||
|
dbOnce.Do(func() {
|
||||||
|
if dbInstance != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info().Msg("init DB instance")
|
||||||
|
dbInstance = &DB{}
|
||||||
|
})
|
||||||
|
return dbInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetDB(db *DB) {
|
||||||
|
if dbInstance != nil {
|
||||||
|
dbInstance.Close()
|
||||||
|
}
|
||||||
|
dbInstance = db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetDBClient() *db.DBClient {
|
||||||
|
d.onceClient.Do(func() {
|
||||||
|
if d.client != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info().Msg("init DBClient instance")
|
||||||
|
client := &db.DBClient{}
|
||||||
|
|
||||||
|
err := client.InitSchema([]interface{}{
|
||||||
|
(*common.UpdateManifest)(nil),
|
||||||
|
(*common.CarUpdate)(nil),
|
||||||
|
(*common.CarToDriver)(nil),
|
||||||
|
(*common.CarSetting)(nil),
|
||||||
|
(*common.SUMSVersion)(nil),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
}
|
||||||
|
d.client = client
|
||||||
|
})
|
||||||
|
return d.client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetDBClient(client *db.DBClient) {
|
||||||
|
if d.client != nil {
|
||||||
|
d.client.Close()
|
||||||
|
}
|
||||||
|
d.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Close() {
|
||||||
|
if d.client == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.client.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
///----------
|
||||||
|
|
||||||
|
func (d *DB) GetCars() q.CarsInterface {
|
||||||
|
d.onceCars.Do(func() {
|
||||||
|
if d.cars != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance := &q.Cars{}
|
||||||
|
instance.SetClient(d.GetDBClient())
|
||||||
|
d.cars = instance
|
||||||
|
})
|
||||||
|
return d.cars
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetCars(cars q.CarsInterface) {
|
||||||
|
d.cars = cars
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetCarVersionsLog() q.CarVersionsLogInterface {
|
||||||
|
d.onceCarVersionsLog.Do(func() {
|
||||||
|
if d.carVersionsLog != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance := &q.CarVersionsLog{}
|
||||||
|
instance.SetClient(d.GetDBClient())
|
||||||
|
d.carVersionsLog = instance
|
||||||
|
})
|
||||||
|
return d.carVersionsLog
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetCarVersionsLog(log q.CarVersionsLogInterface) {
|
||||||
|
d.carVersionsLog = log
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetCertificates() q.CertificatesInterface {
|
||||||
|
d.onceCertificates.Do(func() {
|
||||||
|
if d.certificates != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debug().Msg("Init Certificates instance")
|
||||||
|
certificates := &q.Certificates{}
|
||||||
|
certificates.SetClient(d.GetDBClient())
|
||||||
|
d.certificates = certificates
|
||||||
|
})
|
||||||
|
return d.certificates
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetCertificates(certificates q.CertificatesInterface) {
|
||||||
|
d.certificates = certificates
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetUpdateManifests() q.UpdateManifestsInterface {
|
||||||
|
d.onceUpdateManifest.Do(func() {
|
||||||
|
if d.updateManifest != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debug().Msg("Init UpdateManifest instance")
|
||||||
|
updateManifest := q.NewUpdateManifest(nil)
|
||||||
|
updateManifest.SetClient(d.GetDBClient())
|
||||||
|
d.updateManifest = updateManifest
|
||||||
|
})
|
||||||
|
return d.updateManifest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetUpdateManifests(updateManifest q.UpdateManifestsInterface) {
|
||||||
|
d.updateManifest = updateManifest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetUpdateManifestSUMSVersions() q.SUMSVersionsInterface {
|
||||||
|
d.onceUpdateManifestSUMSVersions.Do(func() {
|
||||||
|
if d.updateManifestSUMSVersions != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance := &q.SUMSVersions{}
|
||||||
|
instance.SetClient(d.GetDBClient())
|
||||||
|
d.updateManifestSUMSVersions = instance
|
||||||
|
})
|
||||||
|
return d.updateManifestSUMSVersions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) SetUpdateManifestVersions(umv q.SUMSVersionsInterface) {
|
||||||
|
d.updateManifestSUMSVersions = umv
|
||||||
|
}
|
||||||
23
services/depot/services/digital_twin.go
Normal file
23
services/depot/services/digital_twin.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/digitaltwin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// There is no need to recreate the digital twin sender every time we send a digital twin
|
||||||
|
// so keeping a reference here
|
||||||
|
|
||||||
|
var sendDigitalTwin *digitaltwin.SendDigitalTwin
|
||||||
|
var sendDigitalTwinOnce sync.Once
|
||||||
|
|
||||||
|
func GetSendDigitalTwin()(*digitaltwin.SendDigitalTwin){
|
||||||
|
sendDigitalTwinOnce.Do(func(){
|
||||||
|
if sendDigitalTwin == nil {
|
||||||
|
notSureWhyIcantTakeAddress := digitaltwin.NewSendDigitalTwin(RedisClientPool(), GetDB().GetCars())
|
||||||
|
sendDigitalTwin = ¬SureWhyIcantTakeAddress
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return sendDigitalTwin
|
||||||
|
}
|
||||||
36
services/depot/services/kafka.go
Normal file
36
services/depot/services/kafka.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/kafka"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
const serviceName = "depot"
|
||||||
|
const oldServiceName = "old-depot"
|
||||||
|
|
||||||
|
var consumer kafka.ConsumerInterface
|
||||||
|
var oldConsumer kafka.ConsumerInterface
|
||||||
|
var consumerOnce sync.Once
|
||||||
|
|
||||||
|
// GetKafkaConsumer returns singleton instance of kafka consumer
|
||||||
|
func GetKafkaConsumer() (kafka.ConsumerInterface, kafka.ConsumerInterface, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
consumerOnce.Do(func() {
|
||||||
|
consumer, err = kafka.NewConsumer(serviceName)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
}
|
||||||
|
oldConsumer, err = kafka.NewConsumer(oldServiceName)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return consumer, oldConsumer, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumer, oldConsumer, nil
|
||||||
|
}
|
||||||
72
services/depot/services/mongo.go
Normal file
72
services/depot/services/mongo.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/mongo"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sony/gobreaker"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
clientlock sync.Mutex
|
||||||
|
client mongo.Client
|
||||||
|
cb *gobreaker.CircuitBreaker
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cb = gobreaker.NewCircuitBreaker(
|
||||||
|
gobreaker.Settings{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
Interval: 15 * time.Minute,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMongoClient returns singleton instance of mongo client
|
||||||
|
func GetMongoClient() (mongo.Client, error) {
|
||||||
|
err := ping()
|
||||||
|
if err != nil {
|
||||||
|
return initMongoClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initMongoClient() (mongo.Client, error) {
|
||||||
|
clientlock.Lock()
|
||||||
|
defer clientlock.Unlock()
|
||||||
|
var err error
|
||||||
|
|
||||||
|
client, err = mongo.NewClient(mongo.StandardDB)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = cb.Execute(func() (interface{}, error) {
|
||||||
|
err = ping()
|
||||||
|
return nil, err
|
||||||
|
})
|
||||||
|
|
||||||
|
return client, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ping() error {
|
||||||
|
if client == nil {
|
||||||
|
return errors.New("client is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||||
|
defer cancel()
|
||||||
|
return client.Ping(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMongoClient is supposed to be used for tests.
|
||||||
|
func SetMongoClient(cl mongo.Client) {
|
||||||
|
client = cl
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// db.dtc_lookup.aggregate({"$match": {"ecuName": "TBOX", "DtcUniqueId": "V2.6.0"}}, {"$unwind": "$dtcData.DtcList"}, {"$match": {"dtcData.DtcList.TroubleCodeHex":"D77F16"}}, {"$project":{"_id": 0, "Obj": "$dtcData.DtcList"}})
|
||||||
28
services/depot/services/redis.go
Normal file
28
services/depot/services/redis.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
clientPoolOnce sync.Once
|
||||||
|
clientPool redis.ClientPoolInterface
|
||||||
|
)
|
||||||
|
|
||||||
|
func RedisClientPool() redis.ClientPoolInterface {
|
||||||
|
clientPoolOnce.Do(func() {
|
||||||
|
if clientPool != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clientPool = redis.NewClientPool()
|
||||||
|
})
|
||||||
|
|
||||||
|
return clientPool
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetRedisClientPool(cp redis.ClientPoolInterface) {
|
||||||
|
clientPool = cp
|
||||||
|
}
|
||||||
42
services/depot/services/sms.go
Normal file
42
services/depot/services/sms.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
|
||||||
|
"fmt"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var smsClient sms.SMSServiceClient
|
||||||
|
var smsClientOnce sync.Once
|
||||||
|
|
||||||
|
func newSmsClient() {
|
||||||
|
logger.Info().Msg("Init SMS client")
|
||||||
|
target := fmt.Sprintf("%s:%s",
|
||||||
|
envtool.GetEnv("SMS_HOST", "sms"),
|
||||||
|
envtool.GetEnv("SMS_PORT", "8077"))
|
||||||
|
c, err := grpc.Dial(target, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
smsClient = sms.NewSMSServiceClient(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSMSClient() sms.SMSServiceClient {
|
||||||
|
smsClientOnce.Do(func() {
|
||||||
|
if smsClient != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newSmsClient()
|
||||||
|
})
|
||||||
|
|
||||||
|
return smsClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetSmsClient(c sms.SMSServiceClient) {
|
||||||
|
smsClient = c
|
||||||
|
}
|
||||||
25
services/jetfire/.env.template
Normal file
25
services/jetfire/.env.template
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
CLICKHOUSE_USER="default"
|
||||||
|
CLICKHOUSE_PASS=""
|
||||||
|
|
||||||
|
CLICKHOUSE_FEATURE_TABLE="feature_table"
|
||||||
|
CLICKHOUSE_VEHICLE_SIGNAL_TABLE="vehicle_signal"
|
||||||
|
|
||||||
|
CLICKHOUSE_HOST="localhost"
|
||||||
|
KAFKA_HOSTS="localhost:9093"
|
||||||
|
|
||||||
|
CLICKHOUSE_MAX_CONNS=5
|
||||||
|
|
||||||
|
JETFIRE_VEHICLE_SIGNAL_BATCH_PERIOD_MS=10000
|
||||||
|
|
||||||
|
JETFIRE_FEATURE_BATCH_PERIOD_MS=10000
|
||||||
|
JETFIRE_FEATURE_DOWNSAMPLE_US=1000000
|
||||||
|
|
||||||
|
JETFIRE_TRIP_TIMEOUT_MS=300000
|
||||||
|
JETFIRE_STATE_TIMEOUT_MS=3600000
|
||||||
|
JETFIRE_FUTURE_THRESHOLD_MS=604800000
|
||||||
|
|
||||||
|
JETFIRE_SCHEMA_RESET_PERIOD_MS=3600000
|
||||||
|
|
||||||
|
JETFIRE_MAX_BUFFER_ROWS=1000
|
||||||
|
|
||||||
|
LOG_LEVEL="debug"
|
||||||
32
services/jetfire/Dockerfile
Normal file
32
services/jetfire/Dockerfile
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
## Build binaries for event detection using cloud_base_go image
|
||||||
|
ARG BASE_IMAGE=cloud_base_go
|
||||||
|
FROM ${BASE_IMAGE} as builder-go
|
||||||
|
|
||||||
|
WORKDIR /build/jetfire
|
||||||
|
|
||||||
|
COPY ./jetfire/go.mod ./jetfire/go.sum ./
|
||||||
|
|
||||||
|
RUN go mod edit -replace fiskerinc.com/modules=../fiskerinc.com/modules \
|
||||||
|
&& go mod download
|
||||||
|
|
||||||
|
COPY ./jetfire ./
|
||||||
|
|
||||||
|
RUN go mod edit -replace fiskerinc.com/modules=../fiskerinc.com/modules \
|
||||||
|
&& go build -tags musl
|
||||||
|
|
||||||
|
|
||||||
|
## Build image for event detection, pulling binaries from builder image
|
||||||
|
FROM alpine:3.17
|
||||||
|
|
||||||
|
RUN apk add --no-cache librdkafka --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community \
|
||||||
|
&& apk add --no-cache ca-certificates
|
||||||
|
|
||||||
|
COPY ./modules_go/logger/log_config .
|
||||||
|
COPY ./jetfire/default-feature-vars.json .
|
||||||
|
COPY --from=builder-go /build/jetfire/jetfire .
|
||||||
|
|
||||||
|
ENV LOG_LEVEL=log_config
|
||||||
|
EXPOSE 8077
|
||||||
|
|
||||||
|
CMD ./jetfire
|
||||||
26
services/jetfire/README.md
Normal file
26
services/jetfire/README.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Jetfire Data Routing Service
|
||||||
|
Jetfire Service listens to CAN Signals on the vehicle_signals topic on Kafka,
|
||||||
|
and performs a pivot operation for populating the feature_table on Clickhouse,
|
||||||
|
and populating the real_time table on Clickhouse.
|
||||||
|
CAN Signals are batched and inserted into vehicle_signal table without any transform performed on the data.
|
||||||
|
|
||||||
|
Feature Table inserts are typically batched at `JETFIRE_FEATURE_BATCH_PERIOD_MS=10000`.
|
||||||
|
|
||||||
|
Vehicle Signal Table inserts are typically batched at `JETFIRE_VEHICLE_SIGNAL_BATCH_PERIOD_MS=10000`
|
||||||
|
|
||||||
|
The schemas for sink tables are fetched periodically, set to `JETFIRE_SCHEMA_RESET_PERIOD_MS=3600000`
|
||||||
|
Additionally, a GET request to the `/reset` endpoint on port `8077` will also trigger an immediate schema reset.
|
||||||
|
|
||||||
|
For more information about Jetfire service, see https://fiskerinc.atlassian.net/wiki/spaces/COM/pages/1401487522/Jetfire+Service
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Copy `./.env.tenmplate` to `./.env` file
|
||||||
|
|
||||||
|
Secrets in the .env file will need to be filled in manually.
|
||||||
|
|
||||||
|
Running jetfire locally
|
||||||
|
```
|
||||||
|
source set_envs.sh
|
||||||
|
go run main.go
|
||||||
|
```
|
||||||
54
services/jetfire/controllers/health_check.go
Normal file
54
services/jetfire/controllers/health_check.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/jetfire/services"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/health"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mismatchTypeError = errors.New("mismatch type error")
|
||||||
|
|
||||||
|
func HealthCheck() {
|
||||||
|
server := health.HealthCheckServer{}
|
||||||
|
err := server.Serve([]health.Config{
|
||||||
|
{
|
||||||
|
Name: "clickhouse",
|
||||||
|
Check: health.NewClickhouseCheck(getClickhouseConsumer),
|
||||||
|
Timeout: time.Second * 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getKafkaConsumer() (health.KafkaConnCheckInterface, error) {
|
||||||
|
client, err := services.GetKafkaConsumer()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, ok := client.(health.KafkaConnCheckInterface)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.WithStack(mismatchTypeError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getClickhouseConsumer() (health.ClickhouseConnCheckInterface, error) {
|
||||||
|
client, err := services.GetClickhouseConnection()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, ok := client.(health.ClickhouseConnCheckInterface)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.WithStack(mismatchTypeError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
65
services/jetfire/default-feature-vars.json
Normal file
65
services/jetfire/default-feature-vars.json
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
[
|
||||||
|
"BCM_DrFrntDoorSts",
|
||||||
|
"BCM_FrntDrDoorLockSts",
|
||||||
|
"BCM_TotMilg_ODO",
|
||||||
|
"BCM_PwrMod",
|
||||||
|
|
||||||
|
"BMS_AccueChrgTotAh",
|
||||||
|
"BMS_AccueDchaTotAh",
|
||||||
|
"BMS_Bat_Actual_Pack_Capacity",
|
||||||
|
"BMS_Bat_HVmeasure_Current",
|
||||||
|
"BMS_Bat_SoC_usable",
|
||||||
|
"BMS_BattAvrgT",
|
||||||
|
"BMS_Bat_measure_Energy",
|
||||||
|
"BMS_PwrBattChrgDchaCrt1",
|
||||||
|
"BMS_PwrBattChrgDchaCrt2",
|
||||||
|
"BMS_PwrBattRmngCpSOC",
|
||||||
|
"BMS_PwrBattSOH",
|
||||||
|
"BMS_VehChrgDchgMod",
|
||||||
|
"BMS_Cell_Volt_max",
|
||||||
|
"BMS_Cell_Volt_min",
|
||||||
|
|
||||||
|
"BMS_Bat_Coolant_in",
|
||||||
|
"BMS_Bat_Coolant_out",
|
||||||
|
|
||||||
|
"ECC_OutdT",
|
||||||
|
|
||||||
|
"ESP_TotBrkTqReq",
|
||||||
|
"ESP_VehSpd",
|
||||||
|
|
||||||
|
"IBS_StateOfCharge",
|
||||||
|
"IBS_StateOfHealth",
|
||||||
|
"IBS_BatteryVoltage",
|
||||||
|
"IBS_BatteryCurrent",
|
||||||
|
"IBS_BatteryTemperature",
|
||||||
|
"IBS_AvgRi",
|
||||||
|
"IBS_AvailableCapacity",
|
||||||
|
|
||||||
|
"ICC_DispVehSpd",
|
||||||
|
"ICC_FrntWiprCtrl",
|
||||||
|
|
||||||
|
"MCU_F_AlrmLamp_FS",
|
||||||
|
"MCU_F_CrtTq",
|
||||||
|
"MCU_F_HVActvDchaSts",
|
||||||
|
"MCU_R_AlrmLamp_FS",
|
||||||
|
"MCU_R_CrtTq",
|
||||||
|
"MCU_R_HVActvDchaSts",
|
||||||
|
|
||||||
|
"OBC_DCPosRlyCtrlSts",
|
||||||
|
|
||||||
|
"VCU_ACChrgShttrSts",
|
||||||
|
"VCU_APSPerc",
|
||||||
|
"VCU_BrkPedlSts_GB",
|
||||||
|
"VCU_BrkSig",
|
||||||
|
"VCU_ChrgSts",
|
||||||
|
"VCU_ChrgSts_GB",
|
||||||
|
"VCU_ChrgSysOperCmd",
|
||||||
|
"VCU_DCChrgShttrSts",
|
||||||
|
"VCU_GearSig_GB",
|
||||||
|
"VCU_VehChrgDchgMod",
|
||||||
|
"VCU_VehOperMod",
|
||||||
|
|
||||||
|
"TBOX_GPSHei",
|
||||||
|
"TBOX_GPSLati",
|
||||||
|
"TBOX_GPSLongi"
|
||||||
|
]
|
||||||
143
services/jetfire/go.mod
Normal file
143
services/jetfire/go.mod
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
module github.com/fiskerinc/cloud-services/services/jetfire
|
||||||
|
|
||||||
|
go 1.25
|
||||||
|
|
||||||
|
toolchain go1.25.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/ClickHouse/ch-go v0.58.2
|
||||||
|
github.com/ClickHouse/clickhouse-go/v2 v2.6.0
|
||||||
|
github.com/fiskerinc/cloud-services/pkg v0.0.0-00010101000000-000000000000
|
||||||
|
github.com/intel-go/fastjson v0.0.0-20170329170629-f846ae58a1ab
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/rs/zerolog v1.29.1
|
||||||
|
github.com/sony/gobreaker v0.5.0
|
||||||
|
github.com/stretchr/testify v1.10.0
|
||||||
|
google.golang.org/protobuf v1.36.1
|
||||||
|
gopkg.in/retry.v1 v1.0.3
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/DataDog/appsec-internal-go v1.4.0 // indirect
|
||||||
|
github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 // indirect
|
||||||
|
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1 // indirect
|
||||||
|
github.com/DataDog/datadog-go/v5 v5.3.0 // indirect
|
||||||
|
github.com/DataDog/go-libddwaf/v2 v2.2.3 // indirect
|
||||||
|
github.com/DataDog/go-tuf v1.0.2-0.5.2 // indirect
|
||||||
|
github.com/DataDog/sketches-go v1.4.2 // indirect
|
||||||
|
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||||
|
github.com/ReneKroon/ttlcache/v2 v2.11.0 // indirect
|
||||||
|
github.com/albenik/bcd v0.0.0-20170831201648-635201416bc7 // indirect
|
||||||
|
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/dmarkham/enumer v1.5.8 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/ebitengine/purego v0.5.2 // indirect
|
||||||
|
github.com/elliotchance/orderedmap/v2 v2.2.0 // indirect
|
||||||
|
github.com/fiskerinc/cloud-services/pkg/can-go v0.0.0-00010101000000-000000000000 // indirect
|
||||||
|
github.com/frankban/quicktest v1.14.6 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
|
github.com/go-faster/city v1.0.1 // indirect
|
||||||
|
github.com/go-faster/errors v0.6.1 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/spec v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/swag v0.23.0 // indirect
|
||||||
|
github.com/go-pg/pg/v10 v10.11.1 // indirect
|
||||||
|
github.com/go-pg/zerochecker v0.2.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.15.1 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/golang/mock v1.7.0-rc.1 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/gomodule/redigo v1.8.9 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/schema v1.2.0 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
|
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||||
|
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||||
|
github.com/jinzhu/copier v0.3.5 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.2 // indirect
|
||||||
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
|
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
|
||||||
|
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
|
||||||
|
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||||
|
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||||
|
github.com/lestrrat-go/jwx v1.2.25 // indirect
|
||||||
|
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||||
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
|
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||||
|
github.com/onsi/gomega v1.25.0 // indirect
|
||||||
|
github.com/outcaste-io/ristretto v0.2.3 // indirect
|
||||||
|
github.com/pascaldekloe/name v1.0.1 // indirect
|
||||||
|
github.com/paulmach/orb v0.8.0 // indirect
|
||||||
|
github.com/philhofer/fwd v1.1.2 // indirect
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
|
github.com/redis/go-redis/v9 v9.5.1 // indirect
|
||||||
|
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
|
||||||
|
github.com/segmentio/asm v1.2.0 // indirect
|
||||||
|
github.com/shopspring/decimal v1.3.1 // indirect
|
||||||
|
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f // indirect
|
||||||
|
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a // indirect
|
||||||
|
github.com/swaggo/http-swagger v1.3.3 // indirect
|
||||||
|
github.com/swaggo/swag v1.8.8 // indirect
|
||||||
|
github.com/tinylib/msgp v1.1.8 // indirect
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
|
github.com/twmb/franz-go v1.20.6 // indirect
|
||||||
|
github.com/twmb/franz-go/pkg/kadm v1.17.2 // indirect
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.12.0 // indirect
|
||||||
|
github.com/vmihailenco/bufpool v0.1.11 // indirect
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||||
|
github.com/vmihailenco/tagparser v0.1.2 // indirect
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
|
github.com/xdg-go/scram v1.1.2 // indirect
|
||||||
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||||
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
go.uber.org/zap v1.25.0 // indirect
|
||||||
|
go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
||||||
|
golang.org/x/crypto v0.45.0 // indirect
|
||||||
|
golang.org/x/mod v0.29.0 // indirect
|
||||||
|
golang.org/x/net v0.47.0 // indirect
|
||||||
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
|
golang.org/x/text v0.31.0 // indirect
|
||||||
|
golang.org/x/time v0.8.0 // indirect
|
||||||
|
golang.org/x/tools v0.38.0 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||||
|
gopkg.in/DataDog/dd-trace-go.v1 v1.60.1 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect
|
||||||
|
mellium.im/sasl v0.3.1 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace (
|
||||||
|
github.com/fiskerinc/cloud-services/pkg => ../../pkg
|
||||||
|
github.com/fiskerinc/cloud-services/pkg/can-go => ../../pkg/can-go
|
||||||
|
)
|
||||||
548
services/jetfire/go.sum
Normal file
548
services/jetfire/go.sum
Normal file
@@ -0,0 +1,548 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/ClickHouse/ch-go v0.58.2 h1:jSm2szHbT9MCAB1rJ3WuCJqmGLi5UTjlNu+f530UTS0=
|
||||||
|
github.com/ClickHouse/ch-go v0.58.2/go.mod h1:Ap/0bEmiLa14gYjCiRkYGbXvbe8vwdrfTYWhsuQ99aw=
|
||||||
|
github.com/ClickHouse/clickhouse-go/v2 v2.6.0 h1:NmnPY2Cg4hCqS2ZGBep9EWHfQPAco2Vkpwb02VXtWew=
|
||||||
|
github.com/ClickHouse/clickhouse-go/v2 v2.6.0/go.mod h1:SvXuWqDsiHJE3VAn2+3+nz9W9exOSigyskcs4DAcxJQ=
|
||||||
|
github.com/DataDog/appsec-internal-go v1.4.0 h1:KFI8ElxkJOgpw+cUm9TXK/jh5EZvRaWM07sXlxGg9Ck=
|
||||||
|
github.com/DataDog/appsec-internal-go v1.4.0/go.mod h1:ONW8aV6R7Thgb4g0bB9ZQCm+oRgyz5eWiW7XoQ19wIc=
|
||||||
|
github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 h1:bUMSNsw1iofWiju9yc1f+kBd33E3hMJtq9GuU602Iy8=
|
||||||
|
github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0/go.mod h1:HzySONXnAgSmIQfL6gOv9hWprKJkx8CicuXuUbmgWfo=
|
||||||
|
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1 h1:5nE6N3JSs2IG3xzMthNFhXfOaXlrsdgqmJ73lndFf8c=
|
||||||
|
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1/go.mod h1:Vc+snp0Bey4MrrJyiV2tVxxJb6BmLomPvN1RgAvjGaQ=
|
||||||
|
github.com/DataDog/datadog-go/v5 v5.3.0 h1:2q2qjFOb3RwAZNU+ez27ZVDwErJv5/VpbBPprz7Z+s8=
|
||||||
|
github.com/DataDog/datadog-go/v5 v5.3.0/go.mod h1:XRDJk1pTc00gm+ZDiBKsjh7oOOtJfYfglVCmFb8C2+Q=
|
||||||
|
github.com/DataDog/go-libddwaf/v2 v2.2.3 h1:LpKE8AYhVrEhlmlw6FGD41udtDf7zW/aMdLNbCXpegQ=
|
||||||
|
github.com/DataDog/go-libddwaf/v2 v2.2.3/go.mod h1:8nX0SYJMB62+fbwYmx5J7zuCGEjiC/RxAo3+AuYJuFE=
|
||||||
|
github.com/DataDog/go-tuf v1.0.2-0.5.2 h1:EeZr937eKAWPxJ26IykAdWA4A0jQXJgkhUjqEI/w7+I=
|
||||||
|
github.com/DataDog/go-tuf v1.0.2-0.5.2/go.mod h1:zBcq6f654iVqmkk8n2Cx81E1JnNTMOAx1UEO/wZR+P0=
|
||||||
|
github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/IGQh4=
|
||||||
|
github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM=
|
||||||
|
github.com/DataDog/sketches-go v1.4.2 h1:gppNudE9d19cQ98RYABOetxIhpTCl4m7CnbRZjvVA/o=
|
||||||
|
github.com/DataDog/sketches-go v1.4.2/go.mod h1:xJIXldczJyyjnbDop7ZZcLxJdV3+7Kra7H1KMgpgkLk=
|
||||||
|
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||||
|
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||||
|
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||||
|
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||||
|
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||||
|
github.com/ReneKroon/ttlcache/v2 v2.11.0 h1:OvlcYFYi941SBN3v9dsDcC2N8vRxyHcCmJb3Vl4QMoM=
|
||||||
|
github.com/ReneKroon/ttlcache/v2 v2.11.0/go.mod h1:mBxvsNY+BT8qLLd6CuAJubbKo6r0jh3nb5et22bbfGY=
|
||||||
|
github.com/albenik/bcd v0.0.0-20170831201648-635201416bc7 h1:m3Ayfs5OcAlIMEdLIQKubBsVLGee4YMUr14+d1256WE=
|
||||||
|
github.com/albenik/bcd v0.0.0-20170831201648-635201416bc7/go.mod h1:QIAMbrwsnQZ2ES3G26RubSrDB5SPyzsp9Hts5NJdTrI=
|
||||||
|
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||||
|
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
|
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||||
|
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
|
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/confluentinc/confluent-kafka-go v1.9.2 h1:gV/GxhMBUb03tFWkN+7kdhg+zf+QUM+wVkI9zwh770Q=
|
||||||
|
github.com/confluentinc/confluent-kafka-go/v2 v2.3.0 h1:icCHutJouWlQREayFwCc7lxDAhws08td+W3/gdqgZts=
|
||||||
|
github.com/confluentinc/confluent-kafka-go/v2 v2.3.0/go.mod h1:/VTy8iEpe6mD9pkCH5BhijlUl8ulUXymKv1Qig5Rgb8=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
|
||||||
|
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||||
|
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||||
|
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
|
github.com/dmarkham/enumer v1.5.8 h1:fIF11F9l5jyD++YYvxcSH5WgHfeaSGPaN/T4kOQ4qEM=
|
||||||
|
github.com/dmarkham/enumer v1.5.8/go.mod h1:d10o8R3t/gROm2p3BXqTkMt2+HMuxEmWCXzorAruYak=
|
||||||
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||||
|
github.com/ebitengine/purego v0.5.2 h1:r2MQEtkGzZ4LRtFZVAg5bjYKnUbxxloaeuGxH0t7qfs=
|
||||||
|
github.com/ebitengine/purego v0.5.2/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||||
|
github.com/elliotchance/orderedmap/v2 v2.2.0 h1:7/2iwO98kYT4XkOjA9mBEIwvi4KpGB4cyHeOFOnj4Vk=
|
||||||
|
github.com/elliotchance/orderedmap/v2 v2.2.0/go.mod h1:85lZyVbpGaGvHvnKa7Qhx7zncAdBIBq6u56Hb1PRU5Q=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20=
|
||||||
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
|
github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
|
||||||
|
github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
|
||||||
|
github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06FI=
|
||||||
|
github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+Jpi2LAyY=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||||
|
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||||
|
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||||
|
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||||
|
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||||
|
github.com/go-pg/pg/v10 v10.11.1 h1:vYwbFpqoMpTDphnzIPshPPepdy3VpzD8qo29OFKp4vo=
|
||||||
|
github.com/go-pg/pg/v10 v10.11.1/go.mod h1:ExJWndhDNNftBdw1Ow83xqpSf4WMSJK8urmXD5VXS1I=
|
||||||
|
github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU=
|
||||||
|
github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.15.1 h1:BSe8uhN+xQ4r5guV/ywQI4gO59C2raYcGffYWZEjZzM=
|
||||||
|
github.com/go-playground/validator/v10 v10.15.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
|
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
|
||||||
|
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
|
||||||
|
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo=
|
||||||
|
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
|
||||||
|
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
|
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||||
|
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
|
||||||
|
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||||
|
github.com/intel-go/fastjson v0.0.0-20170329170629-f846ae58a1ab h1:K7WJJ5AnrQV/6tEh0Qqs19KLzvsq5V15f9CifKii6aU=
|
||||||
|
github.com/intel-go/fastjson v0.0.0-20170329170629-f846ae58a1ab/go.mod h1:xr9Svf97gkxlW+ZDxs47vReKp7m9EUzNhEGOLyBHR+8=
|
||||||
|
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
|
||||||
|
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||||
|
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||||
|
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||||
|
github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
|
||||||
|
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
|
||||||
|
github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
|
||||||
|
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
|
||||||
|
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
|
||||||
|
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
||||||
|
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
|
||||||
|
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
|
||||||
|
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
|
||||||
|
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
|
||||||
|
github.com/lestrrat-go/jwx v1.2.25 h1:tAx93jN2SdPvFn08fHNAhqFJazn5mBBOB8Zli0g0otA=
|
||||||
|
github.com/lestrrat-go/jwx v1.2.25/go.mod h1:zoNuZymNl5lgdcu6P7K6ie2QRll5HVfF4xwxBBK1NxY=
|
||||||
|
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||||
|
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
||||||
|
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||||
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||||
|
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
|
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||||
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
|
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||||
|
github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
|
||||||
|
github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||||
|
github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0=
|
||||||
|
github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac=
|
||||||
|
github.com/pascaldekloe/name v1.0.1 h1:9lnXOHeqeHHnWLbKfH6X98+4+ETVqFqxN09UXSjcMb0=
|
||||||
|
github.com/pascaldekloe/name v1.0.1/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9wR5UZScttM=
|
||||||
|
github.com/paulmach/orb v0.8.0 h1:W5XAt5yNPNnhaMNEf0xNSkBMJ1LzOzdk2MRlB6EN0Vs=
|
||||||
|
github.com/paulmach/orb v0.8.0/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
|
||||||
|
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
|
||||||
|
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
|
||||||
|
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
|
||||||
|
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||||
|
github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052 h1:Qp27Idfgi6ACvFQat5+VJvlYToylpM/hcyLBI3WaKPA=
|
||||||
|
github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052/go.mod h1:uvX/8buq8uVeiZiFht+0lqSLBHF+uGV8BrTv8W/SIwk=
|
||||||
|
github.com/rogpeppe/clock v0.0.0-20190514195947-2896927a307a h1:3QH7VyOaaiUHNrA9Se4YQIRkDTCw1EJls9xTUCaCeRM=
|
||||||
|
github.com/rogpeppe/clock v0.0.0-20190514195947-2896927a307a/go.mod h1:4r5QyqhjIWCcK8DO4KMclc5Iknq5qVBAlbYYzAbUScQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
|
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
|
github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
|
||||||
|
github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
|
||||||
|
github.com/secure-systems-lab/go-securesystemslib v0.7.0 h1:OwvJ5jQf9LnIAS83waAjPbcMsODrTQUpJ02eNLUoxBg=
|
||||||
|
github.com/secure-systems-lab/go-securesystemslib v0.7.0/go.mod h1:/2gYnlnHVQ6xeGtfIqFy7Do03K4cdCY0A/GlJLDKLHI=
|
||||||
|
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||||
|
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||||
|
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||||
|
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
|
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f h1:1R9KdKjCNSd7F8iGTxIpoID9prlYH8nuNYKt0XvweHA=
|
||||||
|
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f/go.mod h1:vQhwQ4meQEDfahT5kd61wLAF5AAeh5ZPLVI4JJ/tYo8=
|
||||||
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
|
||||||
|
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a h1:kAe4YSu0O0UFn1DowNo2MY5p6xzqtJ/wQ7LZynSvGaY=
|
||||||
|
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
|
||||||
|
github.com/swaggo/http-swagger v1.3.3 h1:Hu5Z0L9ssyBLofaama21iYaF2VbWyA8jdohaaCGpHsc=
|
||||||
|
github.com/swaggo/http-swagger v1.3.3/go.mod h1:sE+4PjD89IxMPm77FnkDz0sdO+p5lbXzrVWT6OTVVGo=
|
||||||
|
github.com/swaggo/swag v1.8.8 h1:/GgJmrJ8/c0z4R4hoEPZ5UeEhVGdvsII4JbVDLbR7Xc=
|
||||||
|
github.com/swaggo/swag v1.8.8/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk=
|
||||||
|
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
|
||||||
|
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||||
|
github.com/twmb/franz-go v1.20.6 h1:TpQTt4QcixJ1cHEmQGPOERvTzo99s8jAutmS7rbSD6w=
|
||||||
|
github.com/twmb/franz-go v1.20.6/go.mod h1:u+FzH2sInp7b9HNVv2cZN8AxdXy6y/AQ1Bkptu4c0FM=
|
||||||
|
github.com/twmb/franz-go/pkg/kadm v1.17.2 h1:g5f1sAxnTkYC6G96pV5u715HWhxd66hWaDZUAQ8xHY8=
|
||||||
|
github.com/twmb/franz-go/pkg/kadm v1.17.2/go.mod h1:ST55zUB+sUS+0y+GcKY/Tf1XxgVilaFpB9I19UubLmU=
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.12.0 h1:CbatD7ers1KzDNgJqPbKOq0Bz/WLBdsTH75wgzeVaPc=
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.12.0/go.mod h1:+DPt4NC8RmI6hqb8G09+3giKObE6uD2Eya6CfqBpeJY=
|
||||||
|
github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94=
|
||||||
|
github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||||
|
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
|
||||||
|
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||||
|
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||||
|
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||||
|
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok=
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
|
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
|
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
|
||||||
|
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
|
||||||
|
go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
|
||||||
|
go4.org/intern v0.0.0-20230525184215-6c62f75575cb h1:ae7kzL5Cfdmcecbh22ll7lYP3iuUdnfnhiPcSaDgH/8=
|
||||||
|
go4.org/intern v0.0.0-20230525184215-6c62f75575cb/go.mod h1:Ycrt6raEcnF5FTsLiLKkhBTO6DPX3RCUCUVnks3gFJU=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 h1:lGdhQUN/cnWdSH3291CUuxSEqc+AsGTiDxPP3r2J0l4=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||||
|
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||||
|
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||||
|
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
|
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
|
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||||
|
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||||
|
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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||||
|
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||||
|
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||||
|
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||||
|
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||||
|
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||||
|
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
|
||||||
|
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.67.3 h1:OgPcDAFKHnH8X3O4WcO4XUc8GRDeKsKReqbQtiCj7N8=
|
||||||
|
google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||||
|
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
|
gopkg.in/DataDog/dd-trace-go.v1 v1.60.1 h1:Sqkq62MxQW/RD+sgZsQuUdHWHyXI4JS5x0lxlxrv2Hk=
|
||||||
|
gopkg.in/DataDog/dd-trace-go.v1 v1.60.1/go.mod h1:6aArYrAHjnuaofJ3lKuSRQbhrBx1LcSpiEYCIScJE5Y=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/retry.v1 v1.0.3 h1:a9CArYczAVv6Qs6VGoLMio99GEs7kY9UzSF9+LD+iGs=
|
||||||
|
gopkg.in/retry.v1 v1.0.3/go.mod h1:FJkXmWiMaAo7xB+xhvDF59zhfjDWyzmyAxiT4dB688g=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||||
|
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||||
|
honnef.co/go/gotraceui v0.2.0 h1:dmNsfQ9Vl3GwbiVD7Z8d/osC6WtGGrasyrC2suc4ZIQ=
|
||||||
|
honnef.co/go/gotraceui v0.2.0/go.mod h1:qHo4/W75cA3bX0QQoSvDjbJa4R8mAyyFjbWAj63XElc=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a h1:1XCVEdxrvL6c0TGOhecLuB7U9zYNdxZEjvOqJreKZiM=
|
||||||
|
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a/go.mod h1:e83i32mAQOW1LAqEIweALsuK2Uw4mhQadA5r7b0Wobo=
|
||||||
|
mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo=
|
||||||
|
mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
|
||||||
97
services/jetfire/handlers/batch.go
Normal file
97
services/jetfire/handlers/batch.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fiskerinc/cloud-services/services/jetfire/models"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/jetfire/services"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/jetfire/utils"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//downsampling timers and delays
|
||||||
|
clearCacheTimer = time.Now().Truncate(time.Second)
|
||||||
|
featureDelay = envtool.GetEnvDuration("JETFIRE_FEATURE_DOWNSAMPLE_US", 1000000) * time.Microsecond
|
||||||
|
clearCacheDelay = envtool.GetEnvDuration("JETFIRE_STATE_TIMEOUT_MS", 3600000) * time.Millisecond
|
||||||
|
futureTimeThreshold = envtool.GetEnvDuration("JETFIRE_FUTURE_THRESHOLD_MS", 2*24*60*60*1000) * time.Millisecond
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handles the batch of Signals. returns batchFlag, error
|
||||||
|
// where batchFlag corresponds to FeatureUpdateFlag for which buffers were updated with this batch
|
||||||
|
func HandleSignalBatch(batchData []*kafka_grpc.GRPC_CANSignal, vehicleCache *models.VehicleCache, producerChannel chan models.InsertCommand) (uint, error) {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
batchFlag := uint(0)
|
||||||
|
|
||||||
|
//Iterate through received can signals and add to cache
|
||||||
|
futureWarning := false //only one future warning per batch of data
|
||||||
|
|
||||||
|
//sets of pointers to insert into insertion buffers
|
||||||
|
featureUpdates := make(map[*models.VehicleState]bool)
|
||||||
|
|
||||||
|
logger.Debug().Msgf("Processing batch %d signals...", len(batchData))
|
||||||
|
|
||||||
|
skipLog := false
|
||||||
|
|
||||||
|
for _, signal := range batchData {
|
||||||
|
signal.Timestamp = utils.FixFloatTimestampScale(signal.Timestamp)
|
||||||
|
if start.Add(futureTimeThreshold).Before(utils.FloatToTime(signal.Timestamp)) && !futureWarning {
|
||||||
|
// Throw out any signals that are from the future.
|
||||||
|
futureWarning = true
|
||||||
|
logger.Warn().Msgf("ignoring signal(s) from %s from future: %f (currently %f)", signal.Vin, signal.Timestamp, utils.TimeToFloat(start))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := vehicleCache.UpdateSignal(signal, utils.FeatureUpdateFlag)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
//do not continue yet; attempt to add to vehicle signal buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check vehicle state for update
|
||||||
|
// if signal is not in tracked vars, then skip.
|
||||||
|
// if VIN is not in vehicle cache, then log and skip
|
||||||
|
state, ok := vehicleCache.Cache[signal.Vin]
|
||||||
|
if !ok || state == nil {
|
||||||
|
if len(*vehicleCache.SignalsSet) > 0 {
|
||||||
|
_, ok = (*vehicleCache.SignalsSet)[signal.Name]
|
||||||
|
if ok && !skipLog {
|
||||||
|
// no vin state but signal update should have created a vin state... log warning
|
||||||
|
logger.Error().Msgf("unexpected missing VIN state from cache for VIN {%s} and signal {%s}", signal.Vin, signal.Name)
|
||||||
|
skipLog = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if state.TimeSincePolled(utils.FeatureUpdateFlag) > featureDelay {
|
||||||
|
featureUpdates[state] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
services.SchemaLock.Lock()
|
||||||
|
defer services.SchemaLock.Unlock()
|
||||||
|
|
||||||
|
//TODO: investigate optimizing selecting VINs that need to be appended to buffer
|
||||||
|
// iterating through maps is slow!
|
||||||
|
|
||||||
|
//batch rows for insertion for feature
|
||||||
|
for vinState := range featureUpdates {
|
||||||
|
err := services.GetFeatureBatch().AppendRow(services.GetFeatureVars(), vinState, producerChannel)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
} else {
|
||||||
|
vinState.SetPollTime(utils.FeatureUpdateFlag)
|
||||||
|
batchFlag |= utils.FeatureUpdateFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
err = services.GetFeatureLastBatch().AppendRow(services.GetFeatureVars(), vinState, producerChannel)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return batchFlag, nil
|
||||||
|
}
|
||||||
21
services/jetfire/handlers/reset.go
Normal file
21
services/jetfire/handlers/reset.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/jetfire/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ResetSchemaDefinitions(w http.ResponseWriter, r *http.Request) {
|
||||||
|
startTime := time.Now()
|
||||||
|
logger.Info().Msg("resetting tracked variables and schemas")
|
||||||
|
services.ResetCacheVars()
|
||||||
|
endTime := time.Now()
|
||||||
|
logger.Debug().Msgf("reset tracked variables and schemas, time taken: %fms; %d variables",
|
||||||
|
float64(endTime.UnixMilli()-startTime.UnixMilli()),
|
||||||
|
len(services.GetFeatureVars()),
|
||||||
|
)
|
||||||
|
}
|
||||||
45
services/jetfire/main.go
Normal file
45
services/jetfire/main.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/services/jetfire/controllers"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/jetfire/server"
|
||||||
|
"github.com/fiskerinc/cloud-services/services/jetfire/services"
|
||||||
|
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/kafka"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/tracer"
|
||||||
|
"github.com/fiskerinc/cloud-services/pkg/utils/app"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
SERVICE_NAME = "jetfire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
app.Setup(SERVICE_NAME, cleanup)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
tracer.Start()
|
||||||
|
defer tracer.Stop()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
go controllers.HealthCheck()
|
||||||
|
|
||||||
|
services.ResetCacheVars()
|
||||||
|
go server.StartHTTPServer() //listen for reset requests
|
||||||
|
go server.StartConsumer(ctx, kafka.VehicleSignal)
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanup() {
|
||||||
|
logger.Close()
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user