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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
run: go test $(go list ./... | grep -v /e2e | grep -v /operator/controller) -coverprofile cover.out

- name: Upload coverage
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: coverage
path: cover.out
Expand Down
57 changes: 56 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,50 @@ jobs:
cache-from: type=gha,scope=frontend
cache-to: type=gha,mode=max,scope=frontend

cli-binaries:
name: Build CLI binaries
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix:
include:
- goos: darwin
goarch: arm64
- goos: darwin
goarch: amd64
- goos: linux
goarch: arm64
- goos: linux
goarch: amd64
steps:
- name: Clone the code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
with:
go-version-file: go.mod
cache: true

- name: Build
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: "0"
run: |
go build -trimpath -ldflags="-s -w" -o kuberless-${{ matrix.goos }}-${{ matrix.goarch }} ./cli

- name: Upload artifact
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: kuberless-${{ matrix.goos }}-${{ matrix.goarch }}
path: kuberless-${{ matrix.goos }}-${{ matrix.goarch }}

github-release:
name: GitHub Release
runs-on: ubuntu-latest
needs: [go-images, frontend]
needs: [go-images, frontend, cli-binaries]
permissions:
contents: write
steps:
Expand All @@ -112,11 +152,26 @@ jobs:
VERSION=${GITHUB_REF#refs/tags/}
echo "version=${VERSION}" >> $GITHUB_OUTPUT

- name: Download CLI binaries
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
with:
pattern: kuberless-*
merge-multiple: true

- name: Create GitHub Release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
generate_release_notes: true
files: kuberless-*
body: |
## Install CLI

**macOS / Linux:**
```bash
curl -fsSL https://github.com/${{ github.repository }}/releases/download/${{ steps.version.outputs.version }}/kuberless-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') -o /usr/local/bin/kuberless
chmod +x /usr/local/bin/kuberless
```

## Container Images

Multi-platform images (linux/amd64, linux/arm64) available at:
Expand Down
31 changes: 20 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ Built as a learning project — no backup strategy, no monitoring, error handlin

- Kubernetes cluster with [Cilium](https://docs.cilium.io/en/stable/gettingstarted/) >= 1.15 as CNI
- [`just`](https://just.systems) task runner
- cert-manager, Knative Serving, Capsule, and PostgreSQL — installed separately via `just deploy-prereqs`
- cert-manager, Knative Serving, Capsule, and PostgreSQL — installed via `just deploy-prereqs`

## Install

```bash
just deploy-prereqs
just deploy-kuberless HOST=kuberless.example.com ADMIN_PASSWORD=yourpassword
HOST=kuberless.example.com ADMIN_PASSWORD=yourpassword just deploy-kuberless
```

Or with helm directly:
Expand All @@ -38,8 +38,6 @@ Minimal `my-values.yaml`:
global:
auth:
adminLogin:
enabled: true
username: "admin"
password: "yourpassword"

ingress:
Expand All @@ -64,18 +62,34 @@ just deploy-teardown-all # removes everything

### OpenShift

Requires [Envoy Gateway](https://gateway.envoyproxy.io/) installed on the cluster. Set `HOST` to your apps domain and `gateway.gatewayName` to match your Gateway resource.

```bash
just openshift-deploy
HOST=kuberless.apps.mycluster.example.com \
just openshift-deploy \
-- --set gateway.gatewayName=public --set gateway.appsDomain=apps.mycluster.example.com

just openshift-teardown
```

Or pass overrides via `hack/openshift-values.yaml` before running `just openshift-deploy`.

## Local development (kind)

Requires [`ko`](https://ko.build) in addition to `just` and Docker.

On macOS with Docker Desktop, set `DOCKER_HOST` so `ko` can reach the daemon:

```bash
export DOCKER_HOST=unix://$HOME/.docker/run/docker.sock
just kind-dev # one-shot: cluster + prereqs + images + deploy
just kind-redeploy # rebuild and restart after code changes
just kind-teardown
```

Apps are reachable at `http://<app>-tenant-<tenant>.127.0.0.1.sslip.io` once deployed. Port-forward to use the API and frontend locally:

```bash
kubectl port-forward -n kuberless-system svc/kuberless-apiserver 8080:8080
kubectl port-forward -n kuberless-system svc/kuberless-frontend 3000:3000
```
Expand All @@ -89,7 +103,7 @@ just lint
## CLI

```bash
kuberless login
kuberless login --server http://localhost:8080
kuberless tenant create my-org --plan starter
kuberless deploy ghcr.io/myorg/myapp:latest --name myapp --port 8080
kuberless apps list
Expand All @@ -99,11 +113,6 @@ kuberless domains add myapp api.example.com
kuberless apps pause myapp
```

## Tenant isolation

Each tenant gets a namespace (`tenant-{name}`) with a Capsule Tenant, CiliumNetworkPolicy (cross-tenant traffic denied), and ResourceQuota:

Plans: Free / Starter / Pro / Enterprise (see `api/v1alpha1/`).

## License

Expand Down
29 changes: 18 additions & 11 deletions cli/cmd/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,26 @@ var loginCmd = &cobra.Command{
Use: "login",
Short: "Authenticate with the kuberless platform",
RunE: func(cmd *cobra.Command, args []string) error {
reader := bufio.NewReader(os.Stdin)

serverURL, _ := cmd.Flags().GetString("server")
username, _ := cmd.Flags().GetString("username")
password, _ := cmd.Flags().GetString("password")

fmt.Print("Username: ")
username, _ := reader.ReadString('\n')
username = strings.TrimSpace(username)
if username == "" {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Username: ")
u, _ := reader.ReadString('\n')
username = strings.TrimSpace(u)
}

fmt.Print("Password: ")
passwordBytes, err := term.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return fmt.Errorf("reading password: %w", err)
if password == "" {
fmt.Print("Password: ")
passwordBytes, err := term.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return fmt.Errorf("reading password: %w", err)
}
fmt.Println()
password = string(passwordBytes)
}
fmt.Println()
password := string(passwordBytes)

cfg := &client.Config{
APIBaseURL: serverURL,
Expand Down Expand Up @@ -69,4 +74,6 @@ var loginCmd = &cobra.Command{

func init() {
loginCmd.Flags().String("server", "http://localhost:8080", "API server URL")
loginCmd.Flags().String("username", "", "Username (skips interactive prompt)")
loginCmd.Flags().String("password", "", "Password (skips interactive prompt)")
}
2 changes: 1 addition & 1 deletion deploy/helm/kuberless/templates/httproute.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{- if .Values.gateway.enabled }}
{{- if and .Values.gateway.enabled .Values.gateway.gatewayName }}
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
Expand Down
2 changes: 1 addition & 1 deletion deploy/postgresql.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ spec:
spec:
containers:
- name: postgresql
image: docker.io/library/postgres:16
image: mirror.gcr.io/library/postgres:16
ports:
- containerPort: 5432
env:
Expand Down
4 changes: 3 additions & 1 deletion hack/openshift-values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ ingress:

# Gateway API is used on OpenShift instead of standard Ingress.
# Set gatewayName and gatewayNamespace to match your Envoy Gateway install.
# gatewayName is required -- leaving it empty will cause HTTPRoute validation to fail.
# Example: --set gateway.gatewayName=public --set gateway.hostname=kuberless.apps.<cluster>.example.com
gateway:
enabled: true
gatewayName: ""
gatewayName: "public"
gatewayNamespace: envoy-gateway-system
hostname: kuberless.example.com
appsDomain: example.com
Expand Down
12 changes: 6 additions & 6 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ run-frontend:

# Build all Go images locally with ko (tagged :latest)
ko-build-local:
KO_DOCKER_REPO=ko.local ko build ./operator --bare --platform={{platforms}} --tags=latest
KO_DOCKER_REPO=ko.local ko build ./apiserver --bare --platform={{platforms}} --tags=latest
KO_DOCKER_REPO=ko.local ko build ./cli --bare --platform={{platforms}} --tags=latest
KO_DOCKER_REPO=ko.local/operator ko build ./operator --bare --platform={{platforms}} --tags=latest
KO_DOCKER_REPO=ko.local/apiserver ko build ./apiserver --bare --platform={{platforms}} --tags=latest
KO_DOCKER_REPO=ko.local/cli ko build ./cli --bare --platform={{platforms}} --tags=latest

# Build and push all Go images with ko (git SHA + latest tags)
ko-build:
Expand Down Expand Up @@ -181,7 +181,7 @@ deploy-knative:
--repo https://knative.github.io/operator knative-operator \
--version {{knative_op_ver}} \
--namespace knative-operator --create-namespace \
--wait --timeout 5m
--wait --timeout 10m
# Create namespace ahead of time so we can apply OpenShift SCC before Knative pods start
kubectl create namespace knative-serving --dry-run=client -o yaml | kubectl apply -f -
if [[ "{{openshift}}" == "true" ]]; then
Expand Down Expand Up @@ -336,9 +336,9 @@ _patch-kourier-nodeport:
set -euo pipefail
echo "Waiting for Kourier service..."
for i in $(seq 1 30); do
if kubectl get svc kourier -n kourier-system &>/dev/null; then
if kubectl get svc kourier -n knative-serving &>/dev/null; then
echo "Patching Kourier service with kind NodePort values..."
kubectl patch service kourier -n kourier-system \
kubectl patch service kourier -n knative-serving \
--type merge \
--patch '{
"spec": {
Expand Down
Loading