refactor(sqs-operator): переделка через Operator SDK v1.37.0
- operator-sdk init + create api (QueueService kind, group sqs, v1alpha1) - Стандартная kubebuilder структура: cmd/, internal/controller/, config/ - CRD types с kubebuilder маркерами (validation, defaults, printcolumns) - Reconciler перенесён из ручного кода в internal/controller/ - controller-gen v0.17.0 (совместимость с Go 1.26.1) - Автогенерация: deepcopy, CRD YAML, RBAC ClusterRole - Удалены все ручные файлы (controllers/, main.go, deployments/)
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Package v1alpha1 contains API Schema definitions for the sqs v1alpha1 API group
|
||||
// +kubebuilder:object:generate=true
|
||||
// +groupName=sqs.kube5s.ru
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
)
|
||||
|
||||
var (
|
||||
// GroupVersion is group version used to register these objects
|
||||
GroupVersion = schema.GroupVersion{Group: "sqs.kube5s.ru", Version: "v1alpha1"}
|
||||
|
||||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
|
||||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
|
||||
|
||||
// AddToScheme adds the types in this group-version to the given scheme.
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
@@ -1,107 +1,95 @@
|
||||
// Copyright 2026 SoftwareLess
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// Создан: 2026-04-07
|
||||
// queueservice_types.go — CRD QueueService: managed SQS-совместимый инстанс очередей.
|
||||
// Каждый тенант облачного провайдера создаёт один QueueService и получает изолированный
|
||||
// ElasticMQ pod с SQS-совместимым API. Клиент работает через стандартный AWS SDK,
|
||||
// меняя только endpoint на https://sqs.kube5s.ru/sqs/{tenantId}.
|
||||
// Изменён: 2026-04-07
|
||||
// queueservice_types.go — CRD типы для QueueService.
|
||||
// Описывает Spec (желаемое состояние) и Status (наблюдаемое состояние) ElasticMQ инстанса тенанта.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
)
|
||||
|
||||
// GroupVersion — API group и версия для QueueService CRD.
|
||||
var (
|
||||
GroupVersion = schema.GroupVersion{Group: "sqs.kube5s.ru", Version: "v1alpha1"}
|
||||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
// QueueServiceSpec — желаемое состояние инстанса очередей тенанта.
|
||||
type QueueServiceSpec struct {
|
||||
// TenantID — уникальный ID тенанта облачного провайдера.
|
||||
// Используется как имя namespace (sless-fn-{tenantId}), имена ресурсов и в endpoint URL.
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:MinLength=2
|
||||
// +kubebuilder:validation:MaxLength=53
|
||||
// +kubebuilder:validation:Pattern=`^[a-z0-9][a-z0-9-]*[a-z0-9]$`
|
||||
TenantID string `json:"tenantId"`
|
||||
|
||||
// MemoryMB — лимит RAM для ElasticMQ pod в мегабайтах (default: 64).
|
||||
// ElasticMQ Native (GraalVM) idle потребляет ~30MB.
|
||||
// +kubebuilder:default=64
|
||||
// +kubebuilder:validation:Minimum=32
|
||||
// +kubebuilder:validation:Maximum=1024
|
||||
MemoryMB int32 `json:"memoryMB,omitempty"`
|
||||
|
||||
// StorageMB — размер PVC для H2 persistence в мегабайтах (default: 512).
|
||||
// H2 хранит очереди и сообщения, переживает рестарт pod.
|
||||
// +kubebuilder:default=512
|
||||
// +kubebuilder:validation:Minimum=128
|
||||
// +kubebuilder:validation:Maximum=10240
|
||||
StorageMB int32 `json:"storageMB,omitempty"`
|
||||
|
||||
// Persistence — сохранять сообщения на диск (H2 через PVC).
|
||||
// true (default) — сообщения переживают рестарт pod.
|
||||
// false — только in-memory, при рестарте сообщения теряются.
|
||||
// +kubebuilder:default=true
|
||||
Persistence bool `json:"persistence"`
|
||||
}
|
||||
|
||||
// QueueServicePhase — текущая фаза жизненного цикла инстанса.
|
||||
// QueueServicePhase — фаза жизненного цикла QueueService инстанса.
|
||||
type QueueServicePhase string
|
||||
|
||||
const (
|
||||
// QueueServicePhasePending — CR создан, начинается подготовка ресурсов
|
||||
// QueueServicePhasePending — ресурс создан, ожидает провизионирования.
|
||||
QueueServicePhasePending QueueServicePhase = "Pending"
|
||||
// QueueServicePhaseProvisioning — ресурсы создаются (PVC, Secret, Deployment, Ingress)
|
||||
// QueueServicePhaseProvisioning — k8s ресурсы созданы, ожидаем готовности pod.
|
||||
QueueServicePhaseProvisioning QueueServicePhase = "Provisioning"
|
||||
// QueueServicePhaseReady — ElasticMQ pod запущен, endpoint доступен
|
||||
// QueueServicePhaseReady — ElasticMQ pod запущен и отвечает, endpoint доступен.
|
||||
QueueServicePhaseReady QueueServicePhase = "Ready"
|
||||
// QueueServicePhaseFailed — ошибка при создании или падение pod
|
||||
// QueueServicePhaseFailed — ошибка провизионирования или pod упал.
|
||||
QueueServicePhaseFailed QueueServicePhase = "Failed"
|
||||
)
|
||||
|
||||
// QueueServiceStatus — наблюдаемое состояние инстанса (заполняет контроллер).
|
||||
// QueueServiceSpec — желаемое состояние QueueService (параметры тенанта).
|
||||
type QueueServiceSpec struct {
|
||||
// TenantID — уникальный идентификатор тенанта.
|
||||
// Используется в именах ресурсов (sqs-{tenantId}), namespace (sless-fn-{tenantId}),
|
||||
// и в Ingress path (/sqs/{tenantId}).
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=63
|
||||
// +kubebuilder:validation:Pattern=`^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`
|
||||
TenantID string `json:"tenantId"`
|
||||
|
||||
// MemoryMB — лимит памяти для ElasticMQ pod в мегабайтах.
|
||||
// ElasticMQ Native (GraalVM) в idle потребляет ~30MB.
|
||||
// +kubebuilder:default=64
|
||||
// +kubebuilder:validation:Minimum=32
|
||||
// +kubebuilder:validation:Maximum=1024
|
||||
// +optional
|
||||
MemoryMB int32 `json:"memoryMB,omitempty"`
|
||||
|
||||
// StorageMB — размер PVC для H2 persistence в мегабайтах.
|
||||
// H2 БД хранит очереди и сообщения между рестартами.
|
||||
// +kubebuilder:default=512
|
||||
// +kubebuilder:validation:Minimum=128
|
||||
// +kubebuilder:validation:Maximum=10240
|
||||
// +optional
|
||||
StorageMB int32 `json:"storageMB,omitempty"`
|
||||
|
||||
// Persistence — включить H2 persistence для сохранения сообщений при рестарте pod.
|
||||
// Если false — сообщения in-memory only, теряются при перезапуске.
|
||||
// +kubebuilder:default=true
|
||||
// +optional
|
||||
Persistence bool `json:"persistence,omitempty"`
|
||||
}
|
||||
|
||||
// QueueServiceStatus — наблюдаемое состояние QueueService.
|
||||
type QueueServiceStatus struct {
|
||||
// Phase — текущая фаза: Pending, Provisioning, Ready, Failed
|
||||
// Phase — текущая фаза жизненного цикла.
|
||||
// +kubebuilder:validation:Enum=Pending;Provisioning;Ready;Failed
|
||||
// +optional
|
||||
Phase QueueServicePhase `json:"phase,omitempty"`
|
||||
|
||||
// Endpoint — публичный SQS endpoint тенанта.
|
||||
// Endpoint — публичный URL для SQS API тенанта (заполняется при Ready).
|
||||
// Формат: https://{SQS_EXTERNAL_HOST}/sqs/{tenantId}
|
||||
// Используется AWS SDK как endpoint_url.
|
||||
// +optional
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
|
||||
// SecretName — имя k8s Secret с credentials (accessKey, secretKey).
|
||||
// Secret находится в namespace sless-fn-{tenantId}.
|
||||
// SecretName — имя k8s Secret с credentials (accessKey/secretKey).
|
||||
// +optional
|
||||
SecretName string `json:"secretName,omitempty"`
|
||||
|
||||
// Message — человекочитаемое сообщение об ошибке или статусе.
|
||||
// Message — описание ошибки или промежуточного статуса.
|
||||
// +optional
|
||||
Message string `json:"message,omitempty"`
|
||||
|
||||
// Conditions — стандартные k8s conditions для интеграции с инструментами.
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
|
||||
// ReadyAt — время когда инстанс перешёл в фазу Ready.
|
||||
// +optional
|
||||
ReadyAt *metav1.Time `json:"readyAt,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="TenantID",type=string,JSONPath=`.spec.tenantId`
|
||||
// +kubebuilder:printcolumn:name="Tenant",type=string,JSONPath=`.spec.tenantId`
|
||||
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
|
||||
// +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.status.endpoint`
|
||||
// +kubebuilder:printcolumn:name="Memory",type=integer,JSONPath=`.spec.memoryMB`
|
||||
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
|
||||
// +kubebuilder:resource:shortName=qs
|
||||
|
||||
// QueueService — CRD для managed SQS-совместимого инстанса очередей.
|
||||
// Оператор создаёт ElasticMQ pod + PVC + Service + Ingress + credentials Secret.
|
||||
// QueueService — управляет жизненным циклом ElasticMQ инстанса для тенанта.
|
||||
// Каждый QueueService CR = один ElasticMQ pod + Service + Ingress + PVC + Secret.
|
||||
type QueueService struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
@@ -112,70 +100,13 @@ type QueueService struct {
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// QueueServiceList — список QueueService объектов.
|
||||
// QueueServiceList — список QueueService ресурсов.
|
||||
type QueueServiceList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []QueueService `json:"items"`
|
||||
}
|
||||
|
||||
// DeepCopyObject реализует runtime.Object для QueueService.
|
||||
func (q *QueueService) DeepCopyObject() runtime.Object {
|
||||
if q == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(QueueService)
|
||||
q.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto копирует QueueService в out.
|
||||
func (q *QueueService) DeepCopyInto(out *QueueService) {
|
||||
*out = *q
|
||||
out.TypeMeta = q.TypeMeta
|
||||
q.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = q.Spec
|
||||
q.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopyObject реализует runtime.Object для QueueServiceList.
|
||||
func (q *QueueServiceList) DeepCopyObject() runtime.Object {
|
||||
if q == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(QueueServiceList)
|
||||
q.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto копирует QueueServiceList в out.
|
||||
func (q *QueueServiceList) DeepCopyInto(out *QueueServiceList) {
|
||||
*out = *q
|
||||
out.TypeMeta = q.TypeMeta
|
||||
q.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if q.Items != nil {
|
||||
in, out := &q.Items, &out.Items
|
||||
*out = make([]QueueService, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopyInto копирует QueueServiceStatus в out.
|
||||
func (s *QueueServiceStatus) DeepCopyInto(out *QueueServiceStatus) {
|
||||
*out = *s
|
||||
if s.Conditions != nil {
|
||||
in, out := &s.Conditions, &out.Conditions
|
||||
*out = make([]metav1.Condition, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if s.ReadyAt != nil {
|
||||
in, out := &s.ReadyAt, &out.ReadyAt
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&QueueService{}, &QueueServiceList{})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
//go:build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *QueueService) DeepCopyInto(out *QueueService) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QueueService.
|
||||
func (in *QueueService) DeepCopy() *QueueService {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(QueueService)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *QueueService) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *QueueServiceList) DeepCopyInto(out *QueueServiceList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]QueueService, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QueueServiceList.
|
||||
func (in *QueueServiceList) DeepCopy() *QueueServiceList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(QueueServiceList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *QueueServiceList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *QueueServiceSpec) DeepCopyInto(out *QueueServiceSpec) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QueueServiceSpec.
|
||||
func (in *QueueServiceSpec) DeepCopy() *QueueServiceSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(QueueServiceSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *QueueServiceStatus) DeepCopyInto(out *QueueServiceStatus) {
|
||||
*out = *in
|
||||
if in.ReadyAt != nil {
|
||||
in, out := &in.ReadyAt, &out.ReadyAt
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QueueServiceStatus.
|
||||
func (in *QueueServiceStatus) DeepCopy() *QueueServiceStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(QueueServiceStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user