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
33 changes: 15 additions & 18 deletions cmd/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ EXAMPLES
o Create a default development cluster
$ {{rootCmdUse}} cluster create

o Create a minimal cluster (just Kubernetes + registry)
o Create a minimal cluster (Kubernetes + Contour + registry; no Serving/Eventing)
$ {{rootCmdUse}} cluster create --serving=false --eventing=false

o List existing clusters
Expand Down Expand Up @@ -99,14 +99,17 @@ DESCRIPTION
export KUBECONFIG=~/.config/func/clusters/func.local/kubeconfig.yaml

Installed components are controlled by the --serving, --eventing,
--tekton, and --keda flags. Required binaries are downloaded into the
func config directory on first use unless --skip-binaries is set.
--tekton, and --keda flags. --serving and --eventing default to true
(hack/cluster.sh always installs Serving). Contour is always present for
the registry host Ingress: with Serving (default path) or standalone when
--serving=false. Required binaries are downloaded into the func config
directory on first use unless --skip-binaries is set.

EXAMPLES
o Create a default development cluster
o Create a default development cluster (Serving + Eventing + registry)
$ {{rootCmdUse}} cluster create

o Create a minimal cluster (just Kubernetes + registry)
o Create a minimal cluster (Kubernetes + Contour + registry; no Serving/Eventing)
$ {{rootCmdUse}} cluster create --serving=false --eventing=false

o Create a cluster with a custom name and domain
Expand Down Expand Up @@ -180,7 +183,6 @@ func newClusterCreateConfig() cluster.ClusterConfig {
ContainerEngineOverride: viper.GetString("container-engine"),
KubectlOverride: os.Getenv("FUNC_TEST_KUBECTL"), // override binary path
KindOverride: os.Getenv("FUNC_TEST_KIND"), // override binary path
ActOverride: os.Getenv("FUNC_TEST_ACT"), // override binary path
GitHubActions: os.Getenv("GITHUB_ACTIONS") == "true", // detect CI environments
}
}
Expand All @@ -195,8 +197,7 @@ NAME
{{rootCmdUse}} cluster delete - Delete a local development cluster

SYNOPSIS
{{rootCmdUse}} cluster delete [name] [--container-engine]
[--skip-registry-config]
{{rootCmdUse}} cluster delete [name] [--skip-registry-config]

DESCRIPTION
Deletes a local development cluster. The in-cluster registry is removed
Expand Down Expand Up @@ -225,14 +226,12 @@ EXAMPLES

`,

PreRunE: bindEnv("container-engine", "skip-registry-config"),
PreRunE: bindEnv("skip-registry-config"),
RunE: func(cmd *cobra.Command, args []string) error {
return runClusterDelete(cmd, args)
},
}

cmd.Flags().String("container-engine", "",
"Container engine: docker or podman; auto-detected if unset, preferring docker when both are installed ($FUNC_CONTAINER_ENGINE)")
cmd.Flags().Bool("skip-registry-config", false,
"Skip host registry configuration revert ($FUNC_SKIP_REGISTRY_CONFIG)")

Expand All @@ -241,13 +240,11 @@ EXAMPLES

func newClusterDeleteConfig() cluster.ClusterConfig {
return cluster.ClusterConfig{
Name: "func",
ContainerEngineOverride: viper.GetString("container-engine"),
SkipRegistryConfig: viper.GetBool("skip-registry-config"),
KubectlOverride: os.Getenv("FUNC_TEST_KUBECTL"),
KindOverride: os.Getenv("FUNC_TEST_KIND"),
ActOverride: os.Getenv("FUNC_TEST_ACT"),
GitHubActions: os.Getenv("GITHUB_ACTIONS") == "true",
Name: "func",
SkipRegistryConfig: viper.GetBool("skip-registry-config"),
KubectlOverride: os.Getenv("FUNC_TEST_KUBECTL"),
KindOverride: os.Getenv("FUNC_TEST_KIND"),
GitHubActions: os.Getenv("GITHUB_ACTIONS") == "true",
}
}

Expand Down
14 changes: 13 additions & 1 deletion pkg/cluster/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ func allocateCluster(ctx context.Context, cfg ClusterConfig, out io.Writer) (err
}

// Phase 2: Parallel component installation
// Mirrors hack/cluster.sh: (serving && dns && networking) || registry || …
// in parallel. Shell always installs Serving; Go allows --serving=false
// (e.g. future KEDA-only). Registry Ingress needs Contour (contour-external)
// for host access to registry.localtest.me — when Serving is off we still
// install Contour on the reg path so the Ingress is not inert.
status(out, "Beginning Cluster Configuration")
fmt.Fprintln(out, "Tasks will be executed in parallel. Logs will be prefixed:")
if cfg.Serving {
Expand All @@ -95,6 +100,7 @@ func allocateCluster(ctx context.Context, cfg ClusterConfig, out io.Writer) (err
g, gctx := errgroup.WithContext(ctx)

// svr: serving -> dns -> networking (sequential within goroutine)
// networking = Contour + net-contour + Serving config (hack/cluster.sh networking)
if cfg.Serving {
g.Go(func() error {
w := newPrefixedWriter(out, "svr ")
Expand All @@ -121,10 +127,16 @@ func allocateCluster(ctx context.Context, cfg ClusterConfig, out io.Writer) (err
})
}

// reg: registry (always)
// reg: registry (always). Shell always has Contour via the svr job;
// with --serving=false install Contour first so registry Ingress works.
g.Go(func() error {
w := newPrefixedWriter(out, "reg ")
defer w.Flush()
if !cfg.Serving {
if err := installContour(gctx, cfg, w); err != nil {
return fmt.Errorf("contour (registry host ingress): %w", err)
}
}
return installRegistry(gctx, cfg, w)
})

Expand Down
57 changes: 35 additions & 22 deletions pkg/cluster/serving.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ func configureDNS(ctx context.Context, cfg ClusterConfig, out io.Writer) error {
return fmt.Errorf("unable to set Knative domain after 10 attempts: %w", lastErr)
}

// installNetworking installs Contour ingress controller and configures Knative
// to use it. The Contour YAML is modified in Go (replacing yq) to add IPv6
// dual-stack support args.
func installNetworking(ctx context.Context, cfg ClusterConfig, out io.Writer) error {
start := time.Now()
// installContour installs the Contour ingress controller (contour-external)
// used by standard Kubernetes Ingress objects (registry.localtest.me, broker
// host, etc.). Mirrors the Contour half of hack/cluster.sh networking().
// Does not install knative net-contour or patch Serving — that is installNetworking.
func installContour(ctx context.Context, cfg ClusterConfig, out io.Writer) error {
status(out, "Installing Ingress Controller (Contour)")
fmt.Fprintf(out, "Version: %s\n", contourVersion)

Expand Down Expand Up @@ -106,13 +106,42 @@ func installNetworking(ctx context.Context, cfg ClusterConfig, out io.Writer) er
return fmt.Errorf("waiting for contour pods: %w", err)
}

fmt.Fprintln(out, "Patching contour to prefer dual-stack")
err = run(ctx, out, "",
cfg.kubectl(), "patch", "-n", "contour-external", "svc/envoy",
"--type", "merge",
"--patch", `{"spec":{"ipFamilyPolicy":"PreferDualStack"}}`)
if err != nil {
return fmt.Errorf("patching contour dual-stack: %w", err)
}

err = run(ctx, out, "",
cfg.kubectl(), "wait", "pod", "--for=condition=Ready", "-l", "!job-name",
"-n", "contour-external", "--timeout=10m")
if err != nil {
return fmt.Errorf("waiting for contour: %w", err)
}
return nil
}

// installNetworking installs Contour and configures Knative Serving to use it.
// Mirrors hack/cluster.sh networking() (Contour + net-contour + Serving patches).
// Callers that need Contour without Serving (e.g. --serving=false + registry
// host Ingress) should use installContour only.
func installNetworking(ctx context.Context, cfg ClusterConfig, out io.Writer) error {
start := time.Now()

if err := installContour(ctx, cfg, out); err != nil {
return err
}

fmt.Fprintln(out, "Installing the Knative Contour controller.")
netContourURL := fmt.Sprintf("https://github.com/knative/net-contour/releases/download/knative-%s/net-contour.yaml", contourVersion)
if err := run(ctx, out, "", cfg.kubectl(), "apply", "-f", netContourURL); err != nil {
return fmt.Errorf("applying net-contour: %w", err)
}

err = run(ctx, out, "",
err := run(ctx, out, "",
cfg.kubectl(), "wait", "pod",
"--for=condition=Ready", "-l", "!job-name",
"-n", "knative-serving", "--timeout=10m")
Expand All @@ -138,22 +167,6 @@ func installNetworking(ctx context.Context, cfg ClusterConfig, out io.Writer) er
return fmt.Errorf("patching domain-template: %w", err)
}

fmt.Fprintln(out, "Patching contour to prefer dual-stack")
err = run(ctx, out, "",
cfg.kubectl(), "patch", "-n", "contour-external", "svc/envoy",
"--type", "merge",
"--patch", `{"spec":{"ipFamilyPolicy":"PreferDualStack"}}`)
if err != nil {
return fmt.Errorf("patching contour dual-stack: %w", err)
}

err = run(ctx, out, "",
cfg.kubectl(), "wait", "pod", "--for=condition=Ready", "-l", "!job-name",
"-n", "contour-external", "--timeout=10m")
if err != nil {
return fmt.Errorf("waiting for contour: %w", err)
}

err = run(ctx, out, "",
cfg.kubectl(), "wait", "pod", "--for=condition=Ready", "-l", "!job-name",
"-n", "knative-serving", "--timeout=10m")
Expand Down
Loading