diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 312d3337..77aaaa95 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -26,6 +26,9 @@ jobs: - image_name: jumpstarter-dev/jumpstarter-operator dockerfile: Dockerfile.operator context: . + - image_name: jumpstarter-dev/jumpstarter-operator-bundle + dockerfile: bundle.Dockerfile + context: deploy/operator steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/check-bundle.yaml b/.github/workflows/check-bundle.yaml new file mode 100644 index 00000000..b988051c --- /dev/null +++ b/.github/workflows/check-bundle.yaml @@ -0,0 +1,95 @@ +name: Check Bundle +on: + pull_request: + branches: + - main + - 'release-*' + +jobs: + check-bundle: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.24 + + - name: Cache bin directory (deploy/operator) + uses: actions/cache@v4 + with: + path: deploy/operator/bin/ + key: ${{ runner.os }}-operator-bin-${{ hashFiles('deploy/operator/go.mod') }} + restore-keys: | + ${{ runner.os }}-operator-bin- + + - name: Get version + run: | + if [ "${{ github.event_name }}" == "pull_request" ]; then + BASE_BRANCH="${{ github.base_ref }}" + if [ "$BASE_BRANCH" == "main" ]; then + TAG="latest" + elif [[ "$BASE_BRANCH" =~ ^release- ]]; then + TAG="$BASE_BRANCH" + else + echo "::error::Unknown base branch: $BASE_BRANCH" + exit 1 + fi + else + echo "::error::Unsupported event: ${{ github.event_name }}" + exit 1 + fi + echo "TAG=${TAG}" >> $GITHUB_ENV + echo "TAG=${TAG}" + + - name: Run make bundle + working-directory: deploy/operator + run: | + make bundle IMG="quay.io/jumpstarter-dev/jumpstarter-operator:${TAG}" + + - name: Check for uncommitted changes + run: | + DIFF=$(git diff) + if [ -n "$DIFF" ]; then + # Filter out createdAt timestamp lines and context lines, check if any actual changes remain + FILTERED_DIFF=$(echo "$DIFF" | grep -vE '^(---|\+\+\+|@@|index|diff)' | grep -vE '^[+-].*createdAt:.*[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z' || true) + # Check if there are any non-timestamp, non-context changes + if [ -n "$FILTERED_DIFF" ] && [ -n "$(echo "$FILTERED_DIFF" | grep -E '^[+-]' || true)" ]; then + echo "::error::Uncommitted changes detected after running 'make bundle'. Please commit all bundle changes before pushing." + echo "::error::This can be done by running 'make bundle IMG=\"quay.io/jumpstarter-dev/jumpstarter-operator:${TAG}\"" + git diff + exit 1 + else + echo "Only timestamp changes detected (ignored). Bundle files are up to date." + # Reset the timestamp changes to keep the repo clean + git checkout -- . + fi + else + echo "No uncommitted changes detected. Bundle files are up to date." + fi + + - name: Ensure clean state before build-installer + run: | + # Reset any remaining changes from root + git checkout -- . || true + + - name: Run make build-installer + working-directory: deploy/operator + run: | + make build-installer + + - name: Check for uncommitted changes after build-installer + run: | + if [ -n "$(git diff)" ]; then + echo "::error::Uncommitted changes detected after running 'make build-installer'. Please commit all installer changes before pushing." + echo "::error::This can be done by running 'make build-installer'" + git diff + exit 1 + else + echo "No uncommitted changes detected. Installer files are up to date." + fi + diff --git a/api/v1alpha1/client_types.go b/api/v1alpha1/client_types.go index a278dcd8..73a3d6a0 100644 --- a/api/v1alpha1/client_types.go +++ b/api/v1alpha1/client_types.go @@ -31,8 +31,7 @@ type ClientSpec struct { // ClientStatus defines the observed state of Identity type ClientStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + // Status field for the clients Credential *corev1.LocalObjectReference `json:"credential,omitempty"` Endpoint string `json:"endpoint,omitempty"` } diff --git a/api/v1alpha1/exporter_types.go b/api/v1alpha1/exporter_types.go index f5ec63c8..9f17dd99 100644 --- a/api/v1alpha1/exporter_types.go +++ b/api/v1alpha1/exporter_types.go @@ -31,8 +31,7 @@ type ExporterSpec struct { // ExporterStatus defines the observed state of Exporter type ExporterStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + // Exporter status fields Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` Credential *corev1.LocalObjectReference `json:"credential,omitempty"` Devices []Device `json:"devices,omitempty"` diff --git a/api/v1alpha1/exporteraccesspolicy_types.go b/api/v1alpha1/exporteraccesspolicy_types.go index 4e10d262..864a33c7 100644 --- a/api/v1alpha1/exporteraccesspolicy_types.go +++ b/api/v1alpha1/exporteraccesspolicy_types.go @@ -42,8 +42,7 @@ type ExporterAccessPolicySpec struct { // ExporterAccessPolicyStatus defines the observed state of ExporterAccessPolicy. type ExporterAccessPolicyStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + // Status field for the exporter access policies } // +kubebuilder:object:root=true diff --git a/deploy/helm/jumpstarter/crds/jumpstarter.dev_clients.yaml b/deploy/helm/jumpstarter/crds/jumpstarter.dev_clients.yaml index ae94bdfe..d9dd6d0c 100644 --- a/deploy/helm/jumpstarter/crds/jumpstarter.dev_clients.yaml +++ b/deploy/helm/jumpstarter/crds/jumpstarter.dev_clients.yaml @@ -46,9 +46,7 @@ spec: description: ClientStatus defines the observed state of Identity properties: credential: - description: |- - INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - Important: Run "make" to regenerate code after modifying this file + description: Status field for the clients properties: name: default: "" diff --git a/deploy/helm/jumpstarter/crds/jumpstarter.dev_exporters.yaml b/deploy/helm/jumpstarter/crds/jumpstarter.dev_exporters.yaml index 6383bbf9..931c28b0 100644 --- a/deploy/helm/jumpstarter/crds/jumpstarter.dev_exporters.yaml +++ b/deploy/helm/jumpstarter/crds/jumpstarter.dev_exporters.yaml @@ -46,9 +46,7 @@ spec: description: ExporterStatus defines the observed state of Exporter properties: conditions: - description: |- - INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - Important: Run "make" to regenerate code after modifying this file + description: Exporter status fields items: description: Condition contains details for one aspect of the current state of this API Resource. diff --git a/deploy/operator/Makefile b/deploy/operator/Makefile index 55f8a08a..b87499da 100644 --- a/deploy/operator/Makefile +++ b/deploy/operator/Makefile @@ -3,7 +3,7 @@ # To re-generate a bundle for another specific version without changing the standard setup, you can: # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) -VERSION ?= 0.0.1 +VERSION ?= 0.8.0 # CHANNELS define the bundle channels used in the bundle. # Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") diff --git a/deploy/operator/api/v1alpha1/jumpstarter_types.go b/deploy/operator/api/v1alpha1/jumpstarter_types.go index af9d4271..db9149da 100644 --- a/deploy/operator/api/v1alpha1/jumpstarter_types.go +++ b/deploy/operator/api/v1alpha1/jumpstarter_types.go @@ -514,8 +514,7 @@ type ClusterIPConfig struct { // This field is currently empty but can be extended to include status information // such as deployment status, endpoint URLs, and health information. type JumpstarterStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + // Jumpstarter deployment status } // +kubebuilder:object:root=true diff --git a/deploy/operator/bundle.Dockerfile b/deploy/operator/bundle.Dockerfile new file mode 100644 index 00000000..5d5e689a --- /dev/null +++ b/deploy/operator/bundle.Dockerfile @@ -0,0 +1,20 @@ +FROM scratch + +# Core bundle labels. +LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 +LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ +LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ +LABEL operators.operatorframework.io.bundle.package.v1=jumpstarter-operator +LABEL operators.operatorframework.io.bundle.channels.v1=alpha +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.41.1 +LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 +LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v4 + +# Labels for testing. +LABEL operators.operatorframework.io.test.mediatype.v1=scorecard+v1 +LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ + +# Copy files to locations specified by labels. +COPY bundle/manifests /manifests/ +COPY bundle/metadata /metadata/ +COPY bundle/tests/scorecard /tests/scorecard/ diff --git a/deploy/operator/bundle/manifests/jumpstarter-operator-controller-manager-metrics-service_v1_service.yaml b/deploy/operator/bundle/manifests/jumpstarter-operator-controller-manager-metrics-service_v1_service.yaml new file mode 100644 index 00000000..a6a7e3b8 --- /dev/null +++ b/deploy/operator/bundle/manifests/jumpstarter-operator-controller-manager-metrics-service_v1_service.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + control-plane: controller-manager + name: jumpstarter-operator-controller-manager-metrics-service +spec: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: jumpstarter-operator + control-plane: controller-manager +status: + loadBalancer: {} diff --git a/deploy/operator/bundle/manifests/jumpstarter-operator-jumpstarter-admin-role_rbac.authorization.k8s.io_v1_clusterrole.yaml b/deploy/operator/bundle/manifests/jumpstarter-operator-jumpstarter-admin-role_rbac.authorization.k8s.io_v1_clusterrole.yaml new file mode 100644 index 00000000..c8e11263 --- /dev/null +++ b/deploy/operator/bundle/manifests/jumpstarter-operator-jumpstarter-admin-role_rbac.authorization.k8s.io_v1_clusterrole.yaml @@ -0,0 +1,21 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + name: jumpstarter-operator-jumpstarter-admin-role +rules: +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters + verbs: + - '*' +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters/status + verbs: + - get diff --git a/deploy/operator/bundle/manifests/jumpstarter-operator-jumpstarter-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml b/deploy/operator/bundle/manifests/jumpstarter-operator-jumpstarter-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml new file mode 100644 index 00000000..5bb9255f --- /dev/null +++ b/deploy/operator/bundle/manifests/jumpstarter-operator-jumpstarter-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml @@ -0,0 +1,27 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + name: jumpstarter-operator-jumpstarter-editor-role +rules: +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters/status + verbs: + - get diff --git a/deploy/operator/bundle/manifests/jumpstarter-operator-jumpstarter-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml b/deploy/operator/bundle/manifests/jumpstarter-operator-jumpstarter-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml new file mode 100644 index 00000000..df3c96e6 --- /dev/null +++ b/deploy/operator/bundle/manifests/jumpstarter-operator-jumpstarter-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml @@ -0,0 +1,23 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + name: jumpstarter-operator-jumpstarter-viewer-role +rules: +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters + verbs: + - get + - list + - watch +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters/status + verbs: + - get diff --git a/deploy/operator/bundle/manifests/jumpstarter-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml b/deploy/operator/bundle/manifests/jumpstarter-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml new file mode 100644 index 00000000..262b6509 --- /dev/null +++ b/deploy/operator/bundle/manifests/jumpstarter-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + name: jumpstarter-operator-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get diff --git a/deploy/operator/bundle/manifests/jumpstarter-operator.clusterserviceversion.yaml b/deploy/operator/bundle/manifests/jumpstarter-operator.clusterserviceversion.yaml new file mode 100644 index 00000000..e34de0ab --- /dev/null +++ b/deploy/operator/bundle/manifests/jumpstarter-operator.clusterserviceversion.yaml @@ -0,0 +1,394 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: |- + [ + { + "apiVersion": "operator.jumpstarter.dev/v1alpha1", + "kind": "Jumpstarter", + "metadata": { + "labels": { + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "jumpstarter-operator" + }, + "name": "jumpstarter-sample" + }, + "spec": null + } + ] + capabilities: Basic Install + createdAt: "2025-11-25T08:56:27Z" + operators.operatorframework.io/builder: operator-sdk-v1.41.1 + operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 + name: jumpstarter-operator.v0.8.0 + namespace: placeholder +spec: + apiservicedefinitions: {} + customresourcedefinitions: + owned: + - kind: Client + name: clients.jumpstarter.dev + version: v1alpha1 + - kind: ExporterAccessPolicy + name: exporteraccesspolicies.jumpstarter.dev + version: v1alpha1 + - kind: Exporter + name: exporters.jumpstarter.dev + version: v1alpha1 + - description: Jumpstarter is the Schema for the jumpstarters API. + displayName: Jumpstarter + kind: Jumpstarter + name: jumpstarters.operator.jumpstarter.dev + version: v1alpha1 + - kind: Lease + name: leases.jumpstarter.dev + version: v1alpha1 + description: Jumpstarter is a cloud-native framework for Hardware-in-the-Loop (HIL) + automation that bridges the gap between embedded development workflows and real-world + deployment environments. This operator installs and manages the Jumpstarter Controller, + which acts as the central brain for your testing infrastructure. It orchestrates + secure, shared access to physical hardware and virtual devices (represented as + "exporters") directly from your Kubernetes or OpenShift cluster. + displayName: Jumpstarter Operator + icon: + - base64data: "" + mediatype: "" + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - "" + resources: + - configmaps + - secrets + - serviceaccounts + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - "" + resources: + - services/status + verbs: + - get + - patch + - update + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - apps + resources: + - deployments/status + verbs: + - get + - patch + - update + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - jumpstarter.dev + resources: + - clients + - exporteraccesspolicies + - exporters + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - jumpstarter.dev + resources: + - clients/finalizers + - exporteraccesspolicies/finalizers + - exporters/finalizers + - leases/finalizers + verbs: + - update + - apiGroups: + - jumpstarter.dev + resources: + - clients/status + - exporteraccesspolicies/status + - exporters/status + - leases/status + verbs: + - get + - patch + - update + - apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - get + - patch + - update + - apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters/finalizers + verbs: + - update + - apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters/status + verbs: + - get + - patch + - update + - apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + - roles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - route.openshift.io + resources: + - routes/custom-host + verbs: + - create + - get + - patch + - update + - apiGroups: + - route.openshift.io + resources: + - routes/status + verbs: + - get + - patch + - update + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + serviceAccountName: jumpstarter-operator-controller-manager + deployments: + - label: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + control-plane: controller-manager + name: jumpstarter-operator-controller-manager + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: jumpstarter-operator + control-plane: controller-manager + strategy: {} + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: manager + labels: + app.kubernetes.io/name: jumpstarter-operator + control-plane: controller-manager + spec: + containers: + - args: + - --metrics-bind-address=:8443 + - --leader-elect + - --health-probe-bind-address=:8081 + command: + - /manager + image: quay.io/jumpstarter-dev/jumpstarter-operator:latest + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 10m + memory: 256Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + serviceAccountName: jumpstarter-operator-controller-manager + terminationGracePeriodSeconds: 10 + permissions: + - rules: + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + serviceAccountName: jumpstarter-operator-controller-manager + strategy: deployment + installModes: + - supported: false + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - hil + - hardware-in-the-loop + - hardware + - device + - embedded + - testing + - framework + links: + - name: Jumpstarter Operator + url: https://jumpstarter.dev/main/getting-started/installation/service/index.html + maintainers: + - email: majopela@redhat.com + name: Miguel Angel Ajo + maturity: alpha + provider: + name: The Jumpstarter Community + url: https://jumpstarter.dev + version: 0.8.0 diff --git a/deploy/operator/bundle/manifests/jumpstarter.dev_clients.yaml b/deploy/operator/bundle/manifests/jumpstarter.dev_clients.yaml new file mode 100644 index 00000000..399fc327 --- /dev/null +++ b/deploy/operator/bundle/manifests/jumpstarter.dev_clients.yaml @@ -0,0 +1,75 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.3 + creationTimestamp: null + name: clients.jumpstarter.dev +spec: + group: jumpstarter.dev + names: + kind: Client + listKind: ClientList + plural: clients + singular: client + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Client is the Schema for the identities API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ClientSpec defines the desired state of Identity + properties: + username: + type: string + type: object + status: + description: ClientStatus defines the observed state of Identity + properties: + credential: + description: Status field for the clients + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + endpoint: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/deploy/operator/bundle/manifests/jumpstarter.dev_exporteraccesspolicies.yaml b/deploy/operator/bundle/manifests/jumpstarter.dev_exporteraccesspolicies.yaml new file mode 100644 index 00000000..d4d27fb0 --- /dev/null +++ b/deploy/operator/bundle/manifests/jumpstarter.dev_exporteraccesspolicies.yaml @@ -0,0 +1,172 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.3 + creationTimestamp: null + name: exporteraccesspolicies.jumpstarter.dev +spec: + group: jumpstarter.dev + names: + kind: ExporterAccessPolicy + listKind: ExporterAccessPolicyList + plural: exporteraccesspolicies + singular: exporteraccesspolicy + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ExporterAccessPolicy is the Schema for the exporteraccesspolicies + API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ExporterAccessPolicySpec defines the desired state of ExporterAccessPolicy. + properties: + exporterSelector: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + policies: + items: + properties: + from: + items: + properties: + clientSelector: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + maximumDuration: + type: string + priority: + type: integer + spotAccess: + type: boolean + type: object + type: array + type: object + status: + description: ExporterAccessPolicyStatus defines the observed state of + ExporterAccessPolicy. + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/deploy/operator/bundle/manifests/jumpstarter.dev_exporters.yaml b/deploy/operator/bundle/manifests/jumpstarter.dev_exporters.yaml new file mode 100644 index 00000000..c111bd68 --- /dev/null +++ b/deploy/operator/bundle/manifests/jumpstarter.dev_exporters.yaml @@ -0,0 +1,166 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.3 + creationTimestamp: null + name: exporters.jumpstarter.dev +spec: + group: jumpstarter.dev + names: + kind: Exporter + listKind: ExporterList + plural: exporters + singular: exporter + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Exporter is the Schema for the exporters API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ExporterSpec defines the desired state of Exporter + properties: + username: + type: string + type: object + status: + description: ExporterStatus defines the observed state of Exporter + properties: + conditions: + description: Exporter status fields + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + credential: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + devices: + items: + properties: + labels: + additionalProperties: + type: string + type: object + parent_uuid: + type: string + uuid: + type: string + type: object + type: array + endpoint: + type: string + lastSeen: + format: date-time + type: string + leaseRef: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/deploy/operator/bundle/manifests/jumpstarter.dev_leases.yaml b/deploy/operator/bundle/manifests/jumpstarter.dev_leases.yaml new file mode 100644 index 00000000..2dfde9bb --- /dev/null +++ b/deploy/operator/bundle/manifests/jumpstarter.dev_leases.yaml @@ -0,0 +1,241 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.3 + creationTimestamp: null + name: leases.jumpstarter.dev +spec: + group: jumpstarter.dev + names: + kind: Lease + listKind: LeaseList + plural: leases + singular: lease + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.ended + name: Ended + type: boolean + - jsonPath: .spec.clientRef.name + name: Client + type: string + - jsonPath: .status.exporterRef.name + name: Exporter + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Lease is the Schema for the exporters API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: LeaseSpec defines the desired state of Lease + properties: + beginTime: + description: |- + Requested start time. If omitted, lease starts when exporter is acquired. + Immutable after lease starts (cannot change the past). + format: date-time + type: string + clientRef: + description: The client that is requesting the lease + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + duration: + description: |- + Duration of the lease. Must be positive when provided. + Can be omitted (nil) when both BeginTime and EndTime are provided, + in which case it's calculated as EndTime - BeginTime. + type: string + endTime: + description: |- + Requested end time. If specified with BeginTime, Duration is calculated. + Can be updated to extend or shorten active leases. + format: date-time + type: string + release: + description: The release flag requests the controller to end the lease + now + type: boolean + selector: + description: The selector for the exporter to be used + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - clientRef + - selector + type: object + status: + description: LeaseStatus defines the observed state of Lease + properties: + beginTime: + description: |- + If the lease has been acquired an exporter name is assigned + and then it can be used, it will be empty while still pending + format: date-time + type: string + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + endTime: + format: date-time + type: string + ended: + type: boolean + exporterRef: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + priority: + type: integer + spotAccess: + type: boolean + required: + - ended + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/deploy/operator/bundle/manifests/operator.jumpstarter.dev_jumpstarters.yaml b/deploy/operator/bundle/manifests/operator.jumpstarter.dev_jumpstarters.yaml new file mode 100644 index 00000000..7b28cb83 --- /dev/null +++ b/deploy/operator/bundle/manifests/operator.jumpstarter.dev_jumpstarters.yaml @@ -0,0 +1,1914 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + creationTimestamp: null + name: jumpstarters.operator.jumpstarter.dev +spec: + group: operator.jumpstarter.dev + names: + kind: Jumpstarter + listKind: JumpstarterList + plural: jumpstarters + singular: jumpstarter + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Jumpstarter is the Schema for the jumpstarters API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + JumpstarterSpec defines the desired state of a Jumpstarter deployment. A deployment + can be created in a namespace of the cluster, and that's where all the Jumpstarter + resources and services will reside. + properties: + authentication: + description: |- + Authentication configuration for client and exporter authentication. + Supports multiple authentication methods including internal tokens, Kubernetes tokens, and JWT. + properties: + internal: + description: |- + Internal authentication configuration. + Built-in authenticator that issues tokens for clients and exporters. + This is the simplest authentication method and is enabled by default. + properties: + enabled: + default: true + description: |- + Enable the internal authentication method. + When disabled, clients cannot use internal tokens for authentication. + type: boolean + prefix: + default: 'internal:' + description: |- + Prefix to add to the subject claim of issued tokens. + Helps distinguish internal tokens from other authentication methods. + Example: "internal:" will result in subjects like "internal:user123" + maxLength: 50 + type: string + tokenLifetime: + default: 43800h + description: |- + Token validity duration for issued tokens. + After this duration, tokens expire and must be renewed. + type: string + type: object + jwt: + description: |- + JWT authentication configuration. + Enables authentication using external JWT tokens from OIDC providers. + Supports multiple JWT authenticators for different identity providers. + items: + description: JWTAuthenticator provides the configuration for + a single JWT authenticator. + properties: + claimMappings: + description: claimMappings points claims of a token to be + treated as user attributes. + properties: + extra: + description: |- + extra represents an option for the extra attribute. + expression must produce a string or string array value. + If the value is empty, the extra mapping will not be present. + + hard-coded extra key/value + - key: "foo" + valueExpression: "'bar'" + This will result in an extra attribute - foo: ["bar"] + + hard-coded key, value copying claim value + - key: "foo" + valueExpression: "claims.some_claim" + This will result in an extra attribute - foo: [value of some_claim] + + hard-coded key, value derived from claim value + - key: "admin" + valueExpression: '(has(claims.is_admin) && claims.is_admin) ? "true":""' + This will result in: + - if is_admin claim is present and true, extra attribute - admin: ["true"] + - if is_admin claim is present and false or is_admin claim is not present, no extra attribute will be added + items: + description: ExtraMapping provides the configuration + for a single extra mapping. + properties: + key: + description: |- + key is a string to use as the extra attribute key. + key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid + subdomain as defined by RFC 1123. All characters trailing the first "/" must + be valid HTTP Path characters as defined by RFC 3986. + key must be lowercase. + Required to be unique. + type: string + valueExpression: + description: |- + valueExpression is a CEL expression to extract extra attribute value. + valueExpression must produce a string or string array value. + "", [], and null values are treated as the extra mapping not being present. + Empty string values contained within a string array are filtered out. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + type: string + required: + - key + - valueExpression + type: object + type: array + groups: + description: |- + groups represents an option for the groups attribute. + The claim's value must be a string or string array claim. + If groups.claim is set, the prefix must be specified (and can be the empty string). + If groups.expression is set, the expression must produce a string or string array value. + "", [], and null values are treated as the group mapping not being present. + properties: + claim: + description: |- + claim is the JWT claim to use. + Mutually exclusive with expression. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim and prefix. + type: string + prefix: + description: |- + prefix is prepended to claim's value to prevent clashes with existing names. + prefix needs to be set if claim is set and can be the empty string. + Mutually exclusive with expression. + type: string + type: object + uid: + description: |- + uid represents an option for the uid attribute. + Claim must be a singular string claim. + If uid.expression is set, the expression must produce a string value. + properties: + claim: + description: |- + claim is the JWT claim to use. + Either claim or expression must be set. + Mutually exclusive with expression. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim. + type: string + type: object + username: + description: |- + username represents an option for the username attribute. + The claim's value must be a singular string. + Same as the --oidc-username-claim and --oidc-username-prefix flags. + If username.expression is set, the expression must produce a string value. + If username.expression uses 'claims.email', then 'claims.email_verified' must be used in + username.expression or extra[*].valueExpression or claimValidationRules[*].expression. + An example claim validation rule expression that matches the validation automatically + applied when username.claim is set to 'email' is 'claims.?email_verified.orValue(true) == true'. By explicitly comparing + the value to true, we let type-checking see the result will be a boolean, and to make sure a non-boolean email_verified + claim will be caught at runtime. + + In the flag based approach, the --oidc-username-claim and --oidc-username-prefix are optional. If --oidc-username-claim is not set, + the default value is "sub". For the authentication config, there is no defaulting for claim or prefix. The claim and prefix must be set explicitly. + For claim, if --oidc-username-claim was not set with legacy flag approach, configure username.claim="sub" in the authentication config. + For prefix: + (1) --oidc-username-prefix="-", no prefix was added to the username. For the same behavior using authentication config, + set username.prefix="" + (2) --oidc-username-prefix="" and --oidc-username-claim != "email", prefix was "#". For the same + behavior using authentication config, set username.prefix="#" + (3) --oidc-username-prefix="". For the same behavior using authentication config, set username.prefix="" + properties: + claim: + description: |- + claim is the JWT claim to use. + Mutually exclusive with expression. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim and prefix. + type: string + prefix: + description: |- + prefix is prepended to claim's value to prevent clashes with existing names. + prefix needs to be set if claim is set and can be the empty string. + Mutually exclusive with expression. + type: string + type: object + required: + - username + type: object + claimValidationRules: + description: claimValidationRules are rules that are applied + to validate token claims to authenticate users. + items: + description: ClaimValidationRule provides the configuration + for a single claim validation rule. + properties: + claim: + description: |- + claim is the name of a required claim. + Same as --oidc-required-claim flag. + Only string claim keys are supported. + Mutually exclusive with expression and message. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + Must produce a boolean. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + Must return true for the validation to pass. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim and requiredValue. + type: string + message: + description: |- + message customizes the returned error message when expression returns false. + message is a literal string. + Mutually exclusive with claim and requiredValue. + type: string + requiredValue: + description: |- + requiredValue is the value of a required claim. + Same as --oidc-required-claim flag. + Only string claim values are supported. + If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string. + Mutually exclusive with expression and message. + type: string + type: object + type: array + issuer: + description: issuer contains the basic OIDC provider connection + options. + properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy defines how the "audiences" field is used to match the "aud" claim in the presented JWT. + Allowed values are: + 1. "MatchAny" when multiple audiences are specified and + 2. empty (or unset) or "MatchAny" when a single audience is specified. + + - MatchAny: the "aud" claim in the presented JWT must match at least one of the entries in the "audiences" field. + For example, if "audiences" is ["foo", "bar"], the "aud" claim in the presented JWT must contain either "foo" or "bar" (and may contain both). + + - "": The match policy can be empty (or unset) when a single audience is specified in the "audiences" field. The "aud" claim in the presented JWT must contain the single audience (and may contain others). + + For more nuanced audience validation, use claimValidationRules. + example: claimValidationRule[].expression: 'sets.equivalent(claims.aud, ["bar", "foo", "baz"])' to require an exact match. + type: string + audiences: + description: |- + audiences is the set of acceptable audiences the JWT must be issued to. + At least one of the entries must match the "aud" claim in presented JWTs. + Same value as the --oidc-client-id flag (though this field supports an array). + Required to be non-empty. + items: + type: string + type: array + certificateAuthority: + description: |- + certificateAuthority contains PEM-encoded certificate authority certificates + used to validate the connection when fetching discovery information. + If unset, the system verifier is used. + Same value as the content of the file referenced by the --oidc-ca-file flag. + type: string + discoveryURL: + description: |- + discoveryURL, if specified, overrides the URL used to fetch discovery + information instead of using "{url}/.well-known/openid-configuration". + The exact value specified is used, so "/.well-known/openid-configuration" + must be included in discoveryURL if needed. + + The "issuer" field in the fetched discovery information must match the "issuer.url" field + in the AuthenticationConfiguration and will be used to validate the "iss" claim in the presented JWT. + This is for scenarios where the well-known and jwks endpoints are hosted at a different + location than the issuer (such as locally in the cluster). + + Example: + A discovery url that is exposed using kubernetes service 'oidc' in namespace 'oidc-namespace' + and discovery information is available at '/.well-known/openid-configuration'. + discoveryURL: "https://oidc.oidc-namespace/.well-known/openid-configuration" + certificateAuthority is used to verify the TLS connection and the hostname on the leaf certificate + must be set to 'oidc.oidc-namespace'. + + curl https://oidc.oidc-namespace/.well-known/openid-configuration (.discoveryURL field) + { + issuer: "https://oidc.example.com" (.url field) + } + + discoveryURL must be different from url. + Required to be unique across all JWT authenticators. + Note that egress selection configuration is not used for this network connection. + type: string + url: + description: |- + url points to the issuer URL in a format https://url or https://url/path. + This must match the "iss" claim in the presented JWT, and the issuer returned from discovery. + Same value as the --oidc-issuer-url flag. + Discovery information is fetched from "{url}/.well-known/openid-configuration" unless overridden by discoveryURL. + Required to be unique across all JWT authenticators. + Note that egress selection configuration is not used for this network connection. + type: string + required: + - audiences + - url + type: object + userValidationRules: + description: |- + userValidationRules are rules that are applied to final user before completing authentication. + These allow invariants to be applied to incoming identities such as preventing the + use of the system: prefix that is commonly used by Kubernetes components. + The validation rules are logically ANDed together and must all return true for the validation to pass. + items: + description: UserValidationRule provides the configuration + for a single user info validation rule. + properties: + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + Must return true for the validation to pass. + + CEL expressions have access to the contents of UserInfo, organized into CEL variable: + - 'user' - authentication.k8s.io/v1, Kind=UserInfo object + Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition. + API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + type: string + message: + description: |- + message customizes the returned error message when rule returns false. + message is a literal string. + type: string + required: + - expression + type: object + type: array + required: + - claimMappings + - issuer + type: object + type: array + k8s: + description: |- + Kubernetes authentication configuration. + Enables authentication using Kubernetes service account tokens. + Useful for integrating with existing Kubernetes RBAC policies. + properties: + enabled: + default: false + description: |- + Enable Kubernetes authentication. + When enabled, clients can authenticate using Kubernetes service account tokens. + type: boolean + type: object + type: object + baseDomain: + description: |- + Base domain used to construct FQDNs for all service endpoints. + This domain will be used to generate the default hostnames for Routes, Ingresses, and certificates. + Example: "example.com" will generate endpoints like "grpc.example.com", "router.example.com" + pattern: ^[a-z0-9]([a-z0-9\-\.]*[a-z0-9])?$ + type: string + controller: + description: |- + Controller configuration for the main Jumpstarter API and gRPC services. + The controller handles gRPC and REST API requests from clients and exporters. + properties: + authentication: + description: |- + Authentication configuration for client and exporter authentication. + Configures how clients and exporters can authenticate with Jumpstarter. + Supports multiple authentication methods including internal tokens, Kubernetes tokens, and JWT. + properties: + internal: + description: |- + Internal authentication configuration. + Built-in authenticator that issues tokens for clients and exporters. + This is the simplest authentication method and is enabled by default. + properties: + enabled: + default: true + description: |- + Enable the internal authentication method. + When disabled, clients cannot use internal tokens for authentication. + type: boolean + prefix: + default: 'internal:' + description: |- + Prefix to add to the subject claim of issued tokens. + Helps distinguish internal tokens from other authentication methods. + Example: "internal:" will result in subjects like "internal:user123" + maxLength: 50 + type: string + tokenLifetime: + default: 43800h + description: |- + Token validity duration for issued tokens. + After this duration, tokens expire and must be renewed. + type: string + type: object + jwt: + description: |- + JWT authentication configuration. + Enables authentication using external JWT tokens from OIDC providers. + Supports multiple JWT authenticators for different identity providers. + items: + description: JWTAuthenticator provides the configuration + for a single JWT authenticator. + properties: + claimMappings: + description: claimMappings points claims of a token + to be treated as user attributes. + properties: + extra: + description: |- + extra represents an option for the extra attribute. + expression must produce a string or string array value. + If the value is empty, the extra mapping will not be present. + + hard-coded extra key/value + - key: "foo" + valueExpression: "'bar'" + This will result in an extra attribute - foo: ["bar"] + + hard-coded key, value copying claim value + - key: "foo" + valueExpression: "claims.some_claim" + This will result in an extra attribute - foo: [value of some_claim] + + hard-coded key, value derived from claim value + - key: "admin" + valueExpression: '(has(claims.is_admin) && claims.is_admin) ? "true":""' + This will result in: + - if is_admin claim is present and true, extra attribute - admin: ["true"] + - if is_admin claim is present and false or is_admin claim is not present, no extra attribute will be added + items: + description: ExtraMapping provides the configuration + for a single extra mapping. + properties: + key: + description: |- + key is a string to use as the extra attribute key. + key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid + subdomain as defined by RFC 1123. All characters trailing the first "/" must + be valid HTTP Path characters as defined by RFC 3986. + key must be lowercase. + Required to be unique. + type: string + valueExpression: + description: |- + valueExpression is a CEL expression to extract extra attribute value. + valueExpression must produce a string or string array value. + "", [], and null values are treated as the extra mapping not being present. + Empty string values contained within a string array are filtered out. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + type: string + required: + - key + - valueExpression + type: object + type: array + groups: + description: |- + groups represents an option for the groups attribute. + The claim's value must be a string or string array claim. + If groups.claim is set, the prefix must be specified (and can be the empty string). + If groups.expression is set, the expression must produce a string or string array value. + "", [], and null values are treated as the group mapping not being present. + properties: + claim: + description: |- + claim is the JWT claim to use. + Mutually exclusive with expression. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim and prefix. + type: string + prefix: + description: |- + prefix is prepended to claim's value to prevent clashes with existing names. + prefix needs to be set if claim is set and can be the empty string. + Mutually exclusive with expression. + type: string + type: object + uid: + description: |- + uid represents an option for the uid attribute. + Claim must be a singular string claim. + If uid.expression is set, the expression must produce a string value. + properties: + claim: + description: |- + claim is the JWT claim to use. + Either claim or expression must be set. + Mutually exclusive with expression. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim. + type: string + type: object + username: + description: |- + username represents an option for the username attribute. + The claim's value must be a singular string. + Same as the --oidc-username-claim and --oidc-username-prefix flags. + If username.expression is set, the expression must produce a string value. + If username.expression uses 'claims.email', then 'claims.email_verified' must be used in + username.expression or extra[*].valueExpression or claimValidationRules[*].expression. + An example claim validation rule expression that matches the validation automatically + applied when username.claim is set to 'email' is 'claims.?email_verified.orValue(true) == true'. By explicitly comparing + the value to true, we let type-checking see the result will be a boolean, and to make sure a non-boolean email_verified + claim will be caught at runtime. + + In the flag based approach, the --oidc-username-claim and --oidc-username-prefix are optional. If --oidc-username-claim is not set, + the default value is "sub". For the authentication config, there is no defaulting for claim or prefix. The claim and prefix must be set explicitly. + For claim, if --oidc-username-claim was not set with legacy flag approach, configure username.claim="sub" in the authentication config. + For prefix: + (1) --oidc-username-prefix="-", no prefix was added to the username. For the same behavior using authentication config, + set username.prefix="" + (2) --oidc-username-prefix="" and --oidc-username-claim != "email", prefix was "#". For the same + behavior using authentication config, set username.prefix="#" + (3) --oidc-username-prefix="". For the same behavior using authentication config, set username.prefix="" + properties: + claim: + description: |- + claim is the JWT claim to use. + Mutually exclusive with expression. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim and prefix. + type: string + prefix: + description: |- + prefix is prepended to claim's value to prevent clashes with existing names. + prefix needs to be set if claim is set and can be the empty string. + Mutually exclusive with expression. + type: string + type: object + required: + - username + type: object + claimValidationRules: + description: claimValidationRules are rules that are + applied to validate token claims to authenticate users. + items: + description: ClaimValidationRule provides the configuration + for a single claim validation rule. + properties: + claim: + description: |- + claim is the name of a required claim. + Same as --oidc-required-claim flag. + Only string claim keys are supported. + Mutually exclusive with expression and message. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + Must produce a boolean. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + Must return true for the validation to pass. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim and requiredValue. + type: string + message: + description: |- + message customizes the returned error message when expression returns false. + message is a literal string. + Mutually exclusive with claim and requiredValue. + type: string + requiredValue: + description: |- + requiredValue is the value of a required claim. + Same as --oidc-required-claim flag. + Only string claim values are supported. + If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string. + Mutually exclusive with expression and message. + type: string + type: object + type: array + issuer: + description: issuer contains the basic OIDC provider + connection options. + properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy defines how the "audiences" field is used to match the "aud" claim in the presented JWT. + Allowed values are: + 1. "MatchAny" when multiple audiences are specified and + 2. empty (or unset) or "MatchAny" when a single audience is specified. + + - MatchAny: the "aud" claim in the presented JWT must match at least one of the entries in the "audiences" field. + For example, if "audiences" is ["foo", "bar"], the "aud" claim in the presented JWT must contain either "foo" or "bar" (and may contain both). + + - "": The match policy can be empty (or unset) when a single audience is specified in the "audiences" field. The "aud" claim in the presented JWT must contain the single audience (and may contain others). + + For more nuanced audience validation, use claimValidationRules. + example: claimValidationRule[].expression: 'sets.equivalent(claims.aud, ["bar", "foo", "baz"])' to require an exact match. + type: string + audiences: + description: |- + audiences is the set of acceptable audiences the JWT must be issued to. + At least one of the entries must match the "aud" claim in presented JWTs. + Same value as the --oidc-client-id flag (though this field supports an array). + Required to be non-empty. + items: + type: string + type: array + certificateAuthority: + description: |- + certificateAuthority contains PEM-encoded certificate authority certificates + used to validate the connection when fetching discovery information. + If unset, the system verifier is used. + Same value as the content of the file referenced by the --oidc-ca-file flag. + type: string + discoveryURL: + description: |- + discoveryURL, if specified, overrides the URL used to fetch discovery + information instead of using "{url}/.well-known/openid-configuration". + The exact value specified is used, so "/.well-known/openid-configuration" + must be included in discoveryURL if needed. + + The "issuer" field in the fetched discovery information must match the "issuer.url" field + in the AuthenticationConfiguration and will be used to validate the "iss" claim in the presented JWT. + This is for scenarios where the well-known and jwks endpoints are hosted at a different + location than the issuer (such as locally in the cluster). + + Example: + A discovery url that is exposed using kubernetes service 'oidc' in namespace 'oidc-namespace' + and discovery information is available at '/.well-known/openid-configuration'. + discoveryURL: "https://oidc.oidc-namespace/.well-known/openid-configuration" + certificateAuthority is used to verify the TLS connection and the hostname on the leaf certificate + must be set to 'oidc.oidc-namespace'. + + curl https://oidc.oidc-namespace/.well-known/openid-configuration (.discoveryURL field) + { + issuer: "https://oidc.example.com" (.url field) + } + + discoveryURL must be different from url. + Required to be unique across all JWT authenticators. + Note that egress selection configuration is not used for this network connection. + type: string + url: + description: |- + url points to the issuer URL in a format https://url or https://url/path. + This must match the "iss" claim in the presented JWT, and the issuer returned from discovery. + Same value as the --oidc-issuer-url flag. + Discovery information is fetched from "{url}/.well-known/openid-configuration" unless overridden by discoveryURL. + Required to be unique across all JWT authenticators. + Note that egress selection configuration is not used for this network connection. + type: string + required: + - audiences + - url + type: object + userValidationRules: + description: |- + userValidationRules are rules that are applied to final user before completing authentication. + These allow invariants to be applied to incoming identities such as preventing the + use of the system: prefix that is commonly used by Kubernetes components. + The validation rules are logically ANDed together and must all return true for the validation to pass. + items: + description: UserValidationRule provides the configuration + for a single user info validation rule. + properties: + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + Must return true for the validation to pass. + + CEL expressions have access to the contents of UserInfo, organized into CEL variable: + - 'user' - authentication.k8s.io/v1, Kind=UserInfo object + Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition. + API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + type: string + message: + description: |- + message customizes the returned error message when rule returns false. + message is a literal string. + type: string + required: + - expression + type: object + type: array + required: + - claimMappings + - issuer + type: object + type: array + k8s: + description: |- + Kubernetes authentication configuration. + Enables authentication using Kubernetes service account tokens. + Useful for integrating with existing Kubernetes RBAC policies. + properties: + enabled: + default: false + description: |- + Enable Kubernetes authentication. + When enabled, clients can authenticate using Kubernetes service account tokens. + type: boolean + type: object + type: object + exporterOptions: + description: |- + Exporter options configuration. + Controls how exporters connect and behave when communicating with the controller. + properties: + offlineTimeout: + default: 180s + description: |- + Offline timeout duration for exporters. + After this duration without communication, an exporter is considered offline. + This drives the online/offline status field of exporters, and offline exporters + won't be considered for leases. + type: string + type: object + grpc: + description: |- + gRPC configuration for controller endpoints. + Defines how controller gRPC services are exposed and configured. + properties: + endpoints: + description: |- + List of gRPC endpoints to expose. + Each endpoint can use different networking methods (Route, Ingress, NodePort, or LoadBalancer) + based on your cluster setup. Example: Use Route for OpenShift, Ingress for standard Kubernetes. + items: + description: |- + Endpoint defines a single endpoint configuration. + An endpoint can use one or more networking methods: Route, Ingress, NodePort, or LoadBalancer. + Multiple methods can be configured simultaneously for the same address. + properties: + address: + description: |- + Address for this endpoint in the format "hostname", "hostname:port", "IPv4", "IPv4:port", "[IPv6]", or "[IPv6]:port". + Required for Route and Ingress endpoints. Optional for NodePort and LoadBalancer endpoints. + When optional, the address is used for certificate generation and DNS resolution. + Supports templating with $(replica) for replica-specific addresses. + Examples: "grpc.example.com", "grpc.example.com:9090", "192.168.1.1:8080", "[2001:db8::1]:8443", "router-$(replica).example.com" + pattern: ^(\[[0-9a-fA-F:\.]+\]|[0-9]+(\.[0-9]+){3}|[a-z0-9$]([a-z0-9\-\.\$\(\)]*[a-z0-9\)])?)(:[0-9]+)?$ + type: string + clusterIP: + description: |- + ClusterIP configuration for internal service access. + Creates a ClusterIP service for this endpoint. + Useful for internal service-to-service communication or when + using a different method to expose the service externally. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the ClusterIP service. + Useful for configuring service-specific behavior and load balancer options. + type: object + enabled: + description: |- + Enable the ClusterIP service for this endpoint. + When disabled, no ClusterIP service will be created for this endpoint. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the ClusterIP service. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + ingress: + description: |- + Ingress configuration for standard Kubernetes clusters. + Creates an Ingress resource for this endpoint. + Requires an ingress controller to be installed. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the Kubernetes Ingress resource. + Useful for configuring ingress-specific behavior, TLS settings, and load balancer options. + type: object + class: + default: default + description: |- + Ingress class name for the Kubernetes Ingress. + Specifies which ingress controller should handle this ingress. + type: string + enabled: + description: |- + Enable the Kubernetes Ingress for this endpoint. + When disabled, no Ingress resource will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the Kubernetes Ingress resource. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + loadBalancer: + description: |- + LoadBalancer configuration for cloud environments. + Creates a LoadBalancer service for this endpoint. + Requires cloud provider support for LoadBalancer services. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the LoadBalancer service. + Useful for configuring cloud provider-specific load balancer options. + Example: "service.beta.kubernetes.io/aws-load-balancer-type: nlb" + type: object + enabled: + description: |- + Enable the LoadBalancer service for this endpoint. + When disabled, no LoadBalancer service will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the LoadBalancer service. + Useful for monitoring, cost allocation, and resource organization. + type: object + port: + description: |- + Port number for the LoadBalancer service. + Must be a valid port number (1-65535). + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + nodeport: + description: |- + NodePort configuration for direct node access. + Exposes the service on a specific port on each node. + Useful for bare-metal or simple cluster setups. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the NodePort service. + Useful for configuring service-specific behavior and load balancer options. + type: object + enabled: + description: |- + Enable the NodePort service for this endpoint. + When disabled, no NodePort service will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the NodePort service. + Useful for monitoring, cost allocation, and resource organization. + type: object + port: + description: |- + NodePort port number to expose on each node. + Must be in the range 30000-32767 for most Kubernetes clusters. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + route: + description: |- + Route configuration for OpenShift clusters. + Creates an OpenShift Route resource for this endpoint. + Only applicable in OpenShift environments. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the OpenShift Route resource. + Useful for configuring route-specific behavior and TLS settings. + type: object + enabled: + description: |- + Enable the OpenShift Route for this endpoint. + When disabled, no Route resource will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the OpenShift Route resource. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + type: object + type: array + keepalive: + description: |- + Keepalive configuration for gRPC connections. + Controls connection health checks and idle connection management. + Helps maintain stable connections in load-balanced environments. + properties: + intervalTime: + default: 10s + description: |- + Interval between keepalive pings. + How often to send keepalive pings to check connection health. This is important + to keep TCP gRPC connections alive when traversing load balancers and proxies. + type: string + maxConnectionAge: + description: |- + Maximum age of a connection before it is closed and recreated. + Helps prevent issues with long-lived connections. It defaults to infinity. + type: string + maxConnectionAgeGrace: + description: |- + Grace period for closing connections that exceed MaxConnectionAge. + Allows ongoing RPCs to complete before closing the connection. + type: string + maxConnectionIdle: + description: |- + Maximum time a connection can remain idle before being closed. + It defaults to infinity. + type: string + minTime: + default: 1s + description: |- + Minimum time between keepalives that the connection will accept, under this threshold + the other side will get a GOAWAY signal. + Prevents excessive keepalive traffic on the network. + type: string + permitWithoutStream: + default: true + description: |- + Allow keepalive pings even when there are no active RPC streams. + Useful for detecting connection issues in idle connections. + This is important to keep TCP gRPC connections alive when traversing + load balancers and proxies. + type: boolean + timeout: + default: 180s + description: |- + Timeout for keepalive ping acknowledgment. + If a ping is not acknowledged within this time, the connection is considered broken. + The default is high to avoid issues when the network on a exporter is overloaded, i.e. + during flashing. + type: string + type: object + tls: + description: |- + TLS configuration for secure gRPC communication. + Requires a Kubernetes secret containing the TLS certificate and private key. + If useCertManager is enabled, this secret will be automatically created. + See also: spec.useCertManager for automatic certificate management. + properties: + certSecret: + description: |- + Name of the Kubernetes secret containing the TLS certificate and private key. + The secret must contain 'tls.crt' and 'tls.key' keys. + If useCertManager is enabled, this secret will be automatically created. + pattern: ^[a-z0-9]([a-z0-9\-\.]*[a-z0-9])?$ + type: string + type: object + type: object + image: + description: |- + Container image for the controller pods in 'registry/repository/image:tag' format. + If not specified, defaults to the latest stable version of the Jumpstarter controller. + type: string + imagePullPolicy: + default: IfNotPresent + description: |- + Image pull policy for the controller container. + Controls when the container image should be pulled from the registry. + enum: + - Always + - IfNotPresent + - Never + type: string + replicas: + default: 2 + description: |- + Number of controller replicas to run. + Must be a positive integer. Minimum recommended value is 2 for high availability. + format: int32 + minimum: 1 + type: integer + resources: + description: |- + Resource requirements for controller pods. + Defines CPU and memory requests and limits for each controller pod. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restApi: + description: |- + REST API configuration for HTTP-based clients. + Enables non-gRPC clients to interact with Jumpstarter for listing leases, + managing exporters, and creating new leases. Use this when you need HTTP/JSON access. + properties: + endpoints: + description: |- + List of REST API endpoints to expose. + Each endpoint can use different networking methods (Route, Ingress, NodePort, or LoadBalancer) + based on your cluster setup. + items: + description: |- + Endpoint defines a single endpoint configuration. + An endpoint can use one or more networking methods: Route, Ingress, NodePort, or LoadBalancer. + Multiple methods can be configured simultaneously for the same address. + properties: + address: + description: |- + Address for this endpoint in the format "hostname", "hostname:port", "IPv4", "IPv4:port", "[IPv6]", or "[IPv6]:port". + Required for Route and Ingress endpoints. Optional for NodePort and LoadBalancer endpoints. + When optional, the address is used for certificate generation and DNS resolution. + Supports templating with $(replica) for replica-specific addresses. + Examples: "grpc.example.com", "grpc.example.com:9090", "192.168.1.1:8080", "[2001:db8::1]:8443", "router-$(replica).example.com" + pattern: ^(\[[0-9a-fA-F:\.]+\]|[0-9]+(\.[0-9]+){3}|[a-z0-9$]([a-z0-9\-\.\$\(\)]*[a-z0-9\)])?)(:[0-9]+)?$ + type: string + clusterIP: + description: |- + ClusterIP configuration for internal service access. + Creates a ClusterIP service for this endpoint. + Useful for internal service-to-service communication or when + using a different method to expose the service externally. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the ClusterIP service. + Useful for configuring service-specific behavior and load balancer options. + type: object + enabled: + description: |- + Enable the ClusterIP service for this endpoint. + When disabled, no ClusterIP service will be created for this endpoint. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the ClusterIP service. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + ingress: + description: |- + Ingress configuration for standard Kubernetes clusters. + Creates an Ingress resource for this endpoint. + Requires an ingress controller to be installed. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the Kubernetes Ingress resource. + Useful for configuring ingress-specific behavior, TLS settings, and load balancer options. + type: object + class: + default: default + description: |- + Ingress class name for the Kubernetes Ingress. + Specifies which ingress controller should handle this ingress. + type: string + enabled: + description: |- + Enable the Kubernetes Ingress for this endpoint. + When disabled, no Ingress resource will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the Kubernetes Ingress resource. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + loadBalancer: + description: |- + LoadBalancer configuration for cloud environments. + Creates a LoadBalancer service for this endpoint. + Requires cloud provider support for LoadBalancer services. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the LoadBalancer service. + Useful for configuring cloud provider-specific load balancer options. + Example: "service.beta.kubernetes.io/aws-load-balancer-type: nlb" + type: object + enabled: + description: |- + Enable the LoadBalancer service for this endpoint. + When disabled, no LoadBalancer service will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the LoadBalancer service. + Useful for monitoring, cost allocation, and resource organization. + type: object + port: + description: |- + Port number for the LoadBalancer service. + Must be a valid port number (1-65535). + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + nodeport: + description: |- + NodePort configuration for direct node access. + Exposes the service on a specific port on each node. + Useful for bare-metal or simple cluster setups. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the NodePort service. + Useful for configuring service-specific behavior and load balancer options. + type: object + enabled: + description: |- + Enable the NodePort service for this endpoint. + When disabled, no NodePort service will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the NodePort service. + Useful for monitoring, cost allocation, and resource organization. + type: object + port: + description: |- + NodePort port number to expose on each node. + Must be in the range 30000-32767 for most Kubernetes clusters. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + route: + description: |- + Route configuration for OpenShift clusters. + Creates an OpenShift Route resource for this endpoint. + Only applicable in OpenShift environments. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the OpenShift Route resource. + Useful for configuring route-specific behavior and TLS settings. + type: object + enabled: + description: |- + Enable the OpenShift Route for this endpoint. + When disabled, no Route resource will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the OpenShift Route resource. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + type: object + type: array + tls: + description: |- + TLS configuration for secure HTTP communication. + Requires a Kubernetes secret containing the TLS certificate and private key. + properties: + certSecret: + description: |- + Name of the Kubernetes secret containing the TLS certificate and private key. + The secret must contain 'tls.crt' and 'tls.key' keys. + If useCertManager is enabled, this secret will be automatically created. + pattern: ^[a-z0-9]([a-z0-9\-\.]*[a-z0-9])?$ + type: string + type: object + type: object + type: object + routers: + description: |- + Router configuration for the Jumpstarter router service. + Routers handle gRPC traffic routing and load balancing. + properties: + grpc: + description: |- + gRPC configuration for router endpoints. + Defines how router gRPC services are exposed and configured. + properties: + endpoints: + description: |- + List of gRPC endpoints to expose. + Each endpoint can use different networking methods (Route, Ingress, NodePort, or LoadBalancer) + based on your cluster setup. Example: Use Route for OpenShift, Ingress for standard Kubernetes. + items: + description: |- + Endpoint defines a single endpoint configuration. + An endpoint can use one or more networking methods: Route, Ingress, NodePort, or LoadBalancer. + Multiple methods can be configured simultaneously for the same address. + properties: + address: + description: |- + Address for this endpoint in the format "hostname", "hostname:port", "IPv4", "IPv4:port", "[IPv6]", or "[IPv6]:port". + Required for Route and Ingress endpoints. Optional for NodePort and LoadBalancer endpoints. + When optional, the address is used for certificate generation and DNS resolution. + Supports templating with $(replica) for replica-specific addresses. + Examples: "grpc.example.com", "grpc.example.com:9090", "192.168.1.1:8080", "[2001:db8::1]:8443", "router-$(replica).example.com" + pattern: ^(\[[0-9a-fA-F:\.]+\]|[0-9]+(\.[0-9]+){3}|[a-z0-9$]([a-z0-9\-\.\$\(\)]*[a-z0-9\)])?)(:[0-9]+)?$ + type: string + clusterIP: + description: |- + ClusterIP configuration for internal service access. + Creates a ClusterIP service for this endpoint. + Useful for internal service-to-service communication or when + using a different method to expose the service externally. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the ClusterIP service. + Useful for configuring service-specific behavior and load balancer options. + type: object + enabled: + description: |- + Enable the ClusterIP service for this endpoint. + When disabled, no ClusterIP service will be created for this endpoint. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the ClusterIP service. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + ingress: + description: |- + Ingress configuration for standard Kubernetes clusters. + Creates an Ingress resource for this endpoint. + Requires an ingress controller to be installed. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the Kubernetes Ingress resource. + Useful for configuring ingress-specific behavior, TLS settings, and load balancer options. + type: object + class: + default: default + description: |- + Ingress class name for the Kubernetes Ingress. + Specifies which ingress controller should handle this ingress. + type: string + enabled: + description: |- + Enable the Kubernetes Ingress for this endpoint. + When disabled, no Ingress resource will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the Kubernetes Ingress resource. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + loadBalancer: + description: |- + LoadBalancer configuration for cloud environments. + Creates a LoadBalancer service for this endpoint. + Requires cloud provider support for LoadBalancer services. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the LoadBalancer service. + Useful for configuring cloud provider-specific load balancer options. + Example: "service.beta.kubernetes.io/aws-load-balancer-type: nlb" + type: object + enabled: + description: |- + Enable the LoadBalancer service for this endpoint. + When disabled, no LoadBalancer service will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the LoadBalancer service. + Useful for monitoring, cost allocation, and resource organization. + type: object + port: + description: |- + Port number for the LoadBalancer service. + Must be a valid port number (1-65535). + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + nodeport: + description: |- + NodePort configuration for direct node access. + Exposes the service on a specific port on each node. + Useful for bare-metal or simple cluster setups. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the NodePort service. + Useful for configuring service-specific behavior and load balancer options. + type: object + enabled: + description: |- + Enable the NodePort service for this endpoint. + When disabled, no NodePort service will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the NodePort service. + Useful for monitoring, cost allocation, and resource organization. + type: object + port: + description: |- + NodePort port number to expose on each node. + Must be in the range 30000-32767 for most Kubernetes clusters. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + route: + description: |- + Route configuration for OpenShift clusters. + Creates an OpenShift Route resource for this endpoint. + Only applicable in OpenShift environments. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the OpenShift Route resource. + Useful for configuring route-specific behavior and TLS settings. + type: object + enabled: + description: |- + Enable the OpenShift Route for this endpoint. + When disabled, no Route resource will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the OpenShift Route resource. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + type: object + type: array + keepalive: + description: |- + Keepalive configuration for gRPC connections. + Controls connection health checks and idle connection management. + Helps maintain stable connections in load-balanced environments. + properties: + intervalTime: + default: 10s + description: |- + Interval between keepalive pings. + How often to send keepalive pings to check connection health. This is important + to keep TCP gRPC connections alive when traversing load balancers and proxies. + type: string + maxConnectionAge: + description: |- + Maximum age of a connection before it is closed and recreated. + Helps prevent issues with long-lived connections. It defaults to infinity. + type: string + maxConnectionAgeGrace: + description: |- + Grace period for closing connections that exceed MaxConnectionAge. + Allows ongoing RPCs to complete before closing the connection. + type: string + maxConnectionIdle: + description: |- + Maximum time a connection can remain idle before being closed. + It defaults to infinity. + type: string + minTime: + default: 1s + description: |- + Minimum time between keepalives that the connection will accept, under this threshold + the other side will get a GOAWAY signal. + Prevents excessive keepalive traffic on the network. + type: string + permitWithoutStream: + default: true + description: |- + Allow keepalive pings even when there are no active RPC streams. + Useful for detecting connection issues in idle connections. + This is important to keep TCP gRPC connections alive when traversing + load balancers and proxies. + type: boolean + timeout: + default: 180s + description: |- + Timeout for keepalive ping acknowledgment. + If a ping is not acknowledged within this time, the connection is considered broken. + The default is high to avoid issues when the network on a exporter is overloaded, i.e. + during flashing. + type: string + type: object + tls: + description: |- + TLS configuration for secure gRPC communication. + Requires a Kubernetes secret containing the TLS certificate and private key. + If useCertManager is enabled, this secret will be automatically created. + See also: spec.useCertManager for automatic certificate management. + properties: + certSecret: + description: |- + Name of the Kubernetes secret containing the TLS certificate and private key. + The secret must contain 'tls.crt' and 'tls.key' keys. + If useCertManager is enabled, this secret will be automatically created. + pattern: ^[a-z0-9]([a-z0-9\-\.]*[a-z0-9])?$ + type: string + type: object + type: object + image: + description: |- + Container image for the router pods in 'registry/repository/image:tag' format. + If not specified, defaults to the latest stable version of the Jumpstarter router. + type: string + imagePullPolicy: + default: IfNotPresent + description: |- + Image pull policy for the router container. + Controls when the container image should be pulled from the registry. + enum: + - Always + - IfNotPresent + - Never + type: string + replicas: + default: 3 + description: |- + Number of router replicas to run. + Must be a positive integer. Minimum recommended value is 3 for high availability. + format: int32 + minimum: 1 + type: integer + resources: + description: |- + Resource requirements for router pods. + Defines CPU and memory requests and limits for each router pod. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + topologySpreadConstraints: + description: |- + Topology spread constraints for router pod distribution. + Ensures router pods are distributed evenly across nodes and zones. + Useful for high availability and fault tolerance. + items: + description: TopologySpreadConstraint specifies how to spread + matching pods among the given topology. + properties: + labelSelector: + description: |- + LabelSelector is used to find matching pods. + Pods that match this label selector are counted to determine the number of pods + in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select the pods over which + spreading will be calculated. The keys are used to lookup values from the + incoming pod labels, those key-value labels are ANDed with labelSelector + to select the group of existing pods over which spreading will be calculated + for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + MatchLabelKeys cannot be set when LabelSelector isn't set. + Keys that don't exist in the incoming pod labels will + be ignored. A null or empty list means only match against labelSelector. + + This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: |- + MaxSkew describes the degree to which pods may be unevenly distributed. + When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference + between the number of matching pods in the target topology and the global minimum. + The global minimum is the minimum number of matching pods in an eligible domain + or zero if the number of eligible domains is less than MinDomains. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 2/2/1: + In this case, the global minimum is 1. + | zone1 | zone2 | zone3 | + | P P | P P | P | + - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; + scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) + violate MaxSkew(1). + - if MaxSkew is 2, incoming pod can be scheduled onto any zone. + When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence + to topologies that satisfy it. + It's a required field. Default value is 1 and 0 is not allowed. + format: int32 + type: integer + minDomains: + description: |- + MinDomains indicates a minimum number of eligible domains. + When the number of eligible domains with matching topology keys is less than minDomains, + Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed. + And when the number of eligible domains with matching topology keys equals or greater than minDomains, + this value has no effect on scheduling. + As a result, when the number of eligible domains is less than minDomains, + scheduler won't schedule more than maxSkew Pods to those domains. + If value is nil, the constraint behaves as if MinDomains is equal to 1. + Valid values are integers greater than 0. + When value is not nil, WhenUnsatisfiable must be DoNotSchedule. + + For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same + labelSelector spread as 2/2/2: + | zone1 | zone2 | zone3 | + | P P | P P | P P | + The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0. + In this situation, new pod with the same labelSelector cannot be scheduled, + because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, + it will violate MaxSkew. + format: int32 + type: integer + nodeAffinityPolicy: + description: |- + NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector + when calculating pod topology spread skew. Options are: + - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. + - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. + + If this value is nil, the behavior is equivalent to the Honor policy. + type: string + nodeTaintsPolicy: + description: |- + NodeTaintsPolicy indicates how we will treat node taints when calculating + pod topology spread skew. Options are: + - Honor: nodes without taints, along with tainted nodes for which the incoming pod + has a toleration, are included. + - Ignore: node taints are ignored. All nodes are included. + + If this value is nil, the behavior is equivalent to the Ignore policy. + type: string + topologyKey: + description: |- + TopologyKey is the key of node labels. Nodes that have a label with this key + and identical values are considered to be in the same topology. + We consider each as a "bucket", and try to put balanced number + of pods into each bucket. + We define a domain as a particular instance of a topology. + Also, we define an eligible domain as a domain whose nodes meet the requirements of + nodeAffinityPolicy and nodeTaintsPolicy. + e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology. + And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology. + It's a required field. + type: string + whenUnsatisfiable: + description: |- + WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy + the spread constraint. + - DoNotSchedule (default) tells the scheduler not to schedule it. + - ScheduleAnyway tells the scheduler to schedule the pod in any location, + but giving higher precedence to topologies that would help reduce the + skew. + A constraint is considered "Unsatisfiable" for an incoming pod + if and only if every possible node assignment for that pod would violate + "MaxSkew" on some topology. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 3/1/1: + | zone1 | zone2 | zone3 | + | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled + to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies + MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler + won't make it *more* imbalanced. + It's a required field. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + type: object + useCertManager: + default: true + description: |- + Enable automatic TLS certificate management using cert-manager. + When enabled, jumpstarter will interact with cert-manager to automatically provision + and renew TLS certificates for all endpoints. Requires cert-manager to be installed in the cluster. + type: boolean + type: object + status: + description: |- + JumpstarterStatus defines the observed state of Jumpstarter. + This field is currently empty but can be extended to include status information + such as deployment status, endpoint URLs, and health information. + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/deploy/operator/bundle/metadata/annotations.yaml b/deploy/operator/bundle/metadata/annotations.yaml new file mode 100644 index 00000000..bf1f6fb8 --- /dev/null +++ b/deploy/operator/bundle/metadata/annotations.yaml @@ -0,0 +1,14 @@ +annotations: + # Core bundle annotations. + operators.operatorframework.io.bundle.mediatype.v1: registry+v1 + operators.operatorframework.io.bundle.manifests.v1: manifests/ + operators.operatorframework.io.bundle.metadata.v1: metadata/ + operators.operatorframework.io.bundle.package.v1: jumpstarter-operator + operators.operatorframework.io.bundle.channels.v1: alpha + operators.operatorframework.io.metrics.builder: operator-sdk-v1.41.1 + operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 + operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v4 + + # Annotations for testing. + operators.operatorframework.io.test.mediatype.v1: scorecard+v1 + operators.operatorframework.io.test.config.v1: tests/scorecard/ diff --git a/deploy/operator/bundle/tests/scorecard/config.yaml b/deploy/operator/bundle/tests/scorecard/config.yaml new file mode 100644 index 00000000..6ffe8227 --- /dev/null +++ b/deploy/operator/bundle/tests/scorecard/config.yaml @@ -0,0 +1,70 @@ +apiVersion: scorecard.operatorframework.io/v1alpha3 +kind: Configuration +metadata: + name: config +stages: +- parallel: true + tests: + - entrypoint: + - scorecard-test + - basic-check-spec + image: quay.io/operator-framework/scorecard-test:v1.41.1 + labels: + suite: basic + test: basic-check-spec-test + storage: + spec: + mountPath: {} + - entrypoint: + - scorecard-test + - olm-bundle-validation + image: quay.io/operator-framework/scorecard-test:v1.41.1 + labels: + suite: olm + test: olm-bundle-validation-test + storage: + spec: + mountPath: {} + - entrypoint: + - scorecard-test + - olm-crds-have-validation + image: quay.io/operator-framework/scorecard-test:v1.41.1 + labels: + suite: olm + test: olm-crds-have-validation-test + storage: + spec: + mountPath: {} + - entrypoint: + - scorecard-test + - olm-crds-have-resources + image: quay.io/operator-framework/scorecard-test:v1.41.1 + labels: + suite: olm + test: olm-crds-have-resources-test + storage: + spec: + mountPath: {} + - entrypoint: + - scorecard-test + - olm-spec-descriptors + image: quay.io/operator-framework/scorecard-test:v1.41.1 + labels: + suite: olm + test: olm-spec-descriptors-test + storage: + spec: + mountPath: {} + - entrypoint: + - scorecard-test + - olm-status-descriptors + image: quay.io/operator-framework/scorecard-test:v1.41.1 + labels: + suite: olm + test: olm-status-descriptors-test + storage: + spec: + mountPath: {} +storage: + spec: + mountPath: {} diff --git a/deploy/operator/cmd/main.go b/deploy/operator/cmd/main.go index 284e2857..dd8e2a32 100644 --- a/deploy/operator/cmd/main.go +++ b/deploy/operator/cmd/main.go @@ -26,6 +26,7 @@ import ( // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" + routev1 "github.com/openshift/api/route/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -57,6 +58,9 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(operatorv1alpha1.AddToScheme(scheme)) + + // Register OpenShift Route API + utilruntime.Must(routev1.Install(scheme)) // +kubebuilder:scaffold:scheme } diff --git a/deploy/operator/config/crd/bases/jumpstarter.dev_clients.yaml b/deploy/operator/config/crd/bases/jumpstarter.dev_clients.yaml index ae94bdfe..d9dd6d0c 100644 --- a/deploy/operator/config/crd/bases/jumpstarter.dev_clients.yaml +++ b/deploy/operator/config/crd/bases/jumpstarter.dev_clients.yaml @@ -46,9 +46,7 @@ spec: description: ClientStatus defines the observed state of Identity properties: credential: - description: |- - INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - Important: Run "make" to regenerate code after modifying this file + description: Status field for the clients properties: name: default: "" diff --git a/deploy/operator/config/crd/bases/jumpstarter.dev_exporters.yaml b/deploy/operator/config/crd/bases/jumpstarter.dev_exporters.yaml index 6383bbf9..931c28b0 100644 --- a/deploy/operator/config/crd/bases/jumpstarter.dev_exporters.yaml +++ b/deploy/operator/config/crd/bases/jumpstarter.dev_exporters.yaml @@ -46,9 +46,7 @@ spec: description: ExporterStatus defines the observed state of Exporter properties: conditions: - description: |- - INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - Important: Run "make" to regenerate code after modifying this file + description: Exporter status fields items: description: Condition contains details for one aspect of the current state of this API Resource. diff --git a/deploy/operator/config/manager/manager.yaml b/deploy/operator/config/manager/manager.yaml index 66b270dc..36c28736 100644 --- a/deploy/operator/config/manager/manager.yaml +++ b/deploy/operator/config/manager/manager.yaml @@ -89,10 +89,10 @@ spec: resources: limits: cpu: 500m - memory: 128Mi + memory: 512Mi requests: cpu: 10m - memory: 64Mi + memory: 256Mi volumeMounts: [] volumes: [] serviceAccountName: controller-manager diff --git a/deploy/operator/config/manifests/bases/jumpstarter-operator.clusterserviceversion.yaml b/deploy/operator/config/manifests/bases/jumpstarter-operator.clusterserviceversion.yaml new file mode 100644 index 00000000..7e93f930 --- /dev/null +++ b/deploy/operator/config/manifests/bases/jumpstarter-operator.clusterserviceversion.yaml @@ -0,0 +1,59 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: '[]' + capabilities: Basic Install + name: jumpstarter-operator.v0.0.0 + namespace: placeholder +spec: + apiservicedefinitions: {} + customresourcedefinitions: + owned: + - description: Jumpstarter is the Schema for the jumpstarters API. + displayName: Jumpstarter + kind: Jumpstarter + name: jumpstarters.operator.jumpstarter.dev + version: v1alpha1 + description: Jumpstarter is a cloud-native framework for Hardware-in-the-Loop (HIL) + automation that bridges the gap between embedded development workflows and real-world + deployment environments. This operator installs and manages the Jumpstarter Controller, + which acts as the central brain for your testing infrastructure. It orchestrates + secure, shared access to physical hardware and virtual devices (represented as + "exporters") directly from your Kubernetes or OpenShift cluster. + displayName: Jumpstarter Operator + icon: + - base64data: "" + mediatype: "" + install: + spec: + deployments: null + strategy: "" + installModes: + - supported: false + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - hil + - hardware-in-the-loop + - hardware + - device + - embedded + - testing + - framework + links: + - name: Jumpstarter Operator + url: https://jumpstarter.dev/main/getting-started/installation/service/index.html + maintainers: + - email: majopela@redhat.com + name: Miguel Angel Ajo + maturity: alpha + provider: + name: The Jumpstarter Community + url: https://jumpstarter.dev + version: 0.0.0 diff --git a/deploy/operator/config/rbac/metrics_reader_role.yaml b/deploy/operator/config/rbac/metrics_reader_role.yaml index 51a75db4..ecb2e018 100644 --- a/deploy/operator/config/rbac/metrics_reader_role.yaml +++ b/deploy/operator/config/rbac/metrics_reader_role.yaml @@ -2,6 +2,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: metrics-reader + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator rules: - nonResourceURLs: - "/metrics" diff --git a/deploy/operator/config/rbac/role.yaml b/deploy/operator/config/rbac/role.yaml index d9a8394d..14145e81 100644 --- a/deploy/operator/config/rbac/role.yaml +++ b/deploy/operator/config/rbac/role.yaml @@ -184,6 +184,15 @@ rules: - patch - update - watch +- apiGroups: + - route.openshift.io + resources: + - routes/custom-host + verbs: + - create + - get + - patch + - update - apiGroups: - route.openshift.io resources: diff --git a/deploy/operator/dist/install.yaml b/deploy/operator/dist/install.yaml new file mode 100644 index 00000000..2216c694 --- /dev/null +++ b/deploy/operator/dist/install.yaml @@ -0,0 +1,3033 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + control-plane: controller-manager + name: jumpstarter-operator-system +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.3 + name: clients.jumpstarter.dev +spec: + group: jumpstarter.dev + names: + kind: Client + listKind: ClientList + plural: clients + singular: client + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Client is the Schema for the identities API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ClientSpec defines the desired state of Identity + properties: + username: + type: string + type: object + status: + description: ClientStatus defines the observed state of Identity + properties: + credential: + description: Status field for the clients + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + endpoint: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.3 + name: exporteraccesspolicies.jumpstarter.dev +spec: + group: jumpstarter.dev + names: + kind: ExporterAccessPolicy + listKind: ExporterAccessPolicyList + plural: exporteraccesspolicies + singular: exporteraccesspolicy + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ExporterAccessPolicy is the Schema for the exporteraccesspolicies + API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ExporterAccessPolicySpec defines the desired state of ExporterAccessPolicy. + properties: + exporterSelector: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + policies: + items: + properties: + from: + items: + properties: + clientSelector: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + maximumDuration: + type: string + priority: + type: integer + spotAccess: + type: boolean + type: object + type: array + type: object + status: + description: ExporterAccessPolicyStatus defines the observed state of + ExporterAccessPolicy. + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.3 + name: exporters.jumpstarter.dev +spec: + group: jumpstarter.dev + names: + kind: Exporter + listKind: ExporterList + plural: exporters + singular: exporter + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Exporter is the Schema for the exporters API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ExporterSpec defines the desired state of Exporter + properties: + username: + type: string + type: object + status: + description: ExporterStatus defines the observed state of Exporter + properties: + conditions: + description: Exporter status fields + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + credential: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + devices: + items: + properties: + labels: + additionalProperties: + type: string + type: object + parent_uuid: + type: string + uuid: + type: string + type: object + type: array + endpoint: + type: string + lastSeen: + format: date-time + type: string + leaseRef: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: jumpstarters.operator.jumpstarter.dev +spec: + group: operator.jumpstarter.dev + names: + kind: Jumpstarter + listKind: JumpstarterList + plural: jumpstarters + singular: jumpstarter + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Jumpstarter is the Schema for the jumpstarters API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + JumpstarterSpec defines the desired state of a Jumpstarter deployment. A deployment + can be created in a namespace of the cluster, and that's where all the Jumpstarter + resources and services will reside. + properties: + authentication: + description: |- + Authentication configuration for client and exporter authentication. + Supports multiple authentication methods including internal tokens, Kubernetes tokens, and JWT. + properties: + internal: + description: |- + Internal authentication configuration. + Built-in authenticator that issues tokens for clients and exporters. + This is the simplest authentication method and is enabled by default. + properties: + enabled: + default: true + description: |- + Enable the internal authentication method. + When disabled, clients cannot use internal tokens for authentication. + type: boolean + prefix: + default: 'internal:' + description: |- + Prefix to add to the subject claim of issued tokens. + Helps distinguish internal tokens from other authentication methods. + Example: "internal:" will result in subjects like "internal:user123" + maxLength: 50 + type: string + tokenLifetime: + default: 43800h + description: |- + Token validity duration for issued tokens. + After this duration, tokens expire and must be renewed. + type: string + type: object + jwt: + description: |- + JWT authentication configuration. + Enables authentication using external JWT tokens from OIDC providers. + Supports multiple JWT authenticators for different identity providers. + items: + description: JWTAuthenticator provides the configuration for + a single JWT authenticator. + properties: + claimMappings: + description: claimMappings points claims of a token to be + treated as user attributes. + properties: + extra: + description: |- + extra represents an option for the extra attribute. + expression must produce a string or string array value. + If the value is empty, the extra mapping will not be present. + + hard-coded extra key/value + - key: "foo" + valueExpression: "'bar'" + This will result in an extra attribute - foo: ["bar"] + + hard-coded key, value copying claim value + - key: "foo" + valueExpression: "claims.some_claim" + This will result in an extra attribute - foo: [value of some_claim] + + hard-coded key, value derived from claim value + - key: "admin" + valueExpression: '(has(claims.is_admin) && claims.is_admin) ? "true":""' + This will result in: + - if is_admin claim is present and true, extra attribute - admin: ["true"] + - if is_admin claim is present and false or is_admin claim is not present, no extra attribute will be added + items: + description: ExtraMapping provides the configuration + for a single extra mapping. + properties: + key: + description: |- + key is a string to use as the extra attribute key. + key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid + subdomain as defined by RFC 1123. All characters trailing the first "/" must + be valid HTTP Path characters as defined by RFC 3986. + key must be lowercase. + Required to be unique. + type: string + valueExpression: + description: |- + valueExpression is a CEL expression to extract extra attribute value. + valueExpression must produce a string or string array value. + "", [], and null values are treated as the extra mapping not being present. + Empty string values contained within a string array are filtered out. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + type: string + required: + - key + - valueExpression + type: object + type: array + groups: + description: |- + groups represents an option for the groups attribute. + The claim's value must be a string or string array claim. + If groups.claim is set, the prefix must be specified (and can be the empty string). + If groups.expression is set, the expression must produce a string or string array value. + "", [], and null values are treated as the group mapping not being present. + properties: + claim: + description: |- + claim is the JWT claim to use. + Mutually exclusive with expression. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim and prefix. + type: string + prefix: + description: |- + prefix is prepended to claim's value to prevent clashes with existing names. + prefix needs to be set if claim is set and can be the empty string. + Mutually exclusive with expression. + type: string + type: object + uid: + description: |- + uid represents an option for the uid attribute. + Claim must be a singular string claim. + If uid.expression is set, the expression must produce a string value. + properties: + claim: + description: |- + claim is the JWT claim to use. + Either claim or expression must be set. + Mutually exclusive with expression. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim. + type: string + type: object + username: + description: |- + username represents an option for the username attribute. + The claim's value must be a singular string. + Same as the --oidc-username-claim and --oidc-username-prefix flags. + If username.expression is set, the expression must produce a string value. + If username.expression uses 'claims.email', then 'claims.email_verified' must be used in + username.expression or extra[*].valueExpression or claimValidationRules[*].expression. + An example claim validation rule expression that matches the validation automatically + applied when username.claim is set to 'email' is 'claims.?email_verified.orValue(true) == true'. By explicitly comparing + the value to true, we let type-checking see the result will be a boolean, and to make sure a non-boolean email_verified + claim will be caught at runtime. + + In the flag based approach, the --oidc-username-claim and --oidc-username-prefix are optional. If --oidc-username-claim is not set, + the default value is "sub". For the authentication config, there is no defaulting for claim or prefix. The claim and prefix must be set explicitly. + For claim, if --oidc-username-claim was not set with legacy flag approach, configure username.claim="sub" in the authentication config. + For prefix: + (1) --oidc-username-prefix="-", no prefix was added to the username. For the same behavior using authentication config, + set username.prefix="" + (2) --oidc-username-prefix="" and --oidc-username-claim != "email", prefix was "#". For the same + behavior using authentication config, set username.prefix="#" + (3) --oidc-username-prefix="". For the same behavior using authentication config, set username.prefix="" + properties: + claim: + description: |- + claim is the JWT claim to use. + Mutually exclusive with expression. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim and prefix. + type: string + prefix: + description: |- + prefix is prepended to claim's value to prevent clashes with existing names. + prefix needs to be set if claim is set and can be the empty string. + Mutually exclusive with expression. + type: string + type: object + required: + - username + type: object + claimValidationRules: + description: claimValidationRules are rules that are applied + to validate token claims to authenticate users. + items: + description: ClaimValidationRule provides the configuration + for a single claim validation rule. + properties: + claim: + description: |- + claim is the name of a required claim. + Same as --oidc-required-claim flag. + Only string claim keys are supported. + Mutually exclusive with expression and message. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + Must produce a boolean. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + Must return true for the validation to pass. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim and requiredValue. + type: string + message: + description: |- + message customizes the returned error message when expression returns false. + message is a literal string. + Mutually exclusive with claim and requiredValue. + type: string + requiredValue: + description: |- + requiredValue is the value of a required claim. + Same as --oidc-required-claim flag. + Only string claim values are supported. + If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string. + Mutually exclusive with expression and message. + type: string + type: object + type: array + issuer: + description: issuer contains the basic OIDC provider connection + options. + properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy defines how the "audiences" field is used to match the "aud" claim in the presented JWT. + Allowed values are: + 1. "MatchAny" when multiple audiences are specified and + 2. empty (or unset) or "MatchAny" when a single audience is specified. + + - MatchAny: the "aud" claim in the presented JWT must match at least one of the entries in the "audiences" field. + For example, if "audiences" is ["foo", "bar"], the "aud" claim in the presented JWT must contain either "foo" or "bar" (and may contain both). + + - "": The match policy can be empty (or unset) when a single audience is specified in the "audiences" field. The "aud" claim in the presented JWT must contain the single audience (and may contain others). + + For more nuanced audience validation, use claimValidationRules. + example: claimValidationRule[].expression: 'sets.equivalent(claims.aud, ["bar", "foo", "baz"])' to require an exact match. + type: string + audiences: + description: |- + audiences is the set of acceptable audiences the JWT must be issued to. + At least one of the entries must match the "aud" claim in presented JWTs. + Same value as the --oidc-client-id flag (though this field supports an array). + Required to be non-empty. + items: + type: string + type: array + certificateAuthority: + description: |- + certificateAuthority contains PEM-encoded certificate authority certificates + used to validate the connection when fetching discovery information. + If unset, the system verifier is used. + Same value as the content of the file referenced by the --oidc-ca-file flag. + type: string + discoveryURL: + description: |- + discoveryURL, if specified, overrides the URL used to fetch discovery + information instead of using "{url}/.well-known/openid-configuration". + The exact value specified is used, so "/.well-known/openid-configuration" + must be included in discoveryURL if needed. + + The "issuer" field in the fetched discovery information must match the "issuer.url" field + in the AuthenticationConfiguration and will be used to validate the "iss" claim in the presented JWT. + This is for scenarios where the well-known and jwks endpoints are hosted at a different + location than the issuer (such as locally in the cluster). + + Example: + A discovery url that is exposed using kubernetes service 'oidc' in namespace 'oidc-namespace' + and discovery information is available at '/.well-known/openid-configuration'. + discoveryURL: "https://oidc.oidc-namespace/.well-known/openid-configuration" + certificateAuthority is used to verify the TLS connection and the hostname on the leaf certificate + must be set to 'oidc.oidc-namespace'. + + curl https://oidc.oidc-namespace/.well-known/openid-configuration (.discoveryURL field) + { + issuer: "https://oidc.example.com" (.url field) + } + + discoveryURL must be different from url. + Required to be unique across all JWT authenticators. + Note that egress selection configuration is not used for this network connection. + type: string + url: + description: |- + url points to the issuer URL in a format https://url or https://url/path. + This must match the "iss" claim in the presented JWT, and the issuer returned from discovery. + Same value as the --oidc-issuer-url flag. + Discovery information is fetched from "{url}/.well-known/openid-configuration" unless overridden by discoveryURL. + Required to be unique across all JWT authenticators. + Note that egress selection configuration is not used for this network connection. + type: string + required: + - audiences + - url + type: object + userValidationRules: + description: |- + userValidationRules are rules that are applied to final user before completing authentication. + These allow invariants to be applied to incoming identities such as preventing the + use of the system: prefix that is commonly used by Kubernetes components. + The validation rules are logically ANDed together and must all return true for the validation to pass. + items: + description: UserValidationRule provides the configuration + for a single user info validation rule. + properties: + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + Must return true for the validation to pass. + + CEL expressions have access to the contents of UserInfo, organized into CEL variable: + - 'user' - authentication.k8s.io/v1, Kind=UserInfo object + Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition. + API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + type: string + message: + description: |- + message customizes the returned error message when rule returns false. + message is a literal string. + type: string + required: + - expression + type: object + type: array + required: + - claimMappings + - issuer + type: object + type: array + k8s: + description: |- + Kubernetes authentication configuration. + Enables authentication using Kubernetes service account tokens. + Useful for integrating with existing Kubernetes RBAC policies. + properties: + enabled: + default: false + description: |- + Enable Kubernetes authentication. + When enabled, clients can authenticate using Kubernetes service account tokens. + type: boolean + type: object + type: object + baseDomain: + description: |- + Base domain used to construct FQDNs for all service endpoints. + This domain will be used to generate the default hostnames for Routes, Ingresses, and certificates. + Example: "example.com" will generate endpoints like "grpc.example.com", "router.example.com" + pattern: ^[a-z0-9]([a-z0-9\-\.]*[a-z0-9])?$ + type: string + controller: + description: |- + Controller configuration for the main Jumpstarter API and gRPC services. + The controller handles gRPC and REST API requests from clients and exporters. + properties: + authentication: + description: |- + Authentication configuration for client and exporter authentication. + Configures how clients and exporters can authenticate with Jumpstarter. + Supports multiple authentication methods including internal tokens, Kubernetes tokens, and JWT. + properties: + internal: + description: |- + Internal authentication configuration. + Built-in authenticator that issues tokens for clients and exporters. + This is the simplest authentication method and is enabled by default. + properties: + enabled: + default: true + description: |- + Enable the internal authentication method. + When disabled, clients cannot use internal tokens for authentication. + type: boolean + prefix: + default: 'internal:' + description: |- + Prefix to add to the subject claim of issued tokens. + Helps distinguish internal tokens from other authentication methods. + Example: "internal:" will result in subjects like "internal:user123" + maxLength: 50 + type: string + tokenLifetime: + default: 43800h + description: |- + Token validity duration for issued tokens. + After this duration, tokens expire and must be renewed. + type: string + type: object + jwt: + description: |- + JWT authentication configuration. + Enables authentication using external JWT tokens from OIDC providers. + Supports multiple JWT authenticators for different identity providers. + items: + description: JWTAuthenticator provides the configuration + for a single JWT authenticator. + properties: + claimMappings: + description: claimMappings points claims of a token + to be treated as user attributes. + properties: + extra: + description: |- + extra represents an option for the extra attribute. + expression must produce a string or string array value. + If the value is empty, the extra mapping will not be present. + + hard-coded extra key/value + - key: "foo" + valueExpression: "'bar'" + This will result in an extra attribute - foo: ["bar"] + + hard-coded key, value copying claim value + - key: "foo" + valueExpression: "claims.some_claim" + This will result in an extra attribute - foo: [value of some_claim] + + hard-coded key, value derived from claim value + - key: "admin" + valueExpression: '(has(claims.is_admin) && claims.is_admin) ? "true":""' + This will result in: + - if is_admin claim is present and true, extra attribute - admin: ["true"] + - if is_admin claim is present and false or is_admin claim is not present, no extra attribute will be added + items: + description: ExtraMapping provides the configuration + for a single extra mapping. + properties: + key: + description: |- + key is a string to use as the extra attribute key. + key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid + subdomain as defined by RFC 1123. All characters trailing the first "/" must + be valid HTTP Path characters as defined by RFC 3986. + key must be lowercase. + Required to be unique. + type: string + valueExpression: + description: |- + valueExpression is a CEL expression to extract extra attribute value. + valueExpression must produce a string or string array value. + "", [], and null values are treated as the extra mapping not being present. + Empty string values contained within a string array are filtered out. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + type: string + required: + - key + - valueExpression + type: object + type: array + groups: + description: |- + groups represents an option for the groups attribute. + The claim's value must be a string or string array claim. + If groups.claim is set, the prefix must be specified (and can be the empty string). + If groups.expression is set, the expression must produce a string or string array value. + "", [], and null values are treated as the group mapping not being present. + properties: + claim: + description: |- + claim is the JWT claim to use. + Mutually exclusive with expression. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim and prefix. + type: string + prefix: + description: |- + prefix is prepended to claim's value to prevent clashes with existing names. + prefix needs to be set if claim is set and can be the empty string. + Mutually exclusive with expression. + type: string + type: object + uid: + description: |- + uid represents an option for the uid attribute. + Claim must be a singular string claim. + If uid.expression is set, the expression must produce a string value. + properties: + claim: + description: |- + claim is the JWT claim to use. + Either claim or expression must be set. + Mutually exclusive with expression. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim. + type: string + type: object + username: + description: |- + username represents an option for the username attribute. + The claim's value must be a singular string. + Same as the --oidc-username-claim and --oidc-username-prefix flags. + If username.expression is set, the expression must produce a string value. + If username.expression uses 'claims.email', then 'claims.email_verified' must be used in + username.expression or extra[*].valueExpression or claimValidationRules[*].expression. + An example claim validation rule expression that matches the validation automatically + applied when username.claim is set to 'email' is 'claims.?email_verified.orValue(true) == true'. By explicitly comparing + the value to true, we let type-checking see the result will be a boolean, and to make sure a non-boolean email_verified + claim will be caught at runtime. + + In the flag based approach, the --oidc-username-claim and --oidc-username-prefix are optional. If --oidc-username-claim is not set, + the default value is "sub". For the authentication config, there is no defaulting for claim or prefix. The claim and prefix must be set explicitly. + For claim, if --oidc-username-claim was not set with legacy flag approach, configure username.claim="sub" in the authentication config. + For prefix: + (1) --oidc-username-prefix="-", no prefix was added to the username. For the same behavior using authentication config, + set username.prefix="" + (2) --oidc-username-prefix="" and --oidc-username-claim != "email", prefix was "#". For the same + behavior using authentication config, set username.prefix="#" + (3) --oidc-username-prefix="". For the same behavior using authentication config, set username.prefix="" + properties: + claim: + description: |- + claim is the JWT claim to use. + Mutually exclusive with expression. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim and prefix. + type: string + prefix: + description: |- + prefix is prepended to claim's value to prevent clashes with existing names. + prefix needs to be set if claim is set and can be the empty string. + Mutually exclusive with expression. + type: string + type: object + required: + - username + type: object + claimValidationRules: + description: claimValidationRules are rules that are + applied to validate token claims to authenticate users. + items: + description: ClaimValidationRule provides the configuration + for a single claim validation rule. + properties: + claim: + description: |- + claim is the name of a required claim. + Same as --oidc-required-claim flag. + Only string claim keys are supported. + Mutually exclusive with expression and message. + type: string + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + Must produce a boolean. + + CEL expressions have access to the contents of the token claims, organized into CEL variable: + - 'claims' is a map of claim names to claim values. + For example, a variable named 'sub' can be accessed as 'claims.sub'. + Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. + Must return true for the validation to pass. + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Mutually exclusive with claim and requiredValue. + type: string + message: + description: |- + message customizes the returned error message when expression returns false. + message is a literal string. + Mutually exclusive with claim and requiredValue. + type: string + requiredValue: + description: |- + requiredValue is the value of a required claim. + Same as --oidc-required-claim flag. + Only string claim values are supported. + If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string. + Mutually exclusive with expression and message. + type: string + type: object + type: array + issuer: + description: issuer contains the basic OIDC provider + connection options. + properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy defines how the "audiences" field is used to match the "aud" claim in the presented JWT. + Allowed values are: + 1. "MatchAny" when multiple audiences are specified and + 2. empty (or unset) or "MatchAny" when a single audience is specified. + + - MatchAny: the "aud" claim in the presented JWT must match at least one of the entries in the "audiences" field. + For example, if "audiences" is ["foo", "bar"], the "aud" claim in the presented JWT must contain either "foo" or "bar" (and may contain both). + + - "": The match policy can be empty (or unset) when a single audience is specified in the "audiences" field. The "aud" claim in the presented JWT must contain the single audience (and may contain others). + + For more nuanced audience validation, use claimValidationRules. + example: claimValidationRule[].expression: 'sets.equivalent(claims.aud, ["bar", "foo", "baz"])' to require an exact match. + type: string + audiences: + description: |- + audiences is the set of acceptable audiences the JWT must be issued to. + At least one of the entries must match the "aud" claim in presented JWTs. + Same value as the --oidc-client-id flag (though this field supports an array). + Required to be non-empty. + items: + type: string + type: array + certificateAuthority: + description: |- + certificateAuthority contains PEM-encoded certificate authority certificates + used to validate the connection when fetching discovery information. + If unset, the system verifier is used. + Same value as the content of the file referenced by the --oidc-ca-file flag. + type: string + discoveryURL: + description: |- + discoveryURL, if specified, overrides the URL used to fetch discovery + information instead of using "{url}/.well-known/openid-configuration". + The exact value specified is used, so "/.well-known/openid-configuration" + must be included in discoveryURL if needed. + + The "issuer" field in the fetched discovery information must match the "issuer.url" field + in the AuthenticationConfiguration and will be used to validate the "iss" claim in the presented JWT. + This is for scenarios where the well-known and jwks endpoints are hosted at a different + location than the issuer (such as locally in the cluster). + + Example: + A discovery url that is exposed using kubernetes service 'oidc' in namespace 'oidc-namespace' + and discovery information is available at '/.well-known/openid-configuration'. + discoveryURL: "https://oidc.oidc-namespace/.well-known/openid-configuration" + certificateAuthority is used to verify the TLS connection and the hostname on the leaf certificate + must be set to 'oidc.oidc-namespace'. + + curl https://oidc.oidc-namespace/.well-known/openid-configuration (.discoveryURL field) + { + issuer: "https://oidc.example.com" (.url field) + } + + discoveryURL must be different from url. + Required to be unique across all JWT authenticators. + Note that egress selection configuration is not used for this network connection. + type: string + url: + description: |- + url points to the issuer URL in a format https://url or https://url/path. + This must match the "iss" claim in the presented JWT, and the issuer returned from discovery. + Same value as the --oidc-issuer-url flag. + Discovery information is fetched from "{url}/.well-known/openid-configuration" unless overridden by discoveryURL. + Required to be unique across all JWT authenticators. + Note that egress selection configuration is not used for this network connection. + type: string + required: + - audiences + - url + type: object + userValidationRules: + description: |- + userValidationRules are rules that are applied to final user before completing authentication. + These allow invariants to be applied to incoming identities such as preventing the + use of the system: prefix that is commonly used by Kubernetes components. + The validation rules are logically ANDed together and must all return true for the validation to pass. + items: + description: UserValidationRule provides the configuration + for a single user info validation rule. + properties: + expression: + description: |- + expression represents the expression which will be evaluated by CEL. + Must return true for the validation to pass. + + CEL expressions have access to the contents of UserInfo, organized into CEL variable: + - 'user' - authentication.k8s.io/v1, Kind=UserInfo object + Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition. + API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io + + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + type: string + message: + description: |- + message customizes the returned error message when rule returns false. + message is a literal string. + type: string + required: + - expression + type: object + type: array + required: + - claimMappings + - issuer + type: object + type: array + k8s: + description: |- + Kubernetes authentication configuration. + Enables authentication using Kubernetes service account tokens. + Useful for integrating with existing Kubernetes RBAC policies. + properties: + enabled: + default: false + description: |- + Enable Kubernetes authentication. + When enabled, clients can authenticate using Kubernetes service account tokens. + type: boolean + type: object + type: object + exporterOptions: + description: |- + Exporter options configuration. + Controls how exporters connect and behave when communicating with the controller. + properties: + offlineTimeout: + default: 180s + description: |- + Offline timeout duration for exporters. + After this duration without communication, an exporter is considered offline. + This drives the online/offline status field of exporters, and offline exporters + won't be considered for leases. + type: string + type: object + grpc: + description: |- + gRPC configuration for controller endpoints. + Defines how controller gRPC services are exposed and configured. + properties: + endpoints: + description: |- + List of gRPC endpoints to expose. + Each endpoint can use different networking methods (Route, Ingress, NodePort, or LoadBalancer) + based on your cluster setup. Example: Use Route for OpenShift, Ingress for standard Kubernetes. + items: + description: |- + Endpoint defines a single endpoint configuration. + An endpoint can use one or more networking methods: Route, Ingress, NodePort, or LoadBalancer. + Multiple methods can be configured simultaneously for the same address. + properties: + address: + description: |- + Address for this endpoint in the format "hostname", "hostname:port", "IPv4", "IPv4:port", "[IPv6]", or "[IPv6]:port". + Required for Route and Ingress endpoints. Optional for NodePort and LoadBalancer endpoints. + When optional, the address is used for certificate generation and DNS resolution. + Supports templating with $(replica) for replica-specific addresses. + Examples: "grpc.example.com", "grpc.example.com:9090", "192.168.1.1:8080", "[2001:db8::1]:8443", "router-$(replica).example.com" + pattern: ^(\[[0-9a-fA-F:\.]+\]|[0-9]+(\.[0-9]+){3}|[a-z0-9$]([a-z0-9\-\.\$\(\)]*[a-z0-9\)])?)(:[0-9]+)?$ + type: string + clusterIP: + description: |- + ClusterIP configuration for internal service access. + Creates a ClusterIP service for this endpoint. + Useful for internal service-to-service communication or when + using a different method to expose the service externally. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the ClusterIP service. + Useful for configuring service-specific behavior and load balancer options. + type: object + enabled: + description: |- + Enable the ClusterIP service for this endpoint. + When disabled, no ClusterIP service will be created for this endpoint. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the ClusterIP service. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + ingress: + description: |- + Ingress configuration for standard Kubernetes clusters. + Creates an Ingress resource for this endpoint. + Requires an ingress controller to be installed. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the Kubernetes Ingress resource. + Useful for configuring ingress-specific behavior, TLS settings, and load balancer options. + type: object + class: + default: default + description: |- + Ingress class name for the Kubernetes Ingress. + Specifies which ingress controller should handle this ingress. + type: string + enabled: + description: |- + Enable the Kubernetes Ingress for this endpoint. + When disabled, no Ingress resource will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the Kubernetes Ingress resource. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + loadBalancer: + description: |- + LoadBalancer configuration for cloud environments. + Creates a LoadBalancer service for this endpoint. + Requires cloud provider support for LoadBalancer services. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the LoadBalancer service. + Useful for configuring cloud provider-specific load balancer options. + Example: "service.beta.kubernetes.io/aws-load-balancer-type: nlb" + type: object + enabled: + description: |- + Enable the LoadBalancer service for this endpoint. + When disabled, no LoadBalancer service will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the LoadBalancer service. + Useful for monitoring, cost allocation, and resource organization. + type: object + port: + description: |- + Port number for the LoadBalancer service. + Must be a valid port number (1-65535). + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + nodeport: + description: |- + NodePort configuration for direct node access. + Exposes the service on a specific port on each node. + Useful for bare-metal or simple cluster setups. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the NodePort service. + Useful for configuring service-specific behavior and load balancer options. + type: object + enabled: + description: |- + Enable the NodePort service for this endpoint. + When disabled, no NodePort service will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the NodePort service. + Useful for monitoring, cost allocation, and resource organization. + type: object + port: + description: |- + NodePort port number to expose on each node. + Must be in the range 30000-32767 for most Kubernetes clusters. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + route: + description: |- + Route configuration for OpenShift clusters. + Creates an OpenShift Route resource for this endpoint. + Only applicable in OpenShift environments. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the OpenShift Route resource. + Useful for configuring route-specific behavior and TLS settings. + type: object + enabled: + description: |- + Enable the OpenShift Route for this endpoint. + When disabled, no Route resource will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the OpenShift Route resource. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + type: object + type: array + keepalive: + description: |- + Keepalive configuration for gRPC connections. + Controls connection health checks and idle connection management. + Helps maintain stable connections in load-balanced environments. + properties: + intervalTime: + default: 10s + description: |- + Interval between keepalive pings. + How often to send keepalive pings to check connection health. This is important + to keep TCP gRPC connections alive when traversing load balancers and proxies. + type: string + maxConnectionAge: + description: |- + Maximum age of a connection before it is closed and recreated. + Helps prevent issues with long-lived connections. It defaults to infinity. + type: string + maxConnectionAgeGrace: + description: |- + Grace period for closing connections that exceed MaxConnectionAge. + Allows ongoing RPCs to complete before closing the connection. + type: string + maxConnectionIdle: + description: |- + Maximum time a connection can remain idle before being closed. + It defaults to infinity. + type: string + minTime: + default: 1s + description: |- + Minimum time between keepalives that the connection will accept, under this threshold + the other side will get a GOAWAY signal. + Prevents excessive keepalive traffic on the network. + type: string + permitWithoutStream: + default: true + description: |- + Allow keepalive pings even when there are no active RPC streams. + Useful for detecting connection issues in idle connections. + This is important to keep TCP gRPC connections alive when traversing + load balancers and proxies. + type: boolean + timeout: + default: 180s + description: |- + Timeout for keepalive ping acknowledgment. + If a ping is not acknowledged within this time, the connection is considered broken. + The default is high to avoid issues when the network on a exporter is overloaded, i.e. + during flashing. + type: string + type: object + tls: + description: |- + TLS configuration for secure gRPC communication. + Requires a Kubernetes secret containing the TLS certificate and private key. + If useCertManager is enabled, this secret will be automatically created. + See also: spec.useCertManager for automatic certificate management. + properties: + certSecret: + description: |- + Name of the Kubernetes secret containing the TLS certificate and private key. + The secret must contain 'tls.crt' and 'tls.key' keys. + If useCertManager is enabled, this secret will be automatically created. + pattern: ^[a-z0-9]([a-z0-9\-\.]*[a-z0-9])?$ + type: string + type: object + type: object + image: + description: |- + Container image for the controller pods in 'registry/repository/image:tag' format. + If not specified, defaults to the latest stable version of the Jumpstarter controller. + type: string + imagePullPolicy: + default: IfNotPresent + description: |- + Image pull policy for the controller container. + Controls when the container image should be pulled from the registry. + enum: + - Always + - IfNotPresent + - Never + type: string + replicas: + default: 2 + description: |- + Number of controller replicas to run. + Must be a positive integer. Minimum recommended value is 2 for high availability. + format: int32 + minimum: 1 + type: integer + resources: + description: |- + Resource requirements for controller pods. + Defines CPU and memory requests and limits for each controller pod. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restApi: + description: |- + REST API configuration for HTTP-based clients. + Enables non-gRPC clients to interact with Jumpstarter for listing leases, + managing exporters, and creating new leases. Use this when you need HTTP/JSON access. + properties: + endpoints: + description: |- + List of REST API endpoints to expose. + Each endpoint can use different networking methods (Route, Ingress, NodePort, or LoadBalancer) + based on your cluster setup. + items: + description: |- + Endpoint defines a single endpoint configuration. + An endpoint can use one or more networking methods: Route, Ingress, NodePort, or LoadBalancer. + Multiple methods can be configured simultaneously for the same address. + properties: + address: + description: |- + Address for this endpoint in the format "hostname", "hostname:port", "IPv4", "IPv4:port", "[IPv6]", or "[IPv6]:port". + Required for Route and Ingress endpoints. Optional for NodePort and LoadBalancer endpoints. + When optional, the address is used for certificate generation and DNS resolution. + Supports templating with $(replica) for replica-specific addresses. + Examples: "grpc.example.com", "grpc.example.com:9090", "192.168.1.1:8080", "[2001:db8::1]:8443", "router-$(replica).example.com" + pattern: ^(\[[0-9a-fA-F:\.]+\]|[0-9]+(\.[0-9]+){3}|[a-z0-9$]([a-z0-9\-\.\$\(\)]*[a-z0-9\)])?)(:[0-9]+)?$ + type: string + clusterIP: + description: |- + ClusterIP configuration for internal service access. + Creates a ClusterIP service for this endpoint. + Useful for internal service-to-service communication or when + using a different method to expose the service externally. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the ClusterIP service. + Useful for configuring service-specific behavior and load balancer options. + type: object + enabled: + description: |- + Enable the ClusterIP service for this endpoint. + When disabled, no ClusterIP service will be created for this endpoint. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the ClusterIP service. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + ingress: + description: |- + Ingress configuration for standard Kubernetes clusters. + Creates an Ingress resource for this endpoint. + Requires an ingress controller to be installed. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the Kubernetes Ingress resource. + Useful for configuring ingress-specific behavior, TLS settings, and load balancer options. + type: object + class: + default: default + description: |- + Ingress class name for the Kubernetes Ingress. + Specifies which ingress controller should handle this ingress. + type: string + enabled: + description: |- + Enable the Kubernetes Ingress for this endpoint. + When disabled, no Ingress resource will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the Kubernetes Ingress resource. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + loadBalancer: + description: |- + LoadBalancer configuration for cloud environments. + Creates a LoadBalancer service for this endpoint. + Requires cloud provider support for LoadBalancer services. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the LoadBalancer service. + Useful for configuring cloud provider-specific load balancer options. + Example: "service.beta.kubernetes.io/aws-load-balancer-type: nlb" + type: object + enabled: + description: |- + Enable the LoadBalancer service for this endpoint. + When disabled, no LoadBalancer service will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the LoadBalancer service. + Useful for monitoring, cost allocation, and resource organization. + type: object + port: + description: |- + Port number for the LoadBalancer service. + Must be a valid port number (1-65535). + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + nodeport: + description: |- + NodePort configuration for direct node access. + Exposes the service on a specific port on each node. + Useful for bare-metal or simple cluster setups. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the NodePort service. + Useful for configuring service-specific behavior and load balancer options. + type: object + enabled: + description: |- + Enable the NodePort service for this endpoint. + When disabled, no NodePort service will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the NodePort service. + Useful for monitoring, cost allocation, and resource organization. + type: object + port: + description: |- + NodePort port number to expose on each node. + Must be in the range 30000-32767 for most Kubernetes clusters. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + route: + description: |- + Route configuration for OpenShift clusters. + Creates an OpenShift Route resource for this endpoint. + Only applicable in OpenShift environments. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the OpenShift Route resource. + Useful for configuring route-specific behavior and TLS settings. + type: object + enabled: + description: |- + Enable the OpenShift Route for this endpoint. + When disabled, no Route resource will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the OpenShift Route resource. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + type: object + type: array + tls: + description: |- + TLS configuration for secure HTTP communication. + Requires a Kubernetes secret containing the TLS certificate and private key. + properties: + certSecret: + description: |- + Name of the Kubernetes secret containing the TLS certificate and private key. + The secret must contain 'tls.crt' and 'tls.key' keys. + If useCertManager is enabled, this secret will be automatically created. + pattern: ^[a-z0-9]([a-z0-9\-\.]*[a-z0-9])?$ + type: string + type: object + type: object + type: object + routers: + description: |- + Router configuration for the Jumpstarter router service. + Routers handle gRPC traffic routing and load balancing. + properties: + grpc: + description: |- + gRPC configuration for router endpoints. + Defines how router gRPC services are exposed and configured. + properties: + endpoints: + description: |- + List of gRPC endpoints to expose. + Each endpoint can use different networking methods (Route, Ingress, NodePort, or LoadBalancer) + based on your cluster setup. Example: Use Route for OpenShift, Ingress for standard Kubernetes. + items: + description: |- + Endpoint defines a single endpoint configuration. + An endpoint can use one or more networking methods: Route, Ingress, NodePort, or LoadBalancer. + Multiple methods can be configured simultaneously for the same address. + properties: + address: + description: |- + Address for this endpoint in the format "hostname", "hostname:port", "IPv4", "IPv4:port", "[IPv6]", or "[IPv6]:port". + Required for Route and Ingress endpoints. Optional for NodePort and LoadBalancer endpoints. + When optional, the address is used for certificate generation and DNS resolution. + Supports templating with $(replica) for replica-specific addresses. + Examples: "grpc.example.com", "grpc.example.com:9090", "192.168.1.1:8080", "[2001:db8::1]:8443", "router-$(replica).example.com" + pattern: ^(\[[0-9a-fA-F:\.]+\]|[0-9]+(\.[0-9]+){3}|[a-z0-9$]([a-z0-9\-\.\$\(\)]*[a-z0-9\)])?)(:[0-9]+)?$ + type: string + clusterIP: + description: |- + ClusterIP configuration for internal service access. + Creates a ClusterIP service for this endpoint. + Useful for internal service-to-service communication or when + using a different method to expose the service externally. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the ClusterIP service. + Useful for configuring service-specific behavior and load balancer options. + type: object + enabled: + description: |- + Enable the ClusterIP service for this endpoint. + When disabled, no ClusterIP service will be created for this endpoint. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the ClusterIP service. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + ingress: + description: |- + Ingress configuration for standard Kubernetes clusters. + Creates an Ingress resource for this endpoint. + Requires an ingress controller to be installed. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the Kubernetes Ingress resource. + Useful for configuring ingress-specific behavior, TLS settings, and load balancer options. + type: object + class: + default: default + description: |- + Ingress class name for the Kubernetes Ingress. + Specifies which ingress controller should handle this ingress. + type: string + enabled: + description: |- + Enable the Kubernetes Ingress for this endpoint. + When disabled, no Ingress resource will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the Kubernetes Ingress resource. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + loadBalancer: + description: |- + LoadBalancer configuration for cloud environments. + Creates a LoadBalancer service for this endpoint. + Requires cloud provider support for LoadBalancer services. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the LoadBalancer service. + Useful for configuring cloud provider-specific load balancer options. + Example: "service.beta.kubernetes.io/aws-load-balancer-type: nlb" + type: object + enabled: + description: |- + Enable the LoadBalancer service for this endpoint. + When disabled, no LoadBalancer service will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the LoadBalancer service. + Useful for monitoring, cost allocation, and resource organization. + type: object + port: + description: |- + Port number for the LoadBalancer service. + Must be a valid port number (1-65535). + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + nodeport: + description: |- + NodePort configuration for direct node access. + Exposes the service on a specific port on each node. + Useful for bare-metal or simple cluster setups. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the NodePort service. + Useful for configuring service-specific behavior and load balancer options. + type: object + enabled: + description: |- + Enable the NodePort service for this endpoint. + When disabled, no NodePort service will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the NodePort service. + Useful for monitoring, cost allocation, and resource organization. + type: object + port: + description: |- + NodePort port number to expose on each node. + Must be in the range 30000-32767 for most Kubernetes clusters. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + route: + description: |- + Route configuration for OpenShift clusters. + Creates an OpenShift Route resource for this endpoint. + Only applicable in OpenShift environments. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations to add to the OpenShift Route resource. + Useful for configuring route-specific behavior and TLS settings. + type: object + enabled: + description: |- + Enable the OpenShift Route for this endpoint. + When disabled, no Route resource will be created for this endpoint. + When not specified, the operator will determine the best networking option for your cluster. + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels to add to the OpenShift Route resource. + Useful for monitoring, cost allocation, and resource organization. + type: object + type: object + type: object + type: array + keepalive: + description: |- + Keepalive configuration for gRPC connections. + Controls connection health checks and idle connection management. + Helps maintain stable connections in load-balanced environments. + properties: + intervalTime: + default: 10s + description: |- + Interval between keepalive pings. + How often to send keepalive pings to check connection health. This is important + to keep TCP gRPC connections alive when traversing load balancers and proxies. + type: string + maxConnectionAge: + description: |- + Maximum age of a connection before it is closed and recreated. + Helps prevent issues with long-lived connections. It defaults to infinity. + type: string + maxConnectionAgeGrace: + description: |- + Grace period for closing connections that exceed MaxConnectionAge. + Allows ongoing RPCs to complete before closing the connection. + type: string + maxConnectionIdle: + description: |- + Maximum time a connection can remain idle before being closed. + It defaults to infinity. + type: string + minTime: + default: 1s + description: |- + Minimum time between keepalives that the connection will accept, under this threshold + the other side will get a GOAWAY signal. + Prevents excessive keepalive traffic on the network. + type: string + permitWithoutStream: + default: true + description: |- + Allow keepalive pings even when there are no active RPC streams. + Useful for detecting connection issues in idle connections. + This is important to keep TCP gRPC connections alive when traversing + load balancers and proxies. + type: boolean + timeout: + default: 180s + description: |- + Timeout for keepalive ping acknowledgment. + If a ping is not acknowledged within this time, the connection is considered broken. + The default is high to avoid issues when the network on a exporter is overloaded, i.e. + during flashing. + type: string + type: object + tls: + description: |- + TLS configuration for secure gRPC communication. + Requires a Kubernetes secret containing the TLS certificate and private key. + If useCertManager is enabled, this secret will be automatically created. + See also: spec.useCertManager for automatic certificate management. + properties: + certSecret: + description: |- + Name of the Kubernetes secret containing the TLS certificate and private key. + The secret must contain 'tls.crt' and 'tls.key' keys. + If useCertManager is enabled, this secret will be automatically created. + pattern: ^[a-z0-9]([a-z0-9\-\.]*[a-z0-9])?$ + type: string + type: object + type: object + image: + description: |- + Container image for the router pods in 'registry/repository/image:tag' format. + If not specified, defaults to the latest stable version of the Jumpstarter router. + type: string + imagePullPolicy: + default: IfNotPresent + description: |- + Image pull policy for the router container. + Controls when the container image should be pulled from the registry. + enum: + - Always + - IfNotPresent + - Never + type: string + replicas: + default: 3 + description: |- + Number of router replicas to run. + Must be a positive integer. Minimum recommended value is 3 for high availability. + format: int32 + minimum: 1 + type: integer + resources: + description: |- + Resource requirements for router pods. + Defines CPU and memory requests and limits for each router pod. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + topologySpreadConstraints: + description: |- + Topology spread constraints for router pod distribution. + Ensures router pods are distributed evenly across nodes and zones. + Useful for high availability and fault tolerance. + items: + description: TopologySpreadConstraint specifies how to spread + matching pods among the given topology. + properties: + labelSelector: + description: |- + LabelSelector is used to find matching pods. + Pods that match this label selector are counted to determine the number of pods + in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select the pods over which + spreading will be calculated. The keys are used to lookup values from the + incoming pod labels, those key-value labels are ANDed with labelSelector + to select the group of existing pods over which spreading will be calculated + for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + MatchLabelKeys cannot be set when LabelSelector isn't set. + Keys that don't exist in the incoming pod labels will + be ignored. A null or empty list means only match against labelSelector. + + This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: |- + MaxSkew describes the degree to which pods may be unevenly distributed. + When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference + between the number of matching pods in the target topology and the global minimum. + The global minimum is the minimum number of matching pods in an eligible domain + or zero if the number of eligible domains is less than MinDomains. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 2/2/1: + In this case, the global minimum is 1. + | zone1 | zone2 | zone3 | + | P P | P P | P | + - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; + scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) + violate MaxSkew(1). + - if MaxSkew is 2, incoming pod can be scheduled onto any zone. + When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence + to topologies that satisfy it. + It's a required field. Default value is 1 and 0 is not allowed. + format: int32 + type: integer + minDomains: + description: |- + MinDomains indicates a minimum number of eligible domains. + When the number of eligible domains with matching topology keys is less than minDomains, + Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed. + And when the number of eligible domains with matching topology keys equals or greater than minDomains, + this value has no effect on scheduling. + As a result, when the number of eligible domains is less than minDomains, + scheduler won't schedule more than maxSkew Pods to those domains. + If value is nil, the constraint behaves as if MinDomains is equal to 1. + Valid values are integers greater than 0. + When value is not nil, WhenUnsatisfiable must be DoNotSchedule. + + For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same + labelSelector spread as 2/2/2: + | zone1 | zone2 | zone3 | + | P P | P P | P P | + The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0. + In this situation, new pod with the same labelSelector cannot be scheduled, + because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, + it will violate MaxSkew. + format: int32 + type: integer + nodeAffinityPolicy: + description: |- + NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector + when calculating pod topology spread skew. Options are: + - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. + - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. + + If this value is nil, the behavior is equivalent to the Honor policy. + type: string + nodeTaintsPolicy: + description: |- + NodeTaintsPolicy indicates how we will treat node taints when calculating + pod topology spread skew. Options are: + - Honor: nodes without taints, along with tainted nodes for which the incoming pod + has a toleration, are included. + - Ignore: node taints are ignored. All nodes are included. + + If this value is nil, the behavior is equivalent to the Ignore policy. + type: string + topologyKey: + description: |- + TopologyKey is the key of node labels. Nodes that have a label with this key + and identical values are considered to be in the same topology. + We consider each as a "bucket", and try to put balanced number + of pods into each bucket. + We define a domain as a particular instance of a topology. + Also, we define an eligible domain as a domain whose nodes meet the requirements of + nodeAffinityPolicy and nodeTaintsPolicy. + e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology. + And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology. + It's a required field. + type: string + whenUnsatisfiable: + description: |- + WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy + the spread constraint. + - DoNotSchedule (default) tells the scheduler not to schedule it. + - ScheduleAnyway tells the scheduler to schedule the pod in any location, + but giving higher precedence to topologies that would help reduce the + skew. + A constraint is considered "Unsatisfiable" for an incoming pod + if and only if every possible node assignment for that pod would violate + "MaxSkew" on some topology. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 3/1/1: + | zone1 | zone2 | zone3 | + | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled + to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies + MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler + won't make it *more* imbalanced. + It's a required field. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + type: object + useCertManager: + default: true + description: |- + Enable automatic TLS certificate management using cert-manager. + When enabled, jumpstarter will interact with cert-manager to automatically provision + and renew TLS certificates for all endpoints. Requires cert-manager to be installed in the cluster. + type: boolean + type: object + status: + description: |- + JumpstarterStatus defines the observed state of Jumpstarter. + This field is currently empty but can be extended to include status information + such as deployment status, endpoint URLs, and health information. + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.3 + name: leases.jumpstarter.dev +spec: + group: jumpstarter.dev + names: + kind: Lease + listKind: LeaseList + plural: leases + singular: lease + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.ended + name: Ended + type: boolean + - jsonPath: .spec.clientRef.name + name: Client + type: string + - jsonPath: .status.exporterRef.name + name: Exporter + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Lease is the Schema for the exporters API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: LeaseSpec defines the desired state of Lease + properties: + beginTime: + description: |- + Requested start time. If omitted, lease starts when exporter is acquired. + Immutable after lease starts (cannot change the past). + format: date-time + type: string + clientRef: + description: The client that is requesting the lease + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + duration: + description: |- + Duration of the lease. Must be positive when provided. + Can be omitted (nil) when both BeginTime and EndTime are provided, + in which case it's calculated as EndTime - BeginTime. + type: string + endTime: + description: |- + Requested end time. If specified with BeginTime, Duration is calculated. + Can be updated to extend or shorten active leases. + format: date-time + type: string + release: + description: The release flag requests the controller to end the lease + now + type: boolean + selector: + description: The selector for the exporter to be used + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - clientRef + - selector + type: object + status: + description: LeaseStatus defines the observed state of Lease + properties: + beginTime: + description: |- + If the lease has been acquired an exporter name is assigned + and then it can be used, it will be empty while still pending + format: date-time + type: string + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + endTime: + format: date-time + type: string + ended: + type: boolean + exporterRef: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + priority: + type: integer + spotAccess: + type: boolean + required: + - ended + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + name: jumpstarter-operator-controller-manager + namespace: jumpstarter-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + name: jumpstarter-operator-leader-election-role + namespace: jumpstarter-operator-system +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + name: jumpstarter-operator-jumpstarter-admin-role +rules: +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters + verbs: + - '*' +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + name: jumpstarter-operator-jumpstarter-editor-role +rules: +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + name: jumpstarter-operator-jumpstarter-viewer-role +rules: +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters + verbs: + - get + - list + - watch +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: jumpstarter-operator-manager-role +rules: +- apiGroups: + - "" + resources: + - configmaps + - secrets + - serviceaccounts + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - services/status + verbs: + - get + - patch + - update +- apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - deployments/status + verbs: + - get + - patch + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - jumpstarter.dev + resources: + - clients + - exporteraccesspolicies + - exporters + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - jumpstarter.dev + resources: + - clients/finalizers + - exporteraccesspolicies/finalizers + - exporters/finalizers + - leases/finalizers + verbs: + - update +- apiGroups: + - jumpstarter.dev + resources: + - clients/status + - exporteraccesspolicies/status + - exporters/status + - leases/status + verbs: + - get + - patch + - update +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - get + - patch + - update +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters/finalizers + verbs: + - update +- apiGroups: + - operator.jumpstarter.dev + resources: + - jumpstarters/status + verbs: + - get + - patch + - update +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + - roles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - route.openshift.io + resources: + - routes/custom-host + verbs: + - create + - get + - patch + - update +- apiGroups: + - route.openshift.io + resources: + - routes/status + verbs: + - get + - patch + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: jumpstarter-operator-metrics-auth-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + name: jumpstarter-operator-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + name: jumpstarter-operator-leader-election-rolebinding + namespace: jumpstarter-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: jumpstarter-operator-leader-election-role +subjects: +- kind: ServiceAccount + name: jumpstarter-operator-controller-manager + namespace: jumpstarter-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + name: jumpstarter-operator-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: jumpstarter-operator-manager-role +subjects: +- kind: ServiceAccount + name: jumpstarter-operator-controller-manager + namespace: jumpstarter-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: jumpstarter-operator-metrics-auth-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: jumpstarter-operator-metrics-auth-role +subjects: +- kind: ServiceAccount + name: jumpstarter-operator-controller-manager + namespace: jumpstarter-operator-system +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + control-plane: controller-manager + name: jumpstarter-operator-controller-manager-metrics-service + namespace: jumpstarter-operator-system +spec: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: jumpstarter-operator + control-plane: controller-manager +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: jumpstarter-operator + control-plane: controller-manager + name: jumpstarter-operator-controller-manager + namespace: jumpstarter-operator-system +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: jumpstarter-operator + control-plane: controller-manager + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: manager + labels: + app.kubernetes.io/name: jumpstarter-operator + control-plane: controller-manager + spec: + containers: + - args: + - --metrics-bind-address=:8443 + - --leader-elect + - --health-probe-bind-address=:8081 + command: + - /manager + image: quay.io/jumpstarter-dev/jumpstarter-operator:latest + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + ports: [] + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 10m + memory: 256Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + volumeMounts: [] + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + serviceAccountName: jumpstarter-operator-controller-manager + terminationGracePeriodSeconds: 10 + volumes: [] diff --git a/deploy/operator/internal/controller/jumpstarter/jumpstarter_controller.go b/deploy/operator/internal/controller/jumpstarter/jumpstarter_controller.go index 1cb25ded..ced6c27b 100644 --- a/deploy/operator/internal/controller/jumpstarter/jumpstarter_controller.go +++ b/deploy/operator/internal/controller/jumpstarter/jumpstarter_controller.go @@ -84,6 +84,7 @@ type JumpstarterReconciler struct { // +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses/status,verbs=get;update;patch // +kubebuilder:rbac:groups=route.openshift.io,resources=routes,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=route.openshift.io,resources=routes/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=route.openshift.io,resources=routes/custom-host,verbs=get;create;update;patch // Monitoring resources // +kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors,verbs=get;list;watch;create;update;patch;delete @@ -869,7 +870,7 @@ func (r *JumpstarterReconciler) createConfigMap(jumpstarter *operatorv1alpha1.Ju return &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-controller", jumpstarter.Name), + Name: "jumpstarter-controller", Namespace: jumpstarter.Namespace, Labels: map[string]string{ "app": "jumpstarter-controller", diff --git a/deploy/operator/test/e2e/e2e_test.go b/deploy/operator/test/e2e/e2e_test.go index cce798b1..f304e7d0 100644 --- a/deploy/operator/test/e2e/e2e_test.go +++ b/deploy/operator/test/e2e/e2e_test.go @@ -122,7 +122,7 @@ var _ = Describe("Manager", Ordered, func() { _, _ = fmt.Fprintf(GinkgoWriter, "Kubernetes events:\n") for _, event := range eventList.Items { _, _ = fmt.Fprintf(GinkgoWriter, "%s %s %s %s\n", - event.LastTimestamp.Time.Format(time.RFC3339), + event.LastTimestamp.Format(time.RFC3339), event.InvolvedObject.Name, event.Reason, event.Message)