kubernetes

package module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 17 Imported by: 0

README

scaffold toolbox kubernetes

Warning - this tool requires running k3s in a privileged container. This is useful for local experiments, but it is a broad Docker permission. Use it only where that tradeoff is acceptable.

scaffold's Kubernetes tooling is built around the rancher/k3s image. You'll primarily interact with it through YAML manifest files. The service waits for common rollouts and exposes a kubectl passthrough for interactive work.

Kubeconfig or WriteKubeconfig gets you a Kubernetes config that points host tools at the containerized k3s API. The former returns the config bytes; the latter writes them to a file. Neither is called automatically.

You can enable a hosted Docker registry to push your own container images or Dockerfiles onto the cluster for reference by your manifest files. See Local image registry for more information on this.

Install

go get github.com/hlfshell/scaffold-toolbox/kubernetes
import "github.com/hlfshell/scaffold-toolbox/kubernetes"

Example

package main

import (
	"context"
	"embed"
	"fmt"
	"io/fs"

	"github.com/hlfshell/scaffold"
	"github.com/hlfshell/scaffold-toolbox/kubernetes"
)

//go:embed deploy/*.yaml
var deployFS embed.FS

func main() {
	ctx := context.Background()

	cluster, err := kubernetes.NewCluster("cluster",
		kubernetes.WithNamespace("dev"),
		kubernetes.WithManifest("https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/application/deployment.yaml"),
	)
	if err != nil {
		panic(err)
	}

	stack := scaffold.NewStack("dev", scaffold.WithServices(cluster))
	if err := stack.Create(ctx); err != nil {
		panic(err)
	}
	defer stack.Cleanup(ctx)

	if err := applyEmbedded(ctx, cluster, deployFS, "deploy"); err != nil {
		panic(err)
	}

	kubeconfig, err := cluster.WriteKubeconfig(ctx, "./kubeconfig.dev")
	if err != nil {
		panic(err)
	}
	fmt.Printf("KUBECONFIG=%s\n", kubeconfig)

	status, err := cluster.Status(ctx)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(status))

	logs, err := cluster.Kubectl(ctx, "logs", "deploy/api", "-n", "dev")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(logs))
}

func applyEmbedded(ctx context.Context, cluster *kubernetes.Cluster, files embed.FS, root string) error {
	return fs.WalkDir(files, root, func(path string, entry fs.DirEntry, err error) error {
		if err != nil || entry.IsDir() {
			return err
		}

		contents, err := files.ReadFile(path)
		if err != nil {
			return err
		}

		_, err = cluster.ApplyYAML(ctx, contents)
		return err
	})
}

For host files that are not embedded:

_, err := cluster.ApplyFiles(ctx, "./deploy/api.yaml", "./deploy/service.yaml")

For direct YAML:

_, err := cluster.ApplyYAML(ctx, []byte(`apiVersion: v1
kind: ConfigMap
metadata:
  name: demo
data:
  hello: world
`))

For local kubectl after startup:

KUBECONFIG=./kubeconfig.dev kubectl get pods -n dev

Local image registry

WithRegistry starts a local Docker registry on the same Docker network as the cluster and configures k3s to pull from it over the internal registry address.

cluster, err := kubernetes.NewCluster("cluster",
	kubernetes.WithNamespace("dev"),
	kubernetes.WithRegistry(""),
	kubernetes.WithDockerfileImage("./Dockerfile", "app/api:dev"),
)
if err != nil {
	return err
}

After Create, use RegistryImage when writing or patching manifests:

image := cluster.RegistryImage("app/api:dev")

The host Docker daemon pushes to RegistryAddress, while Kubernetes pulls from RegistryInternalAddress:

fmt.Println(cluster.RegistryAddress())
fmt.Println(cluster.RegistryInternalAddress())
fmt.Println(cluster.RegistryEnv())

You can also push images after the cluster is running:

pushed, err := cluster.PushImage(ctx, "api:local", "app/api:dev")
if err != nil {
	return err
}
fmt.Println(pushed.ClusterImage)

pushed, logs, err := cluster.BuildAndPushImage(ctx, "./Dockerfile", "app/worker:dev")
if err != nil {
	fmt.Println(logs)
	return err
}

RegistryDockerConfigJSON returns a host-side Docker config.json payload for tools that expect one. The default registry has no username or password.

To expose SSH, pass at least one authorized public key. This starts a companion SSH/kubectl container on the same Docker network as k3s. It does not SSH into the rancher/k3s container itself; a companion container gets a generated kubeconfig mounted into /root/.kube/config, so you can SSH in and run kubectl against the k3s cluster.

key, err := os.ReadFile("/home/me/.ssh/id_ed25519.pub")
if err != nil {
	return err
}

cluster, err := kubernetes.NewCluster("cluster",
	kubernetes.WithNamespace("dev"),
	kubernetes.WithSSH("2222", string(key)),
)
if err != nil {
	return err
}

After startup:

ssh -p 2222 root@127.0.0.1

If you leave the host port blank, Docker assigns one:

cluster, err := kubernetes.NewCluster("cluster",
	kubernetes.WithSSH("", string(key)),
)

Then read it with:

fmt.Println(cluster.SSHAddress())

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Cluster

type Cluster struct {
	// contains filtered or unexported fields
}

Cluster is a Docker-backed Kubernetes quickstart. It runs k3s in a privileged container and applies user-provided YAML through kubectl inside the container.

func NewCluster

func NewCluster(name string, options ...Option) (*Cluster, error)

NewCluster creates a k3s-backed Kubernetes cluster service.

func (*Cluster) ApplyFile

func (c *Cluster) ApplyFile(ctx context.Context, path string) ([]byte, error)

ApplyFile reads a manifest file from the host and sends it to kubectl inside the k3s container.

func (*Cluster) ApplyFiles

func (c *Cluster) ApplyFiles(ctx context.Context, paths ...string) ([]byte, error)

ApplyFiles reads host manifest files and applies them in order.

func (*Cluster) ApplyYAML

func (c *Cluster) ApplyYAML(ctx context.Context, yaml []byte) ([]byte, error)

ApplyYAML sends YAML directly to kubectl inside the k3s container.

func (*Cluster) BuildAndPushImage

func (c *Cluster) BuildAndPushImage(ctx context.Context, dockerfile string, clusterImage string) (PushedImage, string, error)

BuildAndPushImage builds a Dockerfile, pushes the result to the registry, and returns the cluster image reference manifests should use.

func (*Cluster) Cleanup

func (c *Cluster) Cleanup(ctx context.Context) error

Cleanup deletes registered manifests in reverse order and removes the k3s container.

func (*Cluster) Create

func (c *Cluster) Create(ctx context.Context) error

Create starts the k3s container, waits for the Kubernetes API, applies registered manifests, and waits for rollouts.

func (*Cluster) DeleteYAML

func (c *Cluster) DeleteYAML(ctx context.Context, yaml []byte) ([]byte, error)

DeleteYAML sends YAML directly to kubectl delete inside the k3s container. Missing resources are ignored.

func (*Cluster) Endpoints

func (c *Cluster) Endpoints() map[string]string

func (*Cluster) Env

func (c *Cluster) Env() map[string]string

func (*Cluster) Kubeconfig

func (c *Cluster) Kubeconfig(ctx context.Context) ([]byte, error)

Kubeconfig returns a host-usable kubeconfig for this cluster without writing it to disk.

func (*Cluster) KubeconfigPath

func (c *Cluster) KubeconfigPath() string

KubeconfigPath returns the last path written by WriteKubeconfig, or the configured default path if WriteKubeconfig has not been called yet.

func (*Cluster) Kubectl

func (c *Cluster) Kubectl(ctx context.Context, args ...string) ([]byte, error)

Kubectl executes kubectl inside the k3s container and returns combined stdout and stderr.

func (*Cluster) Logs

func (c *Cluster) Logs(ctx context.Context) (logs.LogStreams, error)

func (*Cluster) Name

func (c *Cluster) Name() string

func (*Cluster) PushImage

func (c *Cluster) PushImage(ctx context.Context, localImage string, clusterImage string) (PushedImage, error)

PushImage tags a local image, pushes it to the host registry endpoint, and returns the cluster image reference manifests should use.

func (*Cluster) RegistryAddress

func (c *Cluster) RegistryAddress() string

RegistryAddress returns the host-reachable registry address.

func (*Cluster) RegistryDockerConfigJSON

func (c *Cluster) RegistryDockerConfigJSON() ([]byte, error)

RegistryDockerConfigJSON returns a Docker config.json payload for the host-reachable registry. The default registry has no authentication, so the auth entry is intentionally empty.

func (*Cluster) RegistryEnv

func (c *Cluster) RegistryEnv() map[string]string

RegistryEnv returns environment variables useful for CLI commands that build, tag, push, or patch manifests with registry images.

func (*Cluster) RegistryImage

func (c *Cluster) RegistryImage(image string) string

RegistryImage returns the image reference Kubernetes should use for an image stored in the cluster registry.

func (*Cluster) RegistryInternalAddress

func (c *Cluster) RegistryInternalAddress() string

RegistryInternalAddress returns the registry address reachable from the cluster's Docker network.

func (*Cluster) SSHAddress

func (c *Cluster) SSHAddress() string

SSHAddress returns the local SSH address when WithSSH is enabled and the container has started.

func (*Cluster) SetLabels

func (c *Cluster) SetLabels(labels map[string]string)

func (*Cluster) SetNamePrefix

func (c *Cluster) SetNamePrefix(prefix string)

func (*Cluster) SetNetwork

func (c *Cluster) SetNetwork(name string)

func (*Cluster) Status

func (c *Cluster) Status(ctx context.Context) ([]byte, error)

Status returns common cluster objects in the configured namespace.

func (*Cluster) WaitForRollouts

func (c *Cluster) WaitForRollouts(ctx context.Context) error

WaitForRollouts waits for deployment, statefulset, and daemonset rollouts in the configured namespace.

func (*Cluster) WriteKubeconfig

func (c *Cluster) WriteKubeconfig(ctx context.Context, path string) (string, error)

WriteKubeconfig writes a host-usable kubeconfig to path. If path is blank, the path configured by WithKubeconfigPath is used. If neither is set, a temporary file is created.

type Image

type Image struct {
	LocalImage   string
	Dockerfile   string
	ClusterImage string
}

Image describes a container image that should be made available through the cluster registry.

type Option

type Option func(*Cluster)

Option configures the Kubernetes cluster before the container is built.

func WithDockerfileImage

func WithDockerfileImage(dockerfile string, clusterImage string) Option

WithDockerfileImage builds the Dockerfile and pushes the result into the cluster registry before manifests are applied.

func WithK3sArgs

func WithK3sArgs(args ...string) Option

WithK3sArgs appends arguments to the k3s server command.

func WithKubeconfigPath

func WithKubeconfigPath(path string) Option

WithKubeconfigPath sets the default path used by WriteKubeconfig. Nothing is written unless WriteKubeconfig is called.

func WithLocalImage

func WithLocalImage(localImage string, clusterImage string) Option

WithLocalImage tags and pushes an existing local Docker image into the cluster registry before manifests are applied.

func WithManifest

func WithManifest(path string) Option

WithManifest registers a YAML file, directory, or URL to apply after the cluster is ready. Local paths are bind-mounted into the k3s container; URLs are passed directly to kubectl.

func WithNamespace

func WithNamespace(name string) Option

WithNamespace creates and uses a namespace for manifest apply, delete, status, and rollout helpers.

func WithReadyTimeout

func WithReadyTimeout(timeout time.Duration) Option

WithReadyTimeout changes how long Create waits for the Kubernetes API.

func WithRegistry

func WithRegistry(hostPort string) Option

WithRegistry starts a local Docker registry beside the cluster. hostPort may be blank to let Docker assign a free host port.

func WithRegistryImage

func WithRegistryImage(image string, tag string) Option

WithRegistryImage changes the local registry container image.

func WithRolloutTimeout

func WithRolloutTimeout(timeout time.Duration) Option

WithRolloutTimeout changes how long Create waits for deployments, statefulsets, and daemonsets to finish rolling out after manifests are applied.

func WithSSH

func WithSSH(hostPort string, publicKeys ...string) Option

WithSSH starts a companion SSH/kubectl container configured against the k3s API. Public keys are written to root's authorized_keys inside that companion container. hostPort may be blank to let Docker assign a free port.

func WithSSHImage

func WithSSHImage(image string, tag string) Option

WithSSHImage changes the companion SSH/kubectl image. The image must have kubectl, sh, apk, and OpenSSH packages available or installable.

func WithTag

func WithTag(tag string) Option

WithTag sets the rancher/k3s image tag. Pin this in CI for repeatable cluster behavior.

type PushedImage

type PushedImage struct {
	HostImage    string
	ClusterImage string
}

PushedImage describes an image pushed to the local registry. HostImage is the image reference used by the host Docker daemon. ClusterImage is the image reference Kubernetes manifests should use.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL