Skip to content

Commit cf56e5b

Browse files
Merge branch 'main' into sb-aws-finops
2 parents ea43d7c + 577bb1c commit cf56e5b

2,018 files changed

Lines changed: 674176 additions & 762 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.git
2+
.github
3+
.idea
4+
.vscode
5+
bin
6+
dist
7+
tmp
8+
9+
*.test
10+
*.out
11+
coverage.*
12+
13+
.DS_Store
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# OCI Functions Operator for OKE
2+
3+
## Goal
4+
5+
Build a Kubernetes operator that lets OKE users manage and invoke OCI Functions through Kubernetes-native CRDs.
6+
7+
## MVP
8+
9+
The MVP has two CRDs:
10+
11+
- Function
12+
- FunctionJob
13+
14+
FunctionJob should support:
15+
- functionRef
16+
- inline JSON payloads
17+
- parallelism
18+
- retryLimit
19+
- Kubernetes status aggregation
20+
21+
## Design principles
22+
23+
- Do not pretend OCI Functions are Kubernetes Pods.
24+
- Do not support arbitrary PodTemplateSpec in v1alpha1.
25+
- Prefer explicit validation errors over silent behavior.
26+
- Keep OCI SDK access behind small interfaces.
27+
- Make controllers idempotent.
28+
- Make status useful from `kubectl get` and `kubectl describe`.
29+
- Prefer small, testable packages.
30+
31+
## Out of scope for MVP
32+
33+
- Cron scheduling
34+
- Event sources
35+
- Native Kubernetes Job compatibility
36+
- Volumes
37+
- Sidecars
38+
- Init containers
39+
- GPU
40+
- Privileged execution
41+
- Full OCI Function lifecycle management before invocation spike works
42+
43+
## Build commands
44+
45+
Use:
46+
- `make generate`
47+
- `make manifests`
48+
- `make test`
49+
50+
## Language
51+
52+
Go, Kubebuilder/controller-runtime.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Build the manager binary.
2+
ARG GO_VERSION=1.26.0
3+
FROM golang:${GO_VERSION} AS builder
4+
5+
WORKDIR /workspace
6+
7+
COPY go.mod go.sum ./
8+
RUN go mod download
9+
10+
COPY . .
11+
12+
ARG TARGETOS=linux
13+
ARG TARGETARCH=amd64
14+
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -ldflags="-s -w" -o manager ./cmd
15+
16+
# Run as a non-root user in a minimal image.
17+
FROM gcr.io/distroless/static-debian12:nonroot
18+
19+
WORKDIR /
20+
COPY --from=builder /workspace/manager /manager
21+
22+
USER 65532:65532
23+
ENTRYPOINT ["/manager"]
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
LOCALBIN ?= $(shell pwd)/bin
2+
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
3+
CONTROLLER_TOOLS_VERSION ?= v0.21.0
4+
GENERATED_CRD_DIR ?= config/crd/bases
5+
CHART_CRD_DIR ?= charts/oci-functions-operator/crds
6+
7+
.PHONY: all
8+
all: generate manifests test
9+
10+
.PHONY: generate
11+
generate: controller-gen
12+
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
13+
14+
.PHONY: manifests
15+
manifests: controller-gen
16+
$(CONTROLLER_GEN) rbac:roleName=manager-role crd paths="./..." output:crd:artifacts:config=config/crd/bases output:rbac:artifacts:config=config/rbac
17+
18+
.PHONY: test
19+
test:
20+
go test ./...
21+
22+
.PHONY: helm-chart
23+
helm-chart: manifests
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
44+
45+
.PHONY: helm-template
46+
helm-template: helm-chart
47+
helm template oci-functions-operator charts/oci-functions-operator \
48+
--namespace oci-functions-operator-system
49+
50+
.PHONY: fmt
51+
fmt:
52+
go fmt ./...
53+
54+
.PHONY: vet
55+
vet:
56+
go vet ./...
57+
58+
.PHONY: controller-gen
59+
controller-gen: $(CONTROLLER_GEN)
60+
61+
$(CONTROLLER_GEN):
62+
mkdir -p $(LOCALBIN)
63+
GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
domain: oci.oracle.com
2+
layout:
3+
- go.kubebuilder.io/v4
4+
projectName: oci-functions-operator
5+
repo: github.com/oracle/oci-functions-operator
6+
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
16+
- api:
17+
crdVersion: v1
18+
namespaced: true
19+
controller: true
20+
domain: oci.oracle.com
21+
group: functions
22+
kind: Function
23+
path: github.com/oracle/oci-functions-operator/api/v1alpha1
24+
version: v1alpha1
25+
- api:
26+
crdVersion: v1
27+
namespaced: true
28+
controller: true
29+
domain: oci.oracle.com
30+
group: functions
31+
kind: FunctionJob
32+
path: github.com/oracle/oci-functions-operator/api/v1alpha1
33+
version: v1alpha1
34+
version: "3"
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# OCI Functions Operator
2+
3+
Kubernetes-native APIs for managing and invoking OCI Functions from OKE.
4+
5+
Current controller image:
6+
7+
```text
8+
ghcr.io/ronsevetoci/oci-functions-operator/controller:v0.1.7
9+
```
10+
11+
## What This Operator Provides
12+
13+
The operator exposes five namespaced CRDs in `functions.oci.oracle.com/v1alpha1`:
14+
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.
54+
55+
## Two Images
56+
57+
The operator image and function runtime image are separate artifacts:
58+
59+
- Operator/controller image: runs as a Kubernetes Deployment in OKE. It can be in GHCR, OCIR, or any registry OKE can pull from.
60+
- 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.
61+
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.
63+
64+
## Start Here
65+
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.
70+
- [Debugging Functions](docs/debugging-functions.md): image, CRD, Workload Identity, NSG, and invocation failure checks.
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`.
87+
88+
## Modes
89+
90+
`INVOKER_MODE=fake` is the default for local controller development and tests. It requires no OCI auth and creates no OCI resources.
91+
92+
`INVOKER_MODE=oci` uses the OCI Go SDK:
93+
94+
- On OKE, the Helm chart configures Workload Identity with `oci.authMode=workload`.
95+
- For local development only, use `OCI_AUTH_MODE=config` with `OCI_CONFIG_FILE` and `OCI_CONFIG_PROFILE`.
96+
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`.
109+
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.
111+
112+
## Local Development
113+
114+
Install or refresh generated CRDs:
115+
116+
```sh
117+
make generate
118+
make manifests
119+
kubectl apply -k config/crd
120+
```
121+
122+
Run the manager in fake mode:
123+
124+
```sh
125+
INVOKER_MODE=fake go run ./cmd
126+
```
127+
128+
Apply the safe sample resources:
129+
130+
```sh
131+
kubectl apply -k config/samples
132+
kubectl get functions,functionjobs
133+
kubectl describe functionjob hello-job
134+
```
135+
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.

0 commit comments

Comments
 (0)