diff --git a/cmd/cluster.go b/cmd/cluster.go index 0a4ad5d44f..b99259ccaa 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -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 @@ -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 @@ -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 } } @@ -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 @@ -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)") @@ -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", } } diff --git a/pkg/cluster/create.go b/pkg/cluster/create.go index 58f1716bb2..e24c9799e0 100644 --- a/pkg/cluster/create.go +++ b/pkg/cluster/create.go @@ -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 { @@ -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 ") @@ -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) }) diff --git a/pkg/cluster/serving.go b/pkg/cluster/serving.go index a8e2ea448b..57feec2f4b 100644 --- a/pkg/cluster/serving.go +++ b/pkg/cluster/serving.go @@ -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) @@ -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") @@ -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")