Compare commits

..

29 Commits

Author SHA1 Message Date
fb6941e6bd fix recorder npe, implement filters
All checks were successful
Build and Publish / check-chart (push) Successful in 14s
Build and Publish / helm-release (push) Has been skipped
Build and Publish / release (push) Successful in 3m36s
2025-03-21 16:26:23 -04:00
a5abbbec1f move recorder to interface
All checks were successful
Build and Publish / check-chart (push) Successful in 36s
Build and Publish / helm-release (push) Has been skipped
Build and Publish / release (push) Successful in 3m8s
2025-03-21 15:54:28 -04:00
e9b70fe6e0 update gitea ci
All checks were successful
Build and Publish / release (push) Has been skipped
Build and Publish / check-chart (push) Successful in 20s
Build and Publish / helm-release (push) Has been skipped
2025-03-21 13:17:03 -04:00
ca18eba167 update gitea ci
All checks were successful
Build and Publish / chart-updated (push) Successful in 40s
Build and Publish / helm-release (push) Successful in 18s
Build and Publish / release (push) Successful in 3m4s
2025-03-21 13:14:45 -04:00
c020c4eec8 update gitea ci
All checks were successful
Build and Publish / release (push) Has been skipped
Build and Publish / chart-updated (push) Successful in 10s
Build and Publish / helm-release (push) Successful in 18s
2025-03-21 13:12:39 -04:00
df7c2dff40 update gitea ci
All checks were successful
Build and Publish / release (push) Has been skipped
Build and Publish / chart-updated (push) Successful in 10s
Build and Publish / helm-release (push) Has been skipped
2025-03-21 13:09:47 -04:00
0b27285b86 update gitea ci
Some checks failed
Build and Publish / release (push) Has been skipped
Build and Publish / chart-updated (push) Failing after 18s
Build and Publish / helm-release (push) Has been skipped
2025-03-21 13:08:08 -04:00
1f097b1fd7 fix recorder 2025-03-21 12:52:39 -04:00
42eea2346b refactor state to recorder, finish implementing 2025-03-21 09:22:32 -04:00
a5948cf334 bump helm chart
All checks were successful
Build and Publish / release (push) Has been skipped
Build and Publish / helm-release (push) Successful in 17s
2025-03-20 17:04:35 -04:00
deb831a2c5 bump helm chart
All checks were successful
Build and Publish / release (push) Has been skipped
Build and Publish / helm-release (push) Has been skipped
2025-03-20 16:50:06 -04:00
d8291150d4 fix tag
All checks were successful
Build and Publish / release (push) Successful in 2m45s
Build and Publish / helm-release (push) Successful in 21s
2025-03-20 16:41:51 -04:00
4a35f11553 bump helm version
All checks were successful
Build and Publish / release (push) Successful in 2m55s
Build and Publish / helm-release (push) Successful in 20s
2025-03-20 16:31:04 -04:00
399c694c3e add map update test 2025-03-20 16:26:11 -04:00
133c6f7315 map weather update
All checks were successful
Build and Publish / release (push) Successful in 4m12s
Build and Publish / helm-release (push) Successful in 22s
2025-03-20 16:04:28 -04:00
220cc818e7 continue working on weather mapping 2025-03-15 09:53:13 -04:00
69fd27916b implement grpc weather 2025-03-14 23:10:01 -04:00
421c246cc6 remove flux from ale 2025-03-10 16:11:12 -04:00
6f297829fc add flux to ale 2025-03-10 16:08:30 -04:00
797b0a2f3a update TODO 2025-03-08 17:40:03 -05:00
be66762834 add helm ci 2025-03-08 17:30:56 -05:00
7e53afe035 add helm ci
All checks were successful
Build and Publish / helm-release (push) Successful in 37s
Build and Publish / release (push) Successful in 3m7s
2025-03-08 17:29:40 -05:00
be2895695f add helm ci
All checks were successful
Build and Publish / helm-release (push) Successful in 41s
Build and Publish / release (push) Successful in 2m43s
2025-03-08 17:22:19 -05:00
27af90322a add helm ci
Some checks failed
Build and Publish / helm-release (push) Failing after 1m5s
Build and Publish / release (push) Successful in 3m16s
2025-03-08 17:16:35 -05:00
a8e7b24656 add health checks 2025-03-08 16:53:15 -05:00
2653825ac8 implement hull helm chart 2025-03-08 16:35:08 -05:00
a99e52ae4c implement hull helm chart 2025-03-08 16:08:41 -05:00
48828bf8d0 implement hull helm chart 2025-03-08 12:33:45 -05:00
bcf4c0b5ce start helm chart 2025-03-07 20:35:23 -05:00
27 changed files with 1832 additions and 204 deletions

View File

@ -2,6 +2,7 @@ name: Build and Publish
on:
push:
tags: ["v*"]
branches: ["main"]
env:
PACKAGE_NAME: ambient-local-exporter
@ -16,14 +17,15 @@ env:
DOCKER_USER: rmcguire
DOCKER_REPO: rmcguire/ambient-local-exporter
DOCKER_IMG: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_REPO }}
CHART_DIR: helm/ambient-local-exporter
jobs:
release:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v') # Only run on tag push
steps:
- name: Checkout Code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up Go Environment
uses: actions/setup-go@v4
@ -86,3 +88,46 @@ jobs:
build-args: |
VER_PKG=${{ env.VER_PKG }}
VERSION=${{ github.ref_name }}
# Detect if the helm chart was updated
check-chart:
runs-on: ubuntu-latest
outputs:
chart-updated: ${{ steps.filter.outputs.chart }}
steps:
- uses: actions/checkout@v4
- name: Check Chart Changed
uses: dorny/paths-filter@v3
id: filter
with:
base: ${{ github.ref }}
filters: |
chart:
- helm/ambient-local-exporter/Chart.yaml
helm-release:
runs-on: ubuntu-latest
needs: check-chart
if: ${{ needs.check-chart.outputs.chart-updated == 'true' }}
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Install Helm
env:
BINARY_NAME: helm
run: |
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
- name: Package Chart
run: |
helm package --app-version ${VERSION} ${CHART_DIR}
- name: Publish Chart
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: |
curl -X POST \
-H "Authorization: token ${API_TOKEN}" \
--upload-file ./${PACKAGE_NAME}-*.tgz \
https://gitea.libretechconsulting.com/api/packages/${GITHUB_REPOSITORY_OWNER}/helm/api/charts

2
.gitignore vendored
View File

@ -30,3 +30,5 @@ config.y*ml
docker-compose.yml
.vscode
helm/values*.y*ml
helm_upgrade.sh

View File

@ -1,11 +1,11 @@
# TODO
- [ ] Finish implementing weather GRPC
- [ ] Helm Chart
- [ ] Update README
- [ ] Add Grafana dashboard
- [ ] Add new spans
## Done
- [x] Helm Chart
- [x] Add proxy to upstream support
- [x] Fix wunderground 401
- [x] Perform proxy calls in goroutines

View File

@ -25,6 +25,7 @@ const (
type GetWeatherRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Opts *GetWeatherOpts `protobuf:"bytes,1,opt,name=opts,proto3" json:"opts,omitempty"`
Limit *int32 `protobuf:"varint,2,opt,name=limit,proto3,oneof" json:"limit,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -66,6 +67,13 @@ func (x *GetWeatherRequest) GetOpts() *GetWeatherOpts {
return nil
}
func (x *GetWeatherRequest) GetLimit() int32 {
if x != nil && x.Limit != nil {
return *x.Limit
}
return 0
}
type GetWeatherResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
LastUpdated *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=last_updated,json=lastUpdated,proto3" json:"last_updated,omitempty"`
@ -543,149 +551,151 @@ var file_weather_weather_proto_rawDesc = string([]byte{
0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74,
0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x48, 0x0a, 0x11, 0x47, 0x65, 0x74,
0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74,
0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x33,
0x0a, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61,
0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x47,
0x65, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x04, 0x6f,
0x70, 0x74, 0x73, 0x22, 0x9c, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68,
0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x6c, 0x61,
0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x6c, 0x61,
0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x47, 0x0a, 0x0f, 0x77, 0x65, 0x61,
0x74, 0x68, 0x65, 0x72, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x77, 0x65, 0x61,
0x74, 0x68, 0x65, 0x72, 0x2e, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61,
0x74, 0x65, 0x52, 0x0e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74,
0x65, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65,
0x72, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73,
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a,
0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79,
0x70, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0xb7, 0x0c, 0x0a, 0x0d, 0x57, 0x65, 0x61, 0x74,
0x68, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c,
0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12,
0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x29,
0x0a, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x6f, 0x75, 0x74, 0x64, 0x6f, 0x6f, 0x72, 0x5f, 0x66,
0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x4f, 0x75,
0x74, 0x64, 0x6f, 0x6f, 0x72, 0x46, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x74, 0x65, 0x6d,
0x70, 0x5f, 0x69, 0x6e, 0x64, 0x6f, 0x6f, 0x72, 0x5f, 0x66, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01,
0x48, 0x01, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x49, 0x6e, 0x64, 0x6f, 0x6f, 0x72, 0x46, 0x88,
0x01, 0x01, 0x12, 0x2e, 0x0a, 0x10, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x5f, 0x6f,
0x75, 0x74, 0x64, 0x6f, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x0f,
0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x4f, 0x75, 0x74, 0x64, 0x6f, 0x6f, 0x72, 0x88,
0x01, 0x01, 0x12, 0x2c, 0x0a, 0x0f, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x5f, 0x69,
0x6e, 0x64, 0x6f, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x48, 0x03, 0x52, 0x0e, 0x68,
0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x64, 0x6f, 0x6f, 0x72, 0x88, 0x01, 0x01,
0x12, 0x29, 0x0a, 0x0e, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x5f, 0x6d,
0x70, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x48, 0x04, 0x52, 0x0c, 0x77, 0x69, 0x6e, 0x64,
0x53, 0x70, 0x65, 0x65, 0x64, 0x4d, 0x70, 0x68, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x77,
0x69, 0x6e, 0x64, 0x5f, 0x67, 0x75, 0x73, 0x74, 0x5f, 0x6d, 0x70, 0x68, 0x18, 0x09, 0x20, 0x01,
0x28, 0x01, 0x48, 0x05, 0x52, 0x0b, 0x77, 0x69, 0x6e, 0x64, 0x47, 0x75, 0x73, 0x74, 0x4d, 0x70,
0x68, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x61, 0x69, 0x6c,
0x79, 0x5f, 0x67, 0x75, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x48, 0x06, 0x52, 0x0c,
0x6d, 0x61, 0x78, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x47, 0x75, 0x73, 0x74, 0x88, 0x01, 0x01, 0x12,
0x1e, 0x0a, 0x08, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28,
0x05, 0x48, 0x07, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x44, 0x69, 0x72, 0x88, 0x01, 0x01, 0x12,
0x2c, 0x0a, 0x10, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x69, 0x72, 0x5f, 0x61, 0x76, 0x67, 0x5f,
0x31, 0x30, 0x6d, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x48, 0x08, 0x52, 0x0d, 0x77, 0x69, 0x6e,
0x64, 0x44, 0x69, 0x72, 0x41, 0x76, 0x67, 0x31, 0x30, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x13, 0x0a,
0x02, 0x75, 0x76, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x48, 0x09, 0x52, 0x02, 0x75, 0x76, 0x88,
0x01, 0x01, 0x12, 0x2c, 0x0a, 0x0f, 0x73, 0x6f, 0x6c, 0x61, 0x72, 0x5f, 0x72, 0x61, 0x64, 0x69,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x01, 0x48, 0x0a, 0x52, 0x0e, 0x73,
0x6f, 0x6c, 0x61, 0x72, 0x52, 0x61, 0x64, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01,
0x12, 0x29, 0x0a, 0x0e, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f,
0x69, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x01, 0x48, 0x0b, 0x52, 0x0c, 0x68, 0x6f, 0x75, 0x72,
0x6c, 0x79, 0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x65,
0x76, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x10, 0x20, 0x01,
0x28, 0x01, 0x48, 0x0c, 0x52, 0x0b, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x69, 0x6e, 0x49,
0x6e, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x72, 0x61,
0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x01, 0x48, 0x0d, 0x52, 0x0b, 0x64,
0x61, 0x69, 0x6c, 0x79, 0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a,
0x0e, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18,
0x12, 0x20, 0x01, 0x28, 0x01, 0x48, 0x0e, 0x52, 0x0c, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52,
0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x0f, 0x6d, 0x6f, 0x6e, 0x74,
0x68, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x13, 0x20, 0x01, 0x28,
0x01, 0x48, 0x0f, 0x52, 0x0d, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x52, 0x61, 0x69, 0x6e,
0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0e, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x5f,
0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x01, 0x48, 0x10, 0x52,
0x0c, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01,
0x12, 0x27, 0x0a, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69,
0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x01, 0x48, 0x11, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c,
0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x3c, 0x0a, 0x09, 0x62, 0x61, 0x74,
0x74, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61,
0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x42,
0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x09, 0x62, 0x61,
0x74, 0x74, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x11, 0x62, 0x61, 0x72, 0x6f, 0x6d,
0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x18, 0x17, 0x20, 0x01,
0x28, 0x01, 0x48, 0x12, 0x52, 0x0f, 0x62, 0x61, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6c, 0x61, 0x74,
0x69, 0x76, 0x65, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x11, 0x62, 0x61, 0x72, 0x6f,
0x6d, 0x5f, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x18, 0x18, 0x20,
0x01, 0x28, 0x01, 0x48, 0x13, 0x52, 0x0f, 0x62, 0x61, 0x72, 0x6f, 0x6d, 0x41, 0x62, 0x73, 0x6f,
0x6c, 0x75, 0x74, 0x65, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0b, 0x64, 0x65, 0x77,
0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x66, 0x18, 0x19, 0x20, 0x01, 0x28, 0x01, 0x48, 0x14,
0x52, 0x09, 0x64, 0x65, 0x77, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x46, 0x88, 0x01, 0x01, 0x12, 0x25,
0x0a, 0x0c, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x6c, 0x5f, 0x66, 0x18, 0x1a,
0x20, 0x01, 0x28, 0x01, 0x48, 0x15, 0x52, 0x0a, 0x77, 0x69, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c,
0x6c, 0x46, 0x88, 0x01, 0x01, 0x12, 0x57, 0x0a, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x68, 0x75,
0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x73, 0x18, 0x1b,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x77,
0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x48, 0x75, 0x6d, 0x69, 0x64,
0x69, 0x74, 0x79, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x48,
0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x73, 0x42, 0x11,
0x0a, 0x0f, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x6f, 0x75, 0x74, 0x64, 0x6f, 0x6f, 0x72, 0x5f,
0x66, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x69, 0x6e, 0x64, 0x6f, 0x6f,
0x72, 0x5f, 0x66, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79,
0x5f, 0x6f, 0x75, 0x74, 0x64, 0x6f, 0x6f, 0x72, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x68, 0x75, 0x6d,
0x69, 0x64, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x64, 0x6f, 0x6f, 0x72, 0x42, 0x11, 0x0a, 0x0f,
0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x5f, 0x6d, 0x70, 0x68, 0x42,
0x10, 0x0a, 0x0e, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x67, 0x75, 0x73, 0x74, 0x5f, 0x6d, 0x70,
0x68, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f,
0x67, 0x75, 0x73, 0x74, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x69,
0x72, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x69, 0x72, 0x5f, 0x61,
0x76, 0x67, 0x5f, 0x31, 0x30, 0x6d, 0x42, 0x05, 0x0a, 0x03, 0x5f, 0x75, 0x76, 0x42, 0x12, 0x0a,
0x10, 0x5f, 0x73, 0x6f, 0x6c, 0x61, 0x72, 0x5f, 0x72, 0x61, 0x64, 0x69, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69,
0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x72,
0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x64, 0x61, 0x69, 0x6c, 0x79,
0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x77, 0x65, 0x65,
0x6b, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x12, 0x0a, 0x10, 0x5f,
0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x42,
0x11, 0x0a, 0x0f, 0x5f, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f,
0x69, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x72, 0x61, 0x69,
0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x62, 0x61, 0x72, 0x6f, 0x6d, 0x5f, 0x72,
0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x62,
0x61, 0x72, 0x6f, 0x6d, 0x5f, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e,
0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x77, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x66,
0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x6c, 0x5f,
0x66, 0x22, 0x7d, 0x0a, 0x12, 0x54, 0x65, 0x6d, 0x70, 0x48, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74,
0x79, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x06, 0x74,
0x65, 0x6d, 0x70, 0x5f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x05, 0x74,
0x65, 0x6d, 0x70, 0x46, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x68, 0x75, 0x6d, 0x69, 0x64,
0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x08, 0x68, 0x75, 0x6d,
0x69, 0x64, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x74, 0x65, 0x6d,
0x70, 0x5f, 0x66, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79,
0x22, 0x55, 0x0a, 0x0d, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75,
0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12,
0x1b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48,
0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07,
0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x54, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x65, 0x61,
0x2e, 0x6c, 0x69, 0x62, 0x72, 0x65, 0x74, 0x65, 0x63, 0x68, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c,
0x74, 0x69, 0x6e, 0x67, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6d, 0x63, 0x67, 0x75, 0x69, 0x72,
0x65, 0x2f, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x2d,
0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61,
0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
0x70, 0x74, 0x73, 0x12, 0x19, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01,
0x28, 0x05, 0x48, 0x00, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x42, 0x08,
0x0a, 0x06, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x9c, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74,
0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x3d, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x52, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x47,
0x0a, 0x0f, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e,
0x74, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65,
0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72,
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x57,
0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0c, 0x73, 0x74,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x48, 0x00, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x88,
0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79,
0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73,
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x0f, 0x0a, 0x0d, 0x5f,
0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0xb7, 0x0c, 0x0a,
0x0d, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x21,
0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d,
0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x6f, 0x75, 0x74, 0x64,
0x6f, 0x6f, 0x72, 0x5f, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x0c, 0x74,
0x65, 0x6d, 0x70, 0x4f, 0x75, 0x74, 0x64, 0x6f, 0x6f, 0x72, 0x46, 0x88, 0x01, 0x01, 0x12, 0x27,
0x0a, 0x0d, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x69, 0x6e, 0x64, 0x6f, 0x6f, 0x72, 0x5f, 0x66, 0x18,
0x05, 0x20, 0x01, 0x28, 0x01, 0x48, 0x01, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x49, 0x6e, 0x64,
0x6f, 0x6f, 0x72, 0x46, 0x88, 0x01, 0x01, 0x12, 0x2e, 0x0a, 0x10, 0x68, 0x75, 0x6d, 0x69, 0x64,
0x69, 0x74, 0x79, 0x5f, 0x6f, 0x75, 0x74, 0x64, 0x6f, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28,
0x05, 0x48, 0x02, 0x52, 0x0f, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x4f, 0x75, 0x74,
0x64, 0x6f, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x0f, 0x68, 0x75, 0x6d, 0x69, 0x64,
0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x64, 0x6f, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05,
0x48, 0x03, 0x52, 0x0e, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x64, 0x6f,
0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0e, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x73, 0x70,
0x65, 0x65, 0x64, 0x5f, 0x6d, 0x70, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x48, 0x04, 0x52,
0x0c, 0x77, 0x69, 0x6e, 0x64, 0x53, 0x70, 0x65, 0x65, 0x64, 0x4d, 0x70, 0x68, 0x88, 0x01, 0x01,
0x12, 0x27, 0x0a, 0x0d, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x67, 0x75, 0x73, 0x74, 0x5f, 0x6d, 0x70,
0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x48, 0x05, 0x52, 0x0b, 0x77, 0x69, 0x6e, 0x64, 0x47,
0x75, 0x73, 0x74, 0x4d, 0x70, 0x68, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0e, 0x6d, 0x61, 0x78,
0x5f, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x67, 0x75, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28,
0x01, 0x48, 0x06, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x47, 0x75, 0x73,
0x74, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x69, 0x72,
0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x48, 0x07, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x44, 0x69,
0x72, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x10, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x69, 0x72,
0x5f, 0x61, 0x76, 0x67, 0x5f, 0x31, 0x30, 0x6d, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x48, 0x08,
0x52, 0x0d, 0x77, 0x69, 0x6e, 0x64, 0x44, 0x69, 0x72, 0x41, 0x76, 0x67, 0x31, 0x30, 0x6d, 0x88,
0x01, 0x01, 0x12, 0x13, 0x0a, 0x02, 0x75, 0x76, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x48, 0x09,
0x52, 0x02, 0x75, 0x76, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x0f, 0x73, 0x6f, 0x6c, 0x61, 0x72,
0x5f, 0x72, 0x61, 0x64, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x01,
0x48, 0x0a, 0x52, 0x0e, 0x73, 0x6f, 0x6c, 0x61, 0x72, 0x52, 0x61, 0x64, 0x69, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0e, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x5f,
0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x01, 0x48, 0x0b, 0x52,
0x0c, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01,
0x12, 0x27, 0x0a, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69,
0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x01, 0x48, 0x0c, 0x52, 0x0b, 0x65, 0x76, 0x65, 0x6e, 0x74,
0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x64, 0x61, 0x69,
0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x01,
0x48, 0x0d, 0x52, 0x0b, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88,
0x01, 0x01, 0x12, 0x29, 0x0a, 0x0e, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69,
0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x48, 0x0e, 0x52, 0x0c, 0x77, 0x65,
0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a,
0x0f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e,
0x18, 0x13, 0x20, 0x01, 0x28, 0x01, 0x48, 0x0f, 0x52, 0x0d, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c,
0x79, 0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0e, 0x79, 0x65,
0x61, 0x72, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x14, 0x20, 0x01,
0x28, 0x01, 0x48, 0x10, 0x52, 0x0c, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x52, 0x61, 0x69, 0x6e,
0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x72,
0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x01, 0x48, 0x11, 0x52, 0x0b,
0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x3c,
0x0a, 0x09, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x77, 0x65, 0x61, 0x74,
0x68, 0x65, 0x72, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75,
0x73, 0x52, 0x09, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x11,
0x62, 0x61, 0x72, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69,
0x6e, 0x18, 0x17, 0x20, 0x01, 0x28, 0x01, 0x48, 0x12, 0x52, 0x0f, 0x62, 0x61, 0x72, 0x6f, 0x6d,
0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a,
0x11, 0x62, 0x61, 0x72, 0x6f, 0x6d, 0x5f, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f,
0x69, 0x6e, 0x18, 0x18, 0x20, 0x01, 0x28, 0x01, 0x48, 0x13, 0x52, 0x0f, 0x62, 0x61, 0x72, 0x6f,
0x6d, 0x41, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x23,
0x0a, 0x0b, 0x64, 0x65, 0x77, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x66, 0x18, 0x19, 0x20,
0x01, 0x28, 0x01, 0x48, 0x14, 0x52, 0x09, 0x64, 0x65, 0x77, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x46,
0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0c, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x63, 0x68, 0x69, 0x6c,
0x6c, 0x5f, 0x66, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x01, 0x48, 0x15, 0x52, 0x0a, 0x77, 0x69, 0x6e,
0x64, 0x43, 0x68, 0x69, 0x6c, 0x6c, 0x46, 0x88, 0x01, 0x01, 0x12, 0x57, 0x0a, 0x15, 0x74, 0x65,
0x6d, 0x70, 0x5f, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x65, 0x6e, 0x73,
0x6f, 0x72, 0x73, 0x18, 0x1b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6d, 0x62, 0x69,
0x65, 0x6e, 0x74, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70,
0x48, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x52, 0x13,
0x74, 0x65, 0x6d, 0x70, 0x48, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x53, 0x65, 0x6e, 0x73,
0x6f, 0x72, 0x73, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x6f, 0x75, 0x74,
0x64, 0x6f, 0x6f, 0x72, 0x5f, 0x66, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x5f,
0x69, 0x6e, 0x64, 0x6f, 0x6f, 0x72, 0x5f, 0x66, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x68, 0x75, 0x6d,
0x69, 0x64, 0x69, 0x74, 0x79, 0x5f, 0x6f, 0x75, 0x74, 0x64, 0x6f, 0x6f, 0x72, 0x42, 0x12, 0x0a,
0x10, 0x5f, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x64, 0x6f, 0x6f,
0x72, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64,
0x5f, 0x6d, 0x70, 0x68, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x67, 0x75,
0x73, 0x74, 0x5f, 0x6d, 0x70, 0x68, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x64,
0x61, 0x69, 0x6c, 0x79, 0x5f, 0x67, 0x75, 0x73, 0x74, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x77, 0x69,
0x6e, 0x64, 0x5f, 0x64, 0x69, 0x72, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f,
0x64, 0x69, 0x72, 0x5f, 0x61, 0x76, 0x67, 0x5f, 0x31, 0x30, 0x6d, 0x42, 0x05, 0x0a, 0x03, 0x5f,
0x75, 0x76, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x6f, 0x6c, 0x61, 0x72, 0x5f, 0x72, 0x61, 0x64,
0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x6c,
0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x65, 0x76,
0x65, 0x6e, 0x74, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f,
0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x11, 0x0a,
0x0f, 0x5f, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e,
0x42, 0x12, 0x0a, 0x10, 0x5f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69,
0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x5f,
0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x74, 0x6f, 0x74, 0x61,
0x6c, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x62, 0x61,
0x72, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x42,
0x14, 0x0a, 0x12, 0x5f, 0x62, 0x61, 0x72, 0x6f, 0x6d, 0x5f, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75,
0x74, 0x65, 0x5f, 0x69, 0x6e, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x77, 0x5f, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x5f, 0x66, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x63,
0x68, 0x69, 0x6c, 0x6c, 0x5f, 0x66, 0x22, 0x7d, 0x0a, 0x12, 0x54, 0x65, 0x6d, 0x70, 0x48, 0x75,
0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x12, 0x1a, 0x0a, 0x06, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01,
0x48, 0x00, 0x52, 0x05, 0x74, 0x65, 0x6d, 0x70, 0x46, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08,
0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01,
0x52, 0x08, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a,
0x07, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x66, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x68, 0x75, 0x6d,
0x69, 0x64, 0x69, 0x74, 0x79, 0x22, 0x55, 0x0a, 0x0d, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e,
0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f,
0x6e, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02,
0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01,
0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x54, 0x5a, 0x52,
0x67, 0x69, 0x74, 0x65, 0x61, 0x2e, 0x6c, 0x69, 0x62, 0x72, 0x65, 0x74, 0x65, 0x63, 0x68, 0x63,
0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6d,
0x63, 0x67, 0x75, 0x69, 0x72, 0x65, 0x2f, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2d, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x2d, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x61, 0x70,
0x69, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x77, 0x65, 0x61, 0x74, 0x68,
0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
})
var (
@ -728,6 +738,7 @@ func file_weather_weather_proto_init() {
if File_weather_weather_proto != nil {
return
}
file_weather_weather_proto_msgTypes[0].OneofWrappers = []any{}
file_weather_weather_proto_msgTypes[2].OneofWrappers = []any{}
file_weather_weather_proto_msgTypes[3].OneofWrappers = []any{}
file_weather_weather_proto_msgTypes[4].OneofWrappers = []any{}

View File

@ -3,7 +3,8 @@ services:
ambient-local-exporter:
build: .
ports:
- 8080:8080
- 8080:8080 # HTTP
- 8081:8081 # GRPC
volumes:
- ./config.yaml:/app/config.yaml
command:

View File

@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@ -0,0 +1,6 @@
dependencies:
- name: hull
repository: https://vidispine.github.io/hull
version: 1.32.2
digest: sha256:7b73a7f152916fed9842efe4f65081b1cda0fcebd8f36d27e48136b608ce305f
generated: "2025-03-08T12:22:41.343082-05:00"

View File

@ -0,0 +1,30 @@
apiVersion: v2
name: ambient-local-exporter
description: Cloud-free metrics exporter for ambient weather stations
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.2
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "v0.10.2"
dependencies:
- name: hull
repository: https://vidispine.github.io/hull
version: 1.32.2

Binary file not shown.

View File

@ -0,0 +1,643 @@
################################
### values.yaml for HULL
### The basic pre-configuration takes place here.
###
### Do not change this file, use additional values.hull.yaml
### to overwrite the selected fields!
################################
###################################################
### CONFIG
config:
general:
rbac: true
fullnameOverride: ""
nameOverride: ""
namespaceOverride: ""
noObjectNamePrefixes: false
createImagePullSecretsFromRegistries: true
globalImageRegistryServer: ""
globalImageRegistryToFirstRegistrySecretServer: false
serialization:
configmap:
enabled: true
fileExtensions:
json: toPrettyJson
yml: toYaml
yaml: toYaml
secret:
enabled: true
fileExtensions:
json: toPrettyJson
yml: toYaml
yaml: toYaml
render:
passes: 3
emptyLabels: false
emptyAnnotations: false
emptyTemplateLabels: false
emptyTemplateAnnotations: false
emptyHullObjects: false
postRender:
globalStringReplacements:
instanceKey:
enabled: false
string: _HULL_OBJECT_TYPE_DEFAULT_
replacement: OBJECT_INSTANCE_KEY
instanceKeyResolved:
enabled: false
string: _HULL_OBJECT_TYPE_DEFAULT_
replacement: OBJECT_INSTANCE_KEY_RESOLVED
instanceName:
enabled: false
string: _HULL_OBJECT_TYPE_DEFAULT_
replacement: OBJECT_INSTANCE_NAME
errorChecks:
objectYamlValid: true
hullGetTransformationReferenceValid: true
containerImageValid: true
virtualFolderDataPathExists: true
virtualFolderDataInlineValid: false
debug:
renderBrokenHullGetTransformationReferences: false
renderNilWhenInlineIsNil: false
renderPathMissingWhenPathIsNonExistent: false
metadata:
labels:
common:
'app.kubernetes.io/managed-by':
'app.kubernetes.io/version':
'app.kubernetes.io/part-of':
'app.kubernetes.io/name':
'app.kubernetes.io/instance':
'app.kubernetes.io/component':
'helm.sh/chart':
'vidispine.hull/version':
custom: {}
annotations:
hashes: false
custom: {}
data: {}
specific: {}
templates:
pod:
global: {}
container:
global: {}
###################################################
###################################################
### OBJECTS
objects:
# NAMESPACE
namespace:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
staticName: true
annotations: {}
labels: {}
###################################################
# CONFIGMAPS
configmap:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# SECRETS
secret:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# REGISTRIES
registry:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# SERVICEACCOUNTS
serviceaccount:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
default:
enabled: _HT?eq (dig "serviceAccountName" "" _HT*hull.config.templates.pod.global) ""
annotations: {}
labels: {}
###################################################
# ROLES
role:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
rules:
_HULL_OBJECT_TYPE_DEFAULT_: {}
default:
enabled: _HT?eq (dig "serviceAccountName" "" _HT*hull.config.templates.pod.global) ""
rules: {}
###################################################
# ROLEBINDINGS
rolebinding:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
default:
enabled: _HT?eq (dig "serviceAccountName" "" _HT*hull.config.templates.pod.global) ""
roleRef:
apiGroup: "rbac.authorization.k8s.io"
kind: "Role"
name: _HT^default
subjects:
- kind: ServiceAccount
name: _HT^default
namespace: _HT**Release.Namespace
###################################################
# CLUSTERROLES
clusterrole:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
rules:
_HULL_OBJECT_TYPE_DEFAULT_: {}
###################################################
# CLUSTERROLEBINDINGS
clusterrolebinding:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# CUSTOMRESOURCEDEFINITIONS (deprecated with Helm3)
# customresourcedefinitions:
# _HULL_OBJECT_TYPE_DEFAULT_:
# enabled: true
# annotations: {}
# labels: {}
###################################################
# CUSTOMRESOURCES
customresource:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# PERSISTENTVOLUMECLAIMS
persistentvolumeclaim:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# PERSISTENTVOLUMES
persistentvolume:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# STORAGECLASSES
storageclass:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# SERVICES
service:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
ports:
_HULL_OBJECT_TYPE_DEFAULT_: {}
###################################################
# INGRESSES
ingress:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
tls:
_HULL_OBJECT_TYPE_DEFAULT_: {}
rules:
_HULL_OBJECT_TYPE_DEFAULT_:
http:
paths:
_HULL_OBJECT_TYPE_DEFAULT_: {}
###################################################
# INGRESSCLASSES
ingressclass:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# DEPLOYMENTS
deployment:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
templateAnnotations: {}
templateLabels: {}
pod:
initContainers:
_HULL_OBJECT_TYPE_DEFAULT_:
env:
_HULL_OBJECT_TYPE_DEFAULT_: {}
envFrom:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumeMounts:
_HULL_OBJECT_TYPE_DEFAULT_: {}
containers:
_HULL_OBJECT_TYPE_DEFAULT_:
env:
_HULL_OBJECT_TYPE_DEFAULT_: {}
envFrom:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumeMounts:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumes:
_HULL_OBJECT_TYPE_DEFAULT_: {}
###################################################
# JOBS
job:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
templateAnnotations: {}
templateLabels: {}
pod:
initContainers:
_HULL_OBJECT_TYPE_DEFAULT_:
env:
_HULL_OBJECT_TYPE_DEFAULT_: {}
envFrom:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumeMounts:
_HULL_OBJECT_TYPE_DEFAULT_: {}
containers:
_HULL_OBJECT_TYPE_DEFAULT_:
env:
_HULL_OBJECT_TYPE_DEFAULT_: {}
envFrom:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumeMounts:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumes:
_HULL_OBJECT_TYPE_DEFAULT_: {}
###################################################
# CRONJOBS
cronjob:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
job:
templateAnnotations: {}
templateLabels: {}
pod:
initContainers:
_HULL_OBJECT_TYPE_DEFAULT_:
env:
_HULL_OBJECT_TYPE_DEFAULT_: {}
envFrom:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumeMounts:
_HULL_OBJECT_TYPE_DEFAULT_: {}
containers:
_HULL_OBJECT_TYPE_DEFAULT_:
env:
_HULL_OBJECT_TYPE_DEFAULT_: {}
envFrom:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumeMounts:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumes:
_HULL_OBJECT_TYPE_DEFAULT_: {}
###################################################
# DAEMONSETS
daemonset:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
templateAnnotations: {}
templateLabels: {}
pod:
initContainers:
_HULL_OBJECT_TYPE_DEFAULT_:
env:
_HULL_OBJECT_TYPE_DEFAULT_: {}
envFrom:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumeMounts:
_HULL_OBJECT_TYPE_DEFAULT_: {}
containers:
_HULL_OBJECT_TYPE_DEFAULT_:
env:
_HULL_OBJECT_TYPE_DEFAULT_: {}
envFrom:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumeMounts:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumes:
_HULL_OBJECT_TYPE_DEFAULT_: {}
###################################################
# STATEFULSETS
statefulset:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
templateAnnotations: {}
templateLabels: {}
pod:
initContainers:
_HULL_OBJECT_TYPE_DEFAULT_:
env:
_HULL_OBJECT_TYPE_DEFAULT_: {}
envFrom:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumeMounts:
_HULL_OBJECT_TYPE_DEFAULT_: {}
containers:
_HULL_OBJECT_TYPE_DEFAULT_:
env:
_HULL_OBJECT_TYPE_DEFAULT_: {}
envFrom:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumeMounts:
_HULL_OBJECT_TYPE_DEFAULT_: {}
volumes:
_HULL_OBJECT_TYPE_DEFAULT_: {}
###################################################
# SERVICEMONITORS
servicemonitor:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# HORIZONTALPODAUTOSCALER
horizontalpodautoscaler:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# PODDISRUPTIONBUDGET
poddisruptionbudget:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# PRIORITYCLASS
priorityclass:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# ENDPOINTS
endpoints:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# ENDPOINTSLICE
endpointslice:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# LIMITRANGE
limitrange:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
###################################################
# MUTATINGWEBHOOKCONFIGURATION
mutatingwebhookconfiguration:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
webhooks:
_HULL_OBJECT_TYPE_DEFAULT_: {}
###################################################
# VALIDATINGWEBHOOKCONFIGURATION
validatingwebhookconfiguration:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
webhooks:
_HULL_OBJECT_TYPE_DEFAULT_: {}
###################################################
# RESOURCEQUOTA
resourcequota:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
##################################################
# NETWORKPOLICY
networkpolicy:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
##################################################
# GATEWAY API - BACKENDLBPOLICY
backendlbpolicy:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
targetRefs:
_HULL_OBJECT_TYPE_DEFAULT_: {}
##################################################
# GATEWAY API - BACKENDTLSPOLICY
backendtlspolicy:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
targetRefs:
_HULL_OBJECT_TYPE_DEFAULT_: {}
##################################################
# GATEWAY API - GATEWAYCLASS
gatewayclass:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
##################################################
# GATEWAY API - GATEWAY
gateway:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
addresses:
_HULL_OBJECT_TYPE_DEFAULT_: {}
listeners:
_HULL_OBJECT_TYPE_DEFAULT_:
tls:
certificateRefs:
_HULL_OBJECT_TYPE_DEFAULT_: {}
frontendValidation:
caCertificateRefs:
_HULL_OBJECT_TYPE_DEFAULT_: {}
allowedRoutes:
kinds:
_HULL_OBJECT_TYPE_DEFAULT_: {}
##################################################
# GATEWAY API - GRPCROUTE
grpcroute:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
parentRefs:
_HULL_OBJECT_TYPE_DEFAULT_: {}
rules:
_HULL_OBJECT_TYPE_DEFAULT_:
matches:
_HULL_OBJECT_TYPE_DEFAULT_: {}
filters:
_HULL_OBJECT_TYPE_DEFAULT_: {}
backendRefs:
_HULL_OBJECT_TYPE_DEFAULT_:
filters:
_HULL_OBJECT_TYPE_DEFAULT_: {}
##################################################
# GATEWAY API - REFERENCEGRANT
referencegrant:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
from:
_HULL_OBJECT_TYPE_DEFAULT_: {}
to:
_HULL_OBJECT_TYPE_DEFAULT_: {}
##################################################
# GATEWAY API - TCPROUTE
tcproute:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
parentRefs:
_HULL_OBJECT_TYPE_DEFAULT_: {}
rules:
_HULL_OBJECT_TYPE_DEFAULT_:
backendRefs:
_HULL_OBJECT_TYPE_DEFAULT_: {}
##################################################
# GATEWAY API - TLSROUTE
tlsroute:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
parentRefs:
_HULL_OBJECT_TYPE_DEFAULT_: {}
rules:
_HULL_OBJECT_TYPE_DEFAULT_:
backendRefs:
_HULL_OBJECT_TYPE_DEFAULT_: {}
##################################################
# GATEWAY API - UDPROUTE
udproute:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
parentRefs:
_HULL_OBJECT_TYPE_DEFAULT_: {}
rules:
_HULL_OBJECT_TYPE_DEFAULT_:
backendRefs:
_HULL_OBJECT_TYPE_DEFAULT_: {}
##################################################
# GATEWAY API - HTTPROUTE
httproute:
_HULL_OBJECT_TYPE_DEFAULT_:
enabled: true
annotations: {}
labels: {}
parentRefs:
_HULL_OBJECT_TYPE_DEFAULT_: {}
rules:
_HULL_OBJECT_TYPE_DEFAULT_:
matches:
_HULL_OBJECT_TYPE_DEFAULT_: {}
filters:
_HULL_OBJECT_TYPE_DEFAULT_: {}
backendRefs:
_HULL_OBJECT_TYPE_DEFAULT_:
filters:
_HULL_OBJECT_TYPE_DEFAULT_: {}
##################################################

View File

@ -0,0 +1 @@
{{- include "hull.objects.prepare.all" (dict "HULL_ROOT_KEY" "hull" "ROOT_CONTEXT" $) }}

View File

@ -0,0 +1,217 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/vidispine/hull/refs/heads/main/hull/values.schema.json
hull:
config:
## Ambient-Local-Exporter settings (config.yaml)
appConfig:
## App Config
environment: production
name: ambient-local-exporter
logging:
level: info
format: json
output: stdout
timeFormat: rfc3339
http:
listen: :8080
logRequests: false
grpc:
enabled: true
listen: :8081
logRequests: true
enableReflection: true
enableInstrumentation: true
otel:
enabled: true
metricIntervalSecs: 30
stdoutEnabled: false
## Ambient Config
metricPrefix: weather
weatherStations:
[]
# - name: Home Weather Station
# equipment: Ambient WS-2909
# awnPassKey: D3:AD:B3:3F:00:00
# proxyToAWN: true
# proxyToWunderground: false
# keepMetrics:
# - BaromAbsoluteIn
# - BaromRelativeIn
# - HumidityIndoor
# - StationType
# - TempIndoorF
# discardMetrics: []
# - name: Shop Weather Station
# equipment: Ambient WS-5000
# awnPassKey: D3:AD:B3:3F:00:00
# wundergroundID: KINCOLUMXXX
# wundergroundPassword: somekey
# proxyToAWN: true
# proxyToWunderground: true
# keepMetrics: []
# discardMetrics: []
# sensorMappings:
# TempHumiditySensor1: Deep Freezer
## Chart settings
settings:
resources: {} # Applies to the exporter container
repo: gitea.libretechconsulting.com/rmcguire/ambient-local-exporter
tag: _HT**Chart.AppVersion
httpPort: 8080 # Should match appConfig http.listen
grpcPort: 8081 # Should match appConfig grpc.listen
# Use this as a shortcut, or create your own hull.objects.httproute
httproute:
enabled: true
hostnames:
- ambient-local-exporter.mydomain.com
gatewayName: istio-ingressgateway
gatewayNamespace: istio-system
# Use this as a shortcut, or create your own hull.objects.grpcroute
grpcroute:
enabled: true
hostnames:
- ambient-local-exporter.mydomain.com
gatewayName: istio-ingressgateway
gatewayNamespace: istio-system
otelServiceName: ambient-local-exporter
otelResourceAttributes: app=ambient-local-exporter
otlpEndpoint: http://otel.otel.svc.cluster.local:4317 # Replace me
serviceType: ClusterIP
serviceLbIP: "" # Used if serviceTyps=LoadBalancer
general:
rbac: false
render:
passes: 2
# Applies to all objects
metadata:
labels:
custom:
app: _HT**Release.Name
version: _HT**Chart.AppVersion
objects:
configmap:
config:
data:
config.yaml:
serialization: toYaml
inline: _HT*hull.config.appConfig
environment:
data:
OTEL_EXPORTER_OTLP_ENDPOINT:
serialization: none
inline: _HT*hull.config.settings.otlpEndpoint
OTEL_SERVICE_NAME:
serialization: none
inline: _HT*hull.config.settings.otelServiceName
OTEL_RESOURCE_ATTRIBUTES:
serialization: none
inline: _HT!
{{ printf "deployment.name=%s,%s" _HT**Release.Name _HT*hull.config.settings.otelResourceAttributes }}
serviceaccount:
default:
enabled: false
role:
default:
enabled: false
rolebinding:
default:
enabled: false
deployment:
main:
pod:
containers:
main:
image:
repository: _HT*hull.config.settings.repo
tag: _HT*hull.config.settings.tag
imagePullPolicy: Always
args:
- -config
- /app/config.yaml
ports:
http:
containerPort: _HT*hull.config.settings.httpPort
grpc:
containerPort: _HT*hull.config.settings.grpcPort
envFrom:
main:
configMapRef:
name: environment
resources: _HT*hull.config.settings.resources
readinessProbe:
httpGet:
path: /health
port: 8080
scheme: HTTP
periodSeconds: 10
failureThreshold: 2
livenessProbe:
httpGet:
path: /health
port: 8080
scheme: HTTP
periodSeconds: 10
failureThreshold: 2
volumeMounts:
config:
name: config
mountPath: /app/config.yaml
subPath: config.yaml
volumes:
environment:
configMap:
name: environment
config:
configMap:
name: config
service:
main:
type: _HT*hull.config.settings.serviceType
loadBalancerIP: _HT*hull.config.settings.serviceLbIP
ports:
http:
port: _HT*hull.config.settings.httpPort
targetPort: http
grpc:
port: _HT*hull.config.settings.grpcPort
targetPort: grpc
httproute:
main:
enabled: _HT*hull.config.settings.httproute.enabled
hostnames: _HT*hull.config.settings.httproute.hostnames
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: _HT*hull.config.settings.httproute.gatewayName
namespace: _HT*hull.config.settings.httproute.gatewayNamespace
rules:
- backendRefs:
- group: ""
kind: Service
name: _HT^main
port: _HT*hull.config.settings.httpPort
grpcroute:
main:
enabled: _HT*hull.config.settings.grpcroute.enabled
hostnames: _HT*hull.config.settings.grpcroute.hostnames
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: _HT*hull.config.settings.grpcroute.gatewayName
namespace: _HT*hull.config.settings.grpcroute.gatewayNamespace
rules:
- backendRefs:
- group: ""
kind: Service
name: _HT^main
port: _HT*hull.config.settings.grpcPort

48
main.go
View File

@ -17,28 +17,38 @@ import (
weathergrpc "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather/grpc"
)
const defaultMetricPrefix = "weather"
const (
defaultMetricPrefix = "weather"
)
func main() {
ctx, cncl := signal.NotifyContext(context.Background(), os.Kill, os.Interrupt, unix.SIGTERM)
defer cncl()
// Config type for app, which implements go-app/config.AppConfig
awConfig := &config.AmbientLocalExporterConfig{
// Read config and environment, prepare an appCtx, and merge
// go-app config into ambient weather exporter app config
ctx, awConfig := app.MustLoadConfigInto(ctx, &config.AmbientLocalExporterConfig{
MetricPrefix: defaultMetricPrefix,
WeatherStations: make([]config.WeatherStation, 0),
}
})
// Read config and environment, set up logging, load up
// an appCtx, and prepare ambient weather local exporter
ctx, awConfig = app.MustLoadConfigInto(ctx, awConfig)
// Prepare the exporter
// Prepare the ambient exporter with our prepared config
// and set up logging, tracing, etc..
aw := ambient.New(ctx, awConfig).Init()
// Define and prepare the app
awApp := app.App{
// Load http and grpc routes, prepare the app
awApp := prepareApp(ctx, aw)
// Run app and wait
awApp.MustRun()
<-awApp.Done()
}
func prepareApp(ctx context.Context, aw *ambient.AmbientWeather) *app.App {
// Load ambient routes into app
awApp := &app.App{
AppContext: ctx,
// HTTP Endpoints for Ambient Weather Stations
HTTP: &httpopts.AppHTTP{
Funcs: []httpopts.HTTPFunc{
@ -51,8 +61,13 @@ func main() {
HandlerFunc: aw.GetAWNHandlerFunc(ctx),
},
},
// HTTP Listener that fixes broken requests generated by
// some versions of awn firmware
CustomListener: ambienthttp.NewAWNMutatingListener(ctx,
awConfig.HTTP.Listen), // Necessary to fix certain bad AWN firmware
aw.Config.HTTP.Listen), // Necessary to fix certain bad AWN firmware
// Health check funcs
HealthChecks: []httpopts.HealthCheckFunc{
// TODO: Implement
func(ctx context.Context) error {
@ -60,19 +75,18 @@ func main() {
},
},
},
// GRPC Service for retrieving current weather
// GRPC Service for retrieving weather
GRPC: &grpcopts.AppGRPC{
Services: []*grpcopts.GRPCService{
{
Name: "Weather Service",
Type: &weatherpb.AmbientLocalWeatherService_ServiceDesc,
Service: weathergrpc.GRPCWeather{},
Service: weathergrpc.NewGRPCWeather(ctx, aw.GetRecorder()),
},
},
},
}
// Run and wait
awApp.MustRun()
<-awApp.Done()
return awApp
}

View File

@ -20,35 +20,64 @@ import (
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/provider/awn"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/provider/wunderground"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather/recorder"
)
const defUpdatesToKeep = 120
type AmbientWeather struct {
// These providers implement support for the update sent
// when either "AmbientWeather" or "Wunderground" are selected
// in the "Custom" section of the AWNet app, or the web UI
// of an Ambient WeatherHub
config *config.AmbientLocalExporterConfig
awnProvider provider.AmbientProvider
wuProvider provider.AmbientProvider
appCtx context.Context
metrics *weather.WeatherMetrics
l *zerolog.Logger
Config *config.AmbientLocalExporterConfig
awnProvider provider.AmbientProvider
wuProvider provider.AmbientProvider
weatherRecorder *recorder.WeatherRecorder
appCtx context.Context
metrics *weather.WeatherMetrics
l *zerolog.Logger
*sync.RWMutex
}
func New(appCtx context.Context, awConfig *config.AmbientLocalExporterConfig) *AmbientWeather {
return &AmbientWeather{
config: awConfig,
appCtx: appCtx,
Config: awConfig,
appCtx: appCtx,
RWMutex: &sync.RWMutex{},
}
}
// Initialize with defaults, set logger from context
func (aw *AmbientWeather) Init() *AmbientWeather {
tracer := otel.GetTracer(aw.appCtx, "ambientWeather")
_, span := tracer.Start(aw.appCtx, "ambientWeather.init",
trace.WithAttributes(
attribute.String("name", aw.Config.Name),
attribute.Bool("grpcEnabled", aw.Config.GRPCEnabled()),
attribute.Bool("httpEnabled", aw.Config.HTTPEnabled()),
attribute.Int("weatherStations", len(aw.Config.WeatherStations)),
))
defer span.End()
aw.awnProvider = &awn.AWNProvider{}
aw.wuProvider = &wunderground.WUProvider{}
aw.l = zerolog.Ctx(aw.appCtx)
aw.l.Trace().Any("awConfig", aw.config).Send()
updatesToKeep := defUpdatesToKeep
if aw.Config.UpdatesToKeep != nil && *aw.Config.UpdatesToKeep > 0 {
updatesToKeep = *aw.Config.UpdatesToKeep
}
span.SetAttributes(attribute.Int("updatesToKeep", updatesToKeep))
// TODO: Support other recorders (don't rely on default)
aw.weatherRecorder = recorder.NewWeatherRecorder(&recorder.Opts{
Ctx: aw.appCtx,
KeepLast: updatesToKeep,
})
aw.l.Trace().Any("awConfig", aw.Config).Send()
span.SetStatus(codes.Ok, "")
return aw
}
@ -78,6 +107,9 @@ func (aw *AmbientWeather) handleProviderRequest(
w http.ResponseWriter,
r *http.Request,
) {
aw.Lock()
defer aw.Unlock()
l := zerolog.Ctx(aw.appCtx)
tracer := otel.GetTracer(aw.appCtx, p.Name()+".http.handler")
@ -110,6 +142,9 @@ func (aw *AmbientWeather) handleProviderRequest(
updateSpan.SetAttributes(attribute.String("stationName", update.StationConfig.Name))
}
// Record state
aw.weatherRecorder.Set(ctx, update)
// Update metrics
aw.metricsUpdate(ctx, p, update)
@ -227,18 +262,25 @@ func (aw *AmbientWeather) proxyUpdate(
}
func (aw *AmbientWeather) InitMetrics() {
if aw.config.MetricPrefix != "" {
weather.MetricPrefix = aw.config.MetricPrefix
if aw.Config.MetricPrefix != "" {
weather.MetricPrefix = aw.Config.MetricPrefix
}
aw.metrics = weather.MustInitMetrics(aw.appCtx)
}
func (aw *AmbientWeather) enrichStation(update *weather.WeatherUpdate) {
if update != nil && update.StationID != nil && *update.StationID != "" {
for _, station := range aw.config.WeatherStations {
for _, station := range aw.Config.WeatherStations {
if *update.StationID == station.AWNPassKey || *update.StationID == station.WundergroundID {
update.StationConfig = &station
}
}
}
}
func (aw *AmbientWeather) GetRecorder() *recorder.WeatherRecorder {
aw.RLock()
defer aw.RUnlock()
return aw.weatherRecorder
}

View File

@ -7,7 +7,8 @@ import (
// This configuration includes all config from go-app/config.AppConfig
type AmbientLocalExporterConfig struct {
MetricPrefix string `yaml:"metricPrefix" default:"weather" env:"AMBIENT_METRIC_PREFIX"`
WeatherStations []WeatherStation `yaml:"weatherStations"` // No env, too complex, not worth the time
UpdatesToKeep *int `yaml:"updatesToKeep" default:"1" env:"AMBIENT_UPDATES_TO_KEEP"`
WeatherStations []WeatherStation `yaml:"weatherStations" env:"weatherStations"` // No env, too complex, not worth the time
*config.AppConfig // Extends app config
}

17
pkg/util/util.go Normal file
View File

@ -0,0 +1,17 @@
package util
import "k8s.io/utils/ptr"
func DerefStr(s *string) string {
if s == nil {
return ""
}
return *s
}
func Int32ptr(i *int) *int32 {
if i == nil {
return nil
}
return ptr.To(int32(*i))
}

View File

@ -0,0 +1,70 @@
package grpc
import (
pb "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/api/v1alpha1/weather"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/util"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
)
func UpdatesToPbUpdates(u []*weather.WeatherUpdate) []*pb.WeatherUpdate {
updates := make([]*pb.WeatherUpdate, len(u))
for i, update := range u {
updates[i] = UpdateToPbUpdate(update)
}
return updates
}
func UpdateToPbUpdate(u *weather.WeatherUpdate) *pb.WeatherUpdate {
return &pb.WeatherUpdate{
StationName: u.StationConfig.Name,
StationType: util.DerefStr(u.StationType),
StationId: util.DerefStr(u.StationID),
TempOutdoorF: u.TempOutdoorF,
TempIndoorF: u.TempIndoorF,
HumidityOutdoor: util.Int32ptr(u.HumidityOudoor),
HumidityIndoor: util.Int32ptr(u.HumidityIndoor),
WindSpeedMph: u.WindSpeedMPH,
WindGustMph: u.WindGustMPH,
MaxDailyGust: u.MaxDailyGust,
WindDir: util.Int32ptr(u.WindDir),
WindDirAvg_10M: util.Int32ptr(u.WindDirAvg10m),
Uv: util.Int32ptr(u.UV),
SolarRadiation: u.SolarRadiation,
HourlyRainIn: u.HourlyRainIn,
EventRainIn: u.EventRainIn,
DailyRainIn: u.DailyRainIn,
WeeklyRainIn: u.WeeklyRainIn,
MonthlyRainIn: u.MonthlyRainIn,
YearlyRainIn: u.YearlyRainIn,
TotalRainIn: u.TotalRainIn,
Batteries: batteriesToPbBatteries(u.Batteries),
BaromRelativeIn: u.BaromRelativeIn,
BaromAbsoluteIn: u.BaromAbsoluteIn,
DewPointF: u.DewPointF,
WindChillF: u.WindChillF,
TempHumiditySensors: thSensorsToPbSensors(u.TempHumiditySensors),
}
}
func batteriesToPbBatteries(batteries []weather.BatteryStatus) []*pb.BatteryStatus {
pbBatteries := make([]*pb.BatteryStatus, len(batteries))
for i, b := range batteries {
pbBatteries[i] = &pb.BatteryStatus{
Component: b.Component,
Status: util.Int32ptr(b.Status),
}
}
return pbBatteries
}
func thSensorsToPbSensors(sensors []*weather.TempHumiditySensor) []*pb.TempHumiditySensor {
pbSensors := make([]*pb.TempHumiditySensor, len(sensors))
for i, s := range sensors {
pbSensors[i] = &pb.TempHumiditySensor{
Name: s.Name,
TempF: s.TempF,
Humidity: util.Int32ptr(s.Humidity),
}
}
return pbSensors
}

View File

@ -0,0 +1,113 @@
package grpc
import (
"reflect"
"testing"
"time"
"k8s.io/utils/ptr"
pb "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/api/v1alpha1/weather"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/ambient/config"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
)
var mockUpdate = &weather.WeatherUpdate{
DateUTC: &time.Time{},
StationConfig: &config.WeatherStation{
Name: "50W",
Equipment: "WS-5000",
},
StationID: ptr.To("50W"),
StationType: ptr.To("WS-5000"),
TempOutdoorF: ptr.To(97.6),
TempIndoorF: ptr.To(77.6),
HumidityOudoor: ptr.To(50),
HumidityIndoor: ptr.To(50),
WindSpeedMPH: ptr.To(20.5),
WindGustMPH: ptr.To(30.5),
MaxDailyGust: ptr.To(40.0),
WindDir: ptr.To(180),
WindDirAvg10m: ptr.To(180),
UV: nil,
SolarRadiation: ptr.To(9.999),
HourlyRainIn: ptr.To(9.999),
EventRainIn: ptr.To(9.999),
DailyRainIn: ptr.To(9.999),
WeeklyRainIn: ptr.To(9.999),
MonthlyRainIn: ptr.To(9.999),
YearlyRainIn: ptr.To(9.999),
TotalRainIn: ptr.To(9.999),
Batteries: []weather.BatteryStatus{
{Component: "battery1", Status: ptr.To(1)},
{Component: "battery2", Status: ptr.To(1)},
},
BaromRelativeIn: ptr.To(9.999),
BaromAbsoluteIn: ptr.To(9.999),
DewPointF: ptr.To(9.999),
WindChillF: ptr.To(9.999),
TempHumiditySensors: []*weather.TempHumiditySensor{
{Name: "sensor1", TempF: ptr.To(99.999), Humidity: nil},
},
}
func TestUpdateToPbUpdate(t *testing.T) {
type args struct {
u *weather.WeatherUpdate
}
tests := []struct {
name string
args args
want *pb.WeatherUpdate
}{
{
name: "Map Update to PB",
args: args{u: mockUpdate},
want: &pb.WeatherUpdate{
StationName: mockUpdate.StationConfig.Name,
StationType: *mockUpdate.StationType,
StationId: *mockUpdate.StationID,
TempOutdoorF: mockUpdate.TempOutdoorF,
TempIndoorF: mockUpdate.TempIndoorF,
HumidityOutdoor: ptr.To(int32(*mockUpdate.HumidityOudoor)),
HumidityIndoor: ptr.To(int32(*mockUpdate.HumidityIndoor)),
WindSpeedMph: mockUpdate.WindSpeedMPH,
WindGustMph: mockUpdate.WindGustMPH,
MaxDailyGust: mockUpdate.MaxDailyGust,
WindDir: ptr.To(int32(*mockUpdate.WindDir)),
WindDirAvg_10M: ptr.To(int32(*mockUpdate.WindDirAvg10m)),
Uv: nil,
SolarRadiation: mockUpdate.SolarRadiation,
HourlyRainIn: mockUpdate.HourlyRainIn,
EventRainIn: mockUpdate.EventRainIn,
DailyRainIn: mockUpdate.DailyRainIn,
WeeklyRainIn: mockUpdate.WeeklyRainIn,
MonthlyRainIn: mockUpdate.MonthlyRainIn,
YearlyRainIn: mockUpdate.YearlyRainIn,
TotalRainIn: mockUpdate.TotalRainIn,
Batteries: []*pb.BatteryStatus{
{Component: mockUpdate.Batteries[0].Component, Status: ptr.To(int32(*mockUpdate.Batteries[0].Status))},
{Component: mockUpdate.Batteries[1].Component, Status: ptr.To(int32(*mockUpdate.Batteries[1].Status))},
},
BaromRelativeIn: mockUpdate.BaromRelativeIn,
BaromAbsoluteIn: mockUpdate.BaromAbsoluteIn,
DewPointF: mockUpdate.DewPointF,
WindChillF: mockUpdate.WindChillF,
TempHumiditySensors: []*pb.TempHumiditySensor{
{
Name: mockUpdate.TempHumiditySensors[0].Name,
TempF: mockUpdate.TempHumiditySensors[0].TempF,
Humidity: nil,
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := UpdateToPbUpdate(tt.args.u); !reflect.DeepEqual(got, tt.want) {
t.Errorf("UpdateToPbUpdate() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -3,11 +3,64 @@ package grpc
import (
"context"
"go.opentelemetry.io/otel/attribute"
otelcodes "go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
pb "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/api/v1alpha1/weather"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather/recorder"
)
// TODO: Implement
type GRPCWeather struct {
ctx context.Context
ctx context.Context
recorder *recorder.WeatherRecorder
tracer trace.Tracer
*pb.UnimplementedAmbientLocalWeatherServiceServer
}
func NewGRPCWeather(ctx context.Context, recorder *recorder.WeatherRecorder) *GRPCWeather {
return &GRPCWeather{
ctx: ctx,
recorder: recorder,
tracer: otel.GetTracer(ctx, "grpcWeather"),
}
}
func (w *GRPCWeather) GetWeather(ctx context.Context, req *pb.GetWeatherRequest) (
*pb.GetWeatherResponse, error,
) {
ctx, span := w.tracer.Start(ctx, "getWeather")
defer span.End()
limit := 1
if req.Limit != nil {
if *req.Limit > 1 {
limit = int(*req.Limit)
}
}
span.SetAttributes(attribute.Int("limit", limit))
updates, err := w.recorder.Get(ctx, req)
if err != nil {
span.RecordError(err)
span.SetStatus(otelcodes.Error, err.Error())
return nil, status.Errorf(codes.Internal, err.Error())
} else if len(updates) < 1 {
return nil, status.Errorf(codes.OutOfRange, "no weather available")
}
span.SetStatus(otelcodes.Ok, "")
return &pb.GetWeatherResponse{
LastUpdated: &timestamppb.Timestamp{
Seconds: updates[0].DateUTC.Unix(),
},
WeatherUpdates: UpdatesToPbUpdates(updates),
}, nil
}

View File

@ -0,0 +1,51 @@
package recorder
import (
"context"
"sync"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/trace"
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather/recorder/recorders"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather/recorder/recorders/memory"
)
type WeatherRecorder struct {
recorder recorders.Recorder
ctx context.Context
tracer trace.Tracer
meter metric.Meter
*sync.RWMutex
}
type Opts struct {
Ctx context.Context
Recorder recorders.Recorder // If nil, will use memory recorder
KeepLast int
}
func NewWeatherRecorder(opts *Opts) *WeatherRecorder {
if opts.KeepLast < 1 {
opts.KeepLast = 1
}
if opts.Recorder == nil {
opts.Recorder = &memory.MemoryRecorder{}
}
opts.Recorder.Init(opts.Ctx, &recorders.RecorderOpts{
RetainLast: opts.KeepLast,
BaseCtx: opts.Ctx,
})
return &WeatherRecorder{
ctx: opts.Ctx,
recorder: opts.Recorder,
tracer: otel.GetTracer(opts.Ctx, "weatherRecorder"),
meter: otel.GetMeter(opts.Ctx, "weatherRecorder"),
RWMutex: &sync.RWMutex{},
}
}

View File

@ -0,0 +1,65 @@
package recorder
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"k8s.io/utils/ptr"
pb "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/api/v1alpha1/weather"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
)
// Returns last requested number of weather updates
// If negative number given, will return all weather observations
func (w *WeatherRecorder) Get(ctx context.Context, req *pb.GetWeatherRequest) (
[]*weather.WeatherUpdate, error,
) {
if req == nil {
req = &pb.GetWeatherRequest{Limit: ptr.To(int32(-1))}
}
if req.Limit == nil || *req.Limit < 0 {
req.Limit = ptr.To(int32(-1))
} else if *req.Limit == 0 {
req.Limit = ptr.To(int32(1))
}
var filterSN, filterST string
if req.Opts != nil {
filterSN = req.Opts.GetStationName()
filterST = req.Opts.GetStationType()
}
ctx, span := w.tracer.Start(ctx, "getWeatherRecorder")
span.SetAttributes(
attribute.String("stationNameFilter", filterSN),
attribute.String("stationTypeFilter", filterST),
attribute.Int("last", int(*req.Limit)),
attribute.Int("currentSize", w.Count(ctx)),
)
defer span.End()
updates, err := w.recorder.Get(ctx, req)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
} else {
span.SetStatus(codes.Ok, "")
}
return updates, err
}
// Returns count of retained weather updates
func (w *WeatherRecorder) Count(ctx context.Context) int {
ctx, span := w.tracer.Start(ctx, "countWeatherRecorder")
defer span.End()
count := w.recorder.Count(ctx)
span.SetAttributes(attribute.Int("count", count))
span.SetStatus(codes.Ok, "")
return count
}

View File

@ -0,0 +1,21 @@
package recorder
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/util"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
)
func (w *WeatherRecorder) Set(ctx context.Context, u *weather.WeatherUpdate) error {
ctx, span := w.tracer.Start(ctx, "setRecorderUpdate", trace.WithAttributes(
attribute.String("stationName", u.StationConfig.Name),
attribute.String("stationType", util.DerefStr(u.StationType)),
attribute.String("stationEquipment", u.StationConfig.Equipment),
))
defer span.End()
return w.recorder.Set(ctx, u)
}

View File

@ -0,0 +1,108 @@
package memory
import (
"context"
"errors"
"slices"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
pb "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/api/v1alpha1/weather"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/util"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
)
func (r *MemoryRecorder) Get(ctx context.Context, req *pb.GetWeatherRequest) (
[]*weather.WeatherUpdate, error,
) {
ctx, span := r.tracer.Start(ctx, "memoryRecorder.Get")
defer span.End()
r.RLock()
defer r.RUnlock()
span.AddEvent("acquired lock on recorder cache")
if r.count() == 0 {
err := errors.New("no recorded updates to get")
span.RecordError(err)
return nil, err
} else if r.count() <= int(*req.Limit) {
span.RecordError(errors.New("requested more updates than recorded"))
}
updates := r.getUpdatesFromReq(req)
span.AddEvent("request limit/opts applied to updates")
span.SetAttributes(attribute.Int("retrieved", len(updates)))
span.SetStatus(codes.Ok, "")
return updates, nil
}
func (r *MemoryRecorder) getUpdatesFromReq(req *pb.GetWeatherRequest) []*weather.WeatherUpdate {
if req.Opts == nil {
return limitUpdates(r.updates, int(req.GetLimit()))
}
return r.applyOptsToUpdates(r.updates, int(req.GetLimit()), req.Opts)
}
func (r *MemoryRecorder) applyOptsToUpdates(updates []*weather.WeatherUpdate, limit int, opts *pb.GetWeatherOpts) []*weather.WeatherUpdate {
if opts == nil {
return updates
} else if opts.StationName == nil && opts.StationType == nil {
return updates
}
filtered := make([]*weather.WeatherUpdate, 0, limit)
for i := len(updates) - 1; i >= 0; i-- {
update := updates[i]
match := true
if opts.GetStationName() != "" {
if update.GetStationName() != opts.GetStationName() {
match = false
}
}
if opts.GetStationType() != "" {
if util.DerefStr(update.StationType) != opts.GetStationType() {
match = false
}
}
if match {
filtered = append(filtered, update)
if len(filtered) >= limit {
return filtered
}
}
}
return slices.Clip(filtered)
}
func (r *MemoryRecorder) Count(ctx context.Context) int {
_, span := r.tracer.Start(ctx, "countWeatherRecorder")
defer span.End()
count := r.count()
span.SetAttributes(attribute.Int("count", count))
span.SetStatus(codes.Ok, "")
return count
}
func (r *MemoryRecorder) count() int {
return len(r.updates)
}
func limitUpdates(updates []*weather.WeatherUpdate, limit int) []*weather.WeatherUpdate {
if len(updates) > limit {
return updates[len(updates)-limit:]
}
return updates
}

View File

@ -0,0 +1,35 @@
package memory
import (
"context"
"sync"
"go.opentelemetry.io/otel/trace"
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather/recorder/recorders"
)
const defRetainLast = 120
type MemoryRecorder struct {
baseCtx context.Context
updates []*weather.WeatherUpdate
tracer trace.Tracer
keep int
*sync.RWMutex
}
func (r *MemoryRecorder) Init(ctx context.Context, opts *recorders.RecorderOpts) {
if opts.RetainLast < 1 {
opts.RetainLast = defRetainLast
}
r.updates = make([]*weather.WeatherUpdate, 0, opts.RetainLast)
r.keep = opts.RetainLast
r.baseCtx = opts.BaseCtx
r.RWMutex = &sync.RWMutex{}
r.tracer = otel.GetTracer(r.baseCtx, "memoryRecorder")
}

View File

@ -0,0 +1,38 @@
package memory
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
)
func (r *MemoryRecorder) Set(ctx context.Context, u *weather.WeatherUpdate) error {
r.Lock()
defer r.Unlock()
ctx, span := r.tracer.Start(ctx, "memoryRecorder.Set")
span.SetAttributes(
attribute.Int("countWeatherUpdates", r.Count(ctx)),
attribute.Int("keepUpdates", r.keep),
)
defer span.End()
return r.set(ctx, u)
}
func (r *MemoryRecorder) set(ctx context.Context, u *weather.WeatherUpdate) error {
span := trace.SpanFromContext(ctx)
if len(r.updates) > r.keep {
r.updates = r.updates[1:]
span.AddEvent("trimmed recorded updates by 1")
}
r.updates = append(r.updates, u)
span.AddEvent("recorded weather update")
return nil
}

View File

@ -0,0 +1,20 @@
package recorders
import (
"context"
pb "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/api/v1alpha1/weather"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
)
type RecorderOpts struct {
RetainLast int
BaseCtx context.Context
}
type Recorder interface {
Init(context.Context, *RecorderOpts)
Set(context.Context, *weather.WeatherUpdate) error
Get(context.Context, *pb.GetWeatherRequest) ([]*weather.WeatherUpdate, error)
Count(context.Context) int // Best Effort
}

View File

@ -7,6 +7,7 @@ option go_package = "gitea.libretechconsulting.com/rmcguire/ambient-local-export
message GetWeatherRequest {
GetWeatherOpts opts = 1;
optional int32 limit = 2;
}
message GetWeatherResponse{
@ -23,41 +24,41 @@ message WeatherUpdate {
string station_name = 1;
string station_type = 2;
string station_id = 3;
optional double temp_outdoor_f = 4;
optional double temp_indoor_f = 5;
optional int32 humidity_outdoor = 6;
optional int32 humidity_indoor = 7;
optional double wind_speed_mph = 8;
optional double wind_gust_mph = 9;
optional double max_daily_gust = 10;
optional int32 wind_dir = 11;
optional int32 wind_dir_avg_10m = 12;
optional int32 uv = 13;
optional double solar_radiation = 14;
optional double hourly_rain_in = 15;
optional double event_rain_in = 16;
optional double daily_rain_in = 17;
optional double weekly_rain_in = 18;
optional double monthly_rain_in = 19;
optional double yearly_rain_in = 20;
optional double total_rain_in = 21;
repeated BatteryStatus batteries = 22;
optional double barom_relative_in = 23;
optional double barom_absolute_in = 24;
optional double dew_point_f = 25;
optional double wind_chill_f = 26;
repeated TempHumiditySensor temp_humidity_sensors = 27;
optional double temp_outdoor_f = 4;
optional double temp_indoor_f = 5;
optional int32 humidity_outdoor = 6;
optional int32 humidity_indoor = 7;
optional double wind_speed_mph = 8;
optional double wind_gust_mph = 9;
optional double max_daily_gust = 10;
optional int32 wind_dir = 11;
optional int32 wind_dir_avg_10m = 12;
optional int32 uv = 13;
optional double solar_radiation = 14;
optional double hourly_rain_in = 15;
optional double event_rain_in = 16;
optional double daily_rain_in = 17;
optional double weekly_rain_in = 18;
optional double monthly_rain_in = 19;
optional double yearly_rain_in = 20;
optional double total_rain_in = 21;
repeated BatteryStatus batteries = 22;
optional double barom_relative_in = 23;
optional double barom_absolute_in = 24;
optional double dew_point_f = 25;
optional double wind_chill_f = 26;
repeated TempHumiditySensor temp_humidity_sensors = 27;
}
// Represents a temperature and humidity sensor
message TempHumiditySensor {
string name = 1;
optional double temp_f = 2;
optional int32 humidity = 3;
string name = 1;
optional double temp_f = 2;
optional int32 humidity = 3;
}
// Represents battery status for different components
message BatteryStatus {
string component = 1;
optional int32 status = 2;
string component = 1;
optional int32 status = 2;
}