Initial commit

This commit is contained in:
2026-07-04 20:29:29 +00:00
commit 8198e8cfee
40 changed files with 3096 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
# .air.toml
# Root directory of the project
root = "."
# Directory for temporary build artifacts
tmp_dir = "tmp"
[build]
cmd = "go build -o tmp/main ."
bin = "tmp/main"
args_bin = ["-config", "config.yaml"]
# Extensions to watch
include_ext = ["go", "toml", "yaml", "yml"]
# Ignore temp folders and irrelevant dirs
exclude_dir = ["tmp", "vendor", ".git", "cmd", "proto"]
exclude_regex = ["_test.go"]
# Stop process on error and send interrupt for graceful shutdown
stop_on_error = true
send_interrupt = true
kill_delay = 500
[log]
time = true
[color]
main = "magenta"
watcher = "cyan"
build = "yellow"
runner = "green"
[misc]
clean_on_exit = true
+4
View File
@@ -0,0 +1,4 @@
.git
config.y*ml
go.work*
docker-compose-sample*
+146
View File
@@ -0,0 +1,146 @@
name: Build and Publish
on:
push:
tags: ["v*"]
branches: ["main"]
env:
PACKAGE_NAME: go-server-with-otel
BINARY_PATH: bin
BINARY_NAME: go-server-with-otel
GO_MOD_PATH: gitea.libretechconsulting.com/rmcguire/go-server-with-otel
GO_GIT_HOST: gitea.libretechconsulting.com
VER_PKG: gitea.libretechconsulting.com/rmcguire/go-app/pkg/config.Version
VERSION: ${{ github.ref_name }}
PLATFORMS: linux/amd64 linux/arm64 darwin/amd64 darwin/arm64
DOCKER_REGISTRY: gitea.libretechconsulting.com
DOCKER_USER: rmcguire
DOCKER_REPO: rmcguire/go-server-with-otel
DOCKER_IMG: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_REPO }}
CHART_DIR: helm/
jobs:
go-binaries:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v') # Only run on tag push
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Go Environment
uses: actions/setup-go@v4
with:
go-version: '1.24'
- name: Build Binary
run: make build
- name: Upload Binaries to Generic Registry
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: |
for platform in $PLATFORMS; do
OS=$(echo $platform | cut -d/ -f1)
ARCH=$(echo $platform | cut -d/ -f2)
BINARY_FILE="${BINARY_PATH}/${PACKAGE_NAME}-${OS}-${ARCH}"
echo "Uploading $BINARY_FILE"
if [ -f "$BINARY_FILE" ]; then
curl -X PUT \
-H "Authorization: token ${API_TOKEN}" \
--upload-file "$BINARY_FILE" \
"${GITHUB_SERVER_URL}/api/packages/${GITHUB_REPOSITORY_OWNER}/generic/${PACKAGE_NAME}/${{ github.ref_name }}/${PACKAGE_NAME}-${OS}-${ARCH}"
else
echo "Error: Binary $BINARY_FILE not found."
exit 1
fi
done
- name: Run Go List
continue-on-error: true
env:
TAG_NAME: ${{ github.ref_name }} # Use the pushed tag name
run: |
if [[ "$TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
GOPROXY=proxy.golang.org go list -m ${GO_GIT_HOST}/${GITHUB_REPOSITORY}@$TAG_NAME
else
echo "Error: Invalid tag format '$TAG_NAME'. Expected 'vX.X.X'."
exit 1
fi
container-images:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v') # Only run on tag push
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Custom Registry
uses: docker/login-action@v3
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ env.DOCKER_USER }}
password: ${{ secrets.API_TOKEN }}
- name: Build and Push Docker Image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
${{ env.DOCKER_IMG }}:${{ github.ref_name }}
${{ env.DOCKER_IMG }}:latest
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/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: Fetch Dependencies
run: |
cd ${CHART_DIR} && \
helm repo add hull https://vidispine.github.io/hull && \
helm dep build
- 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
+28
View File
@@ -0,0 +1,28 @@
# ---> Go
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work*
# Environment
.env
bin/*
tmp
+16
View File
@@ -0,0 +1,16 @@
{
"$schema": "https://raw.githubusercontent.com/mfussenegger/dapconfig-schema/master/dapconfig-schema.json",
"version": "0.2.0",
"configurations": [
{
"name": "Run Server",
"type": "delve",
"request": "launch",
"program": "${workspaceFolder}",
"env": {
"APP_NAME": "Go HTTP with OTEL"
},
"args": []
}
]
}
+18
View File
@@ -0,0 +1,18 @@
# v0.7.0
* feat: Add HTTP log exclusion regex paths to configuration schema.
* chore: Update Go version to 1.25.
* chore: Update module dependencies.
# v0.6.0
* feat: Introduce Model Context Protocol (MCP) server with a demo random fact tool.
* feat: Add MCP server configuration file (`contrib/mcpinspector.json`).
* deps: Update Go module dependencies, including `bufbuild/protovalidate`, `go-app`, `grpc-gateway`, `googleapis/api`, `grpc`, `protobuf`, `golang.org/x/exp`, and `googleapis/rpc`.
* deps: Add new Go module dependencies: `modelcontextprotocol/go-sdk` and `k8s.io/utils`.
# v0.5.0
* Added OpenTelemetry tracing for application startup.
* Updated Go module dependencies, including `protovalidate`, `go-app`, `grpc`, and OpenTelemetry related packages.
* Enhanced `Makefile` `rename` target to update the application name constant in `main.go`.
* Configured `air` live-reloading to exclude the `proto` directory.
* Refactored application initialization logic for improved modularity and OpenTelemetry integration.
+27
View File
@@ -0,0 +1,27 @@
FROM golang:1-alpine AS build
WORKDIR /app
ENV GO111MODULE=auto CGO_ENABLED=0 GOOS=linux
ARG GOPROXY
ARG GONOSUMDB=gitea.libretechconsulting.com
ARG VER_PKG=gitea.libretechconsulting.com/rmcguire/go-app/pkg/config.Version
ARG VERSION=(devel)
ARG APP_NAME=go-server-with-otel
COPY ./go.mod ./go.sum ./
RUN go mod download
COPY ./ /app
RUN go build -C . -v -ldflags "-extldflags '-static' -X ${VER_PKG}=${VERSION}" -o ${APP_NAME} .
FROM alpine:latest
ARG APP_NAME=go-server-with-otel
WORKDIR /app
USER 100:101
COPY --from=build --chown=100:101 /app/${APP_NAME} /app/app
ENTRYPOINT [ "/app/app" ]
+9
View File
@@ -0,0 +1,9 @@
MIT License
Copyright (c) 2025 rmcguire
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+96
View File
@@ -0,0 +1,96 @@
.PHONY: all test build docker install clean proto check_protoc
CMD_NAME := go-server-with-otel
VERSION ?= development
API_DIR := api/
SCHEMA_DIR := contrib/
PROTO_DIRS := $(wildcard proto/demo/app/*) # TODO: Update path (probably not demo)
PLATFORMS := linux/amd64 linux/arm64 darwin/amd64 darwin/arm64
OUTPUT_DIR := bin
VER_PKG := gitea.libretechconsulting.com/rmcguire/go-app/pkg/config.Version
GIT_REPO := gitea.libretechconsulting.com/rmcguire/go-server-with-otel
all: proto test build docker
proto: check_buf
buf dep update
buf generate
test:
go test -v ./...
build: test
@echo "Building for platforms: $(PLATFORMS)"
@for platform in $(PLATFORMS); do \
OS=$$(echo $$platform | cut -d/ -f1); \
ARCH=$$(echo $$platform | cut -d/ -f2); \
OUTPUT="$(OUTPUT_DIR)/$(CMD_NAME)-$$OS-$$ARCH"; \
mkdir -vp $(OUTPUT_DIR); \
echo "Building for $$platform into $$OUTPUT"; \
GOOS=$$OS GOARCH=$$ARCH go build -ldflags "-X $(VER_PKG)=$(VERSION)" -o $$OUTPUT .; \
echo "Built $$OUTPUT"; \
done
go build -ldflags "-X $(VER_PKG)=$(VERSION)" -o bin/${CMD_NAME}
schema:
go run . -schema > contrib/schema.json
docker:
@echo "Building Docker image $(GIT_REPO):$(VERSION)"
docker build \
--build-arg VER_PKG=$(VER_PKG) \
--build-arg VERSION=$(VERSION) \
--build-arg APP_NAME=$(CMD_NAME) \
-t $(GIT_REPO):$(VERSION) .
docker push $(GIT_REPO):$(VERSION)
install:
go install -v -ldflags "-X $(VER_PKG)=$(VERSION)" .
clean:
rm -rf bin/${CMD_NAME}
check_buf:
@if ! command -v buf > /dev/null; then \
echo "Error: buf not found in PATH"; \
exit 1; \
fi
rename:
@echo "Current module path: $(GIT_REPO)"
@echo "Usage: make rename NAME=your/new/module/name APP=your-app-name"
@if [ -z "$(NAME)" ]; then \
echo "No package name provided. Aborting."; \
exit 1; \
fi
@if [ -z "$(APP)" ]; then \
echo "No app name provided. Aborting."; \
exit 1; \
fi
@echo "New name: app=$(APP) pkg=$(NAME)"
@echo "Are you sure you want to proceed? (y/N): " && read CONFIRM && if [ "$$CONFIRM" != "y" ] && [ "$$CONFIRM" != "Y" ]; then \
echo "Aborted."; \
exit 1; \
fi
@sed -i "s|APP_NAME=.*|APP_NAME=$(APP)|g" Dockerfile
@sed -i "s|^CMD_NAME := .*|CMD_NAME := $(APP)|g" Makefile
@sed -i "s|merge_file_name=.*|merge_file_name=$(APP)|g" buf.gen.yaml
@sed -i "s|^name: .*|name: $(APP)|g" helm/Chart.yaml
@sed -i "s|otelServiceName: .*|otelServiceName: $(APP)|g" helm/values.yaml
@sed -i "s|= \"demo-app\"|= \"$(APP)\"|g" main.go
@sed -i "s|app=.*|app=$(APP)|g" helm/values.yaml
@sed -i "s|$(CMD_NAME)|$(APP)|g" README.md
@sed -i "s|$(CMD_NAME)|$(APP)|g" .gitea/workflows/ci.yml
@sed -i "s|$(GIT_REPO)|$(NAME)|g" .gitea/workflows/ci.yml
@rm contrib/$(CMD_NAME).swagger.json
@find . -type f -a \
\( -name '*.go' -o -name 'go.mod' \
-o -name 'go.sum' -o -name '*.proto' \
-o -name 'Makefile' \
-o -name '*.yml' -o -name '*.yaml' \
\) \
-not -path './.git' -not -path './.git/*' \
-exec sed -i "s|$(GIT_REPO)|$(NAME)|g" {} +
@echo "\n* Project renamed to $(NAME)"
@echo "* NOTE: You will have to update .gitea/workflows/ci.yml"
@echo "* NOTE: You will have to run buf generate"
+74
View File
@@ -0,0 +1,74 @@
# go-server-with-otel 🚀
A powerful and flexible template for building Go HTTP + GRPC servers with full OpenTelemetry (OTEL) support.
Bootstrapped with the go-app framework to provide all the bells and whistles right out of the box.
Ideal for rapidly creating production-ready microservices.
Check out the [go-app framework](https://gitea.libretechconsulting.com/rmcguire/go-app) for more detail there.
## 🌟 Features
- **📈 OpenTelemetry (OTEL) Metrics & Traces** Comprehensive observability with built-in support for metrics and traces.
- 📝 Logging with Zerolog High-performance structured logging with zerolog for ultra-fast, leveled logging.
- 💻 Local dev with air pre-configured (just run `air`)
- **💬 GRPC + GRPC-Gateway** Supports RESTful JSON APIs alongside gRPC with auto-generated Swagger (OpenAPI2) specs.
- 🌐 HTTP and GRPC Middleware Flexible middleware support for HTTP and GRPC to enhance request handling, authentication, and observability.
- **📦 Multi-Arch Builds** Robust Makefile that supports building for multiple architectures (amd64, arm64, etc.).
- **🐳 Docker Image Generation** Easily build Docker images using `make docker`.
- **📜 Config Schema Generation** Automatically generate JSON schemas for configuration validation.
- **📝 Proto Compilation** Seamlessly compile .proto files with `make proto`.
- **🔄 Project Renaming** Easily rename your project using `make rename NAME=your.gitremote.com/pathto/repo`.
- **📦 Helm Chart** Deploy your application with Kubernetes using the provided Helm chart.
- **🤖 Gitea CI Integration** Out-of-the-box Gitea CI pipeline configuration with `.gitea/workflows/ci.yaml`.
- **⚙️ Expandable Configuration** Extend your app-specific configuration with go-app's built-in logging, HTTP, and GRPC config support.
---
## 📚 Getting Started
1. Install tools:
- Install make, protoc, and go using brew, apt, etc..
- Install protoc plugins (provided in go.mod tool()):
- `go get -v tool && go install -v tool`
1. **Rename your package:**
```sh
make rename NAME=my.gitremote.com/pathto/repo
```
1. **Review the config struct:**
Update and customize your app-specific configuration. This merges with go-app's configuration, providing logging, HTTP, and GRPC config for free.
1. **Generate a new JSON schema:**
```sh
make schema
```
- Ensure your structs have `yaml` and `json` tags.
- With the `yaml-language-server` LSP plugin, the schema will be auto-detected in your `config.yaml`.
1. **Compile proto files:**
```sh
make proto
```
- Add paths under `proto/` as necessary.
1. **Implement your application logic.**
1. **Update Gitea CI configuration:**
Modify parameters in `.gitea/workflows/ci.yaml` as needed.
1. Tag your release: `git tag v0.1.0` and push `git push --tags`
---
## 📂 Project Structure
- `proto/` - Protobuf definitions and generated files.
- `api/` - Auto-generated code for proto, grpc-gateway, and OpenAPI2 spec
- `pkg/config/` - Custom config, merged with go-app configuration
- `pkg/demo(http|grpc)/` - HTTP and GRPC server implementations
- `helm/` - Helm chart for deploying your application to Kubernetes.
- `.gitea/workflows/` - CI pipelines for automated builds and tests.
---
## 🔥 Ready to build something awesome? Let's go! 🎉
+10
View File
@@ -0,0 +1,10 @@
# Demo app TODO
- [x] Create generic interface for implenting a service
- [x] Create config sample not called demo, so it is more easily reused
- [x] Update README for tagging/versioning/pipeline info
- [x] Update README for detail on installing protoc tools and make
- [x] Rename project
- [x] Finish grpc sample implementation
- [x] Add Dockerfile
- [x] Add gitea CI
+212
View File
@@ -0,0 +1,212 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc (unknown)
// source: demo/app/v1alpha1/app.proto
package demo
import (
_ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
_ "google.golang.org/genproto/googleapis/api/annotations"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// Options for random fact, in this case
// just a language
type GetDemoRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Language *string `protobuf:"bytes,1,opt,name=language,proto3,oneof" json:"language,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetDemoRequest) Reset() {
*x = GetDemoRequest{}
mi := &file_demo_app_v1alpha1_app_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetDemoRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetDemoRequest) ProtoMessage() {}
func (x *GetDemoRequest) ProtoReflect() protoreflect.Message {
mi := &file_demo_app_v1alpha1_app_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetDemoRequest.ProtoReflect.Descriptor instead.
func (*GetDemoRequest) Descriptor() ([]byte, []int) {
return file_demo_app_v1alpha1_app_proto_rawDescGZIP(), []int{0}
}
func (x *GetDemoRequest) GetLanguage() string {
if x != nil && x.Language != nil {
return *x.Language
}
return ""
}
// Returns a randome fact, because this is a demo app
// so what else do we do?
type GetDemoResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Timestamp *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
Fact string `protobuf:"bytes,2,opt,name=fact,proto3" json:"fact,omitempty"`
Source string `protobuf:"bytes,3,opt,name=source,proto3" json:"source,omitempty"`
Language string `protobuf:"bytes,4,opt,name=language,proto3" json:"language,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetDemoResponse) Reset() {
*x = GetDemoResponse{}
mi := &file_demo_app_v1alpha1_app_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetDemoResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetDemoResponse) ProtoMessage() {}
func (x *GetDemoResponse) ProtoReflect() protoreflect.Message {
mi := &file_demo_app_v1alpha1_app_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetDemoResponse.ProtoReflect.Descriptor instead.
func (*GetDemoResponse) Descriptor() ([]byte, []int) {
return file_demo_app_v1alpha1_app_proto_rawDescGZIP(), []int{1}
}
func (x *GetDemoResponse) GetTimestamp() *timestamppb.Timestamp {
if x != nil {
return x.Timestamp
}
return nil
}
func (x *GetDemoResponse) GetFact() string {
if x != nil {
return x.Fact
}
return ""
}
func (x *GetDemoResponse) GetSource() string {
if x != nil {
return x.Source
}
return ""
}
func (x *GetDemoResponse) GetLanguage() string {
if x != nil {
return x.Language
}
return ""
}
var File_demo_app_v1alpha1_app_proto protoreflect.FileDescriptor
const file_demo_app_v1alpha1_app_proto_rawDesc = "" +
"\n" +
"\x1bdemo/app/v1alpha1/app.proto\x12\x11demo.app.v1alpha1\x1a\x1bbuf/validate/validate.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"G\n" +
"\x0eGetDemoRequest\x12(\n" +
"\blanguage\x18\x01 \x01(\tB\a\xbaH\x04r\x02\x10\x02H\x00R\blanguage\x88\x01\x01B\v\n" +
"\t_language\"\x9c\x01\n" +
"\x0fGetDemoResponse\x128\n" +
"\ttimestamp\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\ttimestamp\x12\x12\n" +
"\x04fact\x18\x02 \x01(\tR\x04fact\x12\x16\n" +
"\x06source\x18\x03 \x01(\tR\x06source\x12#\n" +
"\blanguage\x18\x04 \x01(\tB\a\xbaH\x04r\x02\x10\x02R\blanguage2z\n" +
"\x0eDemoAppService\x12h\n" +
"\aGetDemo\x12!.demo.app.v1alpha1.GetDemoRequest\x1a\".demo.app.v1alpha1.GetDemoResponse\"\x16\x82\xd3\xe4\x93\x02\x10\x12\x0e/v1alpha1/demoB\xd5\x01\n" +
"\x15com.demo.app.v1alpha1B\bAppProtoP\x01ZLgitea.libretechconsulting.com/rmcguire/go-server-with-otel/api/v1alpha1/demo\xa2\x02\x03DAX\xaa\x02\x11Demo.App.V1alpha1\xca\x02\x11Demo\\App\\V1alpha1\xe2\x02\x1dDemo\\App\\V1alpha1\\GPBMetadata\xea\x02\x13Demo::App::V1alpha1b\x06proto3"
var (
file_demo_app_v1alpha1_app_proto_rawDescOnce sync.Once
file_demo_app_v1alpha1_app_proto_rawDescData []byte
)
func file_demo_app_v1alpha1_app_proto_rawDescGZIP() []byte {
file_demo_app_v1alpha1_app_proto_rawDescOnce.Do(func() {
file_demo_app_v1alpha1_app_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_demo_app_v1alpha1_app_proto_rawDesc), len(file_demo_app_v1alpha1_app_proto_rawDesc)))
})
return file_demo_app_v1alpha1_app_proto_rawDescData
}
var file_demo_app_v1alpha1_app_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_demo_app_v1alpha1_app_proto_goTypes = []any{
(*GetDemoRequest)(nil), // 0: demo.app.v1alpha1.GetDemoRequest
(*GetDemoResponse)(nil), // 1: demo.app.v1alpha1.GetDemoResponse
(*timestamppb.Timestamp)(nil), // 2: google.protobuf.Timestamp
}
var file_demo_app_v1alpha1_app_proto_depIdxs = []int32{
2, // 0: demo.app.v1alpha1.GetDemoResponse.timestamp:type_name -> google.protobuf.Timestamp
0, // 1: demo.app.v1alpha1.DemoAppService.GetDemo:input_type -> demo.app.v1alpha1.GetDemoRequest
1, // 2: demo.app.v1alpha1.DemoAppService.GetDemo:output_type -> demo.app.v1alpha1.GetDemoResponse
2, // [2:3] is the sub-list for method output_type
1, // [1:2] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_demo_app_v1alpha1_app_proto_init() }
func file_demo_app_v1alpha1_app_proto_init() {
if File_demo_app_v1alpha1_app_proto != nil {
return
}
file_demo_app_v1alpha1_app_proto_msgTypes[0].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_demo_app_v1alpha1_app_proto_rawDesc), len(file_demo_app_v1alpha1_app_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_demo_app_v1alpha1_app_proto_goTypes,
DependencyIndexes: file_demo_app_v1alpha1_app_proto_depIdxs,
MessageInfos: file_demo_app_v1alpha1_app_proto_msgTypes,
}.Build()
File_demo_app_v1alpha1_app_proto = out.File
file_demo_app_v1alpha1_app_proto_goTypes = nil
file_demo_app_v1alpha1_app_proto_depIdxs = nil
}
+165
View File
@@ -0,0 +1,165 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: demo/app/v1alpha1/app.proto
/*
Package demo is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package demo
import (
"context"
"errors"
"io"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
// Suppress "imported and not used" errors
var (
_ codes.Code
_ io.Reader
_ status.Status
_ = errors.New
_ = runtime.String
_ = utilities.NewDoubleArray
_ = metadata.Join
)
var filter_DemoAppService_GetDemo_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
func request_DemoAppService_GetDemo_0(ctx context.Context, marshaler runtime.Marshaler, client DemoAppServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetDemoRequest
metadata runtime.ServerMetadata
)
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_DemoAppService_GetDemo_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetDemo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_DemoAppService_GetDemo_0(ctx context.Context, marshaler runtime.Marshaler, server DemoAppServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetDemoRequest
metadata runtime.ServerMetadata
)
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_DemoAppService_GetDemo_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GetDemo(ctx, &protoReq)
return msg, metadata, err
}
// RegisterDemoAppServiceHandlerServer registers the http handlers for service DemoAppService to "mux".
// UnaryRPC :call DemoAppServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterDemoAppServiceHandlerFromEndpoint instead.
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterDemoAppServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server DemoAppServiceServer) error {
mux.Handle(http.MethodGet, pattern_DemoAppService_GetDemo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/demo.app.v1alpha1.DemoAppService/GetDemo", runtime.WithHTTPPathPattern("/v1alpha1/demo"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_DemoAppService_GetDemo_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_DemoAppService_GetDemo_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterDemoAppServiceHandlerFromEndpoint is same as RegisterDemoAppServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterDemoAppServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.NewClient(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterDemoAppServiceHandler(ctx, mux, conn)
}
// RegisterDemoAppServiceHandler registers the http handlers for service DemoAppService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterDemoAppServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterDemoAppServiceHandlerClient(ctx, mux, NewDemoAppServiceClient(conn))
}
// RegisterDemoAppServiceHandlerClient registers the http handlers for service DemoAppService
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "DemoAppServiceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "DemoAppServiceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "DemoAppServiceClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterDemoAppServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client DemoAppServiceClient) error {
mux.Handle(http.MethodGet, pattern_DemoAppService_GetDemo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/demo.app.v1alpha1.DemoAppService/GetDemo", runtime.WithHTTPPathPattern("/v1alpha1/demo"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_DemoAppService_GetDemo_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_DemoAppService_GetDemo_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_DemoAppService_GetDemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1alpha1", "demo"}, ""))
)
var (
forward_DemoAppService_GetDemo_0 = runtime.ForwardResponseMessage
)
+119
View File
@@ -0,0 +1,119 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: demo/app/v1alpha1/app.proto
package demo
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
DemoAppService_GetDemo_FullMethodName = "/demo.app.v1alpha1.DemoAppService/GetDemo"
)
// DemoAppServiceClient is the client API for DemoAppService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type DemoAppServiceClient interface {
GetDemo(ctx context.Context, in *GetDemoRequest, opts ...grpc.CallOption) (*GetDemoResponse, error)
}
type demoAppServiceClient struct {
cc grpc.ClientConnInterface
}
func NewDemoAppServiceClient(cc grpc.ClientConnInterface) DemoAppServiceClient {
return &demoAppServiceClient{cc}
}
func (c *demoAppServiceClient) GetDemo(ctx context.Context, in *GetDemoRequest, opts ...grpc.CallOption) (*GetDemoResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetDemoResponse)
err := c.cc.Invoke(ctx, DemoAppService_GetDemo_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// DemoAppServiceServer is the server API for DemoAppService service.
// All implementations should embed UnimplementedDemoAppServiceServer
// for forward compatibility.
type DemoAppServiceServer interface {
GetDemo(context.Context, *GetDemoRequest) (*GetDemoResponse, error)
}
// UnimplementedDemoAppServiceServer should be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedDemoAppServiceServer struct{}
func (UnimplementedDemoAppServiceServer) GetDemo(context.Context, *GetDemoRequest) (*GetDemoResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetDemo not implemented")
}
func (UnimplementedDemoAppServiceServer) testEmbeddedByValue() {}
// UnsafeDemoAppServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to DemoAppServiceServer will
// result in compilation errors.
type UnsafeDemoAppServiceServer interface {
mustEmbedUnimplementedDemoAppServiceServer()
}
func RegisterDemoAppServiceServer(s grpc.ServiceRegistrar, srv DemoAppServiceServer) {
// If the following call pancis, it indicates UnimplementedDemoAppServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&DemoAppService_ServiceDesc, srv)
}
func _DemoAppService_GetDemo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetDemoRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(DemoAppServiceServer).GetDemo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: DemoAppService_GetDemo_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DemoAppServiceServer).GetDemo(ctx, req.(*GetDemoRequest))
}
return interceptor(ctx, in, info, handler)
}
// DemoAppService_ServiceDesc is the grpc.ServiceDesc for DemoAppService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var DemoAppService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "demo.app.v1alpha1.DemoAppService",
HandlerType: (*DemoAppServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetDemo",
Handler: _DemoAppService_GetDemo_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "demo/app/v1alpha1/app.proto",
}
+28
View File
@@ -0,0 +1,28 @@
version: v2
managed:
enabled: true
disable:
- file_option: go_package
module: buf.build/bufbuild/protovalidate
plugins:
- remote: buf.build/protocolbuffers/go
out: api
opt:
- paths=source_relative
- remote: buf.build/grpc/go
out: api
opt:
- paths=source_relative
- require_unimplemented_servers=false
- remote: buf.build/grpc-ecosystem/gateway
out: api
opt:
- paths=source_relative
- generate_unbound_methods=true
- remote: buf.build/grpc-ecosystem/openapiv2
out: contrib/
opt:
- merge_file_name=go-server-with-otel
- allow_merge=true
inputs:
- directory: proto
+12
View File
@@ -0,0 +1,12 @@
# Generated by buf. DO NOT EDIT.
version: v2
deps:
- name: buf.build/bufbuild/protovalidate
commit: 6c6e0d3c608e4549802254a2eee81bc8
digest: b5:a7ca081f38656fc0f5aaa685cc111d3342876723851b47ca6b80cbb810cbb2380f8c444115c495ada58fa1f85eff44e68dc54a445761c195acdb5e8d9af675b6
- name: buf.build/googleapis/googleapis
commit: 61b203b9a9164be9a834f58c37be6f62
digest: b5:7811a98b35bd2e4ae5c3ac73c8b3d9ae429f3a790da15de188dc98fc2b77d6bb10e45711f14903af9553fa9821dff256054f2e4b7795789265bc476bec2f088c
- name: buf.build/grpc-ecosystem/grpc-gateway
commit: 4c5ba75caaf84e928b7137ae5c18c26a
digest: b5:c113e62fb3b29289af785866cae062b55ec8ae19ab3f08f3004098928fbca657730a06810b2012951294326b95669547194fa84476b9e9b688d4f8bf77a0691d
+14
View File
@@ -0,0 +1,14 @@
# For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml
version: v2
modules:
- path: proto
deps:
- buf.build/bufbuild/protovalidate
- buf.build/googleapis/googleapis
- buf.build/grpc-ecosystem/grpc-gateway
lint:
use:
- STANDARD
breaking:
use:
- FILE
+30
View File
@@ -0,0 +1,30 @@
# yaml-language-server: $schema=contrib/schema.json
# Custom demo-app config
timezone: EST5EDT
opts:
factLang: en
factType: random
# go-app config
name: Demo go-app
logging:
format: console
level: trace
enabled: true
otel:
enabled: true
stdoutEnabled: false
http:
enabled: true
listen: :8080
logRequests: true
grpc:
enabled: true
enableReflection: true
listen: :8081
grpcGatewayPath: /api
enableGRPCGateway: true
enableInstrumentation: true
enableProtovalidate: true
logRequests: true
+99
View File
@@ -0,0 +1,99 @@
{
"swagger": "2.0",
"info": {
"title": "demo/app/v1alpha1/app.proto",
"version": "version not set"
},
"tags": [
{
"name": "DemoAppService"
}
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/v1alpha1/demo": {
"get": {
"operationId": "DemoAppService_GetDemo",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1alpha1GetDemoResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "language",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
"DemoAppService"
]
}
}
},
"definitions": {
"protobufAny": {
"type": "object",
"properties": {
"@type": {
"type": "string"
}
},
"additionalProperties": {}
},
"rpcStatus": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
},
"details": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/protobufAny"
}
}
}
},
"v1alpha1GetDemoResponse": {
"type": "object",
"properties": {
"timestamp": {
"type": "string",
"format": "date-time"
},
"fact": {
"type": "string"
},
"source": {
"type": "string"
},
"language": {
"type": "string"
}
},
"title": "Returns a randome fact, because this is a demo app\nso what else do we do?"
}
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"mcpServers": {
"default-server": {
"type": "streamable-http",
"url": "http://localhost:8080/api/mcp/"
}
}
}
+154
View File
@@ -0,0 +1,154 @@
{
"definitions": {
"ConfigDemoOpts": {
"properties": {
"factLang": {
"default": "en",
"type": "string"
},
"factType": {
"default": "random",
"enum": [
"today",
"random"
],
"type": "string"
}
},
"type": "object"
},
"ConfigGRPCConfig": {
"properties": {
"enableGRPCGateway": {
"default": true,
"type": "boolean"
},
"enableInstrumentation": {
"type": "boolean"
},
"enableProtovalidate": {
"description": "Add a chain unary and streaming interceptor for buf.build/go/protovalidate",
"default": false,
"type": "boolean"
},
"enableReflection": {
"type": "boolean"
},
"enabled": {
"type": "boolean"
},
"grpcGatewayPath": {
"default": "/grpc-api",
"type": "string"
},
"listen": {
"type": "string"
},
"logRequests": {
"type": "boolean"
}
},
"type": "object"
},
"ConfigHTTPConfig": {
"properties": {
"enabled": {
"type": "boolean"
},
"idleTimeout": {
"type": "string"
},
"listen": {
"type": "string"
},
"logExcludePathRegexps": {
"items": {
"type": "string"
},
"type": "array"
},
"logRequests": {
"type": "boolean"
},
"readTimeout": {
"type": "string"
},
"writeTimeout": {
"type": "string"
}
},
"type": "object"
},
"ConfigLogConfig": {
"properties": {
"enabled": {
"type": "boolean"
},
"format": {
"type": "string"
},
"level": {
"type": "string"
},
"output": {
"type": "string"
},
"timeFormat": {
"type": "string"
}
},
"type": "object"
},
"ConfigOTELConfig": {
"properties": {
"enabled": {
"type": "boolean"
},
"metricIntervalSecs": {
"type": "integer"
},
"prometheusEnabled": {
"type": "boolean"
},
"prometheusPath": {
"type": "string"
},
"stdoutEnabled": {
"type": "boolean"
}
},
"type": "object"
}
},
"properties": {
"environment": {
"type": "string"
},
"grpc": {
"$ref": "#/definitions/ConfigGRPCConfig"
},
"http": {
"$ref": "#/definitions/ConfigHTTPConfig"
},
"logging": {
"$ref": "#/definitions/ConfigLogConfig"
},
"name": {
"type": "string"
},
"opts": {
"$ref": "#/definitions/ConfigDemoOpts"
},
"otel": {
"$ref": "#/definitions/ConfigOTELConfig"
},
"timezone": {
"default": "UTC",
"type": "string"
},
"version": {
"type": "string"
}
},
"type": "object"
}
+14
View File
@@ -0,0 +1,14 @@
# App Config
APP_NAME="go-server-with-otel"
APP_LOG_LEVEL=trace ## For testing only
APP_LOG_FORMAT=console ## console, json
APP_LOG_TIME_FORMAT=long ## long, short, unix, rfc3339, off
# App OTEL Config
APP_OTEL_STDOUT_ENABLED=true ## For testing only
APP_OTEL_METRIC_INTERVAL_SECS=15
# OTEL SDK Config
OTEL_EXPORTER_OTLP_ENDPOINT="otel-collector.otel.svc.cluster.local" # Set to your otel collector
OTEL_SERVICE_NAME="go-server-with-otel"
OTEL_RESOURCE_ATTRIBUTES="env=development,service.version=(devel)"
+79
View File
@@ -0,0 +1,79 @@
module gitea.libretechconsulting.com/rmcguire/go-server-with-otel
go 1.25
require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.8-20250717185734-6c6e0d3c608e.1
gitea.libretechconsulting.com/rmcguire/go-app v0.12.3
github.com/go-resty/resty/v2 v2.16.5
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2
github.com/modelcontextprotocol/go-sdk v0.3.1
github.com/rs/zerolog v1.34.0
go.opentelemetry.io/otel/trace v1.38.0
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1
google.golang.org/grpc v1.75.0
google.golang.org/protobuf v1.36.8
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d
)
require (
github.com/caarlos0/env/v11 v11.3.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
github.com/swaggest/jsonschema-go v0.3.78 // indirect
github.com/swaggest/refl v1.4.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/prometheus v0.60.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
buf.build/go/protovalidate v0.14.0 // indirect
cel.dev/expr v0.24.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/cel-go v0.26.1 // indirect
github.com/google/jsonschema-go v0.2.1-0.20250825175020-748c325cec76 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_golang v1.23.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/otlptranslator v0.0.2 // indirect
github.com/prometheus/procfs v0.17.0 // indirect
github.com/stoewer/go-strcase v1.3.1 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect
)
tool (
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
google.golang.org/grpc/cmd/protoc-gen-go-grpc
google.golang.org/protobuf/cmd/protoc-gen-go
)
+186
View File
@@ -0,0 +1,186 @@
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.8-20250717185734-6c6e0d3c608e.1 h1:sjY1k5uszbIZfv11HO2keV4SLhNA47SabPO886v7Rvo=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.8-20250717185734-6c6e0d3c608e.1/go.mod h1:8EQ5GzyGJQ5tEIwMSxCl8RKJYsjCpAwkdcENoioXT6g=
buf.build/go/protovalidate v0.14.0 h1:kr/rC/no+DtRyYX+8KXLDxNnI1rINz0imk5K44ZpZ3A=
buf.build/go/protovalidate v0.14.0/go.mod h1:+F/oISho9MO7gJQNYC2VWLzcO1fTPmaTA08SDYJZncA=
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
gitea.libretechconsulting.com/rmcguire/go-app v0.12.1 h1:OWXz8S3NAlBAjIxY7G6ydO18t5/JiJ8f3EMKDeppEg8=
gitea.libretechconsulting.com/rmcguire/go-app v0.12.1/go.mod h1:T8K2vbmt0mApScJDGqfmNlvX5gdJSImjArUMpgfYs8U=
gitea.libretechconsulting.com/rmcguire/go-app v0.12.3 h1:sgSpPx6fkWqfBGsCAHqLOIQ6a1s+4nsG9kW24k+WYI8=
gitea.libretechconsulting.com/rmcguire/go-app v0.12.3/go.mod h1:+zDxLaJgxfPVWzp2YQrUYBFN9A1c7hZgsQkysWz+lSI=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bool64/dev v0.2.39 h1:kP8DnMGlWXhGYJEZE/J0l/gVBdbuhoPGL+MJG4QbofE=
github.com/bool64/dev v0.2.39/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg=
github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E=
github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs=
github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
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/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/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-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ=
github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
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/jsonschema-go v0.2.1-0.20250825175020-748c325cec76 h1:mBlBwtDebdDYr+zdop8N62a44g+Nbv7o2KjWyS1deR4=
github.com/google/jsonschema-go v0.2.1-0.20250825175020-748c325cec76/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
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/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248=
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
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/modelcontextprotocol/go-sdk v0.3.1 h1:0z04yIPlSwTluuelCBaL+wUag4YeflIU2Fr4Icb7M+o=
github.com/modelcontextprotocol/go-sdk v0.3.1/go.mod h1:whv0wHnsTphwq7CTiKYHkLtwLC06WMoY2KpO+RB9yXQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/otlptranslator v0.0.2 h1:+1CdeLVrRQ6Psmhnobldo0kTp96Rj80DRXRd5OSnMEQ=
github.com/prometheus/otlptranslator v0.0.2/go.mod h1:P8AwMgdD7XEr6QRUJ2QWLpiAZTgTE2UYgjlu3svompI=
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs=
github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
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/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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ=
github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU=
github.com/swaggest/jsonschema-go v0.3.78 h1:5+YFQrLxOR8z6CHvgtZc42WRy/Q9zRQQ4HoAxlinlHw=
github.com/swaggest/jsonschema-go v0.3.78/go.mod h1:4nniXBuE+FIGkOGuidjOINMH7OEqZK3HCSbfDuLRI0g=
github.com/swaggest/refl v1.4.0 h1:CftOSdTqRqs100xpFOT/Rifss5xBV/CT0S/FN60Xe9k=
github.com/swaggest/refl v1.4.0/go.mod h1:4uUVFVfPJ0NSX9FPwMPspeHos9wPFlCMGoPRllUbpvA=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
go.opentelemetry.io/otel/exporters/prometheus v0.60.0 h1:cGtQxGvZbnrWdC2GyjZi0PDKVSLWP/Jocix3QWfXtbo=
go.opentelemetry.io/otel/exporters/prometheus v0.60.0/go.mod h1:hkd1EekxNo69PTV4OWFGZcKQiIqg0RfuWExcPKFvepk=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0 h1:wm/Q0GAAykXv83wzcKzGGqAnnfLFyFe7RslekZuv+VI=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0/go.mod h1:ra3Pa40+oKjvYh+ZD3EdxFZZB0xdMfuileHAm4nNN7w=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 h1:kJxSDN4SgWWTjG/hPp3O7LCGLcHXFlvS2/FFOrwL+SE=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0/go.mod h1:mgIOzS7iZeKJdeB8/NYHrJ48fdGc71Llo5bJ1J4DWUE=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 h1:APHvLLYBhtZvsbnpkfknDZ7NyH4z5+ub/I0u8L3Oz6g=
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1/go.mod h1:xUjFWUnWDpZ/C0Gu0qloASKFb6f8/QXiiXhSPFsD668=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/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=
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0=
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+2
View File
@@ -0,0 +1,2 @@
charts/
Chart.lock
+30
View File
@@ -0,0 +1,30 @@
apiVersion: v2
name: go-server-with-otel
description: Golang HTTP and GRPC server with OTEL
# 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.3
# 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.7.0"
dependencies:
- name: hull
repository: https://vidispine.github.io/hull
version: 1.32.2
+643
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_: {}
##################################################
+1
View File
@@ -0,0 +1 @@
{{- include "hull.objects.prepare.all" (dict "HULL_ROOT_KEY" "hull" "ROOT_CONTEXT" $) }}
+195
View File
@@ -0,0 +1,195 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/vidispine/hull/refs/heads/main/hull/values.schema.json
hull:
config:
## go-server-with-otel custom settings (config.yaml)
appConfig:
# Custom app config
timezone: EST5EDT
opts:
factLang: en
factType: random
# go-app config
name: Demo go-app
logging:
format: json
level: debug
enabled: true
otel:
enabled: true
stdoutEnabled: false
http:
enabled: true
listen: :8080
logRequests: true
grpc:
enabled: true
enableReflection: true
listen: :8081
grpcGatewayPath: /api
enableGRPCGateway: true
enableInstrumentation: true
logRequests: true
## Chart settings
settings:
resources: {} # Applies to the app container
repo: gitea.libretechconsulting.com/rmcguire/go-server-with-otel
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: false
hostnames:
- app.mydomain.com
gatewayName: istio-ingressgateway
gatewayNamespace: istio-system
# Use this as a shortcut, or create your own hull.objects.grpcroute
grpcroute:
enabled: false
hostnames:
- app.mydomain.com
gatewayName: istio-ingressgateway
gatewayNamespace: istio-system
otelServiceName: go-server-with-otel
otelResourceAttributes: app=go-server-with-otel
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
+145
View File
@@ -0,0 +1,145 @@
// This template contains a simple
// app with OTEL bootstrap that will create an
// HTTP server configured by environment that exports
// spans and metrics to an OTEL collector if configured
// to do so. Will also stand up a prometheus metrics
// endpoint.
//
// Configuration and logger stored in context
// Reference implementation of the provided packages
package main
import (
"context"
"flag"
"fmt"
"os"
"os/signal"
"sync"
"syscall"
"time"
"github.com/rs/zerolog"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/app"
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/config"
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/demo"
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/service"
)
const (
terminationGracePeriod = 30 * time.Second
appName = "demo-app"
)
var (
// TODO: Register your service.AppService implementers here
// This should be the only change necessary here
// NOTE: At least one service needs to add a dial option
// for transport credentials, otherwise you will have to do it here
appServices = []service.AppService{
&demo.DemoService{},
}
flagSchema bool
)
func main() {
ctx, cncl := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
defer cncl()
// Load configuration and setup logging. The go-app framework
// will handle loading config and environment into our demo
// app config struct which embeds app.AooConfig
ctx, serviceConfig := app.MustLoadConfigInto(ctx, &config.ServiceConfig{})
// Print schema if that's all we have to do
if flagSchema {
printSchema()
os.Exit(0)
}
// Prepare app
app := &app.App{
AppContext: ctx,
}
app.InitOTEL()
tracer := otel.GetTracer(ctx, appName)
ctx, span := tracer.Start(ctx, appName+".startup", trace.WithAttributes(
attribute.Int("appServices", len(appServices)),
))
log := zerolog.Ctx(ctx)
log.Debug().Any("mergedConfig", serviceConfig).Msg("App configuration prepared")
// Prepare services
var err error
sdFuncs := make([]service.ShutdownFunc, len(appServices))
for i, svc := range appServices {
sdFuncs[i], err = svc.Init(ctx, serviceConfig)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
log.Fatal().Err(err).Send()
}
}
span.AddEvent("app services initialized")
// Merge all service implementations and add to app
grpcSvcs, httpSvcs := service.MergeServices(ctx, appServices...)
app.GRPC = grpcSvcs
app.HTTP = httpSvcs
span.SetAttributes(
attribute.Int("grpcServices", len(app.GRPC.Services)),
attribute.Int("httpServices", len(app.HTTP.Funcs)+len(app.HTTP.Handlers)),
)
// Launch app
app.MustRun()
span.AddEvent("app started")
span.SetStatus(codes.Ok, "")
span.End()
// Wait for app to complete and then perform internal shutdown
<-app.Done()
log.Info().Int("numServices", len(appServices)).Msg("shutting down app services")
sdCtx, cncl := context.WithTimeout(context.Background(), terminationGracePeriod)
defer cncl()
// Call registered shutdown funcs from all services
var sdWg sync.WaitGroup
for _, sdFunc := range sdFuncs {
sdWg.Add(1)
go func() {
defer sdWg.Done()
svcName, err := sdFunc(sdCtx)
log.Err(err).Str("service", svcName).Msg("terminated")
}()
}
sdWg.Wait()
}
// flag.Parse will be called by go-app
func init() {
flag.BoolVar(&flagSchema, "schema", false, "generate json schema and exit")
}
func printSchema() {
bytes, err := app.CustomSchema(&config.ServiceConfig{})
if err != nil {
panic(err)
}
fmt.Println(string(bytes))
os.Exit(0)
}
+31
View File
@@ -0,0 +1,31 @@
// Package config contains ServiceConfig, which is intended
// to extend go-app/pkg/config.AppConfig. Add any custom fields
// here, and it will automatically be handled by go-app, including
// overrides by environment, and json schema generation
package config
import (
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/config"
)
type ServiceConfig struct {
Timezone string `yaml:"timezone" json:"timezone,omitempty" default:"UTC"`
Opts *DemoOpts `yaml:"opts" json:"opts,omitempty"`
// Embeds go-app config, used by go-app to
// merge custom config into go-app config, and to produce
// a complete configuration json schema
*config.AppConfig
}
type DemoOpts struct {
FactLang string `yaml:"factLang" json:"factLang,omitempty" default:"en"`
FactType FactType `yaml:"factType" json:"factType,omitempty" default:"random" enum:"today,random"`
}
type FactType string
const (
TypeToday FactType = "today"
TypeRandom FactType = "random"
)
+58
View File
@@ -0,0 +1,58 @@
// Package demo provides a reference implementation
// of service.AppService. It packages out the GRPC and HTTP
// functionality, for the sake of structure.
package demo
import (
"context"
optsgrpc "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/grpc/opts"
optshttp "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/http/opts"
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/config"
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/demo/demogrpc"
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/demo/demohttp"
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/demo/demomcp"
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/service"
)
type DemoService struct {
ctx context.Context
config *config.ServiceConfig
http *demohttp.DemoHTTPServer
grpc *demogrpc.DemoGRPCServer
mcp *demomcp.DemoMCPServer
}
func (d *DemoService) Init(ctx context.Context, config *config.ServiceConfig,
) (service.ShutdownFunc, error) {
d.config = config
d.ctx = ctx
// These don't HAVE to be split into separate packages
// This is, after all, a demo app. Just implement the interface.
d.http = demohttp.NewDemoHTTPServer(ctx, config)
d.grpc = demogrpc.NewDemoGRPCServer(ctx, config)
d.mcp = demomcp.NewDemoMCPServer(ctx, config)
// TODO: This should actually do shutdown stuff
return func(_ context.Context) (string, error) {
return "DemoService", nil
}, nil
}
func (d *DemoService) GetGRPC() *optsgrpc.AppGRPC {
return &optsgrpc.AppGRPC{
Services: d.grpc.GetServices(),
GRPCDialOpts: d.grpc.GetDialOpts(),
}
}
func (d *DemoService) GetHTTP() *optshttp.AppHTTP {
return &optshttp.AppHTTP{
Ctx: d.ctx,
Funcs: d.http.GetHandleFuncs(),
Handlers: d.mcp.GetHandlers(),
HealthChecks: d.http.GetHealthCheckFuncs(),
}
}
+86
View File
@@ -0,0 +1,86 @@
package demogrpc
import (
"context"
"fmt"
"github.com/go-resty/resty/v2"
"github.com/rs/zerolog"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
grpccodes "google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
pb "gitea.libretechconsulting.com/rmcguire/go-server-with-otel/api/demo/app/v1alpha1"
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/config"
)
const (
defaultFactType = config.TypeRandom
factsBaseURI = "https://uselessfacts.jsph.pl/api/v2/facts"
)
type DemoGRPCServer struct {
tracer trace.Tracer
ctx context.Context
cfg *config.ServiceConfig
client *resty.Client
pb.UnimplementedDemoAppServiceServer
}
func NewDemoGRPCServer(ctx context.Context, cfg *config.ServiceConfig) *DemoGRPCServer {
if cfg.Opts == nil {
cfg.Opts = &config.DemoOpts{}
}
if cfg.Opts.FactType == "" {
cfg.Opts.FactType = config.TypeRandom
}
return &DemoGRPCServer{
ctx: ctx,
cfg: cfg,
tracer: otel.GetTracer(ctx, "demoGRPCServer"),
client: resty.New().SetBaseURL(factsBaseURI),
}
}
func (d *DemoGRPCServer) GetDemo(ctx context.Context, req *pb.GetDemoRequest) (
*pb.GetDemoResponse, error,
) {
ctx, span := d.tracer.Start(ctx, "getDemo", trace.WithAttributes(
attribute.String("language", req.GetLanguage()),
))
defer span.End()
r := d.client.R().SetContext(ctx)
if req.GetLanguage() != "" {
r = r.SetQueryParam("language", req.GetLanguage())
}
fact := new(RandomFact)
r.SetResult(fact)
resp, err := r.Get(fmt.Sprintf("/%s", d.cfg.Opts.FactType))
if err != nil {
span.SetStatus(codes.Error, err.Error())
span.RecordError(err)
zerolog.Ctx(d.ctx).Err(err).Send()
return nil, status.Errorf(grpccodes.Unknown, "%s: %s",
err.Error(), string(resp.Body()))
}
span.SetAttributes(attribute.String("fact", fact.Text))
span.SetStatus(codes.Ok, "")
zerolog.Ctx(d.ctx).Debug().Str("fact", fact.Text).Msg("retrieved fact")
return fact.FactToProto(), nil
}
+25
View File
@@ -0,0 +1,25 @@
package demogrpc
import (
"google.golang.org/protobuf/types/known/timestamppb"
pb "gitea.libretechconsulting.com/rmcguire/go-server-with-otel/api/demo/app/v1alpha1"
)
type RandomFact struct {
ID string `json:"id,omitempty"`
Text string `json:"text,omitempty"`
Source string `json:"source,omitempty"`
SourceURL string `json:"source_url,omitempty"`
Language string `json:"language,omitempty"`
Permalink string `json:"permalink,omitempty"`
}
func (f *RandomFact) FactToProto() *pb.GetDemoResponse {
return &pb.GetDemoResponse{
Timestamp: timestamppb.Now(),
Fact: f.Text,
Source: f.SourceURL,
Language: f.Language,
}
}
+30
View File
@@ -0,0 +1,30 @@
package demogrpc
import (
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/grpc/opts"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
demoAppPb "gitea.libretechconsulting.com/rmcguire/go-server-with-otel/api/demo/app/v1alpha1"
)
func (ds *DemoGRPCServer) GetDialOpts() []grpc.DialOption {
return []grpc.DialOption{
// NOTE: Necessary for grpc-gateway to connect to grpc server
// Update if grpc service has credentials, tls, etc..
grpc.WithTransportCredentials(insecure.NewCredentials()),
}
}
func (ds *DemoGRPCServer) GetServices() []*opts.GRPCService {
return []*opts.GRPCService{
{
Name: "Demo App",
Type: &demoAppPb.DemoAppService_ServiceDesc,
Service: ds,
GwRegistrationFuncs: []opts.GwRegistrationFunc{
demoAppPb.RegisterDemoAppServiceHandler,
},
},
}
}
+44
View File
@@ -0,0 +1,44 @@
package demohttp
import (
"context"
"net/http"
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/http/opts"
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/config"
)
type DemoHTTPServer struct {
ctx context.Context
cfg *config.ServiceConfig
}
func NewDemoHTTPServer(ctx context.Context, cfg *config.ServiceConfig) *DemoHTTPServer {
return &DemoHTTPServer{
ctx: ctx,
cfg: cfg,
}
}
func (dh *DemoHTTPServer) GetHandleFuncs() []opts.HTTPFunc {
// TODO: Implement real http handle funcs
return []opts.HTTPFunc{
{
Path: "/test",
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
w.Write([]byte("unimplemented demo app"))
},
},
}
}
func (dh *DemoHTTPServer) GetHealthCheckFuncs() []opts.HealthCheckFunc {
return []opts.HealthCheckFunc{
func(ctx context.Context) error {
// TODO: Implement real health checks
return nil
},
}
}
+70
View File
@@ -0,0 +1,70 @@
/*
Package demomcp contains a simple reference implementation of returning a
HTTPHandler for an MCP server with a single tool used to retrieve random facts
*/
package demomcp
import (
"context"
"net/http"
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/http/opts"
"github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/rs/zerolog"
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/config"
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/demo/demogrpc"
)
var DemoMCPImpl = &mcp.Implementation{
Name: "Demo MCP Server",
Title: "Demo MCP",
}
type DemoMCPServer struct {
ctx context.Context
cfg *config.ServiceConfig
log *zerolog.Logger
server *mcp.Server
demoGRPC *demogrpc.DemoGRPCServer
}
func NewDemoMCPServer(ctx context.Context, cfg *config.ServiceConfig) *DemoMCPServer {
return &DemoMCPServer{
ctx: ctx,
cfg: cfg,
log: zerolog.Ctx(ctx),
demoGRPC: demogrpc.NewDemoGRPCServer(ctx, cfg),
server: mcp.NewServer(DemoMCPImpl, &mcp.ServerOptions{
Instructions: "Call this demo MCP tool if the user asks useless questions or wants a random fact",
HasTools: true,
}),
}
}
func (d *DemoMCPServer) GetHandlers() []opts.HTTPHandler {
// NOTE: Add other tools here
d.addDemoRandomFactTool()
demoHandler := mcp.NewStreamableHTTPHandler(func(*http.Request) *mcp.Server {
return d.server
}, &mcp.StreamableHTTPOptions{})
d.log.Debug().Msg("Demo MCP Tool Ready")
return []opts.HTTPHandler{
{
Prefix: "/api/mcp",
Handler: demoHandler,
},
}
}
func (d *DemoMCPServer) GetHealthCheckFuncs() []opts.HealthCheckFunc {
return []opts.HealthCheckFunc{
func(ctx context.Context) error {
// TODO: Implement real health checks
return nil
},
}
}
+60
View File
@@ -0,0 +1,60 @@
package demomcp
import (
"context"
"github.com/modelcontextprotocol/go-sdk/mcp"
"k8s.io/utils/ptr"
demo "gitea.libretechconsulting.com/rmcguire/go-server-with-otel/api/demo/app/v1alpha1"
)
var DemoTool = &mcp.Tool{
Name: "Demo Tool",
Title: "Demo Random Fact Tool",
Description: "Returns a random fact for demo or boredom purposes",
Annotations: &mcp.ToolAnnotations{
ReadOnlyHint: true,
OpenWorldHint: ptr.To(true),
},
}
type DemoToolParams struct {
Language string `json:"language,omitempty" jsonschema:"Random Fact Language Abbreviation (e.g. en)"`
}
func (d *DemoMCPServer) addDemoRandomFactTool() {
d.server.AddTool(
mcp.ToolFor(DemoTool, d.demoToolHandler),
)
}
func (d *DemoMCPServer) demoToolHandler(ctx context.Context, req *mcp.CallToolRequest, args *DemoToolParams) (
*mcp.CallToolResult, any, error,
) {
d.log.Debug().Str("language", args.Language).Msg("demo fact tool called")
fact, err := d.demoGRPC.GetDemo(ctx, &demo.GetDemoRequest{
Language: &args.Language,
})
if err != nil {
return &mcp.CallToolResult{
IsError: true,
Meta: mcp.Meta{
"error": err,
},
}, nil, err
}
return &mcp.CallToolResult{
Meta: mcp.Meta{
"language": fact.GetLanguage(),
"source": fact.GetSource(),
},
Content: []mcp.Content{
&mcp.TextContent{
Text: fact.GetFact(),
},
},
}, nil, nil
}
+63
View File
@@ -0,0 +1,63 @@
// Package service defines generics around implementation of
// a new service that provides configuration for go-app
//
// The interface is meant to make the template more clear to implement
// without unnecessary changes in main.go
package service
import (
"context"
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/config"
optsgrpc "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/grpc/opts"
optshttp "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/http/opts"
)
type ShutdownFunc func(ctx context.Context) (string, error)
type AppService interface {
Init(ctx context.Context, config *config.ServiceConfig) (ShutdownFunc, error)
GetGRPC() *optsgrpc.AppGRPC
GetHTTP() *optshttp.AppHTTP
}
// MergeServices is used if multiple services are served by this server
// with their own distinct packages and configuration
//
// You should be overriding the done chan, listener, or any other
// configuration used by the SERVER rather than the individual services.
func MergeServices(ctx context.Context, appServices ...AppService) (*optsgrpc.AppGRPC, *optshttp.AppHTTP) {
mergedGRPC := &optsgrpc.AppGRPC{}
for _, svc := range appServices {
mergedGRPC = mergeGRPC(mergedGRPC, svc.GetGRPC())
}
mergedHTTP := &optshttp.AppHTTP{}
for _, svc := range appServices {
mergedHTTP = mergeHTTP(mergedHTTP, svc.GetHTTP())
}
mergedHTTP.Ctx = ctx
return mergedGRPC, mergedHTTP
}
func mergeHTTP(a *optshttp.AppHTTP, b *optshttp.AppHTTP) *optshttp.AppHTTP {
return &optshttp.AppHTTP{
Funcs: append(a.Funcs, b.Funcs...),
Middleware: append(a.Middleware, b.Middleware...),
Handlers: append(a.Handlers, b.Handlers...),
HealthChecks: append(a.HealthChecks, b.HealthChecks...),
}
}
func mergeGRPC(a *optsgrpc.AppGRPC, b *optsgrpc.AppGRPC) *optsgrpc.AppGRPC {
return &optsgrpc.AppGRPC{
Services: append(a.Services, b.Services...),
GRPCOpts: append(a.GRPCOpts, b.GRPCOpts...),
UnaryInterceptors: append(a.UnaryInterceptors, b.UnaryInterceptors...),
StreamInterceptors: append(a.StreamInterceptors, b.StreamInterceptors...),
GRPCDialOpts: append(a.GRPCDialOpts, b.GRPCDialOpts...),
GRPCGatewayOpts: append(a.GRPCGatewayOpts, b.GRPCGatewayOpts...),
}
}
+29
View File
@@ -0,0 +1,29 @@
syntax = "proto3";
package demo.app.v1alpha1;
import "buf/validate/validate.proto";
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
option go_package = "gitea.libretechconsulting.com/rmcguire/go-server-with-otel/api/v1alpha1/demo";
// Options for random fact, in this case
// just a language
message GetDemoRequest {
optional string language = 1 [(buf.validate.field).string.min_len = 2];
}
// Returns a randome fact, because this is a demo app
// so what else do we do?
message GetDemoResponse {
google.protobuf.Timestamp timestamp = 1;
string fact = 2;
string source = 3;
string language = 4 [(buf.validate.field).string.min_len = 2];
}
service DemoAppService {
rpc GetDemo(GetDemoRequest) returns (GetDemoResponse) {
option (google.api.http) = {get: "/v1alpha1/demo"}; // grpc-gateway endpoint
}
}