Skip to content

Commit f2f66e0

Browse files
authored
feat: Initial Implemention of the KCP-listener component (#28)
1 parent 519e84f commit f2f66e0

31 files changed

+58359
-25
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@ go.work
3030
main
3131

3232
# kcp
33-
.kcp
33+
.kcp

.mockery.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
with-expecter: true
2+
packages:
3+
github.com/openmfp/crd-gql-gateway/listener/apischema:
4+
config:
5+
dir: listener/apischema/mocks
6+
outpkg: mocks
7+
interfaces:
8+
Resolver:
9+
10+
github.com/openmfp/crd-gql-gateway/listener/discoveryclient:
11+
config:
12+
dir: listener/discoveryclient/mocks
13+
outpkg: mocks
14+
interfaces:
15+
Factory:
16+
17+
github.com/openmfp/crd-gql-gateway/listener/workspacefile:
18+
config:
19+
dir: listener/workspacefile/mocks
20+
outpkg: mocks
21+
interfaces:
22+
IOHandler:
23+
24+
sigs.k8s.io/controller-runtime/pkg/client:
25+
config:
26+
dir: listener/controller/mocks
27+
outpkg: mocks
28+
interfaces:
29+
Client:

.testcoverage.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ exclude:
55
- gateway
66
- tests
77
- internal/config/config.go
8+
- listener/*

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ COPY --from=builder /app/main .
1313
USER 1001:1001
1414

1515
ENTRYPOINT ["./main"]
16-
CMD ["gql-gateway"]
16+
CMD ["gql-gateway"]

Taskfile.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ tasks:
2626
- go build ./...
2727
docker:
2828
cmds:
29-
- docker build -t ghcr.io/openmfp/gql-gateway .
29+
- docker build -t ghcr.io/openmfp/crd-gql-gateway .
3030
## Testing
3131
fmt:
3232
cmds:
@@ -63,6 +63,10 @@ tasks:
6363
cmds:
6464
- go run main.go start
6565

66-
gql-gateway:
66+
gateway:
6767
cmds:
6868
- go run main.go gql-gateway
69+
70+
listen:
71+
cmds:
72+
- go run main.go listen --openapi-out-dir=./definitions

cmd/listen.go

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"crypto/tls"
6+
"errors"
7+
"net/url"
8+
"os"
9+
10+
kcpapis "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1"
11+
kcpcore "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1"
12+
kcptenancy "github.com/kcp-dev/kcp/sdk/apis/tenancy/v1alpha1"
13+
"github.com/openmfp/crd-gql-gateway/listener/flags"
14+
"github.com/rs/zerolog/log"
15+
"github.com/spf13/cobra"
16+
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
17+
"k8s.io/apimachinery/pkg/runtime"
18+
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
19+
kcpctrl "sigs.k8s.io/controller-runtime/pkg/kcp"
20+
21+
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
22+
"k8s.io/client-go/rest"
23+
ctrl "sigs.k8s.io/controller-runtime"
24+
"sigs.k8s.io/controller-runtime/pkg/client"
25+
"sigs.k8s.io/controller-runtime/pkg/healthz"
26+
"sigs.k8s.io/controller-runtime/pkg/log/zap"
27+
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
28+
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
29+
"sigs.k8s.io/controller-runtime/pkg/webhook"
30+
31+
"github.com/openmfp/crd-gql-gateway/listener/apischema"
32+
"github.com/openmfp/crd-gql-gateway/listener/controller"
33+
"github.com/openmfp/crd-gql-gateway/listener/discoveryclient"
34+
"github.com/openmfp/crd-gql-gateway/listener/workspacefile"
35+
// +kubebuilder:scaffold:imports
36+
)
37+
38+
func init() {
39+
rootCmd.AddCommand(listenCmd)
40+
}
41+
42+
var (
43+
scheme = runtime.NewScheme()
44+
setupLog = ctrl.Log.WithName("setup")
45+
webhookServer webhook.Server
46+
metricsServerOptions metricsserver.Options
47+
opFlags *flags.Flags
48+
)
49+
50+
var listenCmd = &cobra.Command{
51+
Use: "listen",
52+
Example: "KUBECONFIG=.kcp/admin.kubeconfig go run . listen",
53+
PreRun: func(cmd *cobra.Command, args []string) {
54+
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
55+
56+
utilruntime.Must(kcpapis.AddToScheme(scheme))
57+
utilruntime.Must(kcpcore.AddToScheme(scheme))
58+
utilruntime.Must(kcptenancy.AddToScheme(scheme))
59+
utilruntime.Must(apiextensions.AddToScheme(scheme))
60+
// +kubebuilder:scaffold:scheme
61+
62+
var err error
63+
opFlags, err = flags.NewFromEnv()
64+
if err != nil {
65+
log.Fatal().Err(err).Msg("Error getting app restCfg, exiting")
66+
}
67+
opts := zap.Options{
68+
Development: true,
69+
}
70+
71+
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
72+
73+
disableHTTP2 := func(c *tls.Config) {
74+
setupLog.Info("disabling http/2")
75+
c.NextProtos = []string{"http/1.1"}
76+
}
77+
78+
var tlsOpts []func(*tls.Config)
79+
if !opFlags.EnableHTTP2 {
80+
tlsOpts = []func(c *tls.Config){disableHTTP2}
81+
}
82+
83+
webhookServer = webhook.NewServer(webhook.Options{
84+
TLSOpts: tlsOpts,
85+
})
86+
87+
metricsServerOptions = metricsserver.Options{
88+
BindAddress: opFlags.MetricsAddr,
89+
SecureServing: opFlags.SecureMetrics,
90+
TLSOpts: tlsOpts,
91+
}
92+
93+
if opFlags.SecureMetrics {
94+
metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
95+
}
96+
},
97+
Run: func(cmd *cobra.Command, args []string) {
98+
cfg := ctrl.GetConfigOrDie()
99+
cfgURL, err := url.Parse(cfg.Host)
100+
if err != nil {
101+
setupLog.Error(err, "failed to parse config Host")
102+
os.Exit(1)
103+
}
104+
clt, err := client.New(cfg, client.Options{
105+
Scheme: scheme,
106+
})
107+
if err != nil {
108+
setupLog.Error(err, "failed to create client from config")
109+
os.Exit(1)
110+
}
111+
tenancyAPIExport := &kcpapis.APIExport{}
112+
err = clt.Get(context.TODO(), client.ObjectKey{Name: kcptenancy.SchemeGroupVersion.Group}, tenancyAPIExport)
113+
if err != nil {
114+
setupLog.Error(err, "failed to get tenancy APIExport")
115+
os.Exit(1)
116+
}
117+
virtualWorkspaces := tenancyAPIExport.Status.VirtualWorkspaces // nolint: staticcheck
118+
if len(virtualWorkspaces) == 0 {
119+
err := errors.New("empty virtual workspace list")
120+
setupLog.Error(err, "failed to get at least one virtual workspace")
121+
os.Exit(1)
122+
}
123+
vwCFGURL, err := url.Parse(virtualWorkspaces[0].URL)
124+
if err != nil {
125+
setupLog.Error(err, "failed to parse virtual workspace config URL")
126+
os.Exit(1)
127+
}
128+
cfgURL.Path = vwCFGURL.Path
129+
virtualWorkspaceCfg := rest.CopyConfig(cfg)
130+
virtualWorkspaceCfg.Host = cfgURL.String()
131+
132+
mgr, err := kcpctrl.NewClusterAwareManager(virtualWorkspaceCfg, ctrl.Options{
133+
Scheme: scheme,
134+
Metrics: metricsServerOptions,
135+
WebhookServer: webhookServer,
136+
HealthProbeBindAddress: opFlags.ProbeAddr,
137+
LeaderElection: opFlags.EnableLeaderElection,
138+
LeaderElectionID: "72231e1f.openmfp.io",
139+
})
140+
if err != nil {
141+
setupLog.Error(err, "unable to start manager")
142+
os.Exit(1)
143+
}
144+
145+
ioHandler, err := workspacefile.NewIOHandler(opFlags.OpenAPIdefinitionsPath)
146+
if err != nil {
147+
setupLog.Error(err, "failed to create IO Handler")
148+
os.Exit(1)
149+
}
150+
151+
df, err := discoveryclient.NewFactory(virtualWorkspaceCfg)
152+
if err != nil {
153+
setupLog.Error(err, "failed to create Discovery client factory")
154+
os.Exit(1)
155+
}
156+
157+
reconciler := controller.NewAPIBindingReconciler(
158+
ioHandler, df, apischema.NewResolver(),
159+
)
160+
161+
err = reconciler.SetupWithManager(mgr)
162+
if err != nil {
163+
setupLog.Error(err, "unable to create controller", "controller", "Workspace")
164+
os.Exit(1)
165+
}
166+
// +kubebuilder:scaffold:builder
167+
168+
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
169+
setupLog.Error(err, "unable to set up health check")
170+
os.Exit(1)
171+
}
172+
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
173+
setupLog.Error(err, "unable to set up ready check")
174+
os.Exit(1)
175+
}
176+
177+
setupLog.Info("starting manager")
178+
signalHandler := ctrl.SetupSignalHandler()
179+
err = mgr.Start(signalHandler)
180+
if err != nil {
181+
setupLog.Error(err, "problem running manager")
182+
os.Exit(1)
183+
}
184+
},
185+
}

go.mod

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ module github.com/openmfp/crd-gql-gateway
22

33
go 1.23.2
44

5+
replace sigs.k8s.io/controller-runtime => github.com/kcp-dev/controller-runtime v0.19.0-kcp.1
6+
57
require (
68
github.com/fsnotify/fsnotify v1.8.0
79
github.com/go-openapi/spec v0.21.0
810
github.com/golang-jwt/jwt/v5 v5.2.1
911
github.com/google/go-cmp v0.6.0
1012
github.com/graphql-go/graphql v0.8.1
1113
github.com/graphql-go/handler v0.2.4
14+
github.com/kcp-dev/kcp/sdk v0.26.1
1215
github.com/mitchellh/mapstructure v1.5.0
1316
github.com/openmfp/golang-commons v0.124.0
1417
github.com/rs/zerolog v1.33.0
@@ -22,15 +25,22 @@ require (
2225
k8s.io/apiextensions-apiserver v0.32.1
2326
k8s.io/apimachinery v0.32.1
2427
k8s.io/client-go v0.32.1
28+
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f
2529
sigs.k8s.io/controller-runtime v0.20.0
2630
)
2731

2832
require (
33+
cel.dev/expr v0.18.0 // indirect
34+
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
35+
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
2936
github.com/beorn7/perks v1.0.1 // indirect
37+
github.com/blang/semver/v4 v4.0.0 // indirect
38+
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
3039
github.com/cespare/xxhash/v2 v2.3.0 // indirect
3140
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
3241
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
3342
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
43+
github.com/felixge/httpsnoop v1.0.4 // indirect
3444
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
3545
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
3646
github.com/go-logr/logr v1.4.2 // indirect
@@ -42,48 +52,65 @@ require (
4252
github.com/go-openapi/swag v0.23.0 // indirect
4353
github.com/gogo/protobuf v1.3.2 // indirect
4454
github.com/golang/protobuf v1.5.4 // indirect
45-
github.com/google/btree v1.1.3 // indirect
55+
github.com/google/cel-go v0.22.0 // indirect
4656
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
4757
github.com/google/gofuzz v1.2.0 // indirect
4858
github.com/google/uuid v1.6.0 // indirect
59+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
60+
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
4961
github.com/inconshreveable/mousetrap v1.1.0 // indirect
5062
github.com/josharian/intern v1.0.0 // indirect
5163
github.com/json-iterator/go v1.1.12 // indirect
64+
github.com/kcp-dev/apimachinery/v2 v2.0.1-0.20240817110845-a9eb9752bfeb // indirect
65+
github.com/kcp-dev/logicalcluster/v3 v3.0.5 // indirect
5266
github.com/klauspost/compress v1.17.9 // indirect
5367
github.com/mailru/easyjson v0.7.7 // indirect
5468
github.com/mattn/go-colorable v0.1.13 // indirect
5569
github.com/mattn/go-isatty v0.0.20 // indirect
5670
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
5771
github.com/modern-go/reflect2 v1.0.2 // indirect
5872
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
73+
github.com/onsi/gomega v1.35.1 // indirect
5974
github.com/pkg/errors v0.9.1 // indirect
6075
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
6176
github.com/prometheus/client_golang v1.20.5 // indirect
6277
github.com/prometheus/client_model v0.6.1 // indirect
6378
github.com/prometheus/common v0.55.0 // indirect
6479
github.com/prometheus/procfs v0.15.1 // indirect
6580
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace // indirect
81+
github.com/stoewer/go-strcase v1.3.0 // indirect
82+
github.com/stretchr/objx v0.5.2 // indirect
6683
github.com/x448/float16 v0.8.4 // indirect
6784
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
85+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
86+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect
87+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 // indirect
6888
go.opentelemetry.io/otel/metric v1.34.0 // indirect
89+
go.opentelemetry.io/otel/sdk v1.31.0 // indirect
90+
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
6991
go.uber.org/multierr v1.11.0 // indirect
7092
go.uber.org/zap v1.27.0 // indirect
71-
golang.org/x/crypto v0.31.0 // indirect
72-
golang.org/x/net v0.33.0 // indirect
93+
golang.org/x/crypto v0.32.0 // indirect
94+
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
95+
golang.org/x/net v0.34.0 // indirect
7396
golang.org/x/oauth2 v0.25.0 // indirect
7497
golang.org/x/sync v0.10.0 // indirect
75-
golang.org/x/sys v0.28.0 // indirect
76-
golang.org/x/term v0.27.0 // indirect
98+
golang.org/x/sys v0.29.0 // indirect
99+
golang.org/x/term v0.28.0 // indirect
77100
golang.org/x/time v0.7.0 // indirect
78-
golang.org/x/tools v0.27.0 // indirect
79101
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
102+
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect
103+
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
104+
google.golang.org/grpc v1.69.4 // indirect
80105
google.golang.org/protobuf v1.36.1 // indirect
81106
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
82107
gopkg.in/inf.v0 v0.9.1 // indirect
83108
gopkg.in/yaml.v3 v3.0.1 // indirect
109+
k8s.io/apiserver v0.32.1 // indirect
110+
k8s.io/component-base v0.32.1 // indirect
84111
k8s.io/klog/v2 v2.130.1 // indirect
85-
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
86112
k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078 // indirect
113+
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect
87114
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
88115
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
89116
sigs.k8s.io/yaml v1.4.0 // indirect

0 commit comments

Comments
 (0)