ci: add full-node tests (#920)

diff --git a/build/fetch-junit-xml.sh b/build/fetch-junit-xml.sh
new file mode 100755
index 0000000..785df28
--- /dev/null
+++ b/build/fetch-junit-xml.sh
@@ -0,0 +1,18 @@
+# Copyright (c) 2022 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+REPORT_FILE_NAME="${1:-report.xml}"
+
+sudo apt-get install -y subunit python3-junitxml
+sudo subunit-1to2 /tmp/stestr/0 | subunit2junitxml > "${REPORT_FILE_NAME}"
diff --git a/build/fetch-kubernetes-logs.sh b/build/fetch-kubernetes-logs.sh
new file mode 100755
index 0000000..5b028b5
--- /dev/null
+++ b/build/fetch-kubernetes-logs.sh
@@ -0,0 +1,78 @@
+#!/bin/bash -x
+
+# Check if an argument was provided
+if [ "$#" -ne 1 ]; then
+    echo "Usage: $0 <path_to_save_logs>"
+    exit 1
+fi
+
+# Define the base directory where you want to save the logs
+BASE_DIR="$1"
+
+# Create the base directory if it doesn't exist
+mkdir -p "$BASE_DIR"
+
+# Function to fetch logs for a pod and container
+fetch_logs() {
+    local ns="$1"
+    local pod="$2"
+    local container="$3"
+    local pod_dir="$BASE_DIR/$ns/$pod"
+    local log_file="$pod_dir/$container.log"
+    local prev_log_file="$pod_dir/${container}-previous.log"
+
+    # Ensure the pod directory exists
+    mkdir -p "$pod_dir"
+
+    # Fetch current logs
+    kubectl logs "$pod" -n "$ns" -c "$container" > "$log_file" 2>/dev/null
+
+    # Fetch previous logs if they exist
+    if kubectl logs "$pod" -n "$ns" -c "$container" --previous &>/dev/null; then
+        kubectl logs "$pod" -n "$ns" -c "$container" --previous > "$prev_log_file" 2>/dev/null
+    fi
+}
+
+export -f fetch_logs
+export BASE_DIR
+
+# Get all namespaces
+namespaces=$(kubectl get ns -o jsonpath='{.items[*].metadata.name}')
+
+# Loop through each namespace
+for ns in $namespaces; do
+    (
+        # Create a directory for the namespace
+        mkdir -p "$BASE_DIR/$ns"
+
+        # Get all pods in the namespace
+        pods=$(kubectl get pods -n "$ns" -o jsonpath='{.items[*].metadata.name}')
+
+        # Loop through each pod
+        for pod in $pods; do
+            (
+                # Create a directory for the pod
+                mkdir -p "$BASE_DIR/$ns/$pod"
+
+                # Get all containers in the pod
+                containers=$(kubectl get pod "$pod" -n "$ns" -o jsonpath='{.spec.containers[*].name}')
+
+                # Loop through each container
+                for container in $containers; do
+                    # Fetch logs in parallel
+                    fetch_logs "$ns" "$pod" "$container" &
+                done
+
+                # Wait for all background log fetches to complete before moving to the next pod
+                wait
+            ) &
+        done
+        # Wait for all background pods processing to complete before moving to the next namespace
+        wait
+    ) &
+done
+
+# Wait for all background namespaces processing to complete
+wait
+
+echo "Logs have been saved to $BASE_DIR"
diff --git a/build/pin-images.py b/build/pin-images.py
index 53363a3..83ae2d4 100755
--- a/build/pin-images.py
+++ b/build/pin-images.py
@@ -3,11 +3,11 @@
 import argparse
 import functools
 
+import requests
 from docker_image import reference
 from oslo_config import cfg
 from oslo_log import log as logging
 from ruyaml import YAML
-import requests
 
 LOG = logging.getLogger(__name__)
 CONF = cfg.CONF
@@ -46,7 +46,11 @@
 def get_pinned_image(image_src):
     image_ref = reference.Reference.parse(image_src)
 
-    if image_ref.domain() in ("registry.k8s.io", "us-docker.pkg.dev"):
+    if image_ref.domain() in (
+        "registry.k8s.io",
+        "us-docker.pkg.dev",
+        "registry.atmosphere.dev",
+    ):
         digest = get_digest(image_ref)
 
     if image_ref.domain() == "quay.io":
@@ -109,16 +113,30 @@
         "src", help="Path for default values file", type=argparse.FileType("r")
     )
     parser.add_argument("dst", help="Path for output file", type=argparse.FileType("w"))
+    parser.add_argument(
+        "-r",
+        "--registry",
+        default="ghcr.io/vexxhost/atmosphere",
+        help="Registry containing Atmosphere images",
+    )
 
     args = parser.parse_args()
 
+    registry = args.registry
+    if "registry.atmosphere.dev:5000" in registry:
+        registry = registry.replace(
+            "registry.atmosphere.dev:5000", "registry.atmosphere.dev"
+        )
+
     yaml = YAML(typ="rt")
     data = yaml.load(args.src)
 
     for image in data["_atmosphere_images"]:
         if image in SKIP_IMAGE_LIST:
             continue
-        image_src = data["_atmosphere_images"][image]
+        image_src = data["_atmosphere_images"][image].replace(
+            "ghcr.io/vexxhost/atmosphere", registry
+        )
         pinned_image = get_pinned_image(image_src)
 
         LOG.info("Pinning image %s from %s to %s", image, image_src, pinned_image)