Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bgp): logger & enhance nadProvider config #4352

Merged
merged 5 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions pkg/controller/vpc_nat.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
var (
vpcNatImage = ""
vpcNatGwBgpSpeakerImage = ""
vpcNatAPINadName = ""
vpcNatAPINadProvider = ""
)

Expand All @@ -35,9 +34,6 @@ func (c *Controller) resyncVpcNatConfig() {
// Image for the BGP sidecar of the gateway (optional)
vpcNatGwBgpSpeakerImage = cm.Data["bgpSpeakerImage"]

// NetworkAttachmentDefinition name for the BGP speaker to call the API server
vpcNatAPINadName = cm.Data["apiNadName"]

// NetworkAttachmentDefinition provider for the BGP speaker to call the API server
vpcNatAPINadProvider = cm.Data["apiNadProvider"]
}
69 changes: 47 additions & 22 deletions pkg/controller/vpc_nat_gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -736,18 +736,37 @@ func (c *Controller) execNatGwRules(pod *corev1.Pod, operation string, rules []s
return nil
}

func (c *Controller) setNatGwInterface(annotations map[string]string, externalNetwork string, defaultSubnet *kubeovnv1.Subnet) error {
if vpcNatAPINadName == "" {
return errors.New("no NetworkAttachmentDefinition provided to access apiserver, check configmap ovn-vpc-nat-config and field 'apiNadName'")
// setNatGwAPIAccess adds an interface with API access to the NAT gateway and attaches the standard externalNetwork to the gateway.
// This interface is backed by a NetworkAttachmentDefinition (NAD) with a provider corresponding
// to one that is configured on a subnet part of the default VPC (the K8S apiserver runs in the default VPC)
func (c *Controller) setNatGwAPIAccess(annotations map[string]string, externalNetwork string) error {
// Check the NetworkAttachmentDefinition provider exists, must be user-configured
if vpcNatAPINadProvider == "" {
return errors.New("no NetworkAttachmentDefinition provided to access apiserver, check configmap ovn-vpc-nat-config and field 'apiNadProvider'")
}

// Subdivide provider so we can infer the name of the NetworkAttachmentDefinition
providerSplit := strings.Split(vpcNatAPINadProvider, ".")
if len(providerSplit) < 3 {
zhangzujian marked this conversation as resolved.
Show resolved Hide resolved
return fmt.Errorf("name of the provider must have syntax 'name.namespace.ovn', got %s", vpcNatAPINadProvider)
}

nad := fmt.Sprintf("%s/%s, %s/%s", c.config.PodNamespace, externalNetwork, corev1.NamespaceDefault, vpcNatAPINadName)
annotations[util.AttachmentNetworkAnnotation] = nad
// Extract the name of the provider and its namespace
name, namespace := providerSplit[0], providerSplit[1]

// Craft the name of the NAD for the externalNetwork and the apiNetwork
externalNetworkAttachment := fmt.Sprintf("%s/%s", c.config.PodNamespace, externalNetwork)
apiNetworkAttachment := fmt.Sprintf("%s/%s", namespace, name)

return setNatGwRoute(annotations, defaultSubnet.Spec.Gateway)
// Attach the NADs to the Pod by adding them to the special annotation
attachmentAnnotation := fmt.Sprintf("%s, %s", externalNetworkAttachment, apiNetworkAttachment)
annotations[util.AttachmentNetworkAnnotation] = attachmentAnnotation

// Set the network route to the API, so we can reach it
return c.setNatGwAPIRoute(annotations, namespace, name)
}

func setNatGwRoute(annotations map[string]string, subnetGw string) error {
func (c *Controller) setNatGwAPIRoute(annotations map[string]string, nadNamespace, nadName string) error {
dst := os.Getenv("KUBERNETES_SERVICE_HOST")

protocol := util.CheckProtocol(dst)
Expand All @@ -760,13 +779,20 @@ func setNatGwRoute(annotations map[string]string, subnetGw string) error {
}
}

// Check the API NetworkAttachmentDefinition exists, otherwise we won't be able to attach
// the BGP speaker to a network that has access to the K8S apiserver (and won't be able to detect EIPs)
if vpcNatAPINadProvider == "" {
return errors.New("no NetworkAttachmentDefinition provided to access apiserver, check configmap ovn-vpc-nat-config and field 'apiNadName'")
// Retrieve every subnet on the cluster
subnets, err := c.subnetsLister.List(labels.Everything())
if err != nil {
return fmt.Errorf("failed to list subnets: %w", err)
}

for _, gw := range strings.Split(subnetGw, ",") {
// Retrieve the subnet connected to the NAD, this subnet should be in the VPC of the API
apiSubnet, err := c.findSubnetByNetworkAttachmentDefinition(nadNamespace, nadName, subnets)
if err != nil {
return fmt.Errorf("failed to find api subnet using the nad %s/%s: %w", nadNamespace, nadName, err)
}

// Craft the route to reach the API from the subnet we've just retrieved
for _, gw := range strings.Split(apiSubnet.Spec.Gateway, ",") {
if util.CheckProtocol(gw) == protocol {
routes := []request.Route{{Destination: dst, Gateway: gw}}
buf, err := json.Marshal(routes)
Expand All @@ -787,21 +813,18 @@ func (c *Controller) genNatGwStatefulSet(gw *kubeovnv1.VpcNatGateway, oldSts *v1
if oldSts != nil && len(oldSts.Annotations) != 0 {
annotations = maps.Clone(oldSts.Annotations)
}
nadName := util.GetNatGwExternalNetwork(gw.Spec.ExternalSubnets)

externalNetworkNad := util.GetNatGwExternalNetwork(gw.Spec.ExternalSubnets)
podAnnotations := map[string]string{
util.VpcNatGatewayAnnotation: gw.Name,
util.AttachmentNetworkAnnotation: fmt.Sprintf("%s/%s", c.config.PodNamespace, nadName),
util.AttachmentNetworkAnnotation: fmt.Sprintf("%s/%s", c.config.PodNamespace, externalNetworkNad),
util.LogicalSwitchAnnotation: gw.Spec.Subnet,
util.IPAddressAnnotation: gw.Spec.LanIP,
}

if gw.Spec.BgpSpeaker.Enabled { // Add an interface that can reach the API server
defaultSubnet, err := c.subnetsLister.Get(c.config.DefaultLogicalSwitch)
if err != nil {
return nil, fmt.Errorf("failed to get default subnet %s: %w", c.config.DefaultLogicalSwitch, err)
}

if err := c.setNatGwInterface(podAnnotations, nadName, defaultSubnet); err != nil {
// Add an interface that can reach the API server, we access to it to probe Kube-OVN resources
if gw.Spec.BgpSpeaker.Enabled {
if err := c.setNatGwAPIAccess(podAnnotations, externalNetworkNad); err != nil {
zhangzujian marked this conversation as resolved.
Show resolved Hide resolved
return nil, err
}
}
Expand Down Expand Up @@ -847,7 +870,7 @@ func (c *Controller) genNatGwStatefulSet(gw *kubeovnv1.VpcNatGateway, oldSts *v1
return nil, err
}

subnet, err := c.findSubnetByNetworkAttachmentDefinition(c.config.PodNamespace, nadName, subnets)
subnet, err := c.findSubnetByNetworkAttachmentDefinition(c.config.PodNamespace, externalNetworkNad, subnets)
if err != nil {
klog.Error(err)
return nil, err
Expand Down Expand Up @@ -975,6 +998,8 @@ func (c *Controller) genNatGwStatefulSet(gw *kubeovnv1.VpcNatGateway, oldSts *v1
neighIPv4 = append(neighIPv4, neighbor)
case kubeovnv1.ProtocolIPv6:
neighIPv6 = append(neighIPv6, neighbor)
default:
return nil, fmt.Errorf("unsupported protocol for peer %s", neighbor)
}
}

Expand Down
20 changes: 19 additions & 1 deletion pkg/speaker/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"time"

api "github.com/osrg/gobgp/v3/api"
bgplog "github.com/osrg/gobgp/v3/pkg/log"
"github.com/osrg/gobgp/v3/pkg/packet/bgp"
gobgp "github.com/osrg/gobgp/v3/pkg/server"
"github.com/spf13/pflag"
Expand Down Expand Up @@ -244,10 +245,21 @@ func (config *Configuration) checkGracefulRestartOptions() error {
func (config *Configuration) initBgpServer() error {
maxSize := 256 << 20
var listenPort int32 = -1

// Set logger options for GoBGP based on klog's verbosity
var logger bgpLogger
if klog.V(3).Enabled() {
logger.SetLevel(bgplog.TraceLevel)
} else {
logger.SetLevel(bgplog.InfoLevel)
}

grpcOpts := []grpc.ServerOption{grpc.MaxRecvMsgSize(maxSize), grpc.MaxSendMsgSize(maxSize)}
s := gobgp.NewBgpServer(
gobgp.GrpcListenAddress(fmt.Sprintf("%s:%d", config.GrpcHost, config.GrpcPort)),
gobgp.GrpcOption(grpcOpts))
gobgp.GrpcOption(grpcOpts),
gobgp.LoggerOption(logger),
)
go s.Serve()

peersMap := map[api.Family_Afi][]string{
Expand All @@ -258,6 +270,12 @@ func (config *Configuration) initBgpServer() error {
if config.PassiveMode {
listenPort = bgp.BGP_PORT
}

klog.V(5).Infof("Starting bgp server with asn %d, routerId %s on port %d",
zhangzujian marked this conversation as resolved.
Show resolved Hide resolved
config.ClusterAs,
config.RouterID,
listenPort)

if err := s.StartBgp(context.Background(), &api.StartBgpRequest{
Global: &api.Global{
Asn: config.ClusterAs,
Expand Down
41 changes: 41 additions & 0 deletions pkg/speaker/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package speaker

import (
bgplog "github.com/osrg/gobgp/v3/pkg/log"
"k8s.io/klog/v2"
)

// bgpLogger is a struct implementing the GoBGP logger interface
// This is useful to inject our custom klog logger into the GoBGP speaker
type bgpLogger struct{}

func (k bgpLogger) Panic(msg string, fields bgplog.Fields) {
klog.Fatalf("%s %v", msg, fields)
}

func (k bgpLogger) Fatal(msg string, fields bgplog.Fields) {
klog.Fatalf("%s %v", msg, fields)
}

func (k bgpLogger) Error(msg string, fields bgplog.Fields) {
klog.Errorf("%s %v", msg, fields)
}

func (k bgpLogger) Warn(msg string, fields bgplog.Fields) {
klog.Warningf("%s %v", msg, fields)
}

func (k bgpLogger) Info(msg string, fields bgplog.Fields) {
klog.Infof("%s %v", msg, fields)
}

func (k bgpLogger) Debug(msg string, fields bgplog.Fields) {
klog.V(5).Infof("%s %v", msg, fields)
}

func (k bgpLogger) SetLevel(_ bgplog.LogLevel) {
}

func (k bgpLogger) GetLevel() bgplog.LogLevel {
return 0
}
Loading