Skip to content

Commit

Permalink
refactor pkg/kind
Browse files Browse the repository at this point in the history
refactors pkg/kind/cluster.go into pkg/kind/config.go in order to
simplify testing and adds relevant tests for the new utility functions.
Also cleans up some unnecessary code in pkg/kind/cluster_test.go

Signed-off-by: Caleb Boylan <calebboylan@gmail.com>
  • Loading branch information
squidboylan committed Feb 11, 2025
1 parent 2a9f196 commit a133dcd
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 141 deletions.
72 changes: 4 additions & 68 deletions pkg/kind/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@ package kind

import (
"context"
"embed"
"errors"
"fmt"
"github.com/cnoe-io/idpbuilder/pkg/util"
"github.com/cnoe-io/idpbuilder/pkg/util/files"
"io"
"io/fs"
"os"
"strconv"
"strings"

"github.com/cnoe-io/idpbuilder/api/v1alpha1"
"github.com/go-logr/logr"
Expand Down Expand Up @@ -43,11 +38,6 @@ type Cluster struct {
cfg v1alpha1.BuildCustomizationSpec
}

type PortMapping struct {
HostPort string
ContainerPort string
}

type IProvider interface {
List() ([]string, error)
ListNodes(string) ([]nodes.Node, error)
Expand All @@ -57,67 +47,13 @@ type IProvider interface {
ExportKubeConfig(string, string, bool) error
}

type TemplateConfig struct {
v1alpha1.BuildCustomizationSpec
KubernetesVersion string
ExtraPortsMapping []PortMapping
RegistryConfig string
}

//go:embed resources/*
var configFS embed.FS

func (c *Cluster) getConfig() ([]byte, error) {
httpClient := util.GetHttpClient()
rawConfigTempl, err := loadConfig(c.kindConfigPath, httpClient)

var rawConfigTempl []byte
var err error
portMappingPairs := parsePortMappings(c.extraPortsMapping)

if c.kindConfigPath != "" {
if strings.HasPrefix(c.kindConfigPath, "https://") || strings.HasPrefix(c.kindConfigPath, "http://") {
httpClient := util.GetHttpClient()
resp, err := httpClient.Get(c.kindConfigPath)
if err != nil {
return nil, fmt.Errorf("fetching remote kind config: %w", err)
}
defer resp.Body.Close()
rawConfigTempl, err = io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("reading remote kind config body: %w", err)
}
} else {
rawConfigTempl, err = os.ReadFile(c.kindConfigPath)
}
} else {
rawConfigTempl, err = fs.ReadFile(configFS, "resources/kind.yaml.tmpl")
}

if err != nil {
return nil, fmt.Errorf("reading kind config: %w", err)
}

var portMappingPairs []PortMapping
if len(c.extraPortsMapping) > 0 {
// Split pairs of ports "11=1111","22=2222",etc
pairs := strings.Split(c.extraPortsMapping, ",")
// Create a slice to store PortMapping pairs.
portMappingPairs = make([]PortMapping, len(pairs))
// Parse each pair into PortPair objects.
for i, pair := range pairs {
parts := strings.Split(pair, ":")
if len(parts) == 2 {
portMappingPairs[i] = PortMapping{parts[0], parts[1]}
}
}
}

registryConfig := ""
for _, s := range c.registryConfig {
path := os.ExpandEnv(s)
if _, err := os.Stat(path); err == nil {
registryConfig = path
break
}
}
registryConfig := findRegistryConfig(c.registryConfig)

if len(c.registryConfig) > 0 && registryConfig == "" {
return nil, errors.New("--registry-config flag used but no registry config was found")
Expand Down
73 changes: 0 additions & 73 deletions pkg/kind/cluster_test.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
package kind

import (
"context"
"io"
"os"
"testing"

"github.com/cnoe-io/idpbuilder/api/v1alpha1"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/go-logr/logr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"sigs.k8s.io/kind/pkg/cluster/nodes"
"sigs.k8s.io/kind/pkg/exec"
)

func TestGetConfig(t *testing.T) {
Expand Down Expand Up @@ -202,69 +195,3 @@ func TestGetConfigCustom(t *testing.T) {
assert.YAMLEq(t, string(expected), string(b))
}
}

// Mock provider for testing
type mockProvider struct {
mock.Mock
IProvider
}

func (m *mockProvider) ListNodes(name string) ([]nodes.Node, error) {
args := m.Called(name)
return args.Get(0).([]nodes.Node), args.Error(1)
}

type mockRuntime struct {
mock.Mock
}

func (m *mockRuntime) ContainerWithPort(ctx context.Context, name string, port string) (bool, error) {
args := m.Called(ctx, name, port)
return args.Get(0).(bool), args.Error(1)
}

// Mock Docker client for testing
type DockerClientMock struct {
client.APIClient
mock.Mock
}

func (m *DockerClientMock) ContainerList(ctx context.Context, listOptions types.ContainerListOptions) ([]types.Container, error) {
mockArgs := m.Called(ctx, listOptions)
return mockArgs.Get(0).([]types.Container), mockArgs.Error(1)
}

type NodeMock struct {
mock.Mock
}

func (n *NodeMock) Command(command string, args ...string) exec.Cmd {
argsMock := append([]string{command}, args...)
mockArgs := n.Called(argsMock)
return mockArgs.Get(0).(exec.Cmd)
}

func (n *NodeMock) String() string {
args := n.Called()
return args.String(0)
}

func (n *NodeMock) Role() (string, error) {
args := n.Called()
return args.String(0), args.Error(1)
}

func (n *NodeMock) IP() (ipv4 string, ipv6 string, err error) {
args := n.Called()
return args.String(0), args.String(1), args.Error(2)
}

func (n *NodeMock) SerialLogs(writer io.Writer) error {
args := n.Called(writer)
return args.Error(0)
}

func (n *NodeMock) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd {
mockArgs := n.Called(nil)
return mockArgs.Get(0).(exec.Cmd)
}
90 changes: 90 additions & 0 deletions pkg/kind/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package kind

import (
"embed"
"fmt"
"io"
"io/fs"
"net/http"
"os"
"strings"

"github.com/cnoe-io/idpbuilder/api/v1alpha1"
)

type PortMapping struct {
HostPort string
ContainerPort string
}

type TemplateConfig struct {
v1alpha1.BuildCustomizationSpec
KubernetesVersion string
ExtraPortsMapping []PortMapping
RegistryConfig string
}

//go:embed resources/* testdata/custom-kind.yaml.tmpl
var configFS embed.FS

type HttpClient interface {
Get(url string) (resp *http.Response, err error)
}

func loadConfig(path string, httpClient HttpClient) ([]byte, error) {
var rawConfigTempl []byte
var err error
if path != "" {
if strings.HasPrefix(path, "https://") || strings.HasPrefix(path, "http://") {
resp, err := httpClient.Get(path)
if err != nil {
return nil, fmt.Errorf("fetching remote kind config: %w", err)
}
defer resp.Body.Close()
if !(resp.StatusCode < 300 && resp.StatusCode >= 200) {
return nil, fmt.Errorf("got %d status code when fetching kind config", resp.StatusCode)
}
rawConfigTempl, err = io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("reading remote kind config body: %w", err)
}
} else {
rawConfigTempl, err = os.ReadFile(path)
}
} else {
rawConfigTempl, err = fs.ReadFile(configFS, "resources/kind.yaml.tmpl")
}

if err != nil {
return nil, fmt.Errorf("reading kind config: %w", err)
}
return rawConfigTempl, nil
}

func parsePortMappings(extraPortsMapping string) []PortMapping {
var portMappingPairs []PortMapping
if len(extraPortsMapping) > 0 {
// Split pairs of ports "11=1111","22=2222",etc
pairs := strings.Split(extraPortsMapping, ",")
// Create a slice to store PortMapping pairs.
portMappingPairs = make([]PortMapping, len(pairs))
// Parse each pair into PortPair objects.
for i, pair := range pairs {
parts := strings.Split(pair, ":")
if len(parts) == 2 {
portMappingPairs[i] = PortMapping{parts[0], parts[1]}
}
}
}
return portMappingPairs
}

func findRegistryConfig(registryConfigPaths []string) string {
for _, s := range registryConfigPaths {
path := os.ExpandEnv(s)
if _, err := os.Stat(path); err == nil {
return path
}
}
return ""
}
Loading

0 comments on commit a133dcd

Please # to comment.