commit c9d03221779d2dc1f1b1f1fa4b56ee2ca8880ac6 Author: KoCoder Date: Mon Dec 29 16:42:49 2025 +0100 Server-Side-Implementation diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9c0e1b4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "shared"] + path = shared + url = https://git.kocoder.xyz/heek/shared.git diff --git a/api/types/v1alpha1/ingressroute.go b/api/types/v1alpha1/ingressroute.go new file mode 100644 index 0000000..a3a64df --- /dev/null +++ b/api/types/v1alpha1/ingressroute.go @@ -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"` +} diff --git a/api/types/v1alpha1/register.go b/api/types/v1alpha1/register.go new file mode 100644 index 0000000..cd6adb4 --- /dev/null +++ b/api/types/v1alpha1/register.go @@ -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 +} diff --git a/api/types/v1alpha1/zz_generated.deepcopy.go b/api/types/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000..2e574e7 --- /dev/null +++ b/api/types/v1alpha1/zz_generated.deepcopy.go @@ -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 +} diff --git a/auth/psk-interceptor.go b/auth/psk-interceptor.go new file mode 100644 index 0000000..e5f0f8a --- /dev/null +++ b/auth/psk-interceptor.go @@ -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) + } + } + } +} diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000..d11088c --- /dev/null +++ b/buf.gen.yaml @@ -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 diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000..9ff3911 --- /dev/null +++ b/buf.yaml @@ -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 diff --git a/clientset/v1alpha1/api.go b/clientset/v1alpha1/api.go new file mode 100644 index 0000000..eccf6e0 --- /dev/null +++ b/clientset/v1alpha1/api.go @@ -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", + } +} diff --git a/clientset/v1alpha1/ingressroute.go b/clientset/v1alpha1/ingressroute.go new file mode 100644 index 0000000..8d08a5d --- /dev/null +++ b/clientset/v1alpha1/ingressroute.go @@ -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() +} diff --git a/externalService.yaml b/externalService.yaml new file mode 100644 index 0000000..4c5ac3c --- /dev/null +++ b/externalService.yaml @@ -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" diff --git a/gen/portforwarding/portforwarding.pb.go b/gen/portforwarding/portforwarding.pb.go new file mode 100644 index 0000000..8f15013 --- /dev/null +++ b/gen/portforwarding/portforwarding.pb.go @@ -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 +} diff --git a/gen/portforwarding/portforwardingv1connect/portforwarding.connect.go b/gen/portforwarding/portforwardingv1connect/portforwarding.connect.go new file mode 100644 index 0000000..fa73622 --- /dev/null +++ b/gen/portforwarding/portforwardingv1connect/portforwarding.connect.go @@ -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")) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d57ef0d --- /dev/null +++ b/go.mod @@ -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 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b5836e8 --- /dev/null +++ b/go.sum @@ -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= diff --git a/k3s.yaml b/k3s.yaml new file mode 100644 index 0000000..5899bf7 --- /dev/null +++ b/k3s.yaml @@ -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 diff --git a/main.go b/main.go new file mode 100644 index 0000000..b6bc62d --- /dev/null +++ b/main.go @@ -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{}) +} diff --git a/portforwarding/portforwarding.go b/portforwarding/portforwarding.go new file mode 100644 index 0000000..467a3ff --- /dev/null +++ b/portforwarding/portforwarding.go @@ -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 +} diff --git a/shared b/shared new file mode 160000 index 0000000..78326db --- /dev/null +++ b/shared @@ -0,0 +1 @@ +Subproject commit 78326db35821d268779606bb7227e61b00d2fbdf diff --git a/tmp/build-errors.log b/tmp/build-errors.log new file mode 100644 index 0000000..a5c630d --- /dev/null +++ b/tmp/build-errors.log @@ -0,0 +1 @@ +exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file diff --git a/tmp/main b/tmp/main new file mode 100755 index 0000000..a951ca0 Binary files /dev/null and b/tmp/main differ