Skip to content
Merged
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
9 changes: 8 additions & 1 deletion .github/workflows/controller-kind.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,23 @@ on:

jobs:
deploy-kind:
strategy:
matrix:
method:
- helm
- operator
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Run make deploy
- name: Run make deploy (${{ matrix.method }})
working-directory: controller
run: make deploy
env:
METHOD: ${{ matrix.method }}

e2e-test-operator:
runs-on: ubuntu-latest
Expand Down
33 changes: 32 additions & 1 deletion .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,42 @@ permissions:
contents: read

jobs:
e2e-tests:
changes:
if: github.repository_owner == 'jumpstarter-dev'
runs-on: ubuntu-latest
outputs:
should_run: ${{ steps.filter.outputs.e2e }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: dorny/paths-filter@v3
id: filter
with:
base: ${{ github.base_ref || github.event.merge_group.base_ref || 'main' }}
filters: |
e2e:
- 'controller/**'
- 'e2e/**'
- 'python/**'
- '.github/workflows/e2e.yaml'
- 'Makefile'
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
mangelajo marked this conversation as resolved.

e2e-tests:
needs: changes
if: needs.changes.outputs.should_run == 'true' || github.event_name == 'workflow_dispatch'
strategy:
matrix:
os:
- ubuntu-24.04
- ubuntu-24.04-arm
method:
- operator
- helm
exclude:
# Only run operator on ARM, skip helm
- os: ubuntu-24.04-arm
method: helm
runs-on: ${{ matrix.os }}
timeout-minutes: 60
steps:
Expand All @@ -37,8 +66,10 @@ jobs:
run: make e2e-setup
env:
CI: true
METHOD: ${{ matrix.method }}

- name: Run e2e tests
run: make e2e-run
env:
CI: true
METHOD: ${{ matrix.method }}
16 changes: 11 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
# Subdirectories containing projects
SUBDIRS := python protocol controller e2e

# Deployment method for e2e tests: operator (default) or helm
METHOD ?= operator

# Default target
.PHONY: all
all: build
Expand All @@ -30,6 +33,9 @@ help:
@echo " make e2e-full - Full setup + run (for CI or first time)"
@echo " make e2e-clean - Clean up e2e test environment (delete cluster, certs, etc.)"
@echo ""
@echo " Use METHOD=operator (default) or METHOD=helm to select deployment method"
@echo " Example: make e2e-setup METHOD=helm"
@echo ""
@echo "Per-project targets:"
@echo " make build-<project> - Build specific project"
@echo " make test-<project> - Test specific project"
Expand Down Expand Up @@ -115,14 +121,14 @@ test-controller:
# Setup e2e testing environment (one-time)
.PHONY: e2e-setup
e2e-setup:
@echo "Setting up e2e test environment..."
@bash e2e/setup-e2e.sh
@echo "Setting up e2e test environment (method: $(METHOD))..."
@METHOD=$(METHOD) bash e2e/setup-e2e.sh

# Run e2e tests
.PHONY: e2e-run
e2e-run:
@echo "Running e2e tests..."
@bash e2e/run-e2e.sh
@echo "Running e2e tests (method: $(METHOD))..."
@METHOD=$(METHOD) bash e2e/run-e2e.sh

# Convenience alias for running e2e tests
.PHONY: e2e
Expand All @@ -131,7 +137,7 @@ e2e: e2e-run
# Full e2e setup + run
.PHONY: e2e-full
e2e-full:
@bash e2e/run-e2e.sh --full
@METHOD=$(METHOD) bash e2e/run-e2e.sh --full

# Clean up e2e test environment
.PHONY: e2e-clean
Expand Down
21 changes: 16 additions & 5 deletions controller/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ endif
# tools. (i.e. podman)
CONTAINER_TOOL ?= podman

# Deployment method: operator (default) or helm
METHOD ?= operator

# Setting SHELL to bash allows bash commands to be executed by recipes.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
Expand Down Expand Up @@ -171,15 +174,23 @@ uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified
$(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -

.PHONY: deploy
deploy: docker-build cluster grpcurl
deploy: docker-build cluster grpcurl ## Deploy controller using METHOD (operator or helm)
ifeq ($(METHOD),operator)
$(MAKE) build-operator
./hack/deploy_with_operator.sh
else ifeq ($(METHOD),helm)
./hack/deploy_with_helm.sh
else
$(error Unknown METHOD=$(METHOD). Use 'operator' or 'helm')
endif

# Backward compatibility alias
.PHONY: deploy-with-operator
deploy-with-operator: docker-build build-operator cluster grpcurl
./hack/deploy_with_operator.sh
deploy-with-operator:
$(MAKE) deploy METHOD=operator

.PHONY: deploy-operator
deploy-operator: docker-build build-operator cluster grpcurl
deploy-operator: docker-build build-operator cluster grpcurl ## Deploy only the operator (without Jumpstarter CR)
NETWORKING_MODE=ingress DEPLOY_JUMPSTARTER=false ./hack/deploy_with_operator.sh

.PHONY: test-operator-e2e
Expand All @@ -191,7 +202,7 @@ operator-logs:

.PHONY: deploy-with-operator-parallel
deploy-with-operator-parallel:
make deploy-with-operator -j5 --output-sync=target
make deploy METHOD=operator -j5 --output-sync=target

.PHONY: deploy-exporters
deploy-exporters:
Expand Down
13 changes: 13 additions & 0 deletions controller/deploy/operator/api/v1alpha1/jumpstarter_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,19 @@ type AuthenticationConfig struct {
// Enables authentication using external JWT tokens from OIDC providers.
// Supports multiple JWT authenticators for different identity providers.
JWT []apiserverv1beta1.JWTAuthenticator `json:"jwt,omitempty"`

// Automatic user provisioning configuration, this is useful for creating
// users authenticated by external identity providers in Jumpstarter.
AutoProvisioning AutoProvisioningConfig `json:"autoProvisioning,omitempty"`
}

// AutoProvisioningConfig defines auto provisioning configuration.
type AutoProvisioningConfig struct {
// Enable auto provisioning.
// When disabled, users authenticated by external identity providers will
// not be automatically created in Jumpstarter.
// +kubebuilder:default=false
Enabled bool `json:"enabled,omitempty"`
}

// InternalAuthConfig defines the built-in authentication configuration.
Expand Down
16 changes: 16 additions & 0 deletions controller/deploy/operator/api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ metadata:
}
]
capabilities: Basic Install
createdAt: "2026-01-28T15:18:09Z"
createdAt: "2026-01-30T11:40:29Z"
operators.operatorframework.io/builder: operator-sdk-v1.41.1
operators.operatorframework.io/project_layout: go.kubebuilder.io/v4
name: jumpstarter-operator.v0.8.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ spec:
Authentication configuration for client and exporter authentication.
Supports multiple authentication methods including internal tokens, Kubernetes tokens, and JWT.
properties:
autoProvisioning:
description: |-
Automatic user provisioning configuration, this is useful for creating
users authenticated by external identity providers in Jumpstarter.
properties:
enabled:
default: false
description: |-
Enable auto provisioning.
When disabled, users authenticated by external identity providers will
not be automatically created in Jumpstarter.
type: boolean
type: object
internal:
description: |-
Internal authentication configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ spec:
Authentication configuration for client and exporter authentication.
Supports multiple authentication methods including internal tokens, Kubernetes tokens, and JWT.
properties:
autoProvisioning:
description: |-
Automatic user provisioning configuration, this is useful for creating
users authenticated by external identity providers in Jumpstarter.
properties:
enabled:
default: false
description: |-
Enable auto provisioning.
When disabled, users authenticated by external identity providers will
not be automatically created in Jumpstarter.
type: boolean
type: object
internal:
description: |-
Internal authentication configuration.
Expand Down
13 changes: 13 additions & 0 deletions controller/deploy/operator/dist/install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,19 @@ spec:
Authentication configuration for client and exporter authentication.
Supports multiple authentication methods including internal tokens, Kubernetes tokens, and JWT.
properties:
autoProvisioning:
description: |-
Automatic user provisioning configuration, this is useful for creating
users authenticated by external identity providers in Jumpstarter.
properties:
enabled:
default: false
description: |-
Enable auto provisioning.
When disabled, users authenticated by external identity providers will
not be automatically created in Jumpstarter.
type: boolean
type: object
internal:
description: |-
Internal authentication configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,7 @@ func (r *JumpstarterReconciler) createConfigMap(jumpstarter *operatorv1alpha1.Ju
func (r *JumpstarterReconciler) buildConfig(jumpstarter *operatorv1alpha1.Jumpstarter) config.Config {
cfg := config.Config{
Provisioning: config.Provisioning{
Enabled: false,
Enabled: jumpstarter.Spec.Authentication.AutoProvisioning.Enabled,
},
Grpc: config.Grpc{
Keepalive: config.Keepalive{
Expand Down
32 changes: 32 additions & 0 deletions controller/deploy/operator/test/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,38 @@ provisioning:
}, 1*time.Minute).Should(ContainSubstring("router.jumpstarter.127.0.0.1.nip.io:5443"))
})

It("should update provisioning config when autoProvisioning is enabled", func() {
By("updating the Jumpstarter CR to enable auto provisioning")
jumpstarter := &operatorv1alpha1.Jumpstarter{}
err := k8sClient.Get(ctx, types.NamespacedName{
Name: "jumpstarter",
Namespace: dynamicTestNamespace,
}, jumpstarter)
Expect(err).NotTo(HaveOccurred())

jumpstarter.Spec.Authentication.AutoProvisioning.Enabled = true
err = k8sClient.Update(ctx, jumpstarter)
Expect(err).NotTo(HaveOccurred())

By("verifying the ConfigMap contains provisioning.enabled: true")
Eventually(func(g Gomega) {
configmap := &corev1.ConfigMap{}
err := k8sClient.Get(ctx, types.NamespacedName{
Name: "jumpstarter-controller",
Namespace: dynamicTestNamespace,
}, configmap)
g.Expect(err).NotTo(HaveOccurred())

var configObj map[string]interface{}
err = yaml.Unmarshal([]byte(configmap.Data["config"]), &configObj)
g.Expect(err).NotTo(HaveOccurred())

provisioning, ok := configObj["provisioning"].(map[string]interface{})
g.Expect(ok).To(BeTrue())
g.Expect(provisioning["enabled"]).To(Equal(true))
}, 1*time.Minute).Should(Succeed())
})

It("should allow access to ingress grpc endpoints", func() {
// TODO: fix ingress in kind (not working for helm either)
Skip("nginx ingress not working in kind")
Expand Down
Loading
Loading