feat: define Function and Trigger CRD types

Function:
  - runtime (go1.21/python3.11/nodejs20)
  - entrypoint, s3Bucket, s3Key
  - memoryMB (default 128), timeoutSec (default 30)
  - env map
  - status: phase (Pending/Building/Ready/Failed), imageRef, conditions

Trigger:
  - type (http/cron), functionRef
  - schedule (cron only)
  - preWarmSeconds (default 300) - pre-warm before cron run
  - status: active, url, lastScheduleTime

printcolumns for kubectl get output
This commit is contained in:
“Naeel”
2026-03-07 08:24:33 +04:00
parent 3e10ac1d63
commit d9f5ab56bb
5 changed files with 332 additions and 70 deletions
+63 -27
View File
@@ -1,18 +1,6 @@
/* // Изменено: 2026-03-07
Copyright 2026. // Описание CRD Function — основной ресурс serverless функции.
// Пользователь создаёт Function, контроллер строит образ и деплоит в k8s.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1 package v1alpha1
@@ -20,28 +8,76 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! // FunctionSpec — желаемое состояние функции.
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. // Описывает всё необходимое для сборки и запуска пользовательского кода.
// FunctionSpec defines the desired state of Function
type FunctionSpec struct { type FunctionSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Runtime — язык и версия выполнения (go1.21, python3.11, nodejs20)
// Important: Run "make" to regenerate code after modifying this file // +kubebuilder:validation:Enum=go1.21;python3.11;nodejs20
// +kubebuilder:validation:Required
Runtime string `json:"runtime"`
// Foo is an example field of Function. Edit function_types.go to remove/update // Entrypoint — точка входа в код функции (например: handler.handle)
Foo string `json:"foo,omitempty"` // +kubebuilder:validation:Required
Entrypoint string `json:"entrypoint"`
// S3Bucket — бакет S3 где хранится zip архив с кодом
S3Bucket string `json:"s3Bucket"`
// S3Key — ключ объекта в S3 (путь до zip архива)
S3Key string `json:"s3Key"`
// MemoryMB — лимит памяти в мегабайтах (default: 128)
// +kubebuilder:default=128
MemoryMB int32 `json:"memoryMB,omitempty"`
// TimeoutSec — максимальное время выполнения в секундах (default: 30)
// +kubebuilder:default=30
TimeoutSec int32 `json:"timeoutSec,omitempty"`
// Env — переменные окружения, передаются в контейнер функции
Env map[string]string `json:"env,omitempty"`
} }
// FunctionStatus defines the observed state of Function // FunctionPhase — текущая фаза жизненного цикла функции.
// Используем строковый тип чтобы было читаемо в kubectl get functions.
type FunctionPhase string
const (
// FunctionPhasePending — функция создана, ожидает сборки образа
FunctionPhasePending FunctionPhase = "Pending"
// FunctionPhaseBuilding — идёт сборка Docker образа
FunctionPhaseBuilding FunctionPhase = "Building"
// FunctionPhaseReady — образ собран, функция готова к вызову
FunctionPhaseReady FunctionPhase = "Ready"
// FunctionPhaseFailed — ошибка при сборке или деплое
FunctionPhaseFailed FunctionPhase = "Failed"
)
// FunctionStatus — наблюдаемое состояние функции (заполняет контроллер).
type FunctionStatus struct { type FunctionStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Phase — текущая фаза: Pending, Building, Ready, Failed
// Important: Run "make" to regenerate code after modifying this file Phase FunctionPhase `json:"phase,omitempty"`
// ImageRef — полный путь к собранному Docker образу в registry
ImageRef string `json:"imageRef,omitempty"`
// Message — человекочитаемое сообщение об ошибке или статусе
Message string `json:"message,omitempty"`
// Conditions — стандартные k8s conditions для интеграции с инструментами
Conditions []metav1.Condition `json:"conditions,omitempty"`
// LastBuiltAt — время последней успешной сборки образа
LastBuiltAt *metav1.Time `json:"lastBuiltAt,omitempty"`
} }
//+kubebuilder:object:root=true //+kubebuilder:object:root=true
//+kubebuilder:subresource:status //+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Runtime",type=string,JSONPath=`.spec.runtime`
//+kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
//+kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// Function is the Schema for the functions API // Function — основной ресурс для управления serverless функцией.
type Function struct { type Function struct {
metav1.TypeMeta `json:",inline"` metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"` metav1.ObjectMeta `json:"metadata,omitempty"`
+46 -26
View File
@@ -1,18 +1,6 @@
/* // Изменено: 2026-03-07
Copyright 2026. // Описание CRD Trigger — триггер для функции (HTTP или Cron).
// Один Trigger ссылается на одну Function и определяет способ вызова.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1 package v1alpha1
@@ -20,28 +8,60 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! // TriggerType — тип триггера.
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. type TriggerType string
// TriggerSpec defines the desired state of Trigger const (
// TriggerTypeHTTP — функция вызывается по HTTP запросу через Ingress
TriggerTypeHTTP TriggerType = "http"
// TriggerTypeCron — функция вызывается по расписанию (k8s CronJob)
TriggerTypeCron TriggerType = "cron"
)
// TriggerSpec — желаемое состояние триггера.
type TriggerSpec struct { type TriggerSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // FunctionRef — имя Function ресурса в том же namespace
// Important: Run "make" to regenerate code after modifying this file // +kubebuilder:validation:Required
FunctionRef string `json:"functionRef"`
// Foo is an example field of Trigger. Edit trigger_types.go to remove/update // Type — тип триггера: http или cron
Foo string `json:"foo,omitempty"` // +kubebuilder:validation:Enum=http;cron
// +kubebuilder:validation:Required
Type TriggerType `json:"type"`
// Schedule — расписание в формате cron (только для type=cron, например: "0 2 * * *")
Schedule string `json:"schedule,omitempty"`
// PreWarmSeconds — за сколько секунд до запуска прогреть контейнер.
// Актуально для cron: запускаем pod заранее чтобы избежать cold start.
// +kubebuilder:default=300
PreWarmSeconds int32 `json:"preWarmSeconds,omitempty"`
} }
// TriggerStatus defines the observed state of Trigger // TriggerStatus — наблюдаемое состояние триггера (заполняет контроллер).
type TriggerStatus struct { type TriggerStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Active — активен ли триггер в данный момент
// Important: Run "make" to regenerate code after modifying this file Active bool `json:"active,omitempty"`
// URL — публичный URL для вызова функции (только для type=http)
URL string `json:"url,omitempty"`
// LastScheduleTime — время последнего запуска по расписанию (только для type=cron)
LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"`
// Message — человекочитаемое сообщение об ошибке или статусе
Message string `json:"message,omitempty"`
} }
//+kubebuilder:object:root=true //+kubebuilder:object:root=true
//+kubebuilder:subresource:status //+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type`
//+kubebuilder:printcolumn:name="Function",type=string,JSONPath=`.spec.functionRef`
//+kubebuilder:printcolumn:name="Active",type=boolean,JSONPath=`.status.active`
//+kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.status.url`
//+kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// Trigger is the Schema for the triggers API // Trigger — ресурс для управления способом вызова Function.
type Trigger struct { type Trigger struct {
metav1.TypeMeta `json:",inline"` metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"` metav1.ObjectMeta `json:"metadata,omitempty"`
+26 -3
View File
@@ -21,6 +21,7 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
) )
@@ -29,8 +30,8 @@ func (in *Function) DeepCopyInto(out *Function) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status in.Status.DeepCopyInto(&out.Status)
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Function. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Function.
@@ -86,6 +87,13 @@ func (in *FunctionList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FunctionSpec) DeepCopyInto(out *FunctionSpec) { func (in *FunctionSpec) DeepCopyInto(out *FunctionSpec) {
*out = *in *out = *in
if in.Env != nil {
in, out := &in.Env, &out.Env
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FunctionSpec. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FunctionSpec.
@@ -101,6 +109,17 @@ func (in *FunctionSpec) DeepCopy() *FunctionSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FunctionStatus) DeepCopyInto(out *FunctionStatus) { func (in *FunctionStatus) DeepCopyInto(out *FunctionStatus) {
*out = *in *out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.LastBuiltAt != nil {
in, out := &in.LastBuiltAt, &out.LastBuiltAt
*out = (*in).DeepCopy()
}
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FunctionStatus. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FunctionStatus.
@@ -119,7 +138,7 @@ func (in *Trigger) DeepCopyInto(out *Trigger) {
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec out.Spec = in.Spec
out.Status = in.Status in.Status.DeepCopyInto(&out.Status)
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Trigger. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Trigger.
@@ -190,6 +209,10 @@ func (in *TriggerSpec) DeepCopy() *TriggerSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TriggerStatus) DeepCopyInto(out *TriggerStatus) { func (in *TriggerStatus) DeepCopyInto(out *TriggerStatus) {
*out = *in *out = *in
if in.LastScheduleTime != nil {
in, out := &in.LastScheduleTime, &out.LastScheduleTime
*out = (*in).DeepCopy()
}
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerStatus. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerStatus.
+139 -7
View File
@@ -14,10 +14,20 @@ spec:
singular: function singular: function
scope: Namespaced scope: Namespaced
versions: versions:
- name: v1alpha1 - additionalPrinterColumns:
- jsonPath: .spec.runtime
name: Runtime
type: string
- jsonPath: .status.phase
name: Phase
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema: schema:
openAPIV3Schema: openAPIV3Schema:
description: Function is the Schema for the functions API description: Function — основной ресурс для управления serverless функцией.
properties: properties:
apiVersion: apiVersion:
description: |- description: |-
@@ -37,15 +47,137 @@ spec:
metadata: metadata:
type: object type: object
spec: spec:
description: FunctionSpec defines the desired state of Function description: |-
FunctionSpec — желаемое состояние функции.
Описывает всё необходимое для сборки и запуска пользовательского кода.
properties: properties:
foo: entrypoint:
description: Foo is an example field of Function. Edit function_types.go description: 'Entrypoint — точка входа в код функции (например: handler.handle)'
to remove/update
type: string type: string
env:
additionalProperties:
type: string
description: Env — переменные окружения, передаются в контейнер функции
type: object
memoryMB:
default: 128
description: 'MemoryMB — лимит памяти в мегабайтах (default: 128)'
format: int32
type: integer
runtime:
description: Runtime — язык и версия выполнения (go1.21, python3.11,
nodejs20)
enum:
- go1.21
- python3.11
- nodejs20
type: string
s3Bucket:
description: S3Bucket — бакет S3 где хранится zip архив с кодом
type: string
s3Key:
description: S3Key — ключ объекта в S3 (путь до zip архива)
type: string
timeoutSec:
default: 30
description: 'TimeoutSec — максимальное время выполнения в секундах
(default: 30)'
format: int32
type: integer
required:
- entrypoint
- runtime
- s3Bucket
- s3Key
type: object type: object
status: status:
description: FunctionStatus defines the observed state of Function description: FunctionStatus — наблюдаемое состояние функции (заполняет
контроллер).
properties:
conditions:
description: Conditions — стандартные k8s conditions для интеграции
с инструментами
items:
description: "Condition contains details for one aspect of the current
state of this API Resource.\n---\nThis struct is intended for
direct use as an array at the field path .status.conditions. For
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
observations of a foo's current state.\n\t // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
\ // other fields\n\t}"
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.
---
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
useful (see .node.status.conditions), the ability to deconflict is important.
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
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
imageRef:
description: ImageRef — полный путь к собранному Docker образу в registry
type: string
lastBuiltAt:
description: LastBuiltAt — время последней успешной сборки образа
format: date-time
type: string
message:
description: Message — человекочитаемое сообщение об ошибке или статусе
type: string
phase:
description: 'Phase — текущая фаза: Pending, Building, Ready, Failed'
type: string
type: object type: object
type: object type: object
served: true served: true
+58 -7
View File
@@ -14,10 +14,26 @@ spec:
singular: trigger singular: trigger
scope: Namespaced scope: Namespaced
versions: versions:
- name: v1alpha1 - additionalPrinterColumns:
- jsonPath: .spec.type
name: Type
type: string
- jsonPath: .spec.functionRef
name: Function
type: string
- jsonPath: .status.active
name: Active
type: boolean
- jsonPath: .status.url
name: URL
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema: schema:
openAPIV3Schema: openAPIV3Schema:
description: Trigger is the Schema for the triggers API description: Trigger — ресурс для управления способом вызова Function.
properties: properties:
apiVersion: apiVersion:
description: |- description: |-
@@ -37,15 +53,50 @@ spec:
metadata: metadata:
type: object type: object
spec: spec:
description: TriggerSpec defines the desired state of Trigger description: TriggerSpec — желаемое состояние триггера.
properties: properties:
foo: functionRef:
description: Foo is an example field of Trigger. Edit trigger_types.go description: FunctionRef — имя Function ресурса в том же namespace
to remove/update
type: string type: string
preWarmSeconds:
default: 300
description: |-
PreWarmSeconds — за сколько секунд до запуска прогреть контейнер.
Актуально для cron: запускаем pod заранее чтобы избежать cold start.
format: int32
type: integer
schedule:
description: 'Schedule — расписание в формате cron (только для type=cron,
например: "0 2 * * *")'
type: string
type:
description: 'Type — тип триггера: http или cron'
enum:
- http
- cron
type: string
required:
- functionRef
- type
type: object type: object
status: status:
description: TriggerStatus defines the observed state of Trigger description: TriggerStatus — наблюдаемое состояние триггера (заполняет
контроллер).
properties:
active:
description: Active — активен ли триггер в данный момент
type: boolean
lastScheduleTime:
description: LastScheduleTime — время последнего запуска по расписанию
(только для type=cron)
format: date-time
type: string
message:
description: Message — человекочитаемое сообщение об ошибке или статусе
type: string
url:
description: URL — публичный URL для вызова функции (только для type=http)
type: string
type: object type: object
type: object type: object
served: true served: true