Skip to content

Commit

Permalink
even more refactoring!
Browse files Browse the repository at this point in the history
  • Loading branch information
AshleyDumaine committed Mar 6, 2024
1 parent c8e2609 commit 4e4f307
Show file tree
Hide file tree
Showing 10 changed files with 366 additions and 78 deletions.
4 changes: 4 additions & 0 deletions api/v1alpha1/linodecluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ type LinodeClusterSpec struct {
// control plane nodes.
// +optional
ControlPlaneFirewall FirewallSpec `json:"controlPlaneFirewall,omitempty"`

// ControlPlaneFirewallRef is a reference to the Firewall for the control plane nodes.
// +optional
ControlPlaneFirewallRef *corev1.ObjectReference `json:"controlPlaneFirewallRef,omitempty"`
}

// LinodeClusterStatus defines the observed state of LinodeCluster
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

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

30 changes: 12 additions & 18 deletions cloud/scope/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,9 @@ import (

// ClusterScopeParams defines the input parameters used to create a new Scope.
type ClusterScopeParams struct {
Client client.Client
Cluster *clusterv1.Cluster
LinodeCluster *infrav1alpha1.LinodeCluster
ControlPlaneFirewall *infrav1alpha1.LinodeFirewall
Client client.Client
Cluster *clusterv1.Cluster
LinodeCluster *infrav1alpha1.LinodeCluster
}

func validateClusterScopeParams(params ClusterScopeParams) error {
Expand All @@ -45,9 +44,6 @@ func validateClusterScopeParams(params ClusterScopeParams) error {
if params.LinodeCluster == nil {
return errors.New("linodeCluster is required when creating a ClusterScope")
}
if params.ControlPlaneFirewall == nil {
return errors.New("controlPlaneFirewall is required when creating a ClusterScope")
}

return nil
}
Expand Down Expand Up @@ -75,24 +71,22 @@ func NewClusterScope(ctx context.Context, apiKey string, params ClusterScopePara
}

return &ClusterScope{
client: params.Client,
Cluster: params.Cluster,
LinodeClient: linodeClient,
LinodeCluster: params.LinodeCluster,
ControlPlaneFirewall: params.ControlPlaneFirewall,
PatchHelper: helper,
client: params.Client,
Cluster: params.Cluster,
LinodeClient: linodeClient,
LinodeCluster: params.LinodeCluster,
PatchHelper: helper,
}, nil
}

// ClusterScope defines the basic context for an actuator to operate upon.
type ClusterScope struct {
client client.Client

PatchHelper *patch.Helper
LinodeClient *linodego.Client
Cluster *clusterv1.Cluster
LinodeCluster *infrav1alpha1.LinodeCluster
ControlPlaneFirewall *infrav1alpha1.LinodeFirewall
PatchHelper *patch.Helper
LinodeClient *linodego.Client
Cluster *clusterv1.Cluster
LinodeCluster *infrav1alpha1.LinodeCluster
}

// PatchObject persists the cluster configuration and status.
Expand Down
139 changes: 139 additions & 0 deletions cloud/services/firewalls.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/linode/linodego"

infrav1alpha1 "github.com/linode/cluster-api-provider-linode/api/v1alpha1"
"github.com/linode/cluster-api-provider-linode/cloud/scope"
"github.com/linode/cluster-api-provider-linode/util"
)

Expand Down Expand Up @@ -311,3 +312,141 @@ func processACL(firewall *infrav1alpha1.LinodeFirewall, tags []string) (

return createOpts, nil
}

// AddNodeToApiServerFW adds a Node's IPs to the given Cloud Firewall's inbound rules
func AddNodeToApiServerFW(
ctx context.Context,
logger logr.Logger,
machineScope *scope.MachineScope,
firewall *infrav1alpha1.LinodeFirewall,
) error {
if firewall.Spec.FirewallID == nil {
err := errors.New("no firewall ID")
logger.Error(err, "no ID is set for the firewall")

return err
}

ipv4s, ipv6s, err := getInstanceIPs(ctx, machineScope.LinodeClient, machineScope.LinodeMachine.Spec.InstanceID)
if err != nil {
logger.Error(err, "Failed get instance IP addresses")

return err
}

// get the rules and append a new rule for this Node to access the api server
newRule := infrav1alpha1.FirewallRule{
Action: "ACCEPT",
Label: "api-server",
Description: "Rule created by CAPL",
Ports: fmt.Sprint(machineScope.LinodeCluster.Spec.ControlPlaneEndpoint.Port),
Protocol: linodego.TCP,
Addresses: &infrav1alpha1.NetworkAddresses{
IPv4: util.Pointer(ipv4s),
IPv6: util.Pointer(ipv6s),
},
}
// update the inbound rules
firewall.Spec.InboundRules = append(firewall.Spec.InboundRules, newRule)

// reprocess the firewall to make sure we won't exceed the IP and rule limit
clusterUID := firewall.Spec.ClusterUID
fwConfig, err := processACL(firewall, []string{clusterUID})
if err != nil {
logger.Info("Failed to process ACL", "error", err.Error())

return err
}

// finally, update the firewall
if _, err := machineScope.LinodeClient.UpdateFirewallRules(ctx, *firewall.Spec.FirewallID, fwConfig.Rules); err != nil {
logger.Info("Failed to update firewall", "error", err.Error())

return err
}

return nil
}

// DeleteNodeFromApiServerFW removes Node from the given Cloud Firewall's inbound rules
func DeleteNodeFromApiServerFW(
ctx context.Context,
logger logr.Logger,
machineScope *scope.MachineScope,
firewall *infrav1alpha1.LinodeFirewall,
) error {
if firewall.Spec.FirewallID == nil {
logger.Info("Firewall already deleted, no Firewall address to remove")

return nil
}

if machineScope.LinodeMachine.Spec.InstanceID == nil {
return errors.New("no InstanceID")
}

ipv4s, ipv6s, err := getInstanceIPs(ctx, machineScope.LinodeClient, machineScope.LinodeMachine.Spec.InstanceID)
if err != nil {
logger.Error(err, "Failed get instance IP addresses")

return err
}

for _, rule := range firewall.Spec.InboundRules {
rule.Addresses.IPv4 = util.Pointer(setDiff(*rule.Addresses.IPv4, ipv4s))
rule.Addresses.IPv6 = util.Pointer(setDiff(*rule.Addresses.IPv6, ipv6s))
}

// reprocess the firewall
clusterUID := firewall.Spec.ClusterUID
fwConfig, err := processACL(firewall, []string{clusterUID})
if err != nil {
logger.Info("Failed to process ACL", "error", err.Error())

return err
}

// finally, update the firewall
if _, err := machineScope.LinodeClient.UpdateFirewallRules(ctx, *firewall.Spec.FirewallID, fwConfig.Rules); err != nil {
logger.Info("Failed to update firewall", "error", err.Error())

return err
}

return nil
}

func getInstanceIPs(ctx context.Context, client *linodego.Client, instanceID *int) (ipv4s, ipv6s []string, err error) {
addresses, err := client.GetInstanceIPAddresses(ctx, *instanceID)
if err != nil {
return ipv4s, ipv6s, err
}

// get all the ipv4 addresses for the node
for _, addr := range addresses.IPv4.Private {
ipv4s = append(ipv4s, addr.Address)
}
for _, addr := range addresses.IPv4.Public {
ipv4s = append(ipv4s, addr.Address)
}

// get all the ipv6 addresses for the node
ipv6s = []string{addresses.IPv6.SLAAC.Address, addresses.IPv6.LinkLocal.Address}

return ipv4s, ipv6s, nil
}

// setDiff: A - B
func setDiff(a, b []string) (diff []string) {
m := make(map[string]bool)
for _, item := range b {
m[item] = true
}
for _, item := range a {
if _, ok := m[item]; !ok {
diff = append(diff, item)
}
}

return diff
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ spec:
description: |-
AllowedIPV4Addresses specifies additional IPV4 addresses aside from the worker nodes
that should be permitted to reach the K8s API server
Per the Linode API:
Must contain only valid IPv4 addresses or networks (both must be in ip/mask format)
items:
type: string
type: array
Expand All @@ -109,6 +111,51 @@ spec:
description: FirewallID is the ID of the Cloud Firewall.
type: integer
type: object
controlPlaneFirewallRef:
description: ControlPlaneFirewallRef is a reference to the Firewall
for the control plane nodes.
properties:
apiVersion:
description: API version of the referent.
type: string
fieldPath:
description: |-
If referring to a piece of an object instead of an entire object, this string
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
For example, if the object reference is to a container within a pod, this would take on a value like:
"spec.containers{name}" (where "name" refers to the name of the container that triggered
the event) or if no container name is specified "spec.containers[2]" (container with
index 2 in this pod). This syntax is chosen only to have some well-defined way of
referencing a part of an object.
TODO: this design is not final and this field is subject to change in the future.
type: string
kind:
description: |-
Kind of the referent.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
name:
description: |-
Name of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
type: string
namespace:
description: |-
Namespace of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
type: string
resourceVersion:
description: |-
Specific resourceVersion to which this reference is made, if any.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
type: string
uid:
description: |-
UID of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
type: string
type: object
x-kubernetes-map-type: atomic
credentialsRef:
description: |-
CredentialsRef is a reference to a Secret that contains the credentials to use for provisioning this cluster. If not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ spec:
description: |-
AllowedIPV4Addresses specifies additional IPV4 addresses aside from the worker nodes
that should be permitted to reach the K8s API server
Per the Linode API:
Must contain only valid IPv4 addresses or networks (both must be in ip/mask format)
items:
type: string
type: array
Expand All @@ -103,6 +105,51 @@ spec:
description: FirewallID is the ID of the Cloud Firewall.
type: integer
type: object
controlPlaneFirewallRef:
description: ControlPlaneFirewallRef is a reference to the
Firewall for the control plane nodes.
properties:
apiVersion:
description: API version of the referent.
type: string
fieldPath:
description: |-
If referring to a piece of an object instead of an entire object, this string
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
For example, if the object reference is to a container within a pod, this would take on a value like:
"spec.containers{name}" (where "name" refers to the name of the container that triggered
the event) or if no container name is specified "spec.containers[2]" (container with
index 2 in this pod). This syntax is chosen only to have some well-defined way of
referencing a part of an object.
TODO: this design is not final and this field is subject to change in the future.
type: string
kind:
description: |-
Kind of the referent.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
name:
description: |-
Name of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
type: string
namespace:
description: |-
Namespace of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
type: string
resourceVersion:
description: |-
Specific resourceVersion to which this reference is made, if any.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
type: string
uid:
description: |-
UID of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
type: string
type: object
x-kubernetes-map-type: atomic
credentialsRef:
description: |-
CredentialsRef is a reference to a Secret that contains the credentials to use for provisioning this cluster. If not
Expand Down
26 changes: 0 additions & 26 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,32 +68,6 @@ rules:
- get
- patch
- update
- apiGroups:
- infrastructure.cluster.x-k8s.io
resources:
- linodefirewalls
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- infrastructure.cluster.x-k8s.io
resources:
- linodefirewalls/finalizers
verbs:
- update
- apiGroups:
- infrastructure.cluster.x-k8s.io
resources:
- linodefirewalls/status
verbs:
- get
- patch
- update
- apiGroups:
- infrastructure.cluster.x-k8s.io
resources:
Expand Down
Loading

0 comments on commit 4e4f307

Please sign in to comment.