From 7fbf89084d267d35e1fe761cc853328826fc00f4 Mon Sep 17 00:00:00 2001
From: Pablo Chacin <pablochacin@gmail.com>
Date: Sat, 2 Mar 2024 15:42:09 +0100
Subject: [PATCH 1/3] Add WithManifest option to k3s

Signed-off-by: Pablo Chacin <pablochacin@gmail.com>
---
 docs/modules/k3s.md             | 14 ++++++++++++++
 modules/k3s/k3s.go              | 31 +++++++++++++++++++++++++++++++
 modules/k3s/k3s_test.go         | 21 +++++++++++++++++++++
 modules/k3s/nginx-manifest.yaml | 14 ++++++++++++++
 4 files changed, 80 insertions(+)
 create mode 100644 modules/k3s/nginx-manifest.yaml

diff --git a/docs/modules/k3s.md b/docs/modules/k3s.md
index 3e448b8e45..c6edd0d255 100644
--- a/docs/modules/k3s.md
+++ b/docs/modules/k3s.md
@@ -49,6 +49,20 @@ for K3s. E.g. `testcontainers.WithImage("docker.io/rancher/k3s:v1.27.1-k3s1")`.
 
 {% include "../features/common_functional_options.md" %}
 
+## WithManifest
+
+The `WithManifest` option loads a manifest obtained from a local file into the cluster. K3s applies it automatically during the startup process
+
+```golang
+func WithManifest(manifestPath string) testcontainers.CustomizeRequestOption
+```
+
+Example:
+
+```golang
+        WithManifest("nginx-manifest.yaml")
+```
+
 ### Container Methods
 
 The K3s container exposes the following methods:
diff --git a/modules/k3s/k3s.go b/modules/k3s/k3s.go
index 0f7ccb4d16..f851ed92f9 100644
--- a/modules/k3s/k3s.go
+++ b/modules/k3s/k3s.go
@@ -29,6 +29,37 @@ type K3sContainer struct {
 	testcontainers.Container
 }
 
+// path to the k3s manifests directory
+const k3sManifests = "/var/lib/rancher/k3s/server/manifests/"
+
+// WithManifest loads the manifest into the cluster. K3s applies it automatically during the startup process
+func WithManifest(manifestPath string) testcontainers.CustomizeRequestOption {
+	return func(req *testcontainers.GenericContainerRequest) {
+		manifest := filepath.Base(manifestPath)
+		target := k3sManifests + manifest
+
+		// Add a post start hook to copy the manifest.
+		// We cannot use the Files option in the GenericRequest because the target
+		// path is created when the container starts
+		manifestHook := testcontainers.ContainerLifecycleHooks{
+			PostStarts: []testcontainers.ContainerHook{
+				func(ctx context.Context, c testcontainers.Container) error {
+					// if this hook is executed too soon after the container starts,
+					// the manifests directory may not exists yet, so we try to create it here
+					_, _, err := c.Exec(ctx, []string{"mkdir","-p", k3sManifests})
+					if err != nil {
+						return err
+					}
+
+					// copy the manifest
+					return c.CopyFileToContainer(ctx, manifest, target, 0x644)
+				},
+			},
+		}
+		req.LifecycleHooks = append(req.LifecycleHooks, manifestHook)
+	}
+}
+
 // RunContainer creates an instance of the K3s container type
 func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*K3sContainer, error) {
 	host, err := getContainerHost(ctx, opts...)
diff --git a/modules/k3s/k3s_test.go b/modules/k3s/k3s_test.go
index 65510c037c..2a91a335ec 100644
--- a/modules/k3s/k3s_test.go
+++ b/modules/k3s/k3s_test.go
@@ -12,6 +12,7 @@ import (
 
 	"github.com/testcontainers/testcontainers-go"
 	"github.com/testcontainers/testcontainers-go/modules/k3s"
+	"github.com/testcontainers/testcontainers-go/wait"
 )
 
 func Test_LoadImages(t *testing.T) {
@@ -161,3 +162,23 @@ func Test_APIServerReady(t *testing.T) {
 		t.Fatalf("failed to create pod %v", err)
 	}
 }
+
+func Test_WithManifestOption(t *testing.T) {
+	ctx := context.Background()
+
+	k3sContainer, err := k3s.RunContainer(ctx,
+		testcontainers.WithImage("docker.io/rancher/k3s:v1.27.1-k3s1"),
+		k3s.WithManifest("nginx-manifest.yaml"),
+		testcontainers.WithWaitStrategy(wait.ForExec([]string{"kubectl", "wait", "pod", "nginx","--for=condition=Ready"})),
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Clean up the container
+	defer func() {
+		if err := k3sContainer.Terminate(ctx); err != nil {
+			t.Fatal(err)
+		}
+	}()
+}
diff --git a/modules/k3s/nginx-manifest.yaml b/modules/k3s/nginx-manifest.yaml
new file mode 100644
index 0000000000..fd552a1a24
--- /dev/null
+++ b/modules/k3s/nginx-manifest.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  labels:
+    run: pod
+  name: nginx
+  namespace: default
+spec:
+  containers:
+  - name: pod
+    image: nginx
+    imagePullPolicy: Always
+
+ 
\ No newline at end of file

From f38071d12e5243ce98114cfe9e957181391dd232 Mon Sep 17 00:00:00 2001
From: pablochacin <pablochacin@gmail.com>
Date: Tue, 5 Mar 2024 17:06:34 +0100
Subject: [PATCH 2/3] Use new copy file feature to copy manifest
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Manuel de la Peña <social.mdelapenya@gmail.com>
---
 modules/k3s/k3s.go | 24 ++++--------------------
 1 file changed, 4 insertions(+), 20 deletions(-)

diff --git a/modules/k3s/k3s.go b/modules/k3s/k3s.go
index f851ed92f9..bb0d78021c 100644
--- a/modules/k3s/k3s.go
+++ b/modules/k3s/k3s.go
@@ -38,26 +38,10 @@ func WithManifest(manifestPath string) testcontainers.CustomizeRequestOption {
 		manifest := filepath.Base(manifestPath)
 		target := k3sManifests + manifest
 
-		// Add a post start hook to copy the manifest.
-		// We cannot use the Files option in the GenericRequest because the target
-		// path is created when the container starts
-		manifestHook := testcontainers.ContainerLifecycleHooks{
-			PostStarts: []testcontainers.ContainerHook{
-				func(ctx context.Context, c testcontainers.Container) error {
-					// if this hook is executed too soon after the container starts,
-					// the manifests directory may not exists yet, so we try to create it here
-					_, _, err := c.Exec(ctx, []string{"mkdir","-p", k3sManifests})
-					if err != nil {
-						return err
-					}
-
-					// copy the manifest
-					return c.CopyFileToContainer(ctx, manifest, target, 0x644)
-				},
-			},
-		}
-		req.LifecycleHooks = append(req.LifecycleHooks, manifestHook)
-	}
+		req.Files = append(req.Files, testcontainers.ContainerFile{
+			HostFilePath:      manifestPath,
+			ContainerFilePath: target,
+		})
 }
 
 // RunContainer creates an instance of the K3s container type

From eb1d29f170c6e40d5e1396739c206f5d5e8cf111 Mon Sep 17 00:00:00 2001
From: Pablo Chacin <pablochacin@gmail.com>
Date: Tue, 5 Mar 2024 17:10:31 +0100
Subject: [PATCH 3/3] Fix missing bracket

Signed-off-by: Pablo Chacin <pablochacin@gmail.com>
---
 modules/k3s/k3s.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/modules/k3s/k3s.go b/modules/k3s/k3s.go
index bb0d78021c..83fdde4338 100644
--- a/modules/k3s/k3s.go
+++ b/modules/k3s/k3s.go
@@ -42,6 +42,7 @@ func WithManifest(manifestPath string) testcontainers.CustomizeRequestOption {
 			HostFilePath:      manifestPath,
 			ContainerFilePath: target,
 		})
+	}
 }
 
 // RunContainer creates an instance of the K3s container type