Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion pkg/svc/provisioner/cluster/export_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
package clusterprovisioner

import k3dv1alpha5 "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
import (
kindprovisioner "github.com/devantler-tech/ksail/v7/pkg/svc/provisioner/cluster/kind"
k3dv1alpha5 "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
"sigs.k8s.io/kind/pkg/apis/config/v1alpha4"
)

// KindProvisionerFactory is the signature of the injectable Kind provisioner
// factory used by the minimal multi-provisioner path.
type KindProvisionerFactory = func(*v1alpha4.Cluster, string) (*kindprovisioner.Provisioner, error)

// SetKindProvisionerFactory swaps the Kind provisioner factory for a test double
// and returns a restore func. It lets external tests assert the kindConfig and
// kubeconfig arguments passed to the factory without reflecting over the
// provisioner's unexported fields.
func SetKindProvisionerFactory(factory KindProvisionerFactory) func() {
previous := kindProvisionerFactory
kindProvisionerFactory = factory

return func() { kindProvisionerFactory = previous }
}

// ExportWrapK3kServerArgs exposes wrapK3kServerArgs for testing.
func ExportWrapK3kServerArgs(args []string) []string {
Expand Down
11 changes: 10 additions & 1 deletion pkg/svc/provisioner/cluster/multi.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,15 @@ func CreateMinimalProvisioner(
}
}

// kindProvisionerFactory constructs the Kind provisioner for the minimal
// multi-provisioner path. It is an indirected package var (rather than a direct
// call) so tests can substitute a spy and assert the kindConfig and kubeconfig
// arguments it receives, without reflecting over the provisioner's unexported
// fields. Production always uses kindprovisioner.CreateProvisioner.
//
//nolint:gochecknoglobals // injected for testability; see SetKindProvisionerFactory in export_test.go
var kindProvisionerFactory = kindprovisioner.CreateProvisioner

// createMinimalKindProvisioner builds the smallest valid Kind provisioner
// while preserving the caller-owned kubeconfig path.
func createMinimalKindProvisioner(clusterName, kubeconfigPath string) (Provisioner, error) {
Expand All @@ -215,7 +224,7 @@ func createMinimalKindProvisioner(clusterName, kubeconfigPath string) (Provision
Name: clusterName,
}

provisioner, err := kindprovisioner.CreateProvisioner(kindConfig, kubeconfigPath)
provisioner, err := kindProvisionerFactory(kindConfig, kubeconfigPath)
if err != nil {
return nil, fmt.Errorf("failed to create Kind provisioner: %w", err)
}
Expand Down
68 changes: 42 additions & 26 deletions pkg/svc/provisioner/cluster/multi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package clusterprovisioner_test

import (
"context"
"reflect"
"testing"

"github.com/devantler-tech/ksail/v7/pkg/apis/cluster/v1alpha1"
Expand All @@ -13,6 +12,7 @@ import (
kwokprovisioner "github.com/devantler-tech/ksail/v7/pkg/svc/provisioner/cluster/kwok"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/kind/pkg/apis/config/v1alpha4"
)

func TestNewMultiProvisioner(t *testing.T) {
Expand Down Expand Up @@ -65,11 +65,25 @@ func TestCreateMinimalProvisioner_K3s_Succeeds(t *testing.T) {
}

// TestCreateMinimalProvisioner_VanillaForwardsKubeconfigPath verifies richer
// callers can isolate Kind from the shared kubeconfig.
// callers can isolate Kind from the shared kubeconfig by asserting the
// kubeconfig path the minimal path hands to the Kind factory.
//
// It is not parallel: it swaps the package-level Kind provisioner factory.
//
//nolint:paralleltest // mutates the package-level Kind provisioner factory seam; must run serially.
func TestCreateMinimalProvisioner_VanillaForwardsKubeconfigPath(t *testing.T) {
t.Parallel()
const kubeconfigPath = "/tmp/ephemeral-kind-kubeconfig"

var gotKubeconfig string

kubeconfigPath := "/tmp/ephemeral-kind-kubeconfig"
restore := clusterprovisioner.SetKindProvisionerFactory(
func(_ *v1alpha4.Cluster, kubeconfig string) (*kindprovisioner.Provisioner, error) {
gotKubeconfig = kubeconfig

return &kindprovisioner.Provisioner{}, nil
},
)
defer restore()

provisioner, err := clusterprovisioner.CreateMinimalProvisioner(
v1alpha1.DistributionVanilla,
Expand All @@ -79,41 +93,43 @@ func TestCreateMinimalProvisioner_VanillaForwardsKubeconfigPath(t *testing.T) {
)

require.NoError(t, err)

kindProvisioner, ok := provisioner.(*kindprovisioner.Provisioner)
require.True(t, ok, "Vanilla must construct a Kind provisioner")

value := reflect.ValueOf(kindProvisioner).Elem()
assert.Equal(t, kubeconfigPath, value.FieldByName("kubeConfig").String())
require.NotNil(t, provisioner)
assert.Equal(t, kubeconfigPath, gotKubeconfig)
}

// TestCreateMinimalProvisioner_VanillaBuildsValidKindConfig verifies the
// minimal path still emits the TypeMeta required by Kind create.
// minimal path still emits the TypeMeta required by Kind create, by asserting
// the kindConfig it hands to the Kind factory.
//
// It is not parallel: it swaps the package-level Kind provisioner factory.
//
//nolint:paralleltest // mutates the package-level Kind provisioner factory seam; must run serially.
func TestCreateMinimalProvisioner_VanillaBuildsValidKindConfig(t *testing.T) {
t.Parallel()

const clusterName = "test-kind"

provisioner, err := clusterprovisioner.CreateMinimalProvisioner(
var gotConfig *v1alpha4.Cluster

restore := clusterprovisioner.SetKindProvisionerFactory(
func(config *v1alpha4.Cluster, _ string) (*kindprovisioner.Provisioner, error) {
gotConfig = config

return &kindprovisioner.Provisioner{}, nil
},
)
defer restore()

_, err := clusterprovisioner.CreateMinimalProvisioner(
v1alpha1.DistributionVanilla,
clusterName,
"/tmp/ephemeral-kind-kubeconfig",
v1alpha1.ProviderDocker,
)

require.NoError(t, err)

kindProvisioner, ok := provisioner.(*kindprovisioner.Provisioner)
require.True(t, ok, "Vanilla must construct a Kind provisioner")

value := reflect.ValueOf(kindProvisioner).Elem()
kindConfig := value.FieldByName("kindConfig")
require.False(t, kindConfig.IsNil())

kindConfig = kindConfig.Elem()
assert.Equal(t, "kind.x-k8s.io/v1alpha4", kindConfig.FieldByName("APIVersion").String())
assert.Equal(t, "Cluster", kindConfig.FieldByName("Kind").String())
assert.Equal(t, clusterName, kindConfig.FieldByName("Name").String())
require.NotNil(t, gotConfig)
assert.Equal(t, "kind.x-k8s.io/v1alpha4", gotConfig.APIVersion)
assert.Equal(t, "Cluster", gotConfig.Kind)
assert.Equal(t, clusterName, gotConfig.Name)
}

func TestCreateMinimalProvisioner_UnsupportedDistribution(t *testing.T) {
Expand Down
Loading