fix: skip port deletion when instances have no port (#779)

diff --git a/images/Earthfile b/images/Earthfile
new file mode 100644
index 0000000..c87a870
--- /dev/null
+++ b/images/Earthfile
@@ -0,0 +1,10 @@
+VERSION 0.7
+
+APT_INSTALL:
+  COMMAND
+  ARG packages
+  RUN \
+    apt-get update && \
+    apt-get install --no-install-recommends -y ${packages} && \
+    apt-get clean && \
+    rm -rf /var/lib/apt/lists/*
diff --git a/images/base/Earthfile b/images/base/Earthfile
new file mode 100644
index 0000000..c075abc
--- /dev/null
+++ b/images/base/Earthfile
@@ -0,0 +1,4 @@
+VERSION 0.7
+
+image:
+  FROM ubuntu:jammy
diff --git a/images/builder/Earthfile b/images/builder/Earthfile
new file mode 100644
index 0000000..b7025b4
--- /dev/null
+++ b/images/builder/Earthfile
@@ -0,0 +1,7 @@
+VERSION 0.7
+
+image:
+  FROM ../base+image
+  DO ../+APT_INSTALL --packages "build-essential git python3-dev python3-pip python3-venv"
+  ARG POETRY_VERSION=1.4.2
+  RUN pip3 install --no-cache-dir poetry==${POETRY_VERSION}
diff --git a/images/cluster-api-provider-openstack/Earthfile b/images/cluster-api-provider-openstack/Earthfile
new file mode 100644
index 0000000..7a03308
--- /dev/null
+++ b/images/cluster-api-provider-openstack/Earthfile
@@ -0,0 +1,17 @@
+VERSION 0.7
+
+ARG --global CAPO_VERSION=v0.8.0
+ARG --global EPOCH=1
+
+clone:
+  FROM ../builder+image
+  GIT CLONE --branch ${CAPO_VERSION} https://github.com/kubernetes-sigs/cluster-api-provider-openstack /workspace/src
+  WORKDIR /workspace/src
+  COPY patches /workspace/patches
+  RUN git apply --verbose /workspace/patches/*.patch
+  SAVE ARTIFACT /workspace/src
+
+image:
+  FROM DOCKERFILE -f +clone/src/Dockerfile +clone/src/*
+  LABEL org.opencontainers.image.source=https://github.com/vexxhost/atmosphere
+  SAVE IMAGE --push ghcr.io/vexxhost/atmosphere/cluster-api-provider-openstack:${CAPO_VERSION}-${EPOCH}
diff --git a/images/cluster-api-provider-openstack/patches/0000-fix-skip-port-deletion-when-instances-have-no-port.patch b/images/cluster-api-provider-openstack/patches/0000-fix-skip-port-deletion-when-instances-have-no-port.patch
new file mode 100644
index 0000000..6174898
--- /dev/null
+++ b/images/cluster-api-provider-openstack/patches/0000-fix-skip-port-deletion-when-instances-have-no-port.patch
@@ -0,0 +1,24 @@
+From 294b2d3ca34f7d327da3b27bd07edde7f5bbac43 Mon Sep 17 00:00:00 2001
+From: okozachenko <okozachenko1203@users.noreply.github.com>
+Date: Tue, 19 Dec 2023 01:07:36 +1100
+Subject: [PATCH] fix: skip port deletion when instances have no port
+
+---
+ pkg/cloud/services/networking/port.go | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/pkg/cloud/services/networking/port.go b/pkg/cloud/services/networking/port.go
+index 4c213851f8..84b9bfc618 100644
+--- a/pkg/cloud/services/networking/port.go
++++ b/pkg/cloud/services/networking/port.go
+@@ -315,6 +315,10 @@ func (s *Service) GarbageCollectErrorInstancesPort(eventObject runtime.Object, i
+ 			return fmt.Errorf("garbage collection of port %s failed, found %d ports with the same name", portName, len(portList))
+ 		}
+ 
++		if len(portList) == 0 {
++			continue
++		}
++
+ 		if err := s.DeletePort(eventObject, portList[0].ID); err != nil {
+ 			return err
+ 		}