Skip to content

Commit

Permalink
fetch all ip-addresses configured on linode
Browse files Browse the repository at this point in the history
1. fetches public, private and vpc ips
2. builds and uses cache for ips
3. adds flag to enable/disable the auto tagging feature
  • Loading branch information
rahulait committed Feb 23, 2024
1 parent 18c1797 commit 857cb61
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 13 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ Key | Values | Default | Description
---|---|---|---
`linodego-debug` | `boolean` | `false` | enables debug output for the LinodeAPI wrapper.
`linode-node-private-subnet` | `net.IPNet` | `192.168.128.0/17` | specifies backend network to use with nodebalancers.

`auto-annotate-node` | `boolean` | `true` | enables automatically adding node annotation containing ipaddress to use with nodebalancers.

### Configurations
There are other variables that can be set to a different value. For list of all the modifiable variables/values, take a look at './deploy/chart/values.yaml'.
Expand Down
1 change: 1 addition & 0 deletions cloud/linode/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Client interface {
GetInstance(context.Context, int) (*linodego.Instance, error)
ListInstances(context.Context, *linodego.ListOptions) ([]linodego.Instance, error)
GetInstanceIPAddresses(context.Context, int) (*linodego.InstanceIPAddressResponse, error)
ListInstanceConfigs(context.Context, int, *linodego.ListOptions) ([]linodego.InstanceConfig, error)

CreateNodeBalancer(context.Context, linodego.NodeBalancerCreateOptions) (*linodego.NodeBalancer, error)
GetNodeBalancer(context.Context, int) (*linodego.NodeBalancer, error)
Expand Down
1 change: 1 addition & 0 deletions cloud/linode/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var Options struct {
KubeconfigFlag *pflag.Flag
LinodeGoDebug bool
LinodeNodePrivateSubnet net.IPNet
AutoAnnotateNode bool
}

type linodeCloud struct {
Expand Down
73 changes: 73 additions & 0 deletions cloud/linode/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,43 @@ type nodeCache struct {
nodes map[int]*linodego.Instance
lastUpdate time.Time
ttl time.Duration
ips map[int][]string
}

// getInstanceIPv4Addresses returns all ipv4 addresses configured on a linode.
func (nc *nodeCache) getInstanceIPv4Addresses(ctx context.Context, id int, client Client) ([]string, error) {
// Retrieve ipaddresses for the linode
addresses, err := client.GetInstanceIPAddresses(ctx, id)
if err != nil {
return nil, err
}

var ips []string
if len(addresses.IPv4.Private) != 0 {
ips = append(ips, addresses.IPv4.Private[0].Address)
}
if len(addresses.IPv4.Public) != 0 {
ips = append(ips, addresses.IPv4.Public[0].Address)
}

// Retrieve interface configs for the linode
configs, err := client.ListInstanceConfigs(ctx, id, &linodego.ListOptions{})
if err != nil {
return nil, err
}

if len(configs) == 0 {
return ips, nil
}

// Iterate over interface config and find VPC specific ips
for _, iface := range configs[0].Interfaces {
if iface.VPCID != nil && iface.IPv4.VPC != "" {
ips = append(ips, iface.IPv4.VPC)
}
}

return ips, nil
}

// refreshInstances conditionally loads all instances from the Linode API and caches them.
Expand All @@ -36,10 +73,26 @@ func (nc *nodeCache) refreshInstances(ctx context.Context, client Client) error
if err != nil {
return err
}

// get ips for each instance and store temporarily in a new map
ips := make(map[int][]string)
if Options.AutoAnnotateNode {
for _, instance := range instances {
addresses, err := nc.getInstanceIPv4Addresses(ctx, instance.ID, client)
if err != nil {
return err
}
ips[instance.ID] = addresses
}
}

// add/update existing map with node and ip information
nc.nodes = make(map[int]*linodego.Instance)
nc.ips = make(map[int][]string)
for _, instance := range instances {
instance := instance
nc.nodes[instance.ID] = &instance
nc.ips[instance.ID] = ips[instance.ID]
}
nc.lastUpdate = time.Now()

Expand Down Expand Up @@ -186,3 +239,23 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud

return meta, nil
}

func (i *instances) getLinodeIPv4Addresses(ctx context.Context, node *v1.Node) ([]string, error) {
ctx = sentry.SetHubOnContext(ctx)
instance, err := i.lookupLinode(ctx, node)
if err != nil {
sentry.CaptureError(ctx, err)
return nil, err
}

i.nodeCache.RLock()
defer i.nodeCache.RUnlock()
ips, ok := i.nodeCache.ips[instance.ID]
if !ok {
err := instanceNoIPAddressesError{instance.ID}
sentry.CaptureError(ctx, err)
return nil, err
}

return ips, nil
}
15 changes: 15 additions & 0 deletions cloud/linode/mock_client_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 24 additions & 12 deletions cloud/linode/node_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package linode

import (
"context"
"net"
"net/http"
"os"
"strconv"
Expand Down Expand Up @@ -124,13 +125,13 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error {

lastUpdate := s.LastMetadataUpdate(node.Name)

uuid, ok := node.Labels[annLinodeHostUUID]
if ok && time.Since(lastUpdate) < s.ttl {
uuid, foundLabel := node.Labels[annLinodeHostUUID]
privIP, foundAnnotation := node.Annotations[annLinodeNodePrivateIP]
if foundLabel && foundAnnotation && time.Since(lastUpdate) < s.ttl {
return nil
}

privIP, ok := node.Annotations[annLinodeNodePrivateIP]
if ok && time.Since(lastUpdate) < s.ttl {
if foundLabel && !Options.AutoAnnotateNode && time.Since(lastUpdate) < s.ttl {
return nil
}

Expand All @@ -140,14 +141,23 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error {
return err
}

// TODO: Once nodebalancer supports other subnets, instead of using linode.IPv4
// which contains public and private ip, use different call which returns
// all ip's configured on the node
linodePrivIP := ""
for _, ip := range linode.IPv4 {
if Options.LinodeNodePrivateSubnet.Contains(*ip) {
linodePrivIP = ip.String()
break
if Options.AutoAnnotateNode {
ips, err := s.instances.getLinodeIPv4Addresses(ctx, node)
if err != nil {
klog.Infof("failed to get ips for linode: %s", err)
return err
}

if len(ips) == 0 {
klog.Infof("no ips found for node %s", node.Name)
}

for _, ip := range ips {
if Options.LinodeNodePrivateSubnet.Contains(net.ParseIP(ip)) {
linodePrivIP = ip
break
}
}
}

Expand All @@ -170,7 +180,9 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error {

// Try to update the node
n.Labels[annLinodeHostUUID] = linode.HostUUID
n.Annotations[annLinodeNodePrivateIP] = linodePrivIP
if Options.AutoAnnotateNode {
n.Annotations[annLinodeNodePrivateIP] = linodePrivIP
}
_, err = s.kubeclient.CoreV1().Nodes().Update(ctx, n, metav1.UpdateOptions{})
return err
}); err != nil {
Expand Down
3 changes: 3 additions & 0 deletions deploy/chart/templates/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ spec:
{{- if .Values.linodeNodePrivateSubnet }}
- --linode-node-private-subnet={{ .Values.linodeNodePrivateSubnet }}
{{- end }}
{{- if .Values.autoAnnotateNode }}
- --auto-annotate-node={{ .Values.autoAnnotateNode }}
{{- end }}
volumeMounts:
- mountPath: /etc/kubernetes
name: k8s
Expand Down
3 changes: 3 additions & 0 deletions deploy/chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ image:
# enable debug output for the LinodeAPI wrapper
# linodegoDebug: false

# Disable if one doesn't want to add annotations to nodes for ip to be configured with nodebalancer
autoAnnotateNode: true

# Set only if one wants to change the default backend network(192.168.128.0/17) to be configured with nodebalancers
# linodeNodePrivateSubnet: "192.168.128.0/17"

Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ func main() {
// Add Linode-specific flags
command.Flags().BoolVar(&linode.Options.LinodeGoDebug, "linodego-debug", false, "enables debug output for the LinodeAPI wrapper")
command.Flags().IPNetVar(&linode.Options.LinodeNodePrivateSubnet, "linode-node-private-subnet", net.IPNet{IP: net.ParseIP("192.168.128.0"), Mask: net.CIDRMask(17, 32)}, "specifies backend network to use for nodebalancers")
command.Flags().BoolVar(&linode.Options.AutoAnnotateNode, "auto-annotate-node", false, "enables automatically adding node annotation for ip in subnet specified in linode-node-private-subnet")

// Set static flags
command.Flags().VisitAll(func(fl *pflag.Flag) {
Expand Down

0 comments on commit 857cb61

Please sign in to comment.