Skip to content

Commit 6009564

Browse files
authored
Simplify functions API (#36)
1 parent 0d4bb44 commit 6009564

11 files changed

Lines changed: 110 additions & 133 deletions

File tree

README.md

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# func-operator
22

3-
A Kubernetes operator for managing serverless functions using the `func` CLI. This operator automates the deployment and lifecycle management of functions from Git repositories to Kubernetes clusters with Knative.
3+
A Kubernetes operator for managing middleware updates for serverless functions deployed with the `func` CLI. This operator monitors deployed functions and automatically rebuilds them when outdated middleware is detected, ensuring functions stay up-to-date with the latest middleware versions.
44

55
## Prerequisites
66

@@ -21,9 +21,9 @@ kubectl apply -f https://github.com/functions-dev/func-operator/releases/latest/
2121

2222
## Usage
2323

24-
### Create a Function
24+
### Register a Function for Middleware Management
2525

26-
Create a `Function` custom resource to deploy a function from a Git repository:
26+
Create a `Function` custom resource to register an existing function for middleware monitoring and updates:
2727

2828
```yaml
2929
apiVersion: functions.dev/v1alpha1
@@ -32,12 +32,11 @@ metadata:
3232
name: my-function
3333
namespace: default
3434
spec:
35-
source:
36-
repositoryUrl: https://github.com/your-org/your-function.git
35+
repository:
36+
url: https://github.com/your-org/your-function.git
3737
authSecretRef:
3838
name: git-credentials
3939
registry:
40-
path: quay.io/your-username/my-function
4140
authSecretRef:
4241
name: registry-credentials
4342
```
@@ -48,6 +47,12 @@ Apply the resource:
4847
kubectl apply -f function.yaml
4948
```
5049

50+
**Note:** This registers an existing function with the operator for middleware management. To initially deploy a function, use the `func` CLI directly:
51+
52+
```bash
53+
func deploy --path <function-path> --registry <registry-path>
54+
```
55+
5156
### Registry Authentication
5257

5358
For private registries, create a secret with registry credentials:
@@ -100,7 +105,7 @@ data:
100105
password: <base64-encoded-password>
101106
```
102107
103-
Then reference it in the Function under `.spec.source.authSecretRef.name`
108+
Then reference it in the Function under `.spec.repository.authSecretRef.name`
104109

105110
```yaml
106111
apiVersion: functions.dev/v1alpha1
@@ -109,23 +114,24 @@ metadata:
109114
name: my-function
110115
namespace: default
111116
spec:
112-
source:
113-
repositoryUrl: https://github.com/your-org/your-function.git
117+
repository:
118+
url: https://github.com/your-org/your-function.git
114119
authSecretRef:
115120
name: git-credentials
116121
```
117122

118123
### Check Function Status
119124

120-
View the status of your function:
125+
View the middleware status of your function:
121126

122127
```bash
123128
kubectl get function my-function -o yaml
124129
```
125130

126131
The status will include:
127132
- Function name and runtime
128-
- Deployment conditions
133+
- Middleware update conditions
134+
- Whether the function needs rebuilding due to outdated middleware
129135

130136
## Development
131137

@@ -205,13 +211,14 @@ make lint
205211

206212
### Function Spec
207213

208-
| Field | Type | Required | Description |
209-
|--------------------------|---------|----------|--------------------------------------------------------|
210-
| `source.repositoryUrl` | string | Yes | Git repository URL containing the function source code |
211-
| `source.authSecretRef` | object | No | Reference to Git repository authentication secret |
212-
| `registry.path` | string | Yes | Container registry path for the function image |
213-
| `registry.insecure` | boolean | No | Allow insecure registry connections |
214-
| `registry.authSecretRef` | object | No | Reference to registry authentication secret |
214+
| Field | Type | Required | Description |
215+
|-----------------------------|---------|----------|--------------------------------------------------------------------------------------------------|
216+
| `repository.url` | string | Yes | URL of the Git repository containing the function |
217+
| `repository.branch` | string | No | Branch of the repository |
218+
| `repository.path` | string | No | Path to the function inside the repository. Defaults to "." |
219+
| `repository.authSecretRef` | object | No | Reference to the auth secret for private repository authentication |
220+
| `registry.authSecretRef` | object | No | Reference to the secret containing credentials for registry authentication |
221+
| `autoUpdateMiddleware` | boolean | No | Defines if the operator should rebuild when outdated middleware is detected. Defaults to global operator config |
215222

216223
### Function Status
217224

api/v1alpha1/function_types.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,36 @@ type Function struct {
3838

3939
// FunctionSpec defines the desired state of Function.
4040
type FunctionSpec struct {
41-
Source FunctionSpecSource `json:"source,omitempty"`
42-
Registry FunctionSpecRegistry `json:"registry,omitempty"`
41+
Repository FunctionSpecRepository `json:"repository,omitempty"`
42+
Registry FunctionSpecRegistry `json:"registry,omitempty"`
43+
44+
// AutoUpdateMiddleware defines if the operator should rebuild the function when an outdated middleware is detected.
45+
// Defaults to the global operator config.
46+
// TODO: implement logic
47+
AutoUpdateMiddleware *bool `json:"autoUpdateMiddleware,omitempty"`
4348
}
4449

45-
type FunctionSpecSource struct {
50+
type FunctionSpecRepository struct {
4651
// +kubebuilder:validation:Required
4752
// +kubebuilder:validation:MinLength=1
48-
RepositoryURL string `json:"repositoryUrl"`
53+
// URL of the Git repository containing the function
54+
URL string `json:"url"`
4955

5056
// +kubebuilder:validation:Optional
51-
Reference string `json:"reference"`
57+
// Branch of the repository
58+
Branch string `json:"branch,omitempty"`
5259

60+
// AuthSecretRef defines the reference to the auth secret in case the repository is private and needs authentication
5361
AuthSecretRef *v1.LocalObjectReference `json:"authSecretRef,omitempty"`
62+
63+
// +kubebuilder:validation:Optional
64+
// Path points to the function inside the repository. Defaults to "."
65+
// TODO: implement logic
66+
Path string `json:"path,omitempty"`
5467
}
5568

5669
type FunctionSpecRegistry struct {
57-
// +kubebuilder:validation:Required
58-
// +kubebuilder:validation:MinLength=1
59-
Path string `json:"path"`
60-
61-
Insecure bool `json:"insecure,omitempty"`
62-
70+
// AuthSecretRef is the reference to the secret containing the credentials for the registry authentication
6371
AuthSecretRef *v1.LocalObjectReference `json:"authSecretRef,omitempty"`
6472
}
6573

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 10 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/functions.dev_functions.yaml

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,16 @@ spec:
5151
spec:
5252
description: FunctionSpec defines the desired state of Function.
5353
properties:
54+
autoUpdateMiddleware:
55+
description: |-
56+
AutoUpdateMiddleware defines if the operator should rebuild the function when an outdated middleware is detected.
57+
Defaults to the global operator config.
58+
type: boolean
5459
registry:
5560
properties:
5661
authSecretRef:
57-
description: |-
58-
LocalObjectReference contains enough information to let you locate the
59-
referenced object inside the same namespace.
62+
description: AuthSecretRef is the reference to the secret containing
63+
the credentials for the registry authentication
6064
properties:
6165
name:
6266
default: ""
@@ -69,20 +73,12 @@ spec:
6973
type: string
7074
type: object
7175
x-kubernetes-map-type: atomic
72-
insecure:
73-
type: boolean
74-
path:
75-
minLength: 1
76-
type: string
77-
required:
78-
- path
7976
type: object
80-
source:
77+
repository:
8178
properties:
8279
authSecretRef:
83-
description: |-
84-
LocalObjectReference contains enough information to let you locate the
85-
referenced object inside the same namespace.
80+
description: AuthSecretRef defines the reference to the auth secret
81+
in case the repository is private and needs authentication
8682
properties:
8783
name:
8884
default: ""
@@ -95,13 +91,19 @@ spec:
9591
type: string
9692
type: object
9793
x-kubernetes-map-type: atomic
98-
reference:
94+
branch:
95+
description: Branch of the repository
96+
type: string
97+
path:
98+
description: Path points to the function inside the repository.
99+
Defaults to "."
99100
type: string
100-
repositoryUrl:
101+
url:
102+
description: URL of the Git repository containing the function
101103
minLength: 1
102104
type: string
103105
required:
104-
- repositoryUrl
106+
- url
105107
type: object
106108
type: object
107109
status:

config/rbac/role.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ rules:
88
- ""
99
resources:
1010
- persistentvolumeclaims
11+
- pods
12+
- pods/attach
1113
- secrets
1214
- services
1315
verbs:

internal/controller/function_controller.go

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ type FunctionReconciler struct {
6060
// +kubebuilder:rbac:groups=functions.dev,resources=functions,verbs=get;list;watch;create;update;patch;delete
6161
// +kubebuilder:rbac:groups=functions.dev,resources=functions/status,verbs=get;update;patch
6262
// +kubebuilder:rbac:groups=functions.dev,resources=functions/finalizers,verbs=update
63-
// +kubebuilder:rbac:groups="",resources=secrets;services;persistentvolumeclaims,verbs=get;list;watch;create;update;patch;delete
63+
// +kubebuilder:rbac:groups="",resources=pods;pods/attach;secrets;services;persistentvolumeclaims,verbs=get;list;watch;create;update;patch;delete
6464
// +kubebuilder:rbac:groups="apps",resources=deployments,verbs=get;list;watch;create;update;patch;delete
6565
// +kubebuilder:rbac:groups="serving.knative.dev",resources=services;routes,verbs=get;list;watch;create;update;patch;delete
6666
// +kubebuilder:rbac:groups="eventing.knative.dev",resources=triggers,verbs=get;list;watch;create;update;patch;delete
@@ -133,19 +133,19 @@ func (r *FunctionReconciler) reconcile(ctx context.Context, function *v1alpha1.F
133133
// prepareSource clones the git repository and retrieves function metadata
134134
func (r *FunctionReconciler) prepareSource(ctx context.Context, function *v1alpha1.Function) (*git.Repository, *funcfn.Function, error) {
135135
branchReference := "main"
136-
if function.Spec.Source.Reference != "" {
137-
branchReference = function.Spec.Source.Reference
136+
if function.Spec.Repository.Branch != "" {
137+
branchReference = function.Spec.Repository.Branch
138138
}
139139

140140
gitAuthSecret := v1.Secret{}
141-
if function.Spec.Source.AuthSecretRef != nil {
142-
if err := r.Get(ctx, types.NamespacedName{Namespace: function.Namespace, Name: function.Spec.Source.AuthSecretRef.Name}, &gitAuthSecret); err != nil {
141+
if function.Spec.Repository.AuthSecretRef != nil {
142+
if err := r.Get(ctx, types.NamespacedName{Namespace: function.Namespace, Name: function.Spec.Repository.AuthSecretRef.Name}, &gitAuthSecret); err != nil {
143143
function.MarkSourceNotReady("AuthSecretNotFound", "Auth secret not found: %s", err.Error())
144144
return nil, nil, err
145145
}
146146
}
147147

148-
repo, err := r.GitManager.CloneRepository(ctx, function.Spec.Source.RepositoryURL, branchReference, gitAuthSecret.Data)
148+
repo, err := r.GitManager.CloneRepository(ctx, function.Spec.Repository.URL, branchReference, gitAuthSecret.Data)
149149
if err != nil {
150150
function.MarkSourceNotReady("GitCloneFailed", "Failed to clone repository: %s", err.Error())
151151
return nil, nil, fmt.Errorf("failed to setup git repository: %w", err)
@@ -379,12 +379,7 @@ func (r *FunctionReconciler) deploy(ctx context.Context, function *v1alpha1.Func
379379
}
380380

381381
// deploy function
382-
deployOptions := funccli.DeployOptions{
383-
Registry: function.Spec.Registry.Path,
384-
InsecureRegistry: function.Spec.Registry.Insecure,
385-
GitUrl: function.Spec.Source.RepositoryURL,
386-
Builder: "s2i",
387-
}
382+
deployOptions := funccli.DeployOptions{}
388383

389384
if function.Spec.Registry.AuthSecretRef != nil && function.Spec.Registry.AuthSecretRef.Name != "" {
390385
// we have a registry auth secret referenced -> use this for func deploy

internal/controller/function_controller_test.go

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,9 @@ var _ = Describe("Function Controller", func() {
5151
}
5252

5353
defaultSpec := functionsdevv1alpha1.FunctionSpec{
54-
Source: functionsdevv1alpha1.FunctionSpecSource{
55-
RepositoryURL: "https://github.com/foo/bar",
56-
Reference: "my-branch",
57-
},
58-
Registry: functionsdevv1alpha1.FunctionSpecRegistry{
59-
Path: "quay.io/foo/bar",
54+
Repository: functionsdevv1alpha1.FunctionSpecRepository{
55+
URL: "https://github.com/foo/bar",
56+
Branch: "my-branch",
6057
},
6158
}
6259

@@ -115,11 +112,7 @@ var _ = Describe("Function Controller", func() {
115112
}, nil)
116113
funcMock.EXPECT().GetLatestMiddlewareVersion(mock.Anything, mock.Anything, mock.Anything).Return("v2.0.0", nil)
117114
funcMock.EXPECT().GetMiddlewareVersion(mock.Anything, functionName, resourceNamespace).Return("v1.0.0", nil)
118-
funcMock.EXPECT().Deploy(mock.Anything, mock.Anything, resourceNamespace, funccli.DeployOptions{
119-
Registry: "quay.io/foo/bar",
120-
GitUrl: "https://github.com/foo/bar",
121-
Builder: "s2i",
122-
}).Return(nil)
115+
funcMock.EXPECT().Deploy(mock.Anything, mock.Anything, resourceNamespace, funccli.DeployOptions{}).Return(nil)
123116

124117
gitMock.EXPECT().CloneRepository(mock.Anything, "https://github.com/foo/bar", "my-branch", mock.Anything).Return(createTmpGitRepo(functions.Function{Name: "func-go"}), nil)
125118
},
@@ -140,11 +133,8 @@ var _ = Describe("Function Controller", func() {
140133
}),
141134
Entry("should use main as default branch", reconcileTestCase{
142135
spec: functionsdevv1alpha1.FunctionSpec{
143-
Source: functionsdevv1alpha1.FunctionSpecSource{
144-
RepositoryURL: "https://github.com/foo/bar",
145-
},
146-
Registry: functionsdevv1alpha1.FunctionSpecRegistry{
147-
Path: "quay.io/foo/bar",
136+
Repository: functionsdevv1alpha1.FunctionSpecRepository{
137+
URL: "https://github.com/foo/bar",
148138
},
149139
},
150140
configureMocks: func(funcMock *funccli.MockManager, gitMock *git.MockManager) {

internal/funccli/manager.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,7 @@ type Manager interface {
3838
}
3939

4040
type DeployOptions struct {
41-
Registry string
42-
InsecureRegistry bool
4341
RegistryAuthFile string
44-
45-
GitUrl string
46-
47-
Builder string
4842
}
4943

5044
var _ Manager = &managerImpl{}
@@ -216,19 +210,12 @@ func (m *managerImpl) Deploy(ctx context.Context, repoPath string, namespace str
216210
"deploy",
217211
"--remote",
218212
"--namespace", namespace,
219-
"--registry", opts.Registry,
220-
"--git-url", opts.GitUrl,
221-
"--builder", opts.Builder,
222213
}
223214

224215
if opts.RegistryAuthFile != "" {
225216
deployArgs = append(deployArgs, "--registry-authfile", opts.RegistryAuthFile)
226217
}
227218

228-
if opts.InsecureRegistry {
229-
deployArgs = append(deployArgs, "--registry-insecure")
230-
}
231-
232219
out, err := m.Run(ctx, repoPath, deployArgs...)
233220
if err != nil {
234221
return fmt.Errorf("failed to deploy function: %q. %w", out, err)

0 commit comments

Comments
 (0)