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 }