Skip to content

Commit

Permalink
add pod_watcher
Browse files Browse the repository at this point in the history
Signed-off-by: walnuts1018 <r.juglans.1018@gmail.com>
  • Loading branch information
walnuts1018 committed Aug 23, 2024
1 parent ed1682b commit c1401df
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 0 deletions.
163 changes: 163 additions & 0 deletions cmd/nat-gateway/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package main

import (
"crypto/tls"
"flag"
"os"

// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"

"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"

ponav1beta1 "github.com/cybozu-go/pona/api/v1beta1"
"github.com/cybozu-go/pona/internal/controller"
// +kubebuilder:scaffold:imports
)

var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
)

func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))

utilruntime.Must(ponav1beta1.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
}

type Config struct {
FoUPort int
}

func main() {
var metricsAddr string
var enableLeaderElection bool
var probeAddr string
var secureMetrics bool
var enableHTTP2 bool
var tlsOpts []func(*tls.Config)

var config Config

flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
flag.BoolVar(&secureMetrics, "metrics-secure", true,
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
flag.BoolVar(&enableHTTP2, "enable-http2", false,
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
flag.IntVar(&config.FoUPort, "fou-port", 5555, "port number for foo-over-udp tunnels")

opts := zap.Options{
Development: true,
}
opts.BindFlags(flag.CommandLine)
flag.Parse()

ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))

// if the enable-http2 flag is false (the default), http/2 should be disabled
// due to its vulnerabilities. More specifically, disabling http/2 will
// prevent from being vulnerable to the HTTP/2 Stream Cancellation and
// Rapid Reset CVEs. For more information see:
// - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
// - https://github.com/advisories/GHSA-4374-p667-p6c8
disableHTTP2 := func(c *tls.Config) {
setupLog.Info("disabling http/2")
c.NextProtos = []string{"http/1.1"}
}

if !enableHTTP2 {
tlsOpts = append(tlsOpts, disableHTTP2)
}

webhookServer := webhook.NewServer(webhook.Options{
TLSOpts: tlsOpts,
})

// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
// More info:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.18.4/pkg/metrics/server
// - https://book.kubebuilder.io/reference/metrics.html
metricsServerOptions := metricsserver.Options{
BindAddress: metricsAddr,
SecureServing: secureMetrics,
// TODO(user): TLSOpts is used to allow configuring the TLS config used for the server. If certificates are
// not provided, self-signed certificates will be generated by default. This option is not recommended for
// production environments as self-signed certificates do not offer the same level of trust and security
// as certificates issued by a trusted Certificate Authority (CA). The primary risk is potentially allowing
// unauthorized access to sensitive metrics data. Consider replacing with CertDir, CertName, and KeyName
// to provide certificates, ensuring the server communicates using trusted and secure certificates.
TLSOpts: tlsOpts,
}

if secureMetrics {
// FilterProvider is used to protect the metrics endpoint with authn/authz.
// These configurations ensure that only authorized users and service accounts
// can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info:
// https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.18.4/pkg/metrics/filters#WithAuthenticationAndAuthorization
metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
}

mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
Metrics: metricsServerOptions,
WebhookServer: webhookServer,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "7e4aaa0a.pona.cybozu.com",
// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
// when the Manager ends. This requires the binary to immediately end when the
// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
// speeds up voluntary leader transitions as the new leader don't have to wait
// LeaseDuration time first.
//
// In the default scaffold provided, the program ends immediately after
// the manager stops, so would be fine to enable this option. However,
// if you are doing or is intended to do any operation such as perform cleanups
// after the manager stops then its usage might be unsafe.
// LeaderElectionReleaseOnCancel: true,
})
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}

if err = (&controller.PodReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Pod")
os.Exit(1)
}
// +kubebuilder:scaffold:builder

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up health check")
os.Exit(1)
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up ready check")
os.Exit(1)
}

setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
}
86 changes: 86 additions & 0 deletions internal/controller/pod_watcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package controller

import (
"context"
"strings"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
)

const (
EgressAnnotationPrefix = "egress.pona.cybozu.com/"
)

// PodReconciler reconciles a Pod object
type PodReconciler struct {
client.Client
Scheme *runtime.Scheme

EgressName string
EgressNamespace string
}

// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the Pod object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile
func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx)

Check failure on line 39 in internal/controller/pod_watcher.go

View workflow job for this annotation

GitHub Actions / Small tests (1.22, ubuntu-22.04)

logger declared and not used

pod := &corev1.Pod{}
if err := r.Get(ctx, req.NamespacedName, pod); err != nil {

}

return ctrl.Result{}, nil
}

func (r *PodReconciler) shouldHandle(pod *corev1.Pod) bool {
if pod.Spec.HostNetwork {
// Egress feature is not available for Pods running in the host network.
return false
}

return false
}

func (r *PodReconciler) hasEgressAnnotation(pod *corev1.Pod) bool {
for k, v := range pod.Annotations {
if !strings.HasPrefix(k, EgressAnnotationPrefix) {
continue
}

if k[len(EgressAnnotationPrefix):] != r.EgressNamespace {
continue
}

// shortcut for the most typical case
if v == r.myName {

Check failure on line 69 in internal/controller/pod_watcher.go

View workflow job for this annotation

GitHub Actions / Small tests (1.22, ubuntu-22.04)

r.myName undefined (type *PodReconciler has no field or method myName)
return true
}

for _, n := range strings.Split(v, ",") {
if n == r.myName {

Check failure on line 74 in internal/controller/pod_watcher.go

View workflow job for this annotation

GitHub Actions / Small tests (1.22, ubuntu-22.04)

r.myName undefined (type *PodReconciler has no field or method myName)
return true
}
}
}
}

Check failure on line 79 in internal/controller/pod_watcher.go

View workflow job for this annotation

GitHub Actions / Small tests (1.22, ubuntu-22.04)

missing return

// SetupWithManager sets up the controller with the Manager.
func (r *PodReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&corev1.Pod{}).
Complete(r)
}
16 changes: 16 additions & 0 deletions internal/controller/pod_watcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package controller

import (
. "github.com/onsi/ginkgo/v2"
)

var _ = Describe("Pod Controller", func() {
Context("When reconciling a resource", func() {

It("should successfully reconcile the resource", func() {

// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
// Example: If you expect a certain status condition after reconciliation, verify it here.
})
})
})

0 comments on commit c1401df

Please sign in to comment.