diff --git a/api/v1alpha2/linodemachine_types.go b/api/v1alpha2/linodemachine_types.go
index 05869a332..c103c4811 100644
--- a/api/v1alpha2/linodemachine_types.go
+++ b/api/v1alpha2/linodemachine_types.go
@@ -67,7 +67,7 @@ type LinodeMachineSpec struct {
PrivateIP *bool `json:"privateIP,omitempty"`
// Tags is a list of tags to apply to the Linode instance.
Tags []string `json:"tags,omitempty"`
- // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
+ // FirewallID is the id of the cloud firewall to apply to the Linode Instance
FirewallID int `json:"firewallID,omitempty"`
// OSDisk is configuration for the root disk that includes the OS,
// if not specified this defaults to whatever space is not taken up by the DataDisks
diff --git a/api/v1alpha2/linodemachinetemplate_types.go b/api/v1alpha2/linodemachinetemplate_types.go
index 7701de399..a67064d36 100644
--- a/api/v1alpha2/linodemachinetemplate_types.go
+++ b/api/v1alpha2/linodemachinetemplate_types.go
@@ -33,6 +33,10 @@ type LinodeMachineTemplateStatus struct {
// +optional
Tags []string `json:"tags,omitempty"`
+ // Firewall ID that is currently applied to the LinodeMachineTemplate.
+ // +optional
+ FirewallID int `json:"firewallID,omitempty"`
+
// Conditions represent the latest available observations of a LinodeMachineTemplate's current state.
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
diff --git a/clients/clients.go b/clients/clients.go
index 234450642..6b6a50507 100644
--- a/clients/clients.go
+++ b/clients/clients.go
@@ -55,6 +55,8 @@ type LinodeInstanceClient interface {
GetRegion(ctx context.Context, regionID string) (*linodego.Region, error)
GetImage(ctx context.Context, imageID string) (*linodego.Image, error)
GetType(ctx context.Context, typeID string) (*linodego.LinodeType, error)
+ ListInstanceFirewalls(ctx context.Context, linodeID int, opts *linodego.ListOptions) ([]linodego.Firewall, error)
+ UpdateInstanceFirewalls(ctx context.Context, linodeID int, opts linodego.InstanceFirewallUpdateOptions) ([]linodego.Firewall, error)
}
// LinodeVPCClient defines the methods that interact with Linode's VPC service.
diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_linodemachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_linodemachines.yaml
index aaaefa4a5..32a92ace3 100644
--- a/config/crd/bases/infrastructure.cluster.x-k8s.io_linodemachines.yaml
+++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_linodemachines.yaml
@@ -164,10 +164,9 @@ spec:
- message: Value is immutable
rule: self == oldSelf
firewallID:
+ description: FirewallID is the id of the cloud firewall to apply to
+ the Linode Instance
type: integer
- x-kubernetes-validations:
- - message: Value is immutable
- rule: self == oldSelf
firewallRef:
description: FirewallRef is a reference to a firewall object. This
makes the linode use the specified firewall.
diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_linodemachinetemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_linodemachinetemplates.yaml
index 9c674af88..52efc9f41 100644
--- a/config/crd/bases/infrastructure.cluster.x-k8s.io_linodemachinetemplates.yaml
+++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_linodemachinetemplates.yaml
@@ -154,10 +154,9 @@ spec:
- message: Value is immutable
rule: self == oldSelf
firewallID:
+ description: FirewallID is the id of the cloud firewall to
+ apply to the Linode Instance
type: integer
- x-kubernetes-validations:
- - message: Value is immutable
- rule: self == oldSelf
firewallRef:
description: FirewallRef is a reference to a firewall object.
This makes the linode use the specified firewall.
@@ -541,6 +540,9 @@ spec:
- type
type: object
type: array
+ firewallID:
+ description: Firewall ID that is currently applied to the LinodeMachineTemplate.
+ type: integer
tags:
description: tags that are currently applied to the LinodeMachineTemplate.
items:
diff --git a/docs/src/reference/out.md b/docs/src/reference/out.md
index 2895d1ada..c9515e4e6 100644
--- a/docs/src/reference/out.md
+++ b/docs/src/reference/out.md
@@ -629,7 +629,7 @@ _Appears in:_
| `backupsEnabled` _boolean_ | | | |
| `privateIP` _boolean_ | | | |
| `tags` _string array_ | Tags is a list of tags to apply to the Linode instance. | | |
-| `firewallID` _integer_ | | | |
+| `firewallID` _integer_ | FirewallID is the id of the cloud firewall to apply to the Linode Instance | | |
| `osDisk` _[InstanceDisk](#instancedisk)_ | OSDisk is configuration for the root disk that includes the OS,
if not specified this defaults to whatever space is not taken up by the DataDisks | | |
| `dataDisks` _object (keys:string, values:[InstanceDisk](#instancedisk))_ | DataDisks is a map of any additional disks to add to an instance,
The sum of these disks + the OSDisk must not be more than allowed on a linodes plan | | |
| `diskEncryption` _string_ | DiskEncryption determines if the disks of the instance should be encrypted. The default is disabled. | | Enum: [enabled disabled]
|
@@ -755,6 +755,7 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `tags` _string array_ | tags that are currently applied to the LinodeMachineTemplate. | | |
+| `firewallID` _integer_ | Firewall ID that is currently applied to the LinodeMachineTemplate. | | |
| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#condition-v1-meta) array_ | Conditions represent the latest available observations of a LinodeMachineTemplate's current state. | | |
diff --git a/e2e/linodemachine-controller/linodemachine-vpcref-integration/chainsaw-test.yaml b/e2e/linodemachine-controller/linodemachine-vpcref-integration/chainsaw-test.yaml
index 085638aa5..9d19f16b6 100755
--- a/e2e/linodemachine-controller/linodemachine-vpcref-integration/chainsaw-test.yaml
+++ b/e2e/linodemachine-controller/linodemachine-vpcref-integration/chainsaw-test.yaml
@@ -43,6 +43,10 @@ spec:
- describe:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
kind: LinodeVPC
+ - podLogs:
+ namespace: capl-system
+ selector: control-plane=controller-manager
+ tail: 250
- name: Create LinodeMachine with VPCRef
try:
- apply:
@@ -56,6 +60,10 @@ spec:
- describe:
apiVersion: cluster.x-k8s.io/v1beta1
kind: Machine
+ - podLogs:
+ namespace: capl-system
+ selector: control-plane=controller-manager
+ tail: 250
- name: Check if the Linodes & VPC were created
try:
- script:
diff --git a/e2e/linodemachine-controller/minimal-linodemachine/chainsaw-test.yaml b/e2e/linodemachine-controller/minimal-linodemachine/chainsaw-test.yaml
index aa2ea1925..5c69f2437 100755
--- a/e2e/linodemachine-controller/minimal-linodemachine/chainsaw-test.yaml
+++ b/e2e/linodemachine-controller/minimal-linodemachine/chainsaw-test.yaml
@@ -31,6 +31,10 @@ spec:
- describe:
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
+ - podLogs:
+ namespace: capl-system
+ selector: control-plane=controller-manager
+ tail: 250
- name: Create LinodeMachine resource
try:
- apply:
@@ -41,6 +45,10 @@ spec:
- describe:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
kind: LinodeMachineTemplate
+ - podLogs:
+ namespace: capl-system
+ selector: control-plane=controller-manager
+ tail: 250
- describe:
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
diff --git a/go.mod b/go.mod
index fb61515bc..85f5420d0 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
github.com/go-logr/logr v1.4.3
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
- github.com/linode/linodego v1.53.1-0.20250709175023-9b152d30578c
+ github.com/linode/linodego v1.53.1-0.20250728194520-172cba1c457a
github.com/onsi/ginkgo/v2 v2.23.4
github.com/onsi/gomega v1.38.0
github.com/stretchr/testify v1.10.0
@@ -64,7 +64,7 @@ require (
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
- golang.org/x/sync v0.15.0 // indirect
+ golang.org/x/sync v0.16.0 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
k8s.io/apiserver v0.33.0 // indirect
k8s.io/component-base v0.33.0 // indirect
@@ -130,11 +130,11 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/ratelimit v0.2.0 // indirect
go.uber.org/zap v1.27.0 // indirect
- golang.org/x/net v0.41.0 // indirect
+ golang.org/x/net v0.42.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
- golang.org/x/sys v0.33.0 // indirect
- golang.org/x/term v0.32.0 // indirect
- golang.org/x/text v0.26.0 // indirect
+ golang.org/x/sys v0.34.0 // indirect
+ golang.org/x/term v0.33.0 // indirect
+ golang.org/x/text v0.27.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.34.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
diff --git a/go.sum b/go.sum
index e132d27fb..bcc169f9f 100644
--- a/go.sum
+++ b/go.sum
@@ -177,8 +177,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/linode/linodego v1.53.1-0.20250709175023-9b152d30578c h1:WlZm+YNHBuphycMZG2s2+F04hx2wx1ShuOwPAIInjP8=
-github.com/linode/linodego v1.53.1-0.20250709175023-9b152d30578c/go.mod h1:bI949fZaVchjWyKIA08hNyvAcV6BAS+PM2op3p7PAWA=
+github.com/linode/linodego v1.53.1-0.20250728194520-172cba1c457a h1:5PaGcDTgxlOZOaYNChSKHnzZp4oKFvzqEn8TQ7hv2Pg=
+github.com/linode/linodego v1.53.1-0.20250728194520-172cba1c457a/go.mod h1:VHlFAbhj18634Cd7B7L5D723kFKFQMOxzIutSMcWsB4=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
@@ -333,8 +333,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
-golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
+golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
+golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -346,30 +346,30 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
-golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
+golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
+golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
-golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
-golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
-golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
+golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
+golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
+golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
-golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
+golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
+golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/internal/controller/linodemachine_controller.go b/internal/controller/linodemachine_controller.go
index 6405f3099..d4e36363b 100644
--- a/internal/controller/linodemachine_controller.go
+++ b/internal/controller/linodemachine_controller.go
@@ -772,6 +772,11 @@ func (r *LinodeMachineReconciler) reconcileUpdate(ctx context.Context, logger lo
}
}
+ res, err := r.reconcileFirewallID(ctx, logger, machineScope, instanceID)
+ if err != nil || !res.IsZero() {
+ return res, err
+ }
+
// Clean up bootstrap data after instance creation.
if linodeInstance.Status == linodego.InstanceRunning && machineScope.Machine.Status.Phase == "Running" {
if err := deleteBootstrapData(ctx, machineScope); err != nil {
@@ -782,6 +787,41 @@ func (r *LinodeMachineReconciler) reconcileUpdate(ctx context.Context, logger lo
return ctrl.Result{}, nil
}
+func (r *LinodeMachineReconciler) reconcileFirewallID(ctx context.Context, logger logr.Logger, machineScope *scope.MachineScope, instanceID int) (ctrl.Result, error) {
+ // get the instance's firewalls
+ firewalls, err := machineScope.LinodeClient.ListInstanceFirewalls(ctx, instanceID, nil)
+ if err != nil {
+ logger.Error(err, "Failed to list firewalls for Linode instance")
+ return ctrl.Result{RequeueAfter: reconciler.DefaultMachineControllerWaitForRunningDelay}, nil
+ }
+
+ attachedFWIDs := make([]int, 0, len(firewalls))
+ for _, fw := range firewalls {
+ attachedFWIDs = append(attachedFWIDs, fw.ID)
+ }
+
+ var desiredFWIDs []int
+ if machineScope.LinodeMachine.Spec.FirewallID != 0 {
+ desiredFWIDs = []int{machineScope.LinodeMachine.Spec.FirewallID}
+ } else {
+ desiredFWIDs = []int{}
+ }
+
+ // update the firewallID if needed.
+ if !slices.Equal(attachedFWIDs, desiredFWIDs) {
+ _, err := machineScope.LinodeClient.UpdateInstanceFirewalls(ctx, instanceID,
+ linodego.InstanceFirewallUpdateOptions{
+ FirewallIDs: desiredFWIDs,
+ },
+ )
+ if err != nil {
+ logger.Error(err, "Failed to update firewalls for Linode instance")
+ return ctrl.Result{}, err
+ }
+ }
+ return ctrl.Result{}, nil
+}
+
func (r *LinodeMachineReconciler) reconcileDelete(
ctx context.Context,
logger logr.Logger,
diff --git a/internal/controller/linodemachine_controller_test.go b/internal/controller/linodemachine_controller_test.go
index f01fbcfe0..0a8a0ad3c 100644
--- a/internal/controller/linodemachine_controller_test.go
+++ b/internal/controller/linodemachine_controller_test.go
@@ -1733,6 +1733,8 @@ var _ = Describe("machine-update", Ordered, Label("machine", "machine-update"),
Status: linodego.InstanceProvisioning,
Updated: util.Pointer(time.Now()),
}, nil)
+ mck.LinodeClient.EXPECT().ListInstanceFirewalls(ctx, 11111, nil).Return(
+ []linodego.Firewall{}, nil)
}),
Result("machine status updated", func(ctx context.Context, mck Mock) {
linodeMachine.Spec.ProviderID = util.Pointer("linode://11111")
@@ -1777,6 +1779,8 @@ var _ = Describe("machine-update", Ordered, Label("machine", "machine-update"),
Status: linodego.InstanceRunning,
Updated: util.Pointer(time.Now()),
}, nil)
+ mck.LinodeClient.EXPECT().ListInstanceFirewalls(ctx, 11111, nil).Return(
+ []linodego.Firewall{}, nil)
}),
Result("machine tag is updated", func(ctx context.Context, mck Mock) {
linodeMachine.Spec.ProviderID = util.Pointer("linode://11111")
@@ -1787,6 +1791,178 @@ var _ = Describe("machine-update", Ordered, Label("machine", "machine-update"),
Expect(linodeMachine.Status.Tags).To(Equal([]string{"test-tag"}))
}),
),
+ Path(
+ Call("machine firewall is updated", func(ctx context.Context, mck Mock) {
+ mck.LinodeClient.EXPECT().GetInstance(ctx, 11111).Return(
+ &linodego.Instance{
+ ID: 11111,
+ IPv4: []*net.IP{ptr.To(net.IPv4(192, 168, 0, 2))},
+ IPv6: "fd00::",
+ Tags: []string{"test-cluster-2"},
+ Status: linodego.InstanceRunning,
+ Updated: util.Pointer(time.Now()),
+ }, nil)
+ mck.LinodeClient.EXPECT().UpdateInstance(ctx, 11111, gomock.Any()).Return(
+ &linodego.Instance{
+ ID: 11111,
+ IPv4: []*net.IP{ptr.To(net.IPv4(192, 168, 0, 2))},
+ IPv6: "fd00::",
+ Tags: []string{"test-cluster-2", "test-tag"},
+ Status: linodego.InstanceRunning,
+ Updated: util.Pointer(time.Now()),
+ }, nil)
+ mck.LinodeClient.EXPECT().ListInstanceFirewalls(ctx, 11111, nil).Return(
+ []linodego.Firewall{
+ {ID: 5}, // Instance currently has firewall ID 5
+ }, nil)
+ mck.LinodeClient.EXPECT().UpdateInstanceFirewalls(ctx, 11111, linodego.InstanceFirewallUpdateOptions{
+ FirewallIDs: []int{10}, // Update to firewall ID 10
+ }).Return(nil, nil)
+ }),
+ Result("machine firewall is updated", func(ctx context.Context, mck Mock) {
+ linodeMachine.Spec.FirewallID = 10 // Set new firewall ID
+ _, err := reconciler.reconcile(ctx, logr.Logger{}, mScope)
+ Expect(err).NotTo(HaveOccurred())
+ }),
+ ),
+ Path(
+ Call("machine firewall update applied when multiple firewall already attached", func(ctx context.Context, mck Mock) {
+ mck.LinodeClient.EXPECT().GetInstance(ctx, 11111).Return(
+ &linodego.Instance{
+ ID: 11111,
+ IPv4: []*net.IP{ptr.To(net.IPv4(192, 168, 0, 2))},
+ IPv6: "fd00::",
+ Tags: []string{"test-cluster-2"},
+ Status: linodego.InstanceRunning,
+ Updated: util.Pointer(time.Now()),
+ }, nil)
+ mck.LinodeClient.EXPECT().UpdateInstance(ctx, 11111, gomock.Any()).Return(
+ &linodego.Instance{
+ ID: 11111,
+ IPv4: []*net.IP{ptr.To(net.IPv4(192, 168, 0, 2))},
+ IPv6: "fd00::",
+ Tags: []string{"test-cluster-2", "test-tag"},
+ Status: linodego.InstanceRunning,
+ Updated: util.Pointer(time.Now()),
+ }, nil)
+ mck.LinodeClient.EXPECT().ListInstanceFirewalls(ctx, 11111, nil).Return(
+ []linodego.Firewall{
+ {ID: 10}, // Instance already has the desired firewall ID 10
+ {ID: 15}, // Additional firewall
+ }, nil)
+ mck.LinodeClient.EXPECT().UpdateInstanceFirewalls(ctx, 11111, linodego.InstanceFirewallUpdateOptions{
+ FirewallIDs: []int{10}, // Update to firewall ID 10
+ }).Return(nil, nil)
+ }),
+ Result("machine firewall update skipped when firewall already attached", func(ctx context.Context, mck Mock) {
+ linodeMachine.Spec.FirewallID = 10 // Firewall ID already attached
+ _, err := reconciler.reconcile(ctx, logr.Logger{}, mScope)
+ Expect(err).NotTo(HaveOccurred())
+ }),
+ ),
+ Path(
+ Call("machine firewall update called even when FirewallID is zero", func(ctx context.Context, mck Mock) {
+ mck.LinodeClient.EXPECT().GetInstance(ctx, 11111).Return(
+ &linodego.Instance{
+ ID: 11111,
+ IPv4: []*net.IP{ptr.To(net.IPv4(192, 168, 0, 2))},
+ IPv6: "fd00::",
+ Tags: []string{"test-cluster-2"},
+ Status: linodego.InstanceRunning,
+ Updated: util.Pointer(time.Now()),
+ }, nil)
+ mck.LinodeClient.EXPECT().UpdateInstance(ctx, 11111, gomock.Any()).Return(
+ &linodego.Instance{
+ ID: 11111,
+ IPv4: []*net.IP{ptr.To(net.IPv4(192, 168, 0, 2))},
+ IPv6: "fd00::",
+ Tags: []string{"test-cluster-2", "test-tag"},
+ Status: linodego.InstanceRunning,
+ Updated: util.Pointer(time.Now()),
+ }, nil)
+ mck.LinodeClient.EXPECT().ListInstanceFirewalls(ctx, 11111, nil).Return(
+ []linodego.Firewall{
+ {ID: 5}, // Instance has existing firewall
+ }, nil)
+ // UpdateInstanceFirewalls WILL be called since 0 is not in the attachedFirewalls list
+ mck.LinodeClient.EXPECT().UpdateInstanceFirewalls(ctx, 11111, linodego.InstanceFirewallUpdateOptions{
+ FirewallIDs: []int{},
+ }).Return(nil, nil)
+ }),
+ Result("machine firewall gets cleared when firewallID is set to 0", func(ctx context.Context, mck Mock) {
+ linodeMachine.Spec.FirewallID = 0
+ _, err := reconciler.reconcile(ctx, logr.Logger{}, mScope)
+ Expect(err).NotTo(HaveOccurred())
+ }),
+ ),
+ OneOf(
+ Path(
+ Call("machine firewall list fails", func(ctx context.Context, mck Mock) {
+ mck.LinodeClient.EXPECT().GetInstance(ctx, 11111).Return(
+ &linodego.Instance{
+ ID: 11111,
+ IPv4: []*net.IP{ptr.To(net.IPv4(192, 168, 0, 2))},
+ IPv6: "fd00::",
+ Tags: []string{"test-cluster-2"},
+ Status: linodego.InstanceRunning,
+ Updated: util.Pointer(time.Now()),
+ }, nil)
+ mck.LinodeClient.EXPECT().UpdateInstance(ctx, 11111, gomock.Any()).Return(
+ &linodego.Instance{
+ ID: 11111,
+ IPv4: []*net.IP{ptr.To(net.IPv4(192, 168, 0, 2))},
+ IPv6: "fd00::",
+ Tags: []string{"test-cluster-2", "test-tag"},
+ Status: linodego.InstanceRunning,
+ Updated: util.Pointer(time.Now()),
+ }, nil)
+ mck.LinodeClient.EXPECT().ListInstanceFirewalls(ctx, 11111, nil).Return(
+ nil, &linodego.Error{Code: http.StatusInternalServerError})
+ }),
+ Result("machine firewall list error requeues", func(ctx context.Context, mck Mock) {
+ linodeMachine.Spec.FirewallID = 10
+ res, err := reconciler.reconcile(ctx, mck.Logger(), mScope)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(res.RequeueAfter).To(Equal(rutil.DefaultMachineControllerWaitForRunningDelay))
+ Expect(mck.Logs()).To(ContainSubstring("Failed to list firewalls for Linode instance"))
+ }),
+ ),
+ Path(
+ Call("machine firewall update fails", func(ctx context.Context, mck Mock) {
+ mck.LinodeClient.EXPECT().GetInstance(ctx, 11111).Return(
+ &linodego.Instance{
+ ID: 11111,
+ IPv4: []*net.IP{ptr.To(net.IPv4(192, 168, 0, 2))},
+ IPv6: "fd00::",
+ Tags: []string{"test-cluster-2"},
+ Status: linodego.InstanceRunning,
+ Updated: util.Pointer(time.Now()),
+ }, nil)
+ mck.LinodeClient.EXPECT().UpdateInstance(ctx, 11111, gomock.Any()).Return(
+ &linodego.Instance{
+ ID: 11111,
+ IPv4: []*net.IP{ptr.To(net.IPv4(192, 168, 0, 2))},
+ IPv6: "fd00::",
+ Tags: []string{"test-cluster-2", "test-tag"},
+ Status: linodego.InstanceRunning,
+ Updated: util.Pointer(time.Now()),
+ }, nil)
+ mck.LinodeClient.EXPECT().ListInstanceFirewalls(ctx, 11111, nil).Return(
+ []linodego.Firewall{
+ {ID: 5}, // Instance currently has firewall ID 5
+ }, nil)
+ mck.LinodeClient.EXPECT().UpdateInstanceFirewalls(ctx, 11111, linodego.InstanceFirewallUpdateOptions{
+ FirewallIDs: []int{10},
+ }).Return(nil, &linodego.Error{Code: http.StatusBadRequest})
+ }),
+ Result("machine firewall update error requeues", func(ctx context.Context, mck Mock) {
+ linodeMachine.Spec.FirewallID = 10
+ _, err := reconciler.reconcile(ctx, mck.Logger(), mScope)
+ Expect(err).To(HaveOccurred())
+ Expect(mck.Logs()).To(ContainSubstring("Failed to update firewalls for Linode instance"))
+ }),
+ ),
+ ),
)
})
diff --git a/internal/controller/linodemachinetemplate_controller.go b/internal/controller/linodemachinetemplate_controller.go
index f28b85eaf..dfad855f0 100644
--- a/internal/controller/linodemachinetemplate_controller.go
+++ b/internal/controller/linodemachinetemplate_controller.go
@@ -129,10 +129,20 @@ func (lmtr *LinodeMachineTemplateReconciler) reconcile(ctx context.Context, lmtS
if !slices.Equal(lmtScope.LinodeMachineTemplate.Spec.Template.Spec.Tags, lmtScope.LinodeMachineTemplate.Status.Tags) {
err := lmtr.reconcileTags(ctx, lmtScope.LinodeMachineTemplate, &machine)
if err != nil {
- lmtr.Logger.Error(err, "Failed to add tags to LinodeMachine", "template", lmtScope.LinodeMachineTemplate.Name, "machine", machine.Name)
+ lmtr.Logger.Error(err, "Failed to update tags on LinodeMachine", "template", lmtScope.LinodeMachineTemplate.Name, "machine", machine.Name)
outErr = errors.Join(outErr, err)
+ failureReason = "FailedToPatchLinodeMachine"
+ return ctrl.Result{}, outErr
+ }
+ }
+ if lmtScope.LinodeMachineTemplate.Spec.Template.Spec.FirewallID != lmtScope.LinodeMachineTemplate.Status.FirewallID {
+ err := lmtr.reconcileFirewallID(ctx, lmtScope.LinodeMachineTemplate, &machine)
+ if err != nil {
+ lmtr.Logger.Error(err, "Failed to update FirewallID on LinodeMachine", "template", lmtScope.LinodeMachineTemplate.Name, "machine", machine.Name)
+ outErr = errors.Join(outErr, err)
failureReason = "FailedToPatchLinodeMachine"
+ return ctrl.Result{}, outErr
}
}
}
@@ -142,9 +152,10 @@ func (lmtr *LinodeMachineTemplateReconciler) reconcile(ctx context.Context, lmtS
return ctrl.Result{}, nil
}
- // update the LMT status.tags if all the linodeMachines spec.tags is successfully updated.
+ // update the LMT status if all the linodeMachines are successfully updated.
if outErr == nil {
lmtScope.LinodeMachineTemplate.Status.Tags = slices.Clone(lmtScope.LinodeMachineTemplate.Spec.Template.Spec.Tags)
+ lmtScope.LinodeMachineTemplate.Status.FirewallID = lmtScope.LinodeMachineTemplate.Spec.Template.Spec.FirewallID
lmtr.Logger.Info("Successfully reconciled LinodeMachineTemplate", "name", lmtScope.LinodeMachineTemplate.Name)
} else {
lmtr.Logger.Error(outErr, "Error in reconciling LinodeMachineTemplate, retrying..", "name", lmtScope.LinodeMachineTemplate.Name)
@@ -159,11 +170,23 @@ func (lmtr *LinodeMachineTemplateReconciler) reconcileTags(ctx context.Context,
}
machine.Spec.Tags = lmt.Spec.Template.Spec.Tags
-
if err := helper.Patch(ctx, machine); err != nil {
return fmt.Errorf("failed to patch LinodeMachine %s with new tags: %w", machine.Name, err)
}
lmtr.Logger.Info("Patched LinodeMachine with new tags", "machine", machine.Name, "tags", lmt.Spec.Template.Spec.Tags)
+
+ return nil
+}
+
+func (lmtr *LinodeMachineTemplateReconciler) reconcileFirewallID(ctx context.Context, lmt *infrav1alpha2.LinodeMachineTemplate, machine *infrav1alpha2.LinodeMachine) error {
+ helper, err := patch.NewHelper(machine, lmtr.Client)
+ if err != nil {
+ return fmt.Errorf("failed to init patch helper: %w", err)
+ }
+ machine.Spec.FirewallID = lmt.Spec.Template.Spec.FirewallID
+ if err := helper.Patch(ctx, machine); err != nil {
+ return fmt.Errorf("failed to patch LinodeMachine %s with new firewallID: %w", machine.Name, err)
+ }
return nil
}
diff --git a/internal/controller/linodemachinetemplate_controller_test.go b/internal/controller/linodemachinetemplate_controller_test.go
index 94df095f8..092921bfd 100644
--- a/internal/controller/linodemachinetemplate_controller_test.go
+++ b/internal/controller/linodemachinetemplate_controller_test.go
@@ -78,6 +78,35 @@ var _ = Describe("lifecycle", Ordered, Label("LinodeMachineTemplateReconciler",
Tags: []string{"test-tag1"},
},
},
+ {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "machine-template-with-firewall-id",
+ Namespace: "default",
+ },
+ Spec: infrav1alpha2.LinodeMachineTemplateSpec{
+ Template: infrav1alpha2.LinodeMachineTemplateResource{
+ Spec: infrav1alpha2.LinodeMachineSpec{
+ FirewallID: 12345,
+ },
+ },
+ },
+ },
+ {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "machine-template-no-firewall-change",
+ Namespace: "default",
+ },
+ Spec: infrav1alpha2.LinodeMachineTemplateSpec{
+ Template: infrav1alpha2.LinodeMachineTemplateResource{
+ Spec: infrav1alpha2.LinodeMachineSpec{
+ FirewallID: 67890,
+ },
+ },
+ },
+ Status: infrav1alpha2.LinodeMachineTemplateStatus{
+ FirewallID: 67890,
+ },
+ },
}
linodeMachines := []infrav1alpha2.LinodeMachine{
@@ -99,6 +128,24 @@ var _ = Describe("lifecycle", Ordered, Label("LinodeMachineTemplateReconciler",
},
},
},
+ {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "machine-3",
+ Namespace: "default",
+ Annotations: map[string]string{
+ clusterv1.TemplateClonedFromNameAnnotation: "machine-template-with-firewall-id",
+ },
+ },
+ },
+ {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "machine-4",
+ Namespace: "default",
+ Annotations: map[string]string{
+ clusterv1.TemplateClonedFromNameAnnotation: "machine-template-no-firewall-change",
+ },
+ },
+ },
}
BeforeAll(func(ctx SpecContext) {
@@ -198,6 +245,54 @@ var _ = Describe("lifecycle", Ordered, Label("LinodeMachineTemplateReconciler",
Expect(mck.Logs()).NotTo(ContainSubstring("Patched LinodeMachine with new tags"))
}),
),
+ Path(
+ Call("machine template update firewall ID", func(ctx context.Context, mck Mock) {}),
+ Result("success", func(ctx context.Context, mck Mock) {
+ lmtScope, _ := scope.NewMachineTemplateScope(
+ ctx,
+ scope.MachineTemplateScopeParams{
+ Client: k8sClient,
+ LinodeMachineTemplate: &machineTemplates[3],
+ },
+ )
+ reconciler = LinodeMachineTemplateReconciler{
+ Logger: mck.Logger(),
+ Client: k8sClient,
+ }
+
+ res, err := reconciler.reconcile(ctx, lmtScope)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(res).To(Equal(ctrl.Result{}))
+
+ // get the updated machineTemplate
+ updatedMachineTemplate := &infrav1alpha2.LinodeMachineTemplate{}
+ Expect(k8sClient.Get(ctx, client.ObjectKey{
+ Name: machineTemplates[3].Name,
+ Namespace: machineTemplates[3].Namespace,
+ }, updatedMachineTemplate)).To(Succeed())
+ Expect(updatedMachineTemplate.Status.FirewallID).To(Equal(updatedMachineTemplate.Spec.Template.Spec.FirewallID))
+ }),
+ ),
+ Path(
+ Call("machine template no firewall ID update", func(ctx context.Context, mck Mock) {}),
+ Result("success", func(ctx context.Context, mck Mock) {
+ patchHelper, err := patch.NewHelper(&machineTemplates[4], k8sClient)
+ Expect(err).NotTo(HaveOccurred())
+
+ lmtScope = scope.MachineTemplateScope{
+ PatchHelper: patchHelper,
+ LinodeMachineTemplate: &machineTemplates[4],
+ }
+ reconciler = LinodeMachineTemplateReconciler{
+ Logger: mck.Logger(),
+ Client: k8sClient,
+ }
+
+ res, err := reconciler.reconcile(ctx, &lmtScope)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(res).To(Equal(ctrl.Result{}))
+ }),
+ ),
),
)
diff --git a/mock/client.go b/mock/client.go
index f70305470..b6f1f4c34 100644
--- a/mock/client.go
+++ b/mock/client.go
@@ -696,6 +696,21 @@ func (mr *MockLinodeClientMockRecorder) ListInstanceConfigs(ctx, linodeID, opts
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListInstanceConfigs", reflect.TypeOf((*MockLinodeClient)(nil).ListInstanceConfigs), ctx, linodeID, opts)
}
+// ListInstanceFirewalls mocks base method.
+func (m *MockLinodeClient) ListInstanceFirewalls(ctx context.Context, linodeID int, opts *linodego.ListOptions) ([]linodego.Firewall, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "ListInstanceFirewalls", ctx, linodeID, opts)
+ ret0, _ := ret[0].([]linodego.Firewall)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// ListInstanceFirewalls indicates an expected call of ListInstanceFirewalls.
+func (mr *MockLinodeClientMockRecorder) ListInstanceFirewalls(ctx, linodeID, opts any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListInstanceFirewalls", reflect.TypeOf((*MockLinodeClient)(nil).ListInstanceFirewalls), ctx, linodeID, opts)
+}
+
// ListInstances mocks base method.
func (m *MockLinodeClient) ListInstances(ctx context.Context, opts *linodego.ListOptions) ([]linodego.Instance, error) {
m.ctrl.T.Helper()
@@ -886,6 +901,21 @@ func (mr *MockLinodeClientMockRecorder) UpdateInstanceConfig(ctx, linodeID, conf
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateInstanceConfig", reflect.TypeOf((*MockLinodeClient)(nil).UpdateInstanceConfig), ctx, linodeID, configID, opts)
}
+// UpdateInstanceFirewalls mocks base method.
+func (m *MockLinodeClient) UpdateInstanceFirewalls(ctx context.Context, linodeID int, opts linodego.InstanceFirewallUpdateOptions) ([]linodego.Firewall, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "UpdateInstanceFirewalls", ctx, linodeID, opts)
+ ret0, _ := ret[0].([]linodego.Firewall)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// UpdateInstanceFirewalls indicates an expected call of UpdateInstanceFirewalls.
+func (mr *MockLinodeClientMockRecorder) UpdateInstanceFirewalls(ctx, linodeID, opts any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateInstanceFirewalls", reflect.TypeOf((*MockLinodeClient)(nil).UpdateInstanceFirewalls), ctx, linodeID, opts)
+}
+
// UpdateObjectStorageBucketAccess mocks base method.
func (m *MockLinodeClient) UpdateObjectStorageBucketAccess(ctx context.Context, clusterOrRegionID, label string, opts linodego.ObjectStorageBucketUpdateAccessOptions) error {
m.ctrl.T.Helper()
@@ -1291,6 +1321,21 @@ func (mr *MockLinodeInstanceClientMockRecorder) ListInstanceConfigs(ctx, linodeI
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListInstanceConfigs", reflect.TypeOf((*MockLinodeInstanceClient)(nil).ListInstanceConfigs), ctx, linodeID, opts)
}
+// ListInstanceFirewalls mocks base method.
+func (m *MockLinodeInstanceClient) ListInstanceFirewalls(ctx context.Context, linodeID int, opts *linodego.ListOptions) ([]linodego.Firewall, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "ListInstanceFirewalls", ctx, linodeID, opts)
+ ret0, _ := ret[0].([]linodego.Firewall)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// ListInstanceFirewalls indicates an expected call of ListInstanceFirewalls.
+func (mr *MockLinodeInstanceClientMockRecorder) ListInstanceFirewalls(ctx, linodeID, opts any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListInstanceFirewalls", reflect.TypeOf((*MockLinodeInstanceClient)(nil).ListInstanceFirewalls), ctx, linodeID, opts)
+}
+
// ListInstances mocks base method.
func (m *MockLinodeInstanceClient) ListInstances(ctx context.Context, opts *linodego.ListOptions) ([]linodego.Instance, error) {
m.ctrl.T.Helper()
@@ -1350,6 +1395,21 @@ func (mr *MockLinodeInstanceClientMockRecorder) UpdateInstanceConfig(ctx, linode
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateInstanceConfig", reflect.TypeOf((*MockLinodeInstanceClient)(nil).UpdateInstanceConfig), ctx, linodeID, configID, opts)
}
+// UpdateInstanceFirewalls mocks base method.
+func (m *MockLinodeInstanceClient) UpdateInstanceFirewalls(ctx context.Context, linodeID int, opts linodego.InstanceFirewallUpdateOptions) ([]linodego.Firewall, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "UpdateInstanceFirewalls", ctx, linodeID, opts)
+ ret0, _ := ret[0].([]linodego.Firewall)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// UpdateInstanceFirewalls indicates an expected call of UpdateInstanceFirewalls.
+func (mr *MockLinodeInstanceClientMockRecorder) UpdateInstanceFirewalls(ctx, linodeID, opts any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateInstanceFirewalls", reflect.TypeOf((*MockLinodeInstanceClient)(nil).UpdateInstanceFirewalls), ctx, linodeID, opts)
+}
+
// MockLinodeVPCClient is a mock of LinodeVPCClient interface.
type MockLinodeVPCClient struct {
ctrl *gomock.Controller
diff --git a/observability/wrappers/linodeclient/linodeclient.gen.go b/observability/wrappers/linodeclient/linodeclient.gen.go
index bdb352d8e..2a8ff21e5 100644
--- a/observability/wrappers/linodeclient/linodeclient.gen.go
+++ b/observability/wrappers/linodeclient/linodeclient.gen.go
@@ -1144,6 +1144,32 @@ func (_d LinodeClientWithTracing) ListInstanceConfigs(ctx context.Context, linod
return _d.LinodeClient.ListInstanceConfigs(ctx, linodeID, opts)
}
+// ListInstanceFirewalls implements _sourceClients.LinodeClient
+func (_d LinodeClientWithTracing) ListInstanceFirewalls(ctx context.Context, linodeID int, opts *linodego.ListOptions) (fa1 []linodego.Firewall, err error) {
+ ctx, _span := tracing.Start(ctx, "_sourceClients.LinodeClient.ListInstanceFirewalls")
+ defer func() {
+ if _d._spanDecorator != nil {
+ _d._spanDecorator(_span, map[string]interface{}{
+ "ctx": ctx,
+ "linodeID": linodeID,
+ "opts": opts}, map[string]interface{}{
+ "fa1": fa1,
+ "err": err})
+ }
+
+ if err != nil {
+ _span.RecordError(err)
+ _span.SetAttributes(
+ attribute.String("event", "error"),
+ attribute.String("message", err.Error()),
+ )
+ }
+
+ _span.End()
+ }()
+ return _d.LinodeClient.ListInstanceFirewalls(ctx, linodeID, opts)
+}
+
// ListInstances implements _sourceClients.LinodeClient
func (_d LinodeClientWithTracing) ListInstances(ctx context.Context, opts *linodego.ListOptions) (ia1 []linodego.Instance, err error) {
ctx, _span := tracing.Start(ctx, "_sourceClients.LinodeClient.ListInstances")
@@ -1430,6 +1456,32 @@ func (_d LinodeClientWithTracing) UpdateInstanceConfig(ctx context.Context, lino
return _d.LinodeClient.UpdateInstanceConfig(ctx, linodeID, configID, opts)
}
+// UpdateInstanceFirewalls implements _sourceClients.LinodeClient
+func (_d LinodeClientWithTracing) UpdateInstanceFirewalls(ctx context.Context, linodeID int, opts linodego.InstanceFirewallUpdateOptions) (fa1 []linodego.Firewall, err error) {
+ ctx, _span := tracing.Start(ctx, "_sourceClients.LinodeClient.UpdateInstanceFirewalls")
+ defer func() {
+ if _d._spanDecorator != nil {
+ _d._spanDecorator(_span, map[string]interface{}{
+ "ctx": ctx,
+ "linodeID": linodeID,
+ "opts": opts}, map[string]interface{}{
+ "fa1": fa1,
+ "err": err})
+ }
+
+ if err != nil {
+ _span.RecordError(err)
+ _span.SetAttributes(
+ attribute.String("event", "error"),
+ attribute.String("message", err.Error()),
+ )
+ }
+
+ _span.End()
+ }()
+ return _d.LinodeClient.UpdateInstanceFirewalls(ctx, linodeID, opts)
+}
+
// UpdateObjectStorageBucketAccess implements _sourceClients.LinodeClient
func (_d LinodeClientWithTracing) UpdateObjectStorageBucketAccess(ctx context.Context, clusterOrRegionID string, label string, opts linodego.ObjectStorageBucketUpdateAccessOptions) (err error) {
ctx, _span := tracing.Start(ctx, "_sourceClients.LinodeClient.UpdateObjectStorageBucketAccess")