Skip to content
Open
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
1 change: 1 addition & 0 deletions containerfiles/dns/cluster-hosts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
10.88.0.2 registry registry.cluster.local
10.88.0.3 auth-registry auth-registry.cluster.local
7 changes: 1 addition & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/spf13/viper v1.21.0
go.podman.io/common v0.69.1
go.podman.io/podman/v6 v6.1.0
golang.org/x/crypto v0.54.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.36.4
k8s.io/apimachinery v0.36.4
Expand Down Expand Up @@ -75,13 +76,9 @@ require (
github.com/go-openapi/swag/stringutils v0.26.0 // indirect
github.com/go-openapi/swag/typeutils v0.26.0 // indirect
github.com/go-openapi/swag/yamlutils v0.26.0 // indirect
github.com/go-openapi/testify/enable/yaml/v2 v2.4.2 // indirect
github.com/go-openapi/testify/v2 v2.5.1 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/godbus/dbus/v5 v5.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-containerregistry v0.21.6 // indirect
Expand Down Expand Up @@ -121,7 +118,6 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/nxadm/tail v1.4.11 // indirect
github.com/opencontainers/cgroups v0.0.8 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
Expand Down Expand Up @@ -167,7 +163,6 @@ require (
go.podman.io/storage v1.64.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/crypto v0.54.0 // indirect
golang.org/x/mod v0.37.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
Expand Down
230 changes: 12 additions & 218 deletions go.sum

Large diffs are not rendered by default.

15 changes: 13 additions & 2 deletions internal/cli/cluster/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ func newStartCmd() *cobra.Command {
var exposePath string
var hostNetworkPopulator bool
var targetImgRef string
var registryUser string
var registryPassword string

cmd := &cobra.Command{
Use: "start",
Expand All @@ -45,7 +47,7 @@ func newStartCmd() *cobra.Command {
bink cluster start --memory 4096 --expose ./kubeconfig`,
RunE: func(cmd *cobra.Command, args []string) error {
logger := logrus.New()
return runStart(cmd.Context(), logger, nodeName, nodeImage, apiPort, memory, maxMemory, exposePath, hostNetworkPopulator, targetImgRef)
return runStart(cmd.Context(), logger, nodeName, nodeImage, apiPort, memory, maxMemory, exposePath, hostNetworkPopulator, targetImgRef, registryUser, registryPassword)
},
}

Expand All @@ -57,11 +59,13 @@ func newStartCmd() *cobra.Command {
cmd.Flags().StringVar(&exposePath, "expose", "", "Expose API and save kubeconfig to PATH after cluster is up")
cmd.Flags().BoolVar(&hostNetworkPopulator, "host-network-populator", false, "Use host networking for the image populator container (fixes DNS in nested podman)")
cmd.Flags().StringVar(&targetImgRef, "target-imgref", "", "Override the bootc image reference tracked by the VM (e.g., registry.cluster.local:5000/node:latest)")
cmd.Flags().StringVar(&registryUser, "registry-user", config.AuthRegistryUsername, "Username for the authenticated registry")
cmd.Flags().StringVar(&registryPassword, "registry-password", config.AuthRegistryPassword, "Password for the authenticated registry")

return cmd
}

func runStart(ctx context.Context, logger *logrus.Logger, nodeName string, nodeImage string, apiPort int, memory int, maxMemory int, exposePath string, hostNetworkPopulator bool, targetImgRef string) error {
func runStart(ctx context.Context, logger *logrus.Logger, nodeName string, nodeImage string, apiPort int, memory int, maxMemory int, exposePath string, hostNetworkPopulator bool, targetImgRef string, registryUser string, registryPassword string) error {
logger.Info("=== Creating Kubernetes cluster ===")
logger.Info("")

Expand All @@ -85,6 +89,9 @@ func runStart(ctx context.Context, logger *logrus.Logger, nodeName string, nodeI
if err := registryMgr.EnsureRegistry(ctx); err != nil {
return fmt.Errorf("ensuring registry: %w", err)
}
if err := registryMgr.EnsureAuthRegistry(ctx, registryUser, registryPassword); err != nil {
return fmt.Errorf("ensuring auth registry: %w", err)
}
logger.Info("")

logger.Info("Step 3: Ensuring DNS container...")
Expand Down Expand Up @@ -197,6 +204,10 @@ func runStart(ctx context.Context, logger *logrus.Logger, nodeName string, nodeI
logger.Infof(" Push: podman push --tls-verify=false localhost:%d/<image>:<tag>", config.RegistryPort)
logger.Infof(" Pull (in-cluster): %s.%s:%d/<image>:<tag>", config.RegistryHostname, config.ClusterDomain, config.RegistryPort)
logger.Info("")
logger.Info("Auth registry (pull with credentials):")
logger.Infof(" Pull (in-cluster): %s.%s:%d/<image>:<tag>", config.AuthRegistryHostname, config.ClusterDomain, config.AuthRegistryPort)
logger.Infof(" Credentials: %s / %s", config.AuthRegistryUsername, config.AuthRegistryPassword)
logger.Info("")

if exposePath != "" {
logger.Info("Step 9: Exposing API server...")
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/cluster/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func runStop(ctx context.Context, logger *logrus.Logger, force, removeData bool)
} else {
logger.Info("✅ All cluster data removed")
}
logger.Info("Note: Shared registry (bink-registry) is preserved. Use 'bink registry stop' to remove it.")
logger.Info("Note: Shared registries (bink-registry, bink-auth-registry) are preserved. Use 'bink registry stop' to remove them.")
}

return nil
Expand Down
27 changes: 22 additions & 5 deletions internal/cli/registry/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,28 @@ func newInfoCmd() *cobra.Command {
status = define.ContainerStateRunning.String()
}

fmt.Printf("Registry: %s\n", status)
fmt.Printf("IP: %s\n", info.IP)
fmt.Printf("Host port: %d\n", info.HostPort)
fmt.Printf("Push: podman push --tls-verify=false %s/<image>:<tag>\n", info.PushURL)
fmt.Printf("Pull: %s/<image>:<tag>\n", info.PullURL)
fmt.Printf("Registry (unauthenticated): %s\n", status)
fmt.Printf(" IP: %s\n", info.IP)
fmt.Printf(" Host port: %d\n", info.HostPort)
fmt.Printf(" Push: podman push --tls-verify=false %s/<image>:<tag>\n", info.PushURL)
fmt.Printf(" Pull: %s/<image>:<tag>\n", info.PullURL)
fmt.Println()

authInfo, err := mgr.AuthRegistryInfo(cmd.Context())
if err != nil {
return fmt.Errorf("getting auth registry info: %w", err)
}

authStatus := "stopped"
if authInfo.Running {
authStatus = define.ContainerStateRunning.String()
}

fmt.Printf("Registry (authenticated): %s\n", authStatus)
fmt.Printf(" IP: %s\n", authInfo.IP)
fmt.Printf(" Host port: %d\n", authInfo.HostPort)
fmt.Printf(" Pull: %s/<image>:<tag>\n", authInfo.PullURL)
fmt.Printf(" Credentials: %s / %s\n", authInfo.Username, authInfo.Password)

return nil
},
Expand Down
21 changes: 18 additions & 3 deletions internal/cli/registry/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,43 @@ package registry
import (
"fmt"

"github.com/bootc-dev/bink/internal/config"
registrypkg "github.com/bootc-dev/bink/internal/registry"
"github.com/spf13/cobra"
)

func newStartCmd() *cobra.Command {
var authOnly bool
var registryUser string
var registryPassword string

cmd := &cobra.Command{
Use: "start",
Short: "Start the local container registry",
Long: "Start the shared local registry container, creating it if it doesn't exist",
Long: "Start the shared local registry containers, creating them if they don't exist",
RunE: func(cmd *cobra.Command, args []string) error {
mgr, err := registrypkg.NewManager()
if err != nil {
return fmt.Errorf("creating registry manager: %w", err)
}

if err := mgr.EnsureRegistry(cmd.Context()); err != nil {
return fmt.Errorf("starting registry: %w", err)
if !authOnly {
if err := mgr.EnsureRegistry(cmd.Context()); err != nil {
return fmt.Errorf("starting registry: %w", err)
}
}

if err := mgr.EnsureAuthRegistry(cmd.Context(), registryUser, registryPassword); err != nil {
return fmt.Errorf("starting auth registry: %w", err)
}

return nil
},
}

cmd.Flags().BoolVar(&authOnly, "auth", false, "Start only the authenticated registry")
cmd.Flags().StringVar(&registryUser, "registry-user", config.AuthRegistryUsername, "Username for the authenticated registry")
cmd.Flags().StringVar(&registryPassword, "registry-password", config.AuthRegistryPassword, "Password for the authenticated registry")

return cmd
}
22 changes: 19 additions & 3 deletions internal/cli/registry/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,40 @@ import (
)

func newStopCmd() *cobra.Command {
var authOnly bool

cmd := &cobra.Command{
Use: "stop",
Short: "Stop and remove the local registry",
Long: "Stop the shared local registry container and remove its data volume",
Short: "Stop and remove the local registries",
Long: "Stop both local registry containers and remove the shared data volume. Use --auth to stop only the authenticated registry.",
RunE: func(cmd *cobra.Command, args []string) error {
mgr, err := registrypkg.NewManager()
if err != nil {
return fmt.Errorf("creating registry manager: %w", err)
}

if err := mgr.StopAuthRegistry(cmd.Context()); err != nil {
if authOnly {
return fmt.Errorf("stopping auth registry: %w", err)
}
logrus.Warnf("Failed to stop auth registry: %v", err)
}

if authOnly {
logrus.Info("Auth registry stopped and removed")
return nil
}

if err := mgr.StopRegistry(cmd.Context()); err != nil {
return fmt.Errorf("stopping registry: %w", err)
}

logrus.Info("Registry stopped and data removed")
logrus.Info("All registries stopped and data removed")
return nil
},
}

cmd.Flags().BoolVar(&authOnly, "auth", false, "Stop only the authenticated registry")

return cmd
}
11 changes: 11 additions & 0 deletions internal/config/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ const (
LabelKubeadmVersion = "bink.kubeadm-version"
LabelNodeImage = "bink.node-image"

LabelAuthRegistryUser = "bink.auth-registry-user"
LabelAuthRegistryPassword = "bink.auth-registry-password"

ContainerNamePrefix = "k8s-"

DNSContainerName = "dns"
Expand All @@ -76,6 +79,14 @@ const (
RegistryStaticIP = "10.88.0.2"
RegistryHostname = "registry"
RegistryVolume = "bink-registry-data"
RegistryHTTPSecret = "bink-shared-secret"

AuthRegistryContainerName = "bink-auth-registry"
AuthRegistryPort = 5001
AuthRegistryStaticIP = "10.88.0.3"
AuthRegistryHostname = "auth-registry"
AuthRegistryUsername = "testuser"
AuthRegistryPassword = "testpassword"
Comment on lines +88 to +89

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we let the user configure them when starting the cluster. It seems more helpful to me then providing default

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this won't be that helpful enough.. we have all hardcoded port, IP, hostname. what it will all do is add complexity of no real use. happy to revisit if there is use case I am not seeing.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, the ip and hostname aren't really used by the user while the user and password are for pushing the images. We can pass additional flag with --registry-user and registry-password from the bink registry start and bink cluster start


HAProxyImage = "docker.io/library/haproxy:lts-alpine"
HAProxyContainerName = "haproxy"
Expand Down
8 changes: 8 additions & 0 deletions internal/node/cloudinit.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ type CloudInitData struct {
RegistryHostname string
ServiceCIDR string
TargetImgRef string

AuthRegistryStaticIP string
AuthRegistryPort int
AuthRegistryHostname string
}

func (n *Node) newCloudInitData(sshPubKey string) CloudInitData {
Expand All @@ -56,6 +60,10 @@ func (n *Node) newCloudInitData(sshPubKey string) CloudInitData {
RegistryHostname: config.RegistryHostname,
ServiceCIDR: config.ServiceCIDR,
TargetImgRef: n.TargetImgRef,

AuthRegistryStaticIP: config.AuthRegistryStaticIP,
AuthRegistryPort: config.AuthRegistryPort,
AuthRegistryHostname: config.AuthRegistryHostname,
}
}

Expand Down
14 changes: 14 additions & 0 deletions internal/node/cloudinit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package node

import (
"fmt"
"strings"
"testing"

Expand All @@ -25,6 +26,10 @@ func testCloudInitData() CloudInitData {
RegistryPort: config.RegistryPort,
RegistryHostname: config.RegistryHostname,
ServiceCIDR: config.ServiceCIDR,

AuthRegistryStaticIP: config.AuthRegistryStaticIP,
Comment thread
HarshwardhanPatil07 marked this conversation as resolved.
AuthRegistryPort: config.AuthRegistryPort,
AuthRegistryHostname: config.AuthRegistryHostname,
}
}

Expand Down Expand Up @@ -117,6 +122,15 @@ func TestRenderTemplate_UserData(t *testing.T) {
if !strings.Contains(s, registryURL) {
t.Errorf("missing registry URL %s", registryURL)
}

authRegistryURL := fmt.Sprintf("%s:%d", config.AuthRegistryStaticIP, config.AuthRegistryPort)
if !strings.Contains(s, authRegistryURL) {
t.Errorf("missing auth registry URL %s", authRegistryURL)
}
authRegistryFQDN := fmt.Sprintf("%s.%s:%d", config.AuthRegistryHostname, config.ClusterDomain, config.AuthRegistryPort)
if !strings.Contains(s, authRegistryFQDN) {
t.Errorf("missing auth registry FQDN %s", authRegistryFQDN)
}
}

func TestValidateYAML(t *testing.T) {
Expand Down
10 changes: 9 additions & 1 deletion internal/node/templates/user-data.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ write_files:
- path: /etc/crio/crio.conf.d/03-local-registry.conf
content: |
[crio.image]
insecure_registries = ["{{.RegistryStaticIP}}:{{.RegistryPort}}", "{{.RegistryHostname}}.{{.ClusterDomain}}:{{.RegistryPort}}"]
insecure_registries = ["{{.RegistryStaticIP}}:{{.RegistryPort}}", "{{.RegistryHostname}}.{{.ClusterDomain}}:{{.RegistryPort}}", "{{.AuthRegistryStaticIP}}:{{.AuthRegistryPort}}", "{{.AuthRegistryHostname}}.{{.ClusterDomain}}:{{.AuthRegistryPort}}"]
- path: /etc/containers/registries.conf.d/10-local-registry.conf
content: |
[[registry]]
Expand All @@ -52,6 +52,14 @@ write_files:
[[registry]]
location = "{{.RegistryHostname}}.{{.ClusterDomain}}:{{.RegistryPort}}"
insecure = true

[[registry]]
location = "{{.AuthRegistryStaticIP}}:{{.AuthRegistryPort}}"
insecure = true

[[registry]]
location = "{{.AuthRegistryHostname}}.{{.ClusterDomain}}:{{.AuthRegistryPort}}"
insecure = true
- path: /etc/systemd/system/var-mnt-cluster_images.mount
content: |
[Unit]
Expand Down
13 changes: 13 additions & 0 deletions internal/podman/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,19 @@ func (c *Client) ContainerInspect(ctx context.Context, name, format string) (str
}
}

func (c *Client) ContainerLabels(ctx context.Context, name string) (map[string]string, error) {
if err := c.ensureConnection(); err != nil {
return nil, err
}

data, err := containers.Inspect(c.withCtx(ctx), name, nil)
if err != nil {
return nil, err
}

return data.Config.Labels, nil
}

func (c *Client) ContainerCopy(ctx context.Context, srcPath, containerName, destPath string) error {
if err := c.ensureConnection(); err != nil {
return err
Expand Down
Loading
Loading