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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ For a comprehensive analysis of multi-tenancy approaches and KubeFlex's solution

[kind](https://kind.sigs.k8s.io) and [kubectl](https://kubernetes.io/docs/tasks/tools/) are
required. A kind hosting cluster is created automatically by the kubeflex CLI. You may
also install KubeFlex on other Kube distros, as long as they support an nginx ingress
with SSL passthru, or on OpenShift. See the [User's Guide](docs/users.md) for more details.
also install KubeFlex on other Kube distros, as long as they support NGINX Gateway Fabric
with the Gateway API, or on OpenShift. See the [User's Guide](docs/users.md) for more details.

Download the latest kubeflex CLI binary release for your OS/Architecture from the
[release page](https://github.com/kubestellar/kubeflex/releases) and copy it
Expand Down
1 change: 0 additions & 1 deletion api/v1alpha1/conditions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ func TestAreConditionsEqual(t *testing.T) {
}
}


func TestAreConditionSlicesSame(t *testing.T) {
// Create two slices of conditions with the same elements in different orders
c1 := []ControlPlaneCondition{
Expand Down
66 changes: 66 additions & 0 deletions cmd/kflex/init/cluster/gateway.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
Copyright 2025 The KubeStellar Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cluster

import (
"fmt"
"os"
"os/exec"
)

// installNGINXGatewayFabric installs NGINX Gateway Fabric on the kind cluster
func installNGINXGatewayFabric() error {
// Install Gateway API CRDs
cmd := exec.Command("kubectl", "apply", "-f", "https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/standard-install.yaml")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return fmt.Errorf("failed to install Gateway API CRDs: %v", err)
}

// Install NGINX Gateway Fabric using Helm
cmd = exec.Command("helm", "repo", "add", "nginx-stable", "https://helm.nginx.com/stable")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to add nginx helm repo: %v", err)
}

cmd = exec.Command("helm", "repo", "update")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to update helm repos: %v", err)
}

// Install NGINX Gateway Fabric
cmd = exec.Command("helm", "install", "ngf", "oci://ghcr.io/nginx/charts/nginx-gateway-fabric",
"--create-namespace", "-n", "nginx-gateway",
"--set", "nginx.service.type=NodePort",
"--set-json", "nginx.service.nodePorts=[{\"port\":31437,\"listenerPort\":80},{\"port\":31438,\"listenerPort\":443}]")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to install NGINX Gateway Fabric: %v", err)
}

return nil
}
65 changes: 3 additions & 62 deletions cmd/kflex/init/cluster/kind.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,65 +128,6 @@ nodes:
return nil
}

// installAndPatchNginxIngress installs and patches the nginx ingress controller on the kind cluster
func installAndPatchNginxIngress() error {
// run the kubectl apply command to install the nginx ingress controller
cmd := exec.Command("kubectl", "apply", "-f", "https://raw.githubusercontent.com/kubernetes/ingress-nginx/refs/tags/controller-v1.12.1/deploy/static/provider/kind/deploy.yaml")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return fmt.Errorf("failed to run kubectl apply command: %v", err)
}

// create a patch file for the nginx controller deployment
patchFile, err := os.CreateTemp("", "nginx-controller-patch-*.yaml")
if err != nil {
return fmt.Errorf("failed to create patch file: %v", err)
}
defer os.Remove(patchFile.Name())

// write the patch content to the patch file
patchContent := `spec:
template:
spec:
containers:
- name: controller
args:
- /nginx-ingress-controller
- --election-id=ingress-nginx-leader
- --controller-class=k8s.io/ingress-nginx
- --ingress-class=nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
- --validating-webhook=:8443
- --validating-webhook-certificate=/usr/local/certificates/cert
- --validating-webhook-key=/usr/local/certificates/key
- --watch-ingress-without-class=true
- --publish-status-address=localhost
- --enable-ssl-passthrough`
_, err = patchFile.WriteString(patchContent)
if err != nil {
return fmt.Errorf("failed to write patch file: %v", err)
}

err = patchFile.Close()
if err != nil {
return fmt.Errorf("failed to close patch file: %v", err)
}

// run the kubectl patch command to patch the nginx controller deployment with the patch file
cmd = exec.Command("kubectl", "-n", "ingress-nginx", "patch", "deployment/ingress-nginx-controller",
fmt.Sprintf("--patch-file=%s", patchFile.Name()))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to run kubectl patch deployment command: %v", err)
}

return nil
}

func CreateKindCluster(chattyStatus bool, clusterName string) {
done := make(chan bool)
var wg sync.WaitGroup
Expand Down Expand Up @@ -224,10 +165,10 @@ func CreateKindCluster(chattyStatus bool, clusterName string) {
}
}

util.PrintStatus("Installing and patching nginx ingress...", done, &wg, chattyStatus)
err = installAndPatchNginxIngress()
util.PrintStatus("Installing NGINX Gateway Fabric...", done, &wg, chattyStatus)
err = installNGINXGatewayFabric()
if err != nil {
log.Fatalf("Error installing and patching nginx ingress: %v\n", err)
log.Fatalf("Error installing NGINX Gateway Fabric: %v\n", err)
}
done <- true
wg.Wait()
Expand Down
10 changes: 5 additions & 5 deletions cmd/kflex/init/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ import (
)

const (
CreateKindFlag = "create-kind"
DomainFlag = "domain"
HostContainerNameFlag = "host-container-name" // REFACTOR? replace with host-container-name?
ExternalPortFlag = "external-port" // REFACTOR? replace with external-port?
CreateKindFlag = "create-kind"
DomainFlag = "domain"
HostContainerNameFlag = "host-container-name" // REFACTOR? replace with host-container-name?
ExternalPortFlag = "external-port" // REFACTOR? replace with external-port?
DefaultKindClusterName = "kind-kubeflex" // Default cluster name for kind clusters
)

Expand All @@ -58,7 +58,7 @@ func Command() *cobra.Command {
domain, _ := flagset.GetString(DomainFlag)
externalPort, _ := flagset.GetInt(ExternalPortFlag)
hostContainer, _ := flagset.GetString(HostContainerNameFlag)

// Handle positional cluster name parameter
clusterName := DefaultKindClusterName // default
if len(args) > 0 {
Expand Down
3 changes: 1 addition & 2 deletions cmd/kflex/init/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,8 @@ func TestDefaultKindClusterNameUsage(t *testing.T) {
if len(DefaultKindClusterName) <= 5 {
t.Error("DefaultKindClusterName should be longer than 5 characters")
}

if DefaultKindClusterName[:5] != "kind-" {
t.Error("DefaultKindClusterName should start with 'kind-' prefix")
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.17.3
name: controlplanes.tenancy.kflex.kubestellar.org
spec:
group: tenancy.kflex.kubestellar.org
Expand Down Expand Up @@ -142,6 +142,8 @@ spec:
WaitForPostCreateHooks determines if the control plane should wait for all
post create hook resources to be ready before marking the control plane as ready
type: boolean
required:
- type
type: object
status:
description: ControlPlaneStatus defines the observed state of ControlPlane
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.17.3
name: postcreatehooks.tenancy.kflex.kubestellar.org
spec:
group: tenancy.kflex.kubestellar.org
Expand Down
Loading
Loading