Server-Side-Implementation

This commit is contained in:
2025-12-29 16:42:49 +01:00
commit c9d0322177
20 changed files with 1539 additions and 0 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "shared"]
path = shared
url = https://git.kocoder.xyz/heek/shared.git

View File

@ -0,0 +1,78 @@
package v1alpha1
//go:generate controller-gen object paths=$GOFILE
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
type TLS struct {
SecretName string `json:"secretName"`
}
type ResponseForwarding struct {
FlushInterval string `json:"flushInterval"`
}
type Cookie struct {
HttpOnly bool `json:"httpOnly"`
Name string `json:"name"`
Secure bool `json:"secure"`
}
type Sticky struct {
Cookie Cookie `json:"cookie"`
}
type Service struct {
Kind string `json:"kind"`
Name string `json:"name"`
Namespace string `json:"namespace"`
PassHostHeader bool `json:"passHostHeader"`
Port int `json:"port"`
ResponseForwarding ResponseForwarding `json:"responseForwarding"`
Scheme string `json:"scheme"`
Sticky Sticky `json:"sticky"`
Strategy string `json:"strategy"`
Weight int `json:"weight"`
}
type Observability struct {
AccessLogs bool `json:"accessLogs"`
Metrics bool `json:"metrics"`
Tracing bool `json:"tracing"`
}
type Middleware struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
}
type Route struct {
Kind string `json:"kind"`
Match string `json:"match"`
Middlewares []Middleware `json:"middlewares"`
Observability Observability `json:"observability"`
Priority int `json:"priority"`
Services []Service `json:"services"`
}
type IngressRouteSpec struct {
EntryPoints []string `json:"entryPoints"`
Routes []Route `json:"routes"`
TLS TLS `json:"tls"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type IngressRoute struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec IngressRouteSpec `json:"spec"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type IngressRouteList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []IngressRoute `json:"items"`
}

View File

@ -0,0 +1,27 @@
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const GroupName = "traefik.io"
const GroupVersion = "v1alpha1"
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: GroupVersion}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&IngressRoute{},
&IngressRouteList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View File

@ -0,0 +1,67 @@
//go:build !ignore_autogenerated
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IngressRoute) DeepCopyInto(out *IngressRoute) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRoute.
func (in *IngressRoute) DeepCopy() *IngressRoute {
if in == nil {
return nil
}
out := new(IngressRoute)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *IngressRoute) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IngressRouteList) DeepCopyInto(out *IngressRouteList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]IngressRoute, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRouteList.
func (in *IngressRouteList) DeepCopy() *IngressRouteList {
if in == nil {
return nil
}
out := new(IngressRouteList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *IngressRouteList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

33
auth/psk-interceptor.go Normal file
View File

@ -0,0 +1,33 @@
package auth
import (
"context"
"errors"
"strings"
"connectrpc.com/connect"
)
func NewPSKInterceptor(psk string) connect.UnaryInterceptorFunc {
return func(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
if req.Spec().IsClient {
return nil, errors.New("Serverside PSKInterceptor intercepted on the client.")
} else if req.Header().Get("token-header") == "" {
// No Auth Token Present
return nil, errors.New("No Auth Token present!")
} else if !strings.HasPrefix(req.Peer().Addr, "192.168.143") {
// Not from trusted subnet
return nil, errors.New("Request from untrusted subnet")
} else {
authToken := req.Header().Get("token-header")
if authToken != "MWE4MWQ5NDY2OWM1NGI4ZDhmNDNkZDc2Y2M5M2IyYThlMTIzZjNmNzY4ZTg2NDA2MGRjZWFjZjI3M2MxYTkzNDFhZDM5YjA0NmYzYjZiODEzZjNjNDZiYjhkMGU0OTdlOGNkN2FmMDFiYjczMWJmNDZhMGI4Yjk0OTZhNQo=" {
return nil, errors.New("Invalid auth-token")
}
return next(ctx, req)
}
}
}
}

20
buf.gen.yaml Normal file
View File

@ -0,0 +1,20 @@
version: v2
plugins:
- local: protoc-gen-go
out: gen
opt: paths=source_relative
- local: protoc-gen-connect-go
out: gen
opt:
- paths=source_relative
- simple
- remote: buf.build/grpc/java:v1.77.0
out: java-dal
managed:
enabled: true
override:
- file_option: go_package_prefix
value: git.kocoder.xyz/kocoder/portforwarder/gen
disable:
- file_option: go_package
module: buf.build/bufbuild/protovalidate

8
buf.yaml Normal file
View File

@ -0,0 +1,8 @@
# For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml
version: v2
lint:
use:
- STANDARD
breaking:
use:
- FILE

39
clientset/v1alpha1/api.go Normal file
View File

@ -0,0 +1,39 @@
package v1alpha1
import (
"git.kocoder.xyz/kocoder/portforwarder/api/types/v1alpha1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
)
type TraefikV1Alpha1Interface interface {
Ingressroute(namespace string) IngressrouteInterface
}
type TraefikV1Alpha1Client struct {
restClient rest.Interface
}
func NewForConfig(c *rest.Config) (TraefikV1Alpha1Interface, error) {
config := *c
config.ContentConfig.GroupVersion = &schema.GroupVersion{Group: v1alpha1.GroupName, Version: v1alpha1.GroupVersion}
config.APIPath = "/apis"
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
config.UserAgent = rest.DefaultKubernetesUserAgent()
client, err := rest.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &TraefikV1Alpha1Client{restClient: client}, nil
}
func (c *TraefikV1Alpha1Client) Ingressroute(namespace string) IngressrouteInterface {
return ingressrouteClient{
restClient: c.restClient,
ns: namespace,
rn: "ingressroutes",
}
}

View File

@ -0,0 +1,52 @@
package v1alpha1
import (
"context"
"git.kocoder.xyz/kocoder/portforwarder/api/types/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
)
type IngressrouteInterface interface {
List(ctx context.Context, opts metav1.ListOptions) (*v1alpha1.IngressRouteList, error)
Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1alpha1.IngressRoute, error)
Create(ctx context.Context, ir *v1alpha1.IngressRoute) (*v1alpha1.IngressRoute, error)
Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error
}
type ingressrouteClient struct {
restClient rest.Interface
ns string
rn string
}
func (c ingressrouteClient) List(ctx context.Context, opts metav1.ListOptions) (*v1alpha1.IngressRouteList, error) {
result := v1alpha1.IngressRouteList{}
err := c.restClient.Get().Namespace(c.ns).Resource(c.rn).VersionedParams(&opts, scheme.ParameterCodec).Do(ctx).Into(&result)
return &result, err
}
func (c ingressrouteClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1alpha1.IngressRoute, error) {
result := v1alpha1.IngressRoute{}
err := c.restClient.Get().Namespace(c.ns).Resource(c.rn).Name(name).VersionedParams(&opts, scheme.ParameterCodec).Do(ctx).Into(&result)
return &result, err
}
func (c ingressrouteClient) Create(ctx context.Context, ir *v1alpha1.IngressRoute) (*v1alpha1.IngressRoute, error) {
result := v1alpha1.IngressRoute{}
err := c.restClient.Post().Namespace(c.ns).Resource(c.rn).Body(ir).Do(ctx).Into(&result)
return &result, err
}
func (c ingressrouteClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
opts.Watch = true
return c.restClient.Get().Namespace(c.ns).Resource(c.rn).VersionedParams(&opts, scheme.ParameterCodec).Watch(ctx)
}
func (c ingressrouteClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
return c.restClient.Delete().Namespace(c.ns).Resource(c.rn).Name(name).Body(&opts).Do(ctx).Error()
}

23
externalService.yaml Normal file
View File

@ -0,0 +1,23 @@
---
apiVersion: v1
kind: Service
metadata:
name: external-db-svc
spec:
type: ExternalName
externalName: "alumnihub4.kocoder.xyz"
# ---
# apiVersion: discovery.k8s.io/v1
# kind: EndpointSlice
# metadata:
# name: external-db-svc-1
# labels:
# kubernetes.io/service-name: external-db-svc
# addressType: IPv4
# ports:
# - name: http
# protocol: TCP
# port: 80
# endpoints:
# - addresses:
# - "10.1.0.2"

View File

@ -0,0 +1,451 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc (unknown)
// source: portforwarding/portforwarding.proto
package portforwardingv1
import (
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)
)
type Portforwarding struct {
state protoimpl.MessageState `protogen:"open.v1"`
Alias string `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"`
TargetPort int32 `protobuf:"varint,2,opt,name=targetPort,proto3" json:"targetPort,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Portforwarding) Reset() {
*x = Portforwarding{}
mi := &file_portforwarding_portforwarding_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Portforwarding) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Portforwarding) ProtoMessage() {}
func (x *Portforwarding) ProtoReflect() protoreflect.Message {
mi := &file_portforwarding_portforwarding_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 Portforwarding.ProtoReflect.Descriptor instead.
func (*Portforwarding) Descriptor() ([]byte, []int) {
return file_portforwarding_portforwarding_proto_rawDescGZIP(), []int{0}
}
func (x *Portforwarding) GetAlias() string {
if x != nil {
return x.Alias
}
return ""
}
func (x *Portforwarding) GetTargetPort() int32 {
if x != nil {
return x.TargetPort
}
return 0
}
type ListPortforwardingsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListPortforwardingsRequest) Reset() {
*x = ListPortforwardingsRequest{}
mi := &file_portforwarding_portforwarding_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListPortforwardingsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListPortforwardingsRequest) ProtoMessage() {}
func (x *ListPortforwardingsRequest) ProtoReflect() protoreflect.Message {
mi := &file_portforwarding_portforwarding_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 ListPortforwardingsRequest.ProtoReflect.Descriptor instead.
func (*ListPortforwardingsRequest) Descriptor() ([]byte, []int) {
return file_portforwarding_portforwarding_proto_rawDescGZIP(), []int{1}
}
type ListPortforwardingsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Pf []*Portforwarding `protobuf:"bytes,1,rep,name=pf,proto3" json:"pf,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListPortforwardingsResponse) Reset() {
*x = ListPortforwardingsResponse{}
mi := &file_portforwarding_portforwarding_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListPortforwardingsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListPortforwardingsResponse) ProtoMessage() {}
func (x *ListPortforwardingsResponse) ProtoReflect() protoreflect.Message {
mi := &file_portforwarding_portforwarding_proto_msgTypes[2]
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 ListPortforwardingsResponse.ProtoReflect.Descriptor instead.
func (*ListPortforwardingsResponse) Descriptor() ([]byte, []int) {
return file_portforwarding_portforwarding_proto_rawDescGZIP(), []int{2}
}
func (x *ListPortforwardingsResponse) GetPf() []*Portforwarding {
if x != nil {
return x.Pf
}
return nil
}
type CreatePortforwardingRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Alias string `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"`
TargetPort int32 `protobuf:"varint,2,opt,name=targetPort,proto3" json:"targetPort,omitempty"`
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=createdAt,proto3" json:"createdAt,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CreatePortforwardingRequest) Reset() {
*x = CreatePortforwardingRequest{}
mi := &file_portforwarding_portforwarding_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CreatePortforwardingRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreatePortforwardingRequest) ProtoMessage() {}
func (x *CreatePortforwardingRequest) ProtoReflect() protoreflect.Message {
mi := &file_portforwarding_portforwarding_proto_msgTypes[3]
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 CreatePortforwardingRequest.ProtoReflect.Descriptor instead.
func (*CreatePortforwardingRequest) Descriptor() ([]byte, []int) {
return file_portforwarding_portforwarding_proto_rawDescGZIP(), []int{3}
}
func (x *CreatePortforwardingRequest) GetAlias() string {
if x != nil {
return x.Alias
}
return ""
}
func (x *CreatePortforwardingRequest) GetTargetPort() int32 {
if x != nil {
return x.TargetPort
}
return 0
}
func (x *CreatePortforwardingRequest) GetCreatedAt() *timestamppb.Timestamp {
if x != nil {
return x.CreatedAt
}
return nil
}
type CreatePortforwardingResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
Pf *Portforwarding `protobuf:"bytes,2,opt,name=pf,proto3" json:"pf,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CreatePortforwardingResponse) Reset() {
*x = CreatePortforwardingResponse{}
mi := &file_portforwarding_portforwarding_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CreatePortforwardingResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreatePortforwardingResponse) ProtoMessage() {}
func (x *CreatePortforwardingResponse) ProtoReflect() protoreflect.Message {
mi := &file_portforwarding_portforwarding_proto_msgTypes[4]
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 CreatePortforwardingResponse.ProtoReflect.Descriptor instead.
func (*CreatePortforwardingResponse) Descriptor() ([]byte, []int) {
return file_portforwarding_portforwarding_proto_rawDescGZIP(), []int{4}
}
func (x *CreatePortforwardingResponse) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
func (x *CreatePortforwardingResponse) GetPf() *Portforwarding {
if x != nil {
return x.Pf
}
return nil
}
type DeletePortforwardingRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Alias string `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *DeletePortforwardingRequest) Reset() {
*x = DeletePortforwardingRequest{}
mi := &file_portforwarding_portforwarding_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *DeletePortforwardingRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeletePortforwardingRequest) ProtoMessage() {}
func (x *DeletePortforwardingRequest) ProtoReflect() protoreflect.Message {
mi := &file_portforwarding_portforwarding_proto_msgTypes[5]
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 DeletePortforwardingRequest.ProtoReflect.Descriptor instead.
func (*DeletePortforwardingRequest) Descriptor() ([]byte, []int) {
return file_portforwarding_portforwarding_proto_rawDescGZIP(), []int{5}
}
func (x *DeletePortforwardingRequest) GetAlias() string {
if x != nil {
return x.Alias
}
return ""
}
type DeletePortforwardingResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *DeletePortforwardingResponse) Reset() {
*x = DeletePortforwardingResponse{}
mi := &file_portforwarding_portforwarding_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *DeletePortforwardingResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeletePortforwardingResponse) ProtoMessage() {}
func (x *DeletePortforwardingResponse) ProtoReflect() protoreflect.Message {
mi := &file_portforwarding_portforwarding_proto_msgTypes[6]
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 DeletePortforwardingResponse.ProtoReflect.Descriptor instead.
func (*DeletePortforwardingResponse) Descriptor() ([]byte, []int) {
return file_portforwarding_portforwarding_proto_rawDescGZIP(), []int{6}
}
func (x *DeletePortforwardingResponse) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
var File_portforwarding_portforwarding_proto protoreflect.FileDescriptor
const file_portforwarding_portforwarding_proto_rawDesc = "" +
"\n" +
"#portforwarding/portforwarding.proto\x12\x11portforwarding.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"F\n" +
"\x0ePortforwarding\x12\x14\n" +
"\x05alias\x18\x01 \x01(\tR\x05alias\x12\x1e\n" +
"\n" +
"targetPort\x18\x02 \x01(\x05R\n" +
"targetPort\"\x1c\n" +
"\x1aListPortforwardingsRequest\"P\n" +
"\x1bListPortforwardingsResponse\x121\n" +
"\x02pf\x18\x01 \x03(\v2!.portforwarding.v1.PortforwardingR\x02pf\"\x8d\x01\n" +
"\x1bCreatePortforwardingRequest\x12\x14\n" +
"\x05alias\x18\x01 \x01(\tR\x05alias\x12\x1e\n" +
"\n" +
"targetPort\x18\x02 \x01(\x05R\n" +
"targetPort\x128\n" +
"\tcreatedAt\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"k\n" +
"\x1cCreatePortforwardingResponse\x12\x18\n" +
"\asuccess\x18\x01 \x01(\bR\asuccess\x121\n" +
"\x02pf\x18\x02 \x01(\v2!.portforwarding.v1.PortforwardingR\x02pf\"3\n" +
"\x1bDeletePortforwardingRequest\x12\x14\n" +
"\x05alias\x18\x01 \x01(\tR\x05alias\"8\n" +
"\x1cDeletePortforwardingResponse\x12\x18\n" +
"\asuccess\x18\x01 \x01(\bR\asuccess2\xff\x02\n" +
"\x15PortForwardingService\x12t\n" +
"\x13ListPortforwardings\x12-.portforwarding.v1.ListPortforwardingsRequest\x1a..portforwarding.v1.ListPortforwardingsResponse\x12w\n" +
"\x14CreatePortforwarding\x12..portforwarding.v1.CreatePortforwardingRequest\x1a/.portforwarding.v1.CreatePortforwardingResponse\x12w\n" +
"\x14DeletePortforwarding\x12..portforwarding.v1.DeletePortforwardingRequest\x1a/.portforwarding.v1.DeletePortforwardingResponseB\xdc\x01\n" +
"\x15com.portforwarding.v1B\x13PortforwardingProtoP\x01ZIgit.kocoder.xyz/kocoder/portforwarder/gen/portforwarding;portforwardingv1\xa2\x02\x03PXX\xaa\x02\x11Portforwarding.V1\xca\x02\x11Portforwarding\\V1\xe2\x02\x1dPortforwarding\\V1\\GPBMetadata\xea\x02\x12Portforwarding::V1b\x06proto3"
var (
file_portforwarding_portforwarding_proto_rawDescOnce sync.Once
file_portforwarding_portforwarding_proto_rawDescData []byte
)
func file_portforwarding_portforwarding_proto_rawDescGZIP() []byte {
file_portforwarding_portforwarding_proto_rawDescOnce.Do(func() {
file_portforwarding_portforwarding_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_portforwarding_portforwarding_proto_rawDesc), len(file_portforwarding_portforwarding_proto_rawDesc)))
})
return file_portforwarding_portforwarding_proto_rawDescData
}
var file_portforwarding_portforwarding_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_portforwarding_portforwarding_proto_goTypes = []any{
(*Portforwarding)(nil), // 0: portforwarding.v1.Portforwarding
(*ListPortforwardingsRequest)(nil), // 1: portforwarding.v1.ListPortforwardingsRequest
(*ListPortforwardingsResponse)(nil), // 2: portforwarding.v1.ListPortforwardingsResponse
(*CreatePortforwardingRequest)(nil), // 3: portforwarding.v1.CreatePortforwardingRequest
(*CreatePortforwardingResponse)(nil), // 4: portforwarding.v1.CreatePortforwardingResponse
(*DeletePortforwardingRequest)(nil), // 5: portforwarding.v1.DeletePortforwardingRequest
(*DeletePortforwardingResponse)(nil), // 6: portforwarding.v1.DeletePortforwardingResponse
(*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp
}
var file_portforwarding_portforwarding_proto_depIdxs = []int32{
0, // 0: portforwarding.v1.ListPortforwardingsResponse.pf:type_name -> portforwarding.v1.Portforwarding
7, // 1: portforwarding.v1.CreatePortforwardingRequest.createdAt:type_name -> google.protobuf.Timestamp
0, // 2: portforwarding.v1.CreatePortforwardingResponse.pf:type_name -> portforwarding.v1.Portforwarding
1, // 3: portforwarding.v1.PortForwardingService.ListPortforwardings:input_type -> portforwarding.v1.ListPortforwardingsRequest
3, // 4: portforwarding.v1.PortForwardingService.CreatePortforwarding:input_type -> portforwarding.v1.CreatePortforwardingRequest
5, // 5: portforwarding.v1.PortForwardingService.DeletePortforwarding:input_type -> portforwarding.v1.DeletePortforwardingRequest
2, // 6: portforwarding.v1.PortForwardingService.ListPortforwardings:output_type -> portforwarding.v1.ListPortforwardingsResponse
4, // 7: portforwarding.v1.PortForwardingService.CreatePortforwarding:output_type -> portforwarding.v1.CreatePortforwardingResponse
6, // 8: portforwarding.v1.PortForwardingService.DeletePortforwarding:output_type -> portforwarding.v1.DeletePortforwardingResponse
6, // [6:9] is the sub-list for method output_type
3, // [3:6] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_portforwarding_portforwarding_proto_init() }
func file_portforwarding_portforwarding_proto_init() {
if File_portforwarding_portforwarding_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_portforwarding_portforwarding_proto_rawDesc), len(file_portforwarding_portforwarding_proto_rawDesc)),
NumEnums: 0,
NumMessages: 7,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_portforwarding_portforwarding_proto_goTypes,
DependencyIndexes: file_portforwarding_portforwarding_proto_depIdxs,
MessageInfos: file_portforwarding_portforwarding_proto_msgTypes,
}.Build()
File_portforwarding_portforwarding_proto = out.File
file_portforwarding_portforwarding_proto_goTypes = nil
file_portforwarding_portforwarding_proto_depIdxs = nil
}

View File

@ -0,0 +1,180 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: portforwarding/portforwarding.proto
package portforwardingv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
portforwarding "git.kocoder.xyz/kocoder/portforwarder/gen/portforwarding"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// PortForwardingServiceName is the fully-qualified name of the PortForwardingService service.
PortForwardingServiceName = "portforwarding.v1.PortForwardingService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// PortForwardingServiceListPortforwardingsProcedure is the fully-qualified name of the
// PortForwardingService's ListPortforwardings RPC.
PortForwardingServiceListPortforwardingsProcedure = "/portforwarding.v1.PortForwardingService/ListPortforwardings"
// PortForwardingServiceCreatePortforwardingProcedure is the fully-qualified name of the
// PortForwardingService's CreatePortforwarding RPC.
PortForwardingServiceCreatePortforwardingProcedure = "/portforwarding.v1.PortForwardingService/CreatePortforwarding"
// PortForwardingServiceDeletePortforwardingProcedure is the fully-qualified name of the
// PortForwardingService's DeletePortforwarding RPC.
PortForwardingServiceDeletePortforwardingProcedure = "/portforwarding.v1.PortForwardingService/DeletePortforwarding"
)
// PortForwardingServiceClient is a client for the portforwarding.v1.PortForwardingService service.
type PortForwardingServiceClient interface {
ListPortforwardings(context.Context, *portforwarding.ListPortforwardingsRequest) (*portforwarding.ListPortforwardingsResponse, error)
CreatePortforwarding(context.Context, *portforwarding.CreatePortforwardingRequest) (*portforwarding.CreatePortforwardingResponse, error)
DeletePortforwarding(context.Context, *portforwarding.DeletePortforwardingRequest) (*portforwarding.DeletePortforwardingResponse, error)
}
// NewPortForwardingServiceClient constructs a client for the
// portforwarding.v1.PortForwardingService service. By default, it uses the Connect protocol with
// the binary Protobuf Codec, asks for gzipped responses, and sends uncompressed requests. To use
// the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewPortForwardingServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) PortForwardingServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
portForwardingServiceMethods := portforwarding.File_portforwarding_portforwarding_proto.Services().ByName("PortForwardingService").Methods()
return &portForwardingServiceClient{
listPortforwardings: connect.NewClient[portforwarding.ListPortforwardingsRequest, portforwarding.ListPortforwardingsResponse](
httpClient,
baseURL+PortForwardingServiceListPortforwardingsProcedure,
connect.WithSchema(portForwardingServiceMethods.ByName("ListPortforwardings")),
connect.WithClientOptions(opts...),
),
createPortforwarding: connect.NewClient[portforwarding.CreatePortforwardingRequest, portforwarding.CreatePortforwardingResponse](
httpClient,
baseURL+PortForwardingServiceCreatePortforwardingProcedure,
connect.WithSchema(portForwardingServiceMethods.ByName("CreatePortforwarding")),
connect.WithClientOptions(opts...),
),
deletePortforwarding: connect.NewClient[portforwarding.DeletePortforwardingRequest, portforwarding.DeletePortforwardingResponse](
httpClient,
baseURL+PortForwardingServiceDeletePortforwardingProcedure,
connect.WithSchema(portForwardingServiceMethods.ByName("DeletePortforwarding")),
connect.WithClientOptions(opts...),
),
}
}
// portForwardingServiceClient implements PortForwardingServiceClient.
type portForwardingServiceClient struct {
listPortforwardings *connect.Client[portforwarding.ListPortforwardingsRequest, portforwarding.ListPortforwardingsResponse]
createPortforwarding *connect.Client[portforwarding.CreatePortforwardingRequest, portforwarding.CreatePortforwardingResponse]
deletePortforwarding *connect.Client[portforwarding.DeletePortforwardingRequest, portforwarding.DeletePortforwardingResponse]
}
// ListPortforwardings calls portforwarding.v1.PortForwardingService.ListPortforwardings.
func (c *portForwardingServiceClient) ListPortforwardings(ctx context.Context, req *portforwarding.ListPortforwardingsRequest) (*portforwarding.ListPortforwardingsResponse, error) {
response, err := c.listPortforwardings.CallUnary(ctx, connect.NewRequest(req))
if response != nil {
return response.Msg, err
}
return nil, err
}
// CreatePortforwarding calls portforwarding.v1.PortForwardingService.CreatePortforwarding.
func (c *portForwardingServiceClient) CreatePortforwarding(ctx context.Context, req *portforwarding.CreatePortforwardingRequest) (*portforwarding.CreatePortforwardingResponse, error) {
response, err := c.createPortforwarding.CallUnary(ctx, connect.NewRequest(req))
if response != nil {
return response.Msg, err
}
return nil, err
}
// DeletePortforwarding calls portforwarding.v1.PortForwardingService.DeletePortforwarding.
func (c *portForwardingServiceClient) DeletePortforwarding(ctx context.Context, req *portforwarding.DeletePortforwardingRequest) (*portforwarding.DeletePortforwardingResponse, error) {
response, err := c.deletePortforwarding.CallUnary(ctx, connect.NewRequest(req))
if response != nil {
return response.Msg, err
}
return nil, err
}
// PortForwardingServiceHandler is an implementation of the portforwarding.v1.PortForwardingService
// service.
type PortForwardingServiceHandler interface {
ListPortforwardings(context.Context, *portforwarding.ListPortforwardingsRequest) (*portforwarding.ListPortforwardingsResponse, error)
CreatePortforwarding(context.Context, *portforwarding.CreatePortforwardingRequest) (*portforwarding.CreatePortforwardingResponse, error)
DeletePortforwarding(context.Context, *portforwarding.DeletePortforwardingRequest) (*portforwarding.DeletePortforwardingResponse, error)
}
// NewPortForwardingServiceHandler builds an HTTP handler from the service implementation. It
// returns the path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewPortForwardingServiceHandler(svc PortForwardingServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
portForwardingServiceMethods := portforwarding.File_portforwarding_portforwarding_proto.Services().ByName("PortForwardingService").Methods()
portForwardingServiceListPortforwardingsHandler := connect.NewUnaryHandlerSimple(
PortForwardingServiceListPortforwardingsProcedure,
svc.ListPortforwardings,
connect.WithSchema(portForwardingServiceMethods.ByName("ListPortforwardings")),
connect.WithHandlerOptions(opts...),
)
portForwardingServiceCreatePortforwardingHandler := connect.NewUnaryHandlerSimple(
PortForwardingServiceCreatePortforwardingProcedure,
svc.CreatePortforwarding,
connect.WithSchema(portForwardingServiceMethods.ByName("CreatePortforwarding")),
connect.WithHandlerOptions(opts...),
)
portForwardingServiceDeletePortforwardingHandler := connect.NewUnaryHandlerSimple(
PortForwardingServiceDeletePortforwardingProcedure,
svc.DeletePortforwarding,
connect.WithSchema(portForwardingServiceMethods.ByName("DeletePortforwarding")),
connect.WithHandlerOptions(opts...),
)
return "/portforwarding.v1.PortForwardingService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case PortForwardingServiceListPortforwardingsProcedure:
portForwardingServiceListPortforwardingsHandler.ServeHTTP(w, r)
case PortForwardingServiceCreatePortforwardingProcedure:
portForwardingServiceCreatePortforwardingHandler.ServeHTTP(w, r)
case PortForwardingServiceDeletePortforwardingProcedure:
portForwardingServiceDeletePortforwardingHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedPortForwardingServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedPortForwardingServiceHandler struct{}
func (UnimplementedPortForwardingServiceHandler) ListPortforwardings(context.Context, *portforwarding.ListPortforwardingsRequest) (*portforwarding.ListPortforwardingsResponse, error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("portforwarding.v1.PortForwardingService.ListPortforwardings is not implemented"))
}
func (UnimplementedPortForwardingServiceHandler) CreatePortforwarding(context.Context, *portforwarding.CreatePortforwardingRequest) (*portforwarding.CreatePortforwardingResponse, error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("portforwarding.v1.PortForwardingService.CreatePortforwarding is not implemented"))
}
func (UnimplementedPortForwardingServiceHandler) DeletePortforwarding(context.Context, *portforwarding.DeletePortforwardingRequest) (*portforwarding.DeletePortforwardingResponse, error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("portforwarding.v1.PortForwardingService.DeletePortforwarding is not implemented"))
}

83
go.mod Normal file
View File

@ -0,0 +1,83 @@
module git.kocoder.xyz/kocoder/portforwarder
go 1.25.5
require (
k8s.io/apimachinery v0.35.0
k8s.io/client-go v0.35.0
)
require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.10-20251209175733-2a1774d88802.1 // indirect
buf.build/go/protovalidate v1.1.0 // indirect
cel.dev/expr v0.24.0 // indirect
connectrpc.com/connect v1.19.1 // indirect
connectrpc.com/validate v0.6.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-openapi/jsonpointer v0.22.4 // indirect
github.com/go-openapi/jsonreference v0.21.4 // indirect
github.com/go-openapi/swag v0.25.4 // indirect
github.com/go-openapi/swag/cmdutils v0.25.4 // indirect
github.com/go-openapi/swag/conv v0.25.4 // indirect
github.com/go-openapi/swag/fileutils v0.25.4 // indirect
github.com/go-openapi/swag/jsonname v0.25.4 // indirect
github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
github.com/go-openapi/swag/loading v0.25.4 // indirect
github.com/go-openapi/swag/mangling v0.25.4 // indirect
github.com/go-openapi/swag/netutils v0.25.4 // indirect
github.com/go-openapi/swag/stringutils v0.25.4 // indirect
github.com/go-openapi/swag/typeutils v0.25.4 // indirect
github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
github.com/gobuffalo/flect v1.0.3 // indirect
github.com/google/cel-go v0.26.1 // indirect
github.com/google/gnostic-models v0.7.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/stoewer/go-strcase v1.3.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect
golang.org/x/mod v0.31.0 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/oauth2 v0.34.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/term v0.38.0 // indirect
golang.org/x/text v0.32.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.40.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250922171735-9219d122eba9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.35.0 // indirect
k8s.io/apiextensions-apiserver v0.35.0 // indirect
k8s.io/code-generator v0.35.0 // indirect
k8s.io/gengo/v2 v2.0.0-20251215205346-5ee0d033ba5b // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e // indirect
k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2 // indirect
sigs.k8s.io/controller-tools v0.20.0 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)

207
go.sum Normal file
View File

@ -0,0 +1,207 @@
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.9-20250912141014-52f32327d4b0.1 h1:DQLS/rRxLHuugVzjJU5AvOwD57pdFl9he/0O7e5P294=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.9-20250912141014-52f32327d4b0.1/go.mod h1:aY3zbkNan5F+cGm9lITDP6oxJIwu0dn9KjJuJjWaHkg=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.10-20251209175733-2a1774d88802.1 h1:ZnX3qpF/pDiYrf+Q3p+/zCzZ5ELSpszy5hdVarDMSV4=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.10-20251209175733-2a1774d88802.1/go.mod h1:fUl8CEN/6ZAMk6bP8ahBJPUJw7rbp+j4x+wCcYi2IG4=
buf.build/go/protovalidate v1.0.0 h1:IAG1etULddAy93fiBsFVhpj7es5zL53AfB/79CVGtyY=
buf.build/go/protovalidate v1.0.0/go.mod h1:KQmEUrcQuC99hAw+juzOEAmILScQiKBP1Oc36vvCLW8=
buf.build/go/protovalidate v1.1.0 h1:pQqEQRpOo4SqS60qkvmhLTTQU9JwzEvdyiqAtXa5SeY=
buf.build/go/protovalidate v1.1.0/go.mod h1:bGZcPiAQDC3ErCHK3t74jSoJDFOs2JH3d7LWuTEIdss=
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14=
connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
connectrpc.com/validate v0.6.0 h1:DcrgDKt2ZScrUs/d/mh9itD2yeEa0UbBBa+i0mwzx+4=
connectrpc.com/validate v0.6.0/go.mod h1:ihrpI+8gVbLH1fvVWJL1I3j0CfWnF8P/90LsmluRiZs=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
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/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
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-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU=
github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ=
github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4=
github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y=
github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk=
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48=
github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg=
github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0=
github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg=
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4=
github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
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/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c=
github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
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/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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
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/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM=
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/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
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-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=
golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
google.golang.org/genproto/googleapis/api v0.0.0-20250922171735-9219d122eba9 h1:jm6v6kMRpTYKxBRrDkYAitNJegUeO1Mf3Kt80obv0gg=
google.golang.org/genproto/googleapis/api v0.0.0-20250922171735-9219d122eba9/go.mod h1:LmwNphe5Afor5V3R5BppOULHOnt2mCIf+NxMd4XiygE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 h1:V1jCN2HBa8sySkR5vLcCSqJSTMv093Rw9EJefhQGP7M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
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/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
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/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY=
k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA=
k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4=
k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU=
k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8=
k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE=
k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o=
k8s.io/code-generator v0.35.0 h1:TvrtfKYZTm9oDF2z+veFKSCcgZE3Igv0svY+ehCmjHQ=
k8s.io/code-generator v0.35.0/go.mod h1:iS1gvVf3c/T71N5DOGYO+Gt3PdJ6B9LYSvIyQ4FHzgc=
k8s.io/gengo/v2 v2.0.0-20251215205346-5ee0d033ba5b h1:0YkdvW3rX2vaBWsqCGZAekxPRwaI5NuYNprOsMNVLns=
k8s.io/gengo/v2 v2.0.0-20251215205346-5ee0d033ba5b/go.mod h1:yvyl3l9E+UxlqOMUULdKTAYB0rEhsmjr7+2Vb/1pCSo=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e h1:iW9ChlU0cU16w8MpVYjXk12dqQ4BPFBEgif+ap7/hqQ=
k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
k8s.io/utils v0.0.0-20251220205832-9d40a56c1308 h1:rk+D2uTO79bbNsICltOdVoA6mcJb0NpvBcts+ACymBQ=
k8s.io/utils v0.0.0-20251220205832-9d40a56c1308/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2 h1:OfgiEo21hGiwx1oJUU5MpEaeOEg6coWndBkZF/lkFuE=
k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
sigs.k8s.io/controller-tools v0.20.0 h1:VWZF71pwSQ2lZZCt7hFGJsOfDc5dVG28/IysjjMWXL8=
sigs.k8s.io/controller-tools v0.20.0/go.mod h1:b4qPmjGU3iZwqn34alUU5tILhNa9+VXK+J3QV0fT/uU=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E=
sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

34
k3s.yaml Normal file
View File

@ -0,0 +1,34 @@
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: vt-fe
namespace: vt
spec:
entryPoints:
- web
- websecure
routes:
- kind: Rule
match: Host(`vt.kocoder.xyz`)
observability:
accessLogs: true
metrics: true
tracing: true
priority: 10
services:
- kind: Service
name: vt-fe
namespace: vt
passHostHeader: true
port: 3000
responseForwarding:
flushInterval: 1ms
scheme: http
sticky:
cookie:
httpOnly: true
name: cookie
secure: true
strategy: wrr
weight: 10

107
main.go Normal file
View File

@ -0,0 +1,107 @@
package main
import (
"flag"
"log"
"net/http"
"path/filepath"
"connectrpc.com/connect"
"connectrpc.com/validate"
"git.kocoder.xyz/kocoder/portforwarder/api/types/v1alpha1"
clientV1alpha1 "git.kocoder.xyz/kocoder/portforwarder/clientset/v1alpha1"
"git.kocoder.xyz/kocoder/portforwarder/gen/portforwarding/portforwardingv1connect"
"git.kocoder.xyz/kocoder/portforwarder/portforwarding"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
)
func main() {
var kubeconfig *string
if home := homedir.HomeDir(); home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
flag.Parse()
// use the current context in kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
panic(err.Error())
}
err = v1alpha1.AddToScheme(scheme.Scheme)
if err != nil {
panic(err.Error())
}
client, err := clientV1alpha1.NewForConfig(config)
if err != nil {
panic(err)
}
const namespace = "default"
pf := portforwarding.NewPortforwardingService(client.Ingressroute(namespace), namespace, "external-db-svc", ".kocoder.xyz", "k3s-kocoder-xyz-tls")
mux := http.NewServeMux()
path, handler := portforwardingv1connect.NewPortForwardingServiceHandler(
pf,
connect.WithInterceptors(validate.NewInterceptor()),
)
mux.Handle(path, handler)
p := new(http.Protocols)
p.SetHTTP1(true)
// Use h2c so we can serve HTTP/2 without TLS.
p.SetUnencryptedHTTP2(true)
s := http.Server{
Addr: "localhost:8080",
Handler: mux,
Protocols: p,
}
err = s.ListenAndServe()
log.Fatal(err)
// ir := &v1alpha1.IngressRoute{
// ObjectMeta: v1.ObjectMeta{
// Name: "beary-times",
// },
// Spec: v1alpha1.IngressRouteSpec{
// EntryPoints: []string{"websecure", "web"},
// Routes: []v1alpha1.Route{
// {
// Kind: "Rule",
// Match: "Host(`beary-times.kocoder.xyz`)",
// Services: []v1alpha1.Service{
// {
// Kind: "Service",
// Name: "external-db-svc",
// Namespace: "default",
// PassHostHeader: false,
// Port: 63419,
// ResponseForwarding: v1alpha1.ResponseForwarding{FlushInterval: "10ms"},
// Scheme: "https",
// Sticky: v1alpha1.Sticky{Cookie: v1alpha1.Cookie{HttpOnly: false, Name: "", Secure: false}},
// Strategy: "wrr",
// Weight: 0,
// },
// },
// Middlewares: []v1alpha1.Middleware{},
// Observability: v1alpha1.Observability{AccessLogs: true, Metrics: true, Tracing: true},
// Priority: 10,
// },
// },
// },
// }
// ir, err = client.Ingressroute("default").Create(context.TODO(), ir)
// if err != nil {
// panic(err)
// }
// client.Ingressroute("default").Delete(context.TODO(), "hannes", v1.DeleteOptions{})
}

View File

@ -0,0 +1,125 @@
package portforwarding
import (
"context"
"time"
"connectrpc.com/connect"
"git.kocoder.xyz/kocoder/portforwarder/api/types/v1alpha1"
csv1 "git.kocoder.xyz/kocoder/portforwarder/clientset/v1alpha1"
portforwardingv1 "git.kocoder.xyz/kocoder/portforwarder/gen/portforwarding"
"git.kocoder.xyz/kocoder/portforwarder/gen/portforwarding/portforwardingv1connect"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type PortforwardingService struct {
ir csv1.IngressrouteInterface
ns string
svc string
basedomain string
tlssecret string
}
func NewPortforwardingService(ingressroutehandler csv1.IngressrouteInterface, namespace string, servicename string, basedomain string, secretname string) portforwardingv1connect.PortForwardingServiceHandler {
return &PortforwardingService{ir: ingressroutehandler, ns: namespace, svc: servicename, basedomain: basedomain, tlssecret: secretname}
}
// CreatePortforwarding implements portforwardingv1connect.PortForwardingServiceHandler.
func (p *PortforwardingService) CreatePortforwarding(ctx context.Context, req *portforwardingv1.CreatePortforwardingRequest) (*portforwardingv1.CreatePortforwardingResponse, error) {
ir := &v1alpha1.IngressRoute{
ObjectMeta: v1.ObjectMeta{
Name: req.Alias,
Labels: map[string]string{"alias": req.Alias, "mangedBy": "heek-portforwarder"},
},
Spec: v1alpha1.IngressRouteSpec{
EntryPoints: []string{"websecure", "web"},
Routes: []v1alpha1.Route{
{
Kind: "Rule",
Match: "Host(`" + req.Alias + p.basedomain + "`)",
Services: []v1alpha1.Service{
{
Kind: "Service",
Name: p.svc,
Namespace: p.ns,
PassHostHeader: true,
Port: int(req.TargetPort),
ResponseForwarding: v1alpha1.ResponseForwarding{FlushInterval: "10ms"},
Scheme: "http",
Sticky: v1alpha1.Sticky{Cookie: v1alpha1.Cookie{HttpOnly: false, Name: "", Secure: false}},
Strategy: "wrr",
Weight: 10,
},
},
Middlewares: []v1alpha1.Middleware{},
Observability: v1alpha1.Observability{AccessLogs: false, Metrics: false, Tracing: false},
Priority: 10,
},
},
},
}
downstreamContext, cancel := context.WithDeadline(ctx, time.Now().Add(time.Second*2))
ir, err := p.ir.Create(downstreamContext, ir)
cancel()
if err != nil {
return nil, connect.NewError(connect.CodeInternal, err)
}
return &portforwardingv1.CreatePortforwardingResponse{
Success: true,
Pf: &portforwardingv1.Portforwarding{
Alias: ir.Name,
TargetPort: int32(ir.Spec.Routes[0].Services[0].Port),
},
}, nil
}
// DeletePortforwarding implements portforwardingv1connect.PortForwardingServiceHandler.
func (p *PortforwardingService) DeletePortforwarding(ctx context.Context, req *portforwardingv1.DeletePortforwardingRequest) (*portforwardingv1.DeletePortforwardingResponse, error) {
downstreamContext, cancel := context.WithDeadline(ctx, time.Now().Add(time.Second*2))
opts := *v1.NewDeleteOptions(1)
err := p.ir.Delete(downstreamContext, req.Alias, opts)
cancel()
if err != nil {
return nil, connect.NewError(connect.CodeInternal, err)
}
return &portforwardingv1.DeletePortforwardingResponse{
Success: true,
}, nil
}
// ListPortforwardings implements portforwardingv1connect.PortForwardingServiceHandler.
func (p *PortforwardingService) ListPortforwardings(ctx context.Context, req *portforwardingv1.ListPortforwardingsRequest) (*portforwardingv1.ListPortforwardingsResponse, error) {
downstreamContext, cancel := context.WithDeadline(ctx, time.Now().Add(time.Second*2))
opts := v1.ListOptions{}
res, err := p.ir.List(downstreamContext, opts)
cancel()
if err != nil {
return nil, connect.NewError(connect.CodeInternal, err)
}
items := res.Items
pf := make([]*portforwardingv1.Portforwarding, len(items))
for i, v := range items {
pf[i] = &portforwardingv1.Portforwarding{
Alias: v.Name,
TargetPort: int32(v.Spec.Routes[0].Services[0].Port),
}
}
return &portforwardingv1.ListPortforwardingsResponse{
Pf: pf,
}, nil
}

1
shared Submodule

Submodule shared added at 78326db358

1
tmp/build-errors.log Normal file
View File

@ -0,0 +1 @@
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1

BIN
tmp/main Executable file

Binary file not shown.