From d9f5ab56bbf2abb488a3ede9d40b7ab2ae026fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sat, 7 Mar 2026 08:24:33 +0400 Subject: [PATCH] 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 --- api/v1alpha1/function_types.go | 90 +++++++---- api/v1alpha1/trigger_types.go | 72 +++++---- api/v1alpha1/zz_generated.deepcopy.go | 29 +++- .../crd/bases/sless.kube5s.ru_functions.yaml | 146 +++++++++++++++++- .../crd/bases/sless.kube5s.ru_triggers.yaml | 65 +++++++- 5 files changed, 332 insertions(+), 70 deletions(-) diff --git a/api/v1alpha1/function_types.go b/api/v1alpha1/function_types.go index fe86966..4daf5d2 100644 --- a/api/v1alpha1/function_types.go +++ b/api/v1alpha1/function_types.go @@ -1,18 +1,6 @@ -/* -Copyright 2026. - -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. -*/ +// Изменено: 2026-03-07 +// Описание CRD Function — основной ресурс serverless функции. +// Пользователь создаёт Function, контроллер строит образ и деплоит в k8s. package v1alpha1 @@ -20,28 +8,76 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// 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 +// FunctionSpec — желаемое состояние функции. +// Описывает всё необходимое для сборки и запуска пользовательского кода. type FunctionSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file + // Runtime — язык и версия выполнения (go1.21, python3.11, nodejs20) + // +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 - Foo string `json:"foo,omitempty"` + // Entrypoint — точка входа в код функции (например: handler.handle) + // +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 { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + // Phase — текущая фаза: Pending, Building, Ready, Failed + 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: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 { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/api/v1alpha1/trigger_types.go b/api/v1alpha1/trigger_types.go index edfbaf1..17f8a1f 100644 --- a/api/v1alpha1/trigger_types.go +++ b/api/v1alpha1/trigger_types.go @@ -1,18 +1,6 @@ -/* -Copyright 2026. - -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. -*/ +// Изменено: 2026-03-07 +// Описание CRD Trigger — триггер для функции (HTTP или Cron). +// Один Trigger ссылается на одну Function и определяет способ вызова. package v1alpha1 @@ -20,28 +8,60 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. +// TriggerType — тип триггера. +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 { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file + // FunctionRef — имя Function ресурса в том же namespace + // +kubebuilder:validation:Required + FunctionRef string `json:"functionRef"` - // Foo is an example field of Trigger. Edit trigger_types.go to remove/update - Foo string `json:"foo,omitempty"` + // Type — тип триггера: http или cron + // +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 { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + // Active — активен ли триггер в данный момент + 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: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 { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index df6355f..1623f3c 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -21,6 +21,7 @@ limitations under the License. package v1alpha1 import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -29,8 +30,8 @@ func (in *Function) DeepCopyInto(out *Function) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } // 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. func (in *FunctionSpec) DeepCopyInto(out *FunctionSpec) { *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. @@ -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. func (in *FunctionStatus) DeepCopyInto(out *FunctionStatus) { *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. @@ -119,7 +138,7 @@ func (in *Trigger) DeepCopyInto(out *Trigger) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 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. @@ -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. func (in *TriggerStatus) DeepCopyInto(out *TriggerStatus) { *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. diff --git a/config/crd/bases/sless.kube5s.ru_functions.yaml b/config/crd/bases/sless.kube5s.ru_functions.yaml index ea8d8c3..ddd89ea 100644 --- a/config/crd/bases/sless.kube5s.ru_functions.yaml +++ b/config/crd/bases/sless.kube5s.ru_functions.yaml @@ -14,10 +14,20 @@ spec: singular: function scope: Namespaced 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: openAPIV3Schema: - description: Function is the Schema for the functions API + description: Function — основной ресурс для управления serverless функцией. properties: apiVersion: description: |- @@ -37,15 +47,137 @@ spec: metadata: type: object spec: - description: FunctionSpec defines the desired state of Function + description: |- + FunctionSpec — желаемое состояние функции. + Описывает всё необходимое для сборки и запуска пользовательского кода. properties: - foo: - description: Foo is an example field of Function. Edit function_types.go - to remove/update + entrypoint: + description: 'Entrypoint — точка входа в код функции (например: handler.handle)' 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 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 served: true diff --git a/config/crd/bases/sless.kube5s.ru_triggers.yaml b/config/crd/bases/sless.kube5s.ru_triggers.yaml index 465fdbd..dafe638 100644 --- a/config/crd/bases/sless.kube5s.ru_triggers.yaml +++ b/config/crd/bases/sless.kube5s.ru_triggers.yaml @@ -14,10 +14,26 @@ spec: singular: trigger scope: Namespaced 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: openAPIV3Schema: - description: Trigger is the Schema for the triggers API + description: Trigger — ресурс для управления способом вызова Function. properties: apiVersion: description: |- @@ -37,15 +53,50 @@ spec: metadata: type: object spec: - description: TriggerSpec defines the desired state of Trigger + description: TriggerSpec — желаемое состояние триггера. properties: - foo: - description: Foo is an example field of Trigger. Edit trigger_types.go - to remove/update + functionRef: + description: FunctionRef — имя Function ресурса в том же namespace 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 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 served: true