diff --git a/.gitignore b/.gitignore index f3ddfc4d4c..3b06c5bb76 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,10 @@ # Dependency directories (remove the comment below to include it) # vendor/ +# Vim +*.swp +*.swo + # GoLand IDE .idea diff --git a/cmd/gateway/main.go b/cmd/gateway/main.go index 6d862e110a..39b182bd07 100644 --- a/cmd/gateway/main.go +++ b/cmd/gateway/main.go @@ -1,17 +1,13 @@ package main import ( - "flag" - "fmt" "os" - "github.com/nginxinc/nginx-gateway-kubernetes/internal/implementation" - "github.com/nginxinc/nginx-gateway-kubernetes/pkg/sdk" - "k8s.io/client-go/rest" + "github.com/nginxinc/nginx-gateway-kubernetes/internal/config" + "github.com/nginxinc/nginx-gateway-kubernetes/internal/controller" + + flag "github.com/spf13/pflag" "sigs.k8s.io/controller-runtime/pkg/log/zap" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/manager/signals" - "sigs.k8s.io/gateway-api/apis/v1alpha2" ) var ( @@ -21,55 +17,31 @@ var ( date string // Command-line flags - gatewayClass = flag.String("gatewayclass", "", "Tha name of the GatewayClass resource") + gatewayCtlrName = flag.String("gateway-ctlr-name", "", "The name of the Gateway controller") ) func main() { flag.Parse() - if *gatewayClass == "" { - fmt.Fprintln(os.Stderr, "-gatewayclass argument must be set") + if *gatewayCtlrName == "" { + flag.PrintDefaults() os.Exit(1) } logger := zap.New() + conf := config.Config{ + GatewayCtlrName: *gatewayCtlrName, + Logger: logger, + } logger.Info("Starting NGINX Gateway", "version", version, "commit", commit, "date", date) - config, err := rest.InClusterConfig() - if err != nil { - logger.Error(err, "Failed to create InClusterConfig") - os.Exit(1) - } - - mgr, err := manager.New(config, manager.Options{ - Logger: logger, - }) - if err != nil { - logger.Error(err, "Failed to create Manager") - os.Exit(1) - } - - err = v1alpha2.AddToScheme(mgr.GetScheme()) - if err != nil { - logger.Error(err, "Failed to add Gateway API scheme") - os.Exit(1) - } - - err = sdk.RegisterGatewayClassController(mgr, implementation.NewGatewayClassImplementation(logger)) - if err != nil { - logger.Error(err, "Failed to register GatewayClassController") - os.Exit(1) - } - - logger.Info("Starting manager") - - err = mgr.Start(signals.SetupSignalHandler()) + err := controller.Start(conf) if err != nil { - logger.Error(err, "Failed to start Manager") + logger.Error(err, "Failed to start control loop") os.Exit(1) } } diff --git a/deploy/manifests/gateway.yaml b/deploy/manifests/gateway.yaml new file mode 100644 index 0000000000..c7618ba877 --- /dev/null +++ b/deploy/manifests/gateway.yaml @@ -0,0 +1,14 @@ +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: Gateway +metadata: + name: gateway + namespace: nginx-gateway + labels: + domain: k8s-gateway.nginx.org +spec: + gatewayClassName: nginx + listeners: + - name: my-listener + hostname: example.com + port: 8080 + protocol: HTTP diff --git a/deploy/manifests/gatewayclass.yaml b/deploy/manifests/gatewayclass.yaml index 7ca78f74ba..36ca9e9bbb 100644 --- a/deploy/manifests/gatewayclass.yaml +++ b/deploy/manifests/gatewayclass.yaml @@ -3,4 +3,4 @@ kind: GatewayClass metadata: name: nginx spec: - controllerName: k8s-gateway.nginx.org/gateway \ No newline at end of file + controllerName: k8s-gateway.nginx.org/nginx-gateway/gateway diff --git a/deploy/manifests/nginx-gateway.yaml b/deploy/manifests/nginx-gateway.yaml index f2a8de8968..9085bb9117 100644 --- a/deploy/manifests/nginx-gateway.yaml +++ b/deploy/manifests/nginx-gateway.yaml @@ -18,6 +18,7 @@ rules: - gateway.networking.k8s.io resources: - gatewayclasses + - gateways verbs: - list - watch @@ -66,7 +67,7 @@ spec: imagePullPolicy: IfNotPresent name: nginx-gateway args: - - -gatewayclass=nginx + - --gateway-ctlr-name=k8s-gateway.nginx.org/nginx-gateway/gateway - image: nginx:1.21.3 imagePullPolicy: IfNotPresent name: nginx diff --git a/go.mod b/go.mod index f465b6bd2b..abc0782016 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,8 @@ go 1.17 require ( github.com/go-logr/logr v0.4.0 - go.uber.org/zap v1.19.1 golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 k8s.io/apimachinery v0.22.2 - k8s.io/client-go v0.22.2 sigs.k8s.io/controller-runtime v0.10.2 sigs.k8s.io/gateway-api v0.4.0 ) @@ -39,8 +37,9 @@ require ( github.com/spf13/pflag v1.0.5 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.7.0 // indirect + go.uber.org/zap v1.19.1 // indirect golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 // indirect - golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 // indirect + golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/text v0.3.6 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect @@ -52,6 +51,7 @@ require ( gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect k8s.io/api v0.22.2 // indirect k8s.io/apiextensions-apiserver v0.22.2 // indirect + k8s.io/client-go v0.22.2 // indirect k8s.io/component-base v0.22.2 // indirect k8s.io/klog/v2 v2.10.0 // indirect k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect diff --git a/go.sum b/go.sum index 23ebeacf3b..c58e717aed 100644 --- a/go.sum +++ b/go.sum @@ -83,6 +83,7 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -716,8 +717,9 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 h1:c8PlLMqBbOHoqtjteWm5/kbe6rNY2pbRfbIMVnepueo= golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000000..8b1e89a8ef --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,10 @@ +package config + +import ( + "github.com/go-logr/logr" +) + +type Config struct { + GatewayCtlrName string + Logger logr.Logger +} diff --git a/internal/controller/controller.go b/internal/controller/controller.go new file mode 100644 index 0000000000..e003d41675 --- /dev/null +++ b/internal/controller/controller.go @@ -0,0 +1,50 @@ +package controller + +import ( + "fmt" + + "github.com/nginxinc/nginx-gateway-kubernetes/internal/config" + gw "github.com/nginxinc/nginx-gateway-kubernetes/internal/implementations/gateway" + gc "github.com/nginxinc/nginx-gateway-kubernetes/internal/implementations/gatewayclass" + "github.com/nginxinc/nginx-gateway-kubernetes/pkg/sdk" + + "k8s.io/apimachinery/pkg/runtime" + ctlr "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/manager" + gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +var ( + scheme = runtime.NewScheme() +) + +func init() { + _ = gatewayv1alpha2.AddToScheme(scheme) +} + +func Start(conf config.Config) error { + logger := conf.Logger + + options := manager.Options{ + Scheme: scheme, + } + + mgr, err := manager.New(ctlr.GetConfigOrDie(), options) + if err != nil { + return fmt.Errorf("cannot build runtime manager: %w", err) + } + + err = sdk.RegisterGatewayController(mgr, gw.NewGatewayImplementation(conf)) + if err != nil { + return fmt.Errorf("cannot register gateway implementation: %w", err) + } + err = sdk.RegisterGatewayClassController(mgr, gc.NewGatewayClassImplementation(conf)) + if err != nil { + return fmt.Errorf("cannot reguster gatewayclass implementation: %w", err) + } + + ctx := ctlr.SetupSignalHandler() + + logger.Info("Starting manager") + return mgr.Start(ctx) +} diff --git a/internal/implementation/gatewayclass.go b/internal/implementation/gatewayclass.go deleted file mode 100644 index 14f22a5667..0000000000 --- a/internal/implementation/gatewayclass.go +++ /dev/null @@ -1,31 +0,0 @@ -package implementation - -import ( - "github.com/go-logr/logr" - "github.com/nginxinc/nginx-gateway-kubernetes/pkg/sdk" - "sigs.k8s.io/gateway-api/apis/v1alpha2" -) - -type gatewayClassImplementation struct { - logger logr.Logger -} - -func NewGatewayClassImplementation(logger logr.Logger) sdk.GatewayClassImpl { - return &gatewayClassImplementation{logger: logger} -} - -func (impl *gatewayClassImplementation) Upsert(gc *v1alpha2.GatewayClass) { - if gc.Spec.ControllerName != "k8s-gateway.nginx.org/gateway" { - impl.logger.Info("Wrong ControllerName in the GatewayClass resource", - "expected", "k8s-gateway.nginx.org/gateway", - "got", "gc.Spec.ControllerName") - return - } - - impl.logger.Info("Processing GatewayClass resource", - "name", gc.Name) -} -func (impl *gatewayClassImplementation) Remove(key string) { - impl.logger.Info("GatewayClass resource was removed", - "name", key) -} diff --git a/internal/implementations/gateway/gateway.go b/internal/implementations/gateway/gateway.go new file mode 100644 index 0000000000..0840fe11ae --- /dev/null +++ b/internal/implementations/gateway/gateway.go @@ -0,0 +1,42 @@ +package implementation + +import ( + "github.com/go-logr/logr" + "github.com/nginxinc/nginx-gateway-kubernetes/internal/config" + "github.com/nginxinc/nginx-gateway-kubernetes/pkg/sdk" + + "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +type gatewayImplementation struct { + conf config.Config +} + +func NewGatewayImplementation(conf config.Config) sdk.GatewayImpl { + return &gatewayImplementation{ + conf: conf, + } +} + +func (impl *gatewayImplementation) Logger() logr.Logger { + return impl.conf.Logger +} + +func (impl *gatewayImplementation) ControllerName() string { + return impl.conf.GatewayCtlrName +} + +func (impl *gatewayImplementation) Upsert(gw *v1alpha2.Gateway) { + if gw.Name == impl.ControllerName() { + impl.Logger().Info("Found correct Gateway resource", + "name", gw.Name, + ) + return + } +} + +func (impl *gatewayImplementation) Remove(key string) { + impl.Logger().Info("Gateway resource was removed", + "name", key, + ) +} diff --git a/internal/implementations/gatewayclass/gatewayclass.go b/internal/implementations/gatewayclass/gatewayclass.go new file mode 100644 index 0000000000..640746cdbc --- /dev/null +++ b/internal/implementations/gatewayclass/gatewayclass.go @@ -0,0 +1,43 @@ +package implementation + +import ( + "github.com/nginxinc/nginx-gateway-kubernetes/internal/config" + "github.com/nginxinc/nginx-gateway-kubernetes/pkg/sdk" + + "github.com/go-logr/logr" + "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +type gatewayClassImplementation struct { + conf config.Config +} + +func NewGatewayClassImplementation(conf config.Config) sdk.GatewayClassImpl { + return &gatewayClassImplementation{ + conf: conf, + } +} + +func (impl *gatewayClassImplementation) Logger() logr.Logger { + return impl.conf.Logger +} + +func (impl *gatewayClassImplementation) ControllerName() string { + return impl.conf.GatewayCtlrName +} + +func (impl *gatewayClassImplementation) Upsert(gc *v1alpha2.GatewayClass) { + if string(gc.Spec.ControllerName) != impl.ControllerName() { + impl.Logger().Info("Wrong ControllerName in the GatewayClass resource", + "expected", impl.ControllerName(), + "got", gc.Spec.ControllerName) + return + } + + impl.Logger().Info("Processing GatewayClass resource", + "name", gc.Name) +} +func (impl *gatewayClassImplementation) Remove(key string) { + impl.Logger().Info("GatewayClass resource was removed", + "name", key) +} diff --git a/pkg/sdk/gateway_controller.go b/pkg/sdk/gateway_controller.go new file mode 100644 index 0000000000..c824b4c98a --- /dev/null +++ b/pkg/sdk/gateway_controller.go @@ -0,0 +1,56 @@ +package sdk + +import ( + "context" + + "github.com/go-logr/logr" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctlr "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +type gatewayReconciler struct { + client.Client + scheme *runtime.Scheme + impl GatewayImpl +} + +func RegisterGatewayController(mgr manager.Manager, impl GatewayImpl) error { + r := &gatewayReconciler{ + Client: mgr.GetClient(), + scheme: mgr.GetScheme(), + impl: impl, + } + + return ctlr.NewControllerManagedBy(mgr). + For(&v1alpha2.Gateway{}). + Complete(r) +} + +func (r *gatewayReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { + log := logr.FromContext(ctx).WithValues("gateway", req.Name) + log.V(3).Info("Reconciling Gateway") + + var found bool = true + var gw v1alpha2.Gateway + err := r.Get(ctx, req.NamespacedName, &gw) + if err != nil { + if !apierrors.IsNotFound(err) { + log.Error(err, "Failed to get Gateway") + return reconcile.Result{}, err + } + found = false + } + + if !found { + r.impl.Remove(req.Name) + return reconcile.Result{}, nil + } + + r.impl.Upsert(&gw) + return reconcile.Result{}, nil +} diff --git a/pkg/sdk/gatewayclass_controller.go b/pkg/sdk/gatewayclass_controller.go index f98ca267d5..c97561da2c 100644 --- a/pkg/sdk/gatewayclass_controller.go +++ b/pkg/sdk/gatewayclass_controller.go @@ -32,7 +32,7 @@ func RegisterGatewayClassController(mgr manager.Manager, impl GatewayClassImpl) func (r *gatewayClassReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { log := logr.FromContext(ctx).WithValues("gatewayclass", req.Name) - log.V(3).Info("reconciling GatewayClass") + log.V(3).Info("Reconciling GatewayClass") var gc v1alpha2.GatewayClass found := true @@ -43,7 +43,7 @@ func (r *gatewayClassReconciler) Reconcile(ctx context.Context, req reconcile.Re err := r.Get(ctx, req.NamespacedName, &gc) if err != nil { if !apierrors.IsNotFound(err) { - log.Error(err, "fail to get GatewayClass") + log.Error(err, "Failed to get GatewayClass") return reconcile.Result{}, err } found = false diff --git a/pkg/sdk/interfaces.go b/pkg/sdk/interfaces.go index 47ec432757..e43017d7be 100644 --- a/pkg/sdk/interfaces.go +++ b/pkg/sdk/interfaces.go @@ -1,8 +1,15 @@ package sdk -import "sigs.k8s.io/gateway-api/apis/v1alpha2" +import ( + "sigs.k8s.io/gateway-api/apis/v1alpha2" +) type GatewayClassImpl interface { Upsert(gc *v1alpha2.GatewayClass) Remove(key string) } + +type GatewayImpl interface { + Upsert(*v1alpha2.Gateway) + Remove(string) +}