-
Notifications
You must be signed in to change notification settings - Fork 3
feat(gateway): add GatewayGRPCAPI CRD for gRPC service exposure #469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e0059ec
3971821
de1e325
c0fdbff
6aa2687
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| FROM golang:1.26-alpine AS builder | ||
|
|
||
| RUN apk add --no-cache git | ||
|
|
||
| WORKDIR /src | ||
|
|
||
| COPY go.mod go.sum ./ | ||
| RUN go mod download | ||
|
|
||
| COPY . . | ||
|
|
||
| ARG VERSION=latest | ||
| ARG COMMIT=unknown | ||
| ARG LICENCE_PUBLIC_KEY_B64="" | ||
|
|
||
| WORKDIR /src/cmd | ||
|
|
||
| RUN set -e; \ | ||
| LDFLAGS="-X github.com/formancehq/operator/v3/cmd.Version=${VERSION} \ | ||
| -X github.com/formancehq/operator/v3/cmd.BuildDate=$(date +%s) \ | ||
| -X github.com/formancehq/operator/v3/cmd.Commit=${COMMIT}"; \ | ||
| if [ -n "$LICENCE_PUBLIC_KEY_B64" ]; then \ | ||
| LICENCE_PUBLIC_KEY="$(printf '%s' "$LICENCE_PUBLIC_KEY_B64" | base64 -d)"; \ | ||
| LDFLAGS="${LDFLAGS} -X 'github.com/formancehq/go-libs/v5/pkg/authn/licence.formancePublicKey=${LICENCE_PUBLIC_KEY}'"; \ | ||
| fi; \ | ||
| CGO_ENABLED=0 go build -buildvcs=false -o /usr/bin/operator -ldflags="${LDFLAGS}" . | ||
|
|
||
| FROM alpine:3.20 | ||
|
|
||
| RUN apk update && apk add --no-cache ca-certificates curl | ||
|
|
||
| ENTRYPOINT ["/usr/bin/operator"] | ||
|
|
||
| COPY --from=builder /usr/bin/operator /usr/bin/operator | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,9 +45,13 @@ compile: | |
| SAVE ARTIFACT main | ||
|
|
||
| build-image: | ||
| FROM core+final-image | ||
| ENTRYPOINT ["/usr/bin/operator"] | ||
| COPY --pass-args (+compile/main) /usr/bin/operator | ||
| ARG LICENCE_PUBLIC_KEY_B64="" | ||
| ARG EARTHLY_BUILD_SHA | ||
| FROM DOCKERFILE \ | ||
| --build-arg LICENCE_PUBLIC_KEY_B64=$LICENCE_PUBLIC_KEY_B64 \ | ||
| --build-arg VERSION=$tag \ | ||
| --build-arg COMMIT=$EARTHLY_BUILD_SHA \ | ||
| -f Dockerfile . | ||
| ARG REPOSITORY=ghcr.io | ||
| ARG tag=latest | ||
| DO --pass-args core+SAVE_IMAGE --COMPONENT=operator --TAG=$tag | ||
|
Comment on lines
47
to
57
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OH we still have a earthfile here
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately yes ^^ |
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,88 @@ | ||||||||||||||||||||||
| /* | ||||||||||||||||||||||
| Copyright 2022. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||||||||||||||||||||
| you may not use this file except in compliance with the License. | ||||||||||||||||||||||
| You may obtain a copy of the License at | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| http://www.apache.org/licenses/LICENSE-2.0 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Unless required by applicable law or agreed to in writing, software | ||||||||||||||||||||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||||||||||||||||||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||||||||||||||||||
| See the License for the specific language governing permissions and | ||||||||||||||||||||||
| limitations under the License. | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| package v1beta1 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import ( | ||||||||||||||||||||||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| type GatewayGRPCAPISpec struct { | ||||||||||||||||||||||
| StackDependency `json:",inline"` | ||||||||||||||||||||||
| // Name indicates the module name (e.g. "ledger") | ||||||||||||||||||||||
| Name string `json:"name"` | ||||||||||||||||||||||
| // GRPCServices is the list of fully-qualified gRPC service names | ||||||||||||||||||||||
| // exposed by this module (e.g. "formance.ledger.v1.LedgerService") | ||||||||||||||||||||||
| GRPCServices []string `json:"grpcServices"` | ||||||||||||||||||||||
| // Port is the gRPC port on the backend service | ||||||||||||||||||||||
| //+optional | ||||||||||||||||||||||
| //+kubebuilder:default:=8081 | ||||||||||||||||||||||
| Port int32 `json:"port,omitempty"` | ||||||||||||||||||||||
|
Comment on lines
+30
to
+33
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Kubernetes Service.spec.ports[].port is the TCP/UDP/SCTP service port exposed by the Service. Its valid range is 1–65535 (inclusive). [1] Kubernetes does not allow 0 for spec.ports[*].port; the API field is an integer port number exposed by the Service. [1] Citations: 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Locate the file and show the relevant section
ls -la api/formance.com/v1beta1/gatewaygrpcapi_types.go >/dev/null 2>&1 || true
sed -n '1,120p' api/formance.com/v1beta1/gatewaygrpcapi_types.go | nl -ba
# Find all uses of the Port field (likely .Port or spec.port) across the repo
rg -n --hidden --glob '!*vendor/*' "Gateway.*grpc|gatewaygrpc|gatewaygrpcapi|spec\.port|\.Port\b|json:\"port\"|Port int32" .
# Look for existing kubebuilder numeric bound markers to match repo style
rg -n --hidden --glob '!*vendor/*' "kubebuilder:validation:(Minimum|Maximum):=" .Repository: formancehq/operator Length of output: 106 🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE="api/formance.com/v1beta1/gatewaygrpcapi_types.go"
if [ -f "$FILE" ]; then
sed -n '1,120p' "$FILE" | sed -n '1,120p'
else
echo "Missing expected file: $FILE"
echo "Listing matching gatewaygrpcapi_types.go files:"
find . -name 'gatewaygrpcapi_types.go' -print
fi
# Show the exact Port field vicinity (more focused)
if [ -f "$FILE" ]; then
rg -n "Port int32" "$FILE" -n
sed -n '20,60p' "$FILE"
fi
# Find usages of this Port field / json:"port" tags / gateway grpc port conversion
rg -n --hidden --glob '!*vendor/*' --glob '!*dist/*' \
'json:"port"|spec\.port|\.Port\b|gatewaygrpcapi|Gatewaygrpc|grpc port|Service.*port|spec\.ports' .
# Find existing kubebuilder numeric bound markers in the repo
rg -n --hidden --glob '!*vendor/*' 'kubebuilder:validation:(Minimum|Maximum):=' .Repository: formancehq/operator Length of output: 10858 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== CRD base schema (formance.com_gatewaygrpcapis.yaml) ==="
CRD="config/crd/bases/formance.com_gatewaygrpcapis.yaml"
if [ -f "$CRD" ]; then
# show only port schema area
rg -n "port" "$CRD" | head -n 80
# show likely properties section around "port"
sed -n '1,120p' "$CRD"
# try to locate port property block by searching for "port:" and show nearby lines
LINE=$(rg -n "port" "$CRD" | head -n 1 | cut -d: -f1 || true)
if [ -n "${LINE:-}" ]; then
START=$((LINE-20)); END=$((LINE+60))
sed -n "${START},${END}p" "$CRD"
fi
else
echo "Missing $CRD"
fi
echo "=== Helm CRD template ==="
HELM_CRD="helm/crds/templates/crds/apiextensions.k8s.io_v1_customresourcedefinition_gatewaygrpcapis.formance.com.yaml"
if [ -f "$HELM_CRD" ]; then
rg -n "port" "$HELM_CRD" | head -n 80
LINE=$(rg -n "port" "$HELM_CRD" | head -n 1 | cut -d: -f1 || true)
if [ -n "${LINE:-}" ]; then
START=$((LINE-20)); END=$((LINE+60))
sed -n "${START},${END}p" "$HELM_CRD"
fi
else
echo "Missing $HELM_CRD"
fi
echo "=== Code usage ==="
echo "--- internal/resources/gatewaygrpcapis/create.go (port setting) ---"
sed -n '1,120p' internal/resources/gatewaygrpcapis/create.go
echo "--- internal/resources/gatewaygrpcapis/init.go (service creation input) ---"
sed -n '1,120p' internal/resources/gatewaygrpcapis/init.go
echo "--- internal/resources/services/services.go (service spec ports) ---"
sed -n '1,120p' internal/resources/services/services.go
echo "=== Any explicit handling of port==0 ==="
rg -n --hidden --glob '!*vendor/*' --glob '!*dist/*' '== *0|<= *0|minimum|maximum|Port[^A-Za-z_]|cfg\.Port|Spec\.Port' internal | head -n 200Repository: formancehq/operator Length of output: 26154 🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n --hidden --glob '!*vendor/*' 'kubebuilder:validation:' .
rg -n --hidden --glob '!*vendor/*' 'kubebuilder:validation:(Minimum|Maximum):=' .
rg -n --hidden --glob '!*vendor/*' 'kubebuilder:validation:(ExclusiveMinimum|ExclusiveMaximum):=' .Repository: formancehq/operator Length of output: 1465 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== shared.go kubebuilder validation section (around Minimum=0 etc.) ==="
sed -n '40,120p' api/formance.com/v1beta1/shared.go | cat
echo "=== Any Maximum= / Minimum= markers in repo ==="
rg -n --hidden --glob '!*vendor/*' 'kubebuilder:validation:(Minimum|Maximum)=' api/formance.com/v1beta1Repository: formancehq/operator Length of output: 3943 Add CRD validation bounds for
Suggested fix // Port is the gRPC port on the backend service
//+optional
//+kubebuilder:default:=8081
+ //+kubebuilder:validation:Minimum=1
+ //+kubebuilder:validation:Maximum=65535
Port int32 `json:"port,omitempty"`📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| type GatewayGRPCAPIStatus struct { | ||||||||||||||||||||||
| Status `json:",inline"` | ||||||||||||||||||||||
| //+optional | ||||||||||||||||||||||
| Ready bool `json:"ready,omitempty"` | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Comment on lines
+36
to
+40
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove duplicate
Suggested fix type GatewayGRPCAPIStatus struct {
Status `json:",inline"`
- //+optional
- Ready bool `json:"ready,omitempty"`
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| //+kubebuilder:object:root=true | ||||||||||||||||||||||
| //+kubebuilder:subresource:status | ||||||||||||||||||||||
| //+kubebuilder:resource:scope=Cluster | ||||||||||||||||||||||
| //+kubebuilder:printcolumn:name="Stack",type=string,JSONPath=".spec.stack",description="Stack" | ||||||||||||||||||||||
| //+kubebuilder:printcolumn:name="Ready",type=string,JSONPath=".status.ready",description="Ready" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // GatewayGRPCAPI is the Schema for the GRPCAPIs API | ||||||||||||||||||||||
| type GatewayGRPCAPI struct { | ||||||||||||||||||||||
| metav1.TypeMeta `json:",inline"` | ||||||||||||||||||||||
| metav1.ObjectMeta `json:"metadata,omitempty"` | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Spec GatewayGRPCAPISpec `json:"spec,omitempty"` | ||||||||||||||||||||||
| Status GatewayGRPCAPIStatus `json:"status,omitempty"` | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| func (in *GatewayGRPCAPI) SetReady(b bool) { | ||||||||||||||||||||||
| in.Status.Ready = b | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| func (in *GatewayGRPCAPI) IsReady() bool { | ||||||||||||||||||||||
| return in.Status.Ready | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| func (in *GatewayGRPCAPI) SetError(s string) { | ||||||||||||||||||||||
| in.Status.Info = s | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| func (a GatewayGRPCAPI) GetStack() string { | ||||||||||||||||||||||
| return a.Spec.Stack | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| func (in *GatewayGRPCAPI) GetConditions() *Conditions { | ||||||||||||||||||||||
| return &in.Status.Conditions | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| //+kubebuilder:object:root=true | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // GatewayGRPCAPIList contains a list of GatewayGRPCAPI | ||||||||||||||||||||||
| type GatewayGRPCAPIList struct { | ||||||||||||||||||||||
| metav1.TypeMeta `json:",inline"` | ||||||||||||||||||||||
| metav1.ListMeta `json:"metadata,omitempty"` | ||||||||||||||||||||||
| Items []GatewayGRPCAPI `json:"items"` | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| func init() { | ||||||||||||||||||||||
| SchemeBuilder.Register(&GatewayGRPCAPI{}, &GatewayGRPCAPIList{}) | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Run the runtime container as a non-root user.
The final image runs as root by default. Please create a dedicated user/group and switch to it in the runtime stage.
Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents