Skip to content

Commit 56ff3f2

Browse files
committed
Add OCI Functions Operator for OKE MVP
1 parent 43dfdfe commit 56ff3f2

37 files changed

Lines changed: 4864 additions & 219 deletions

app-dev/devops-and-containers/oke/oci-functions-operator/Makefile

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
LOCALBIN ?= $(shell pwd)/bin
22
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
33
CONTROLLER_TOOLS_VERSION ?= v0.21.0
4+
GENERATED_CRD_DIR ?= config/crd/bases
5+
CHART_CRD_DIR ?= charts/oci-functions-operator/crds
46

57
.PHONY: all
68
all: generate manifests test
@@ -19,11 +21,26 @@ test:
1921

2022
.PHONY: helm-chart
2123
helm-chart: manifests
22-
mkdir -p charts/oci-functions-operator/crds
23-
cp config/crd/bases/functions.oci.oracle.com_functions.yaml charts/oci-functions-operator/crds/functions.functions.oci.oracle.com.yaml
24-
cp config/crd/bases/functions.oci.oracle.com_functionjobs.yaml charts/oci-functions-operator/crds/functionjobs.functions.oci.oracle.com.yaml
25-
cp config/crd/bases/functions.oci.oracle.com_functionevents.yaml charts/oci-functions-operator/crds/functionevents.functions.oci.oracle.com.yaml
26-
cp config/crd/bases/functions.oci.oracle.com_functioneventtriggers.yaml charts/oci-functions-operator/crds/functioneventtriggers.functions.oci.oracle.com.yaml
24+
mkdir -p $(CHART_CRD_DIR)
25+
cp $(GENERATED_CRD_DIR)/functions.oci.oracle.com_functionapplications.yaml $(CHART_CRD_DIR)/functionapplications.functions.oci.oracle.com.yaml
26+
cp $(GENERATED_CRD_DIR)/functions.oci.oracle.com_functions.yaml $(CHART_CRD_DIR)/functions.functions.oci.oracle.com.yaml
27+
cp $(GENERATED_CRD_DIR)/functions.oci.oracle.com_functionjobs.yaml $(CHART_CRD_DIR)/functionjobs.functions.oci.oracle.com.yaml
28+
cp $(GENERATED_CRD_DIR)/functions.oci.oracle.com_functionevents.yaml $(CHART_CRD_DIR)/functionevents.functions.oci.oracle.com.yaml
29+
cp $(GENERATED_CRD_DIR)/functions.oci.oracle.com_functioneventtriggers.yaml $(CHART_CRD_DIR)/functioneventtriggers.functions.oci.oracle.com.yaml
30+
$(MAKE) helm-crds-check
31+
32+
.PHONY: helm-crds-check
33+
helm-crds-check:
34+
test -f $(CHART_CRD_DIR)/functionapplications.functions.oci.oracle.com.yaml
35+
test -f $(CHART_CRD_DIR)/functions.functions.oci.oracle.com.yaml
36+
test -f $(CHART_CRD_DIR)/functionjobs.functions.oci.oracle.com.yaml
37+
test -f $(CHART_CRD_DIR)/functioneventtriggers.functions.oci.oracle.com.yaml
38+
test -f $(CHART_CRD_DIR)/functionevents.functions.oci.oracle.com.yaml
39+
cmp -s $(GENERATED_CRD_DIR)/functions.oci.oracle.com_functionapplications.yaml $(CHART_CRD_DIR)/functionapplications.functions.oci.oracle.com.yaml
40+
cmp -s $(GENERATED_CRD_DIR)/functions.oci.oracle.com_functions.yaml $(CHART_CRD_DIR)/functions.functions.oci.oracle.com.yaml
41+
cmp -s $(GENERATED_CRD_DIR)/functions.oci.oracle.com_functionjobs.yaml $(CHART_CRD_DIR)/functionjobs.functions.oci.oracle.com.yaml
42+
cmp -s $(GENERATED_CRD_DIR)/functions.oci.oracle.com_functioneventtriggers.yaml $(CHART_CRD_DIR)/functioneventtriggers.functions.oci.oracle.com.yaml
43+
cmp -s $(GENERATED_CRD_DIR)/functions.oci.oracle.com_functionevents.yaml $(CHART_CRD_DIR)/functionevents.functions.oci.oracle.com.yaml
2744

2845
.PHONY: helm-template
2946
helm-template: helm-chart

app-dev/devops-and-containers/oke/oci-functions-operator/PROJECT

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ layout:
44
projectName: oci-functions-operator
55
repo: github.com/oracle/oci-functions-operator
66
resources:
7+
- api:
8+
crdVersion: v1
9+
namespaced: true
10+
controller: true
11+
domain: oci.oracle.com
12+
group: functions
13+
kind: FunctionApplication
14+
path: github.com/oracle/oci-functions-operator/api/v1alpha1
15+
version: v1alpha1
716
- api:
817
crdVersion: v1
918
namespaced: true
Lines changed: 89 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,136 @@
11
# OCI Functions Operator
22

3-
Kubernetes-native management and job-style invocation of OCI Functions from OKE.
3+
Kubernetes-native APIs for managing and invoking OCI Functions from OKE.
44

5-
MVP controller image:
5+
Current controller image:
66

77
```text
8-
ghcr.io/ronsevet/oci-functions-operator/controller:mvp-events-functionevents-v1
8+
ghcr.io/ronsevetoci/oci-functions-operator/controller:v0.1.7
99
```
1010

11-
## MVP Feature Summary
11+
## What This Operator Provides
1212

13-
The MVP adds four namespaced CRDs:
13+
The operator exposes five namespaced CRDs in `functions.oci.oracle.com/v1alpha1`:
1414

15-
- `Function`: references an existing OCI Function or manages an OCI Functions application/function.
16-
- `FunctionJob`: invokes a referenced `Function`, fans out inline JSON payloads, retries failed invocations, and records aggregate/per-payload status.
17-
- `FunctionEventTrigger`: creates OCI Events Rules for OCI service events such as Object Storage events, or routes Kubernetes-native `functionevent.*` events to a referenced `Function`.
18-
- `FunctionEvent`: Kubernetes-native event object for direct operator-routed invocation through `functionevent.*` event types.
15+
- `FunctionApplication`: maps to an OCI Functions Application. It owns app-level settings such as compartment, region, subnets, NSGs, application config, and invocation log settings.
16+
- `Function`: maps to an OCI Function. It can reference an existing OCI Function or manage one inside a `FunctionApplication`.
17+
- `FunctionJob`: invokes a referenced `Function` with one or more inline JSON payloads, parallelism, retry limits, and per-payload status.
18+
- `FunctionEventTrigger`: routes an OCI Events rule or a Kubernetes-native `functionevent.*` event type to a `Function`.
19+
- `FunctionEvent`: an in-cluster event envelope that the operator matches against `FunctionEventTrigger` resources.
20+
21+
`FunctionJob` and `FunctionEvent` both end in a Function invocation, but they are not the same resource. Use `FunctionJob` when a user or system wants to submit explicit invocation work and track that work as a job. Use `FunctionEvent` when an application emits an event and wants the operator to route it through matching triggers.
22+
23+
## Resource Flow
24+
25+
```mermaid
26+
flowchart TB
27+
APPCR[FunctionApplication CR]
28+
APP[OCI Functions Application]
29+
FNCR[Function CR]
30+
FN[OCI Function]
31+
INVOKE[Function Invocation]
32+
33+
APPCR --> APP
34+
APP --> FNCR
35+
FNCR --> FN
36+
FN --> INVOKE
37+
38+
JOB[FunctionJob CR]
39+
TRIGGER[FunctionEventTrigger CR]
40+
EVENT[FunctionEvent CR]
41+
OCIEVENT[OCI service event]
42+
RULE[OCI Events Rule]
43+
44+
JOB --> INVOKE
45+
EVENT --> TRIGGER
46+
OCIEVENT --> RULE
47+
RULE --> TRIGGER
48+
TRIGGER --> INVOKE
49+
```
50+
51+
Read it top to bottom: a `FunctionApplication` is the application wrapper, a `Function` lives inside it, and every invocation path eventually invokes that function. `FunctionJob` is direct work submission. `FunctionEventTrigger` routes either OCI service events through an OCI Events Rule or in-cluster `FunctionEvent` objects.
52+
53+
The important boundary is that the operator does not turn OCI Functions into Pods. It keeps OCI Functions as OCI resources and gives Kubernetes users a clear control plane for application setup, function setup, invocation, event routing, status, and events.
1954

2055
## Two Images
2156

22-
The operator image and function runtime image are different artifacts:
57+
The operator image and function runtime image are separate artifacts:
2358

2459
- Operator/controller image: runs as a Kubernetes Deployment in OKE. It can be in GHCR, OCIR, or any registry OKE can pull from.
2560
- Function runtime image: runs in OCI Functions. It must be an OCI Functions-compatible Fn image in same-region OCIR, for example `jed.ocir.io/<TENANCY_NAMESPACE>/hello-function:fn-v1` for Jeddah.
2661

27-
Do not use GHCR for the OCI Functions runtime image. OCI Functions pulls the runtime image from the Functions application network during invocation, so the application subnet/NSG must have egress to Oracle Services Network/OCIR even when the OCIR repository is public.
62+
Do not use GHCR for the OCI Functions runtime image. OCI Functions pulls the runtime image from the Functions application network during invocation, so the application subnet and any attached NSGs must allow egress to Oracle Services Network/OCIR even when the OCIR repository is public.
2863

2964
## Start Here
3065

31-
- [Helm install](docs/helm-install.md): recommended OKE installation and upgrade path.
32-
- [MVP demo flow](docs/demo/mvp-demo-flow.md): primary concise handoff/demo guide for the final MVP image.
33-
- [MVP checklist](docs/demo/mvp-checklist.md): short secondary pre-demo checklist.
34-
- [MVP video script](docs/demo/mvp-video-script.md): short secondary narration outline.
35-
- [Managed Function demo](docs/managed-function-demo.md): primary OKE walkthrough for managed application/function creation and invocation.
36-
- [Function events](docs/function-events.md): Kubernetes-native `functionevent.*` events routed directly by the operator.
37-
- [Function event triggers](docs/event-triggers.md): OCI Events Rule and FunctionEvent trigger setup.
38-
- [OKE deployment](docs/oke-deployment.md): supported Helm deployment, Workload Identity, IAM, and network setup.
39-
- [Design overview](docs/design.md): CRDs, controllers, lifecycle, invoker contracts, and limitations.
40-
- [Local existing Function demo](docs/oci-mode-demo.md): local `OCI_AUTH_MODE=config` path against an already-created OCI Function.
66+
- [Helm install](docs/helm-install.md): supported OKE installation and upgrade path.
67+
- [OKE deployment](docs/oke-deployment.md): Workload Identity, IAM, networking, and the core resource sequence.
68+
- [Function event triggers](docs/event-triggers.md): OCI Events and `functionevent.*` routing.
69+
- [Function events](docs/function-events.md): Kubernetes-native event emission.
4170
- [Debugging Functions](docs/debugging-functions.md): image, CRD, Workload Identity, NSG, and invocation failure checks.
42-
- [Validation notes](docs/validation-notes.md): template for recording real OCI-mode runs.
43-
- [Sample function image](examples/hello-function/README.md): Fn-compatible Python function runtime image for the managed demo.
71+
- [Design overview](docs/design.md): controller architecture, resource behavior, and current limitations.
72+
- [Sample function image](examples/hello-function/README.md): Fn-compatible Python function runtime image.
73+
74+
Tracked files under `config/samples/` are generic examples with placeholders. Live walkthrough manifests with OCIDs, tenancy namespaces, bucket names, or temporary cleanup settings should live in the ignored `local/` directory, not in the repository history.
75+
76+
## Helm CRDs
77+
78+
The Helm chart includes CRDs for `FunctionApplication`, `Function`, `FunctionJob`, `FunctionEventTrigger`, and `FunctionEvent`.
79+
80+
Fresh Helm installs install CRDs from `charts/oci-functions-operator/crds/`, but existing Helm upgrades do not add or update CRDs from that directory. Before installing or upgrading after API changes, apply the chart CRDs first:
81+
82+
```sh
83+
kubectl apply -f charts/oci-functions-operator/crds/
84+
```
85+
86+
Then run `helm upgrade --install`.
4487

4588
## Modes
4689

47-
`INVOKER_MODE=fake` is the default. It requires no OCI auth, creates no OCI resources, and is useful only for CRD/controller/status demos.
90+
`INVOKER_MODE=fake` is the default for local controller development and tests. It requires no OCI auth and creates no OCI resources.
4891

4992
`INVOKER_MODE=oci` uses the OCI Go SDK:
5093

5194
- On OKE, the Helm chart configures Workload Identity with `oci.authMode=workload`.
5295
- For local development only, use `OCI_AUTH_MODE=config` with `OCI_CONFIG_FILE` and `OCI_CONFIG_PROFILE`.
5396

54-
Existing mode requires `spec.functionId` and `spec.invokeEndpoint` on the `Function`. Managed mode uses `spec.config` to create/update the OCI Functions application and function, then writes `status.applicationId`, `status.functionId`, and `status.invokeEndpoint`.
97+
The Helm chart is the supported way to deploy the operator on OKE. Kustomize under `config/` is kept for Kubebuilder-generated manifests and local development only. Do not mix Helm and Kustomize for the same cluster install.
98+
99+
## Resource Model
100+
101+
Preferred managed mode is explicit:
102+
103+
1. Create a `FunctionApplication` for the shared OCI Functions Application.
104+
2. Create one or more managed `Function` resources with `spec.applicationRef.name`.
105+
3. Wait for `Function.status.functionId` and `Function.status.invokeEndpoint`.
106+
4. Invoke through `FunctionJob`, OCI Events-backed `FunctionEventTrigger`, or Kubernetes-native `FunctionEvent`.
107+
108+
Legacy managed `Function` manifests that put app-level settings under `spec.config` still work for compatibility, but new manifests should use `FunctionApplication`.
55109

56-
## Local Fake Demo
110+
`Function.spec.deletionPolicy` defaults to `Retain`. Deleting a managed `Function` with `Retain` leaves OCI resources untouched. Set `deletionPolicy: Delete` only when Kubernetes deletion should also delete the managed OCI Function. `FunctionApplication.spec.deletionPolicy` controls OCI Application cleanup separately; `Delete` is honored only for managed applications and only when no functions remain. Existing-mode resources never delete OCI resources.
57111

58-
Install or refresh generated manifests:
112+
## Local Development
113+
114+
Install or refresh generated CRDs:
59115

60116
```sh
61117
make generate
62118
make manifests
63119
kubectl apply -k config/crd
64120
```
65121

66-
Run the manager against your current kubeconfig:
122+
Run the manager in fake mode:
67123

68124
```sh
69125
INVOKER_MODE=fake go run ./cmd
70126
```
71127

72-
In another terminal:
128+
Apply the safe sample resources:
73129

74130
```sh
75-
scripts/check-demo-prereqs.sh
76-
scripts/demo-fake.sh
131+
kubectl apply -k config/samples
132+
kubectl get functions,functionjobs
133+
kubectl describe functionjob hello-job
77134
```
78135

79-
Fake mode proves only the Kubernetes reconciliation/status path. It does not prove OCI auth, OCI Functions network egress, OCIR image access, or function image compatibility.
80-
81-
## Primary OKE Path
82-
83-
For OKE managed mode:
84-
85-
1. Build and push the operator/controller image to a registry OKE can pull.
86-
2. Build a Fn-compatible function runtime image and push it to same-region OCIR.
87-
3. Deploy the operator with Helm. The chart is the supported OKE path and defaults to OCI mode with Workload Identity.
88-
4. Apply a managed `Function` with `spec.config.region`, `compartmentId`, `applicationName`, `subnetIds`, optional `nsgIds`, and same-region OCIR `image`.
89-
5. Submit a `FunctionJob`, create a `FunctionEventTrigger`, or emit a `FunctionEvent` after the `Function` is Ready.
90-
91-
See [docs/helm-install.md](docs/helm-install.md) for installation and [docs/managed-function-demo.md](docs/managed-function-demo.md) for the full Function sequence.
92-
93-
Kustomize under `config/` is kept for Kubebuilder-generated manifests and local controller development only. Do not mix Helm and Kustomize for the same OKE operator install.
136+
Fake mode proves only Kubernetes reconciliation and status behavior. It does not prove OCI auth, OCI Functions network egress, OCIR image access, or function image compatibility.

app-dev/devops-and-containers/oke/oci-functions-operator/api/v1alpha1/function_types.go

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,19 @@ const (
3434
FunctionPhaseError FunctionPhase = "Error"
3535
)
3636

37+
// FunctionDeletionPolicy controls OCI resource cleanup when a Function CR is deleted.
38+
// +kubebuilder:validation:Enum=Retain;Delete
39+
type FunctionDeletionPolicy string
40+
41+
const (
42+
// FunctionDeletionPolicyRetain leaves OCI resources untouched when the Kubernetes Function is deleted.
43+
FunctionDeletionPolicyRetain FunctionDeletionPolicy = "Retain"
44+
// FunctionDeletionPolicyDelete deletes the managed OCI Function when the Kubernetes Function is deleted.
45+
FunctionDeletionPolicyDelete FunctionDeletionPolicy = "Delete"
46+
)
47+
3748
// FunctionSpec defines the desired state of Function.
38-
// +kubebuilder:validation:XValidation:rule="has(self.functionId) || has(self.existingFunctionOcid) || has(self.config)",message="one of spec.functionId, spec.existingFunctionOcid, or spec.config is required"
49+
// +kubebuilder:validation:XValidation:rule="has(self.functionId) || has(self.existingFunctionOcid) || has(self.config) || has(self.applicationRef)",message="one of spec.functionId, spec.existingFunctionOcid, spec.config, or spec.applicationRef is required"
3950
// +kubebuilder:validation:XValidation:rule="!(has(self.config) && (has(self.functionId) || has(self.existingFunctionOcid)))",message="spec.config is mutually exclusive with existing function references"
4051
// +kubebuilder:validation:XValidation:rule="!(has(self.functionId) && has(self.existingFunctionOcid))",message="spec.functionId and spec.existingFunctionOcid are mutually exclusive"
4152
// +kubebuilder:validation:XValidation:rule="!(has(self.functionId) || has(self.existingFunctionOcid)) || has(self.invokeEndpoint)",message="existing Function mode requires spec.invokeEndpoint"
@@ -67,27 +78,55 @@ type FunctionSpec struct {
6778
// Config describes desired function configuration for managed lifecycle.
6879
// +optional
6980
Config *FunctionConfig `json:"config,omitempty"`
81+
82+
// ApplicationRef references a FunctionApplication that manages or resolves the OCI Functions application.
83+
// When set, app-level settings such as region, compartmentId, applicationName, subnetIds, and nsgIds
84+
// are read from the referenced FunctionApplication instead of this Function's legacy config.
85+
// +optional
86+
ApplicationRef *FunctionApplicationReference `json:"applicationRef,omitempty"`
87+
88+
// DeletionPolicy controls what happens to OCI resources when this Kubernetes Function is deleted.
89+
// Defaults to Retain for safety. Delete is honored only for Managed mode and deletes the managed OCI Function.
90+
// The OCI Functions application is controlled separately by FunctionApplication.
91+
// +optional
92+
// +kubebuilder:default=Retain
93+
DeletionPolicy FunctionDeletionPolicy `json:"deletionPolicy,omitempty"`
94+
}
95+
96+
// FunctionApplicationReference identifies a FunctionApplication in the same namespace.
97+
type FunctionApplicationReference struct {
98+
// Name is the referenced FunctionApplication name.
99+
// +kubebuilder:validation:MinLength=1
100+
Name string `json:"name"`
70101
}
71102

72103
// FunctionConfig contains the minimal OCI Functions configuration this API manages.
73104
type FunctionConfig struct {
74105
// Region is the OCI region identifier, such as me-jeddah-1.
106+
// Required for legacy managed Functions that do not set applicationRef.
107+
// +optional
75108
// +kubebuilder:validation:MinLength=1
76-
Region string `json:"region"`
109+
Region string `json:"region,omitempty"`
77110

78111
// CompartmentID is the compartment OCID for the managed application/function.
112+
// Required for legacy managed Functions that do not set applicationRef.
113+
// +optional
79114
// +kubebuilder:validation:Pattern=^ocid1\.compartment\..+
80-
CompartmentID string `json:"compartmentId"`
115+
CompartmentID string `json:"compartmentId,omitempty"`
81116

82117
// ApplicationName is the display name of the OCI Functions application to ensure.
118+
// Required for legacy managed Functions that do not set applicationRef.
119+
// +optional
83120
// +kubebuilder:validation:MinLength=1
84121
// +kubebuilder:validation:MaxLength=255
85-
ApplicationName string `json:"applicationName"`
122+
ApplicationName string `json:"applicationName,omitempty"`
86123

87124
// SubnetIDs are the subnet OCIDs for a newly created application.
125+
// Required for legacy managed Functions that do not set applicationRef.
126+
// +optional
88127
// +kubebuilder:validation:MinItems=1
89128
// +listType=atomic
90-
SubnetIDs []string `json:"subnetIds"`
129+
SubnetIDs []string `json:"subnetIds,omitempty"`
91130

92131
// NSGIDs are the Network Security Group OCIDs to attach to the managed OCI Functions application.
93132
// When omitted, the operator leaves NSGs unmanaged on existing applications.
@@ -224,6 +263,14 @@ func (f *Function) IsReady() bool {
224263
return meta.IsStatusConditionTrue(f.Status.Conditions, FunctionConditionReady)
225264
}
226265

266+
// DeletionPolicy returns the effective deletion policy when API defaulting has not run.
267+
func (f *Function) DeletionPolicy() FunctionDeletionPolicy {
268+
if f.Spec.DeletionPolicy == FunctionDeletionPolicyDelete {
269+
return FunctionDeletionPolicyDelete
270+
}
271+
return FunctionDeletionPolicyRetain
272+
}
273+
227274
func init() {
228275
SchemeBuilder.Register(&Function{}, &FunctionList{})
229276
}

0 commit comments

Comments
 (0)