434 lines
10 KiB
Go
434 lines
10 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/ocdp/cluster-service/internal/domain/entity"
|
|
"github.com/ocdp/cluster-service/internal/domain/repository"
|
|
)
|
|
|
|
// InstanceRepository PostgreSQL 实例仓储实现
|
|
type InstanceRepository struct {
|
|
db *DB
|
|
}
|
|
|
|
// NewInstanceRepository 创建 PostgreSQL 实例仓储
|
|
func NewInstanceRepository(db *DB) repository.InstanceRepository {
|
|
return &InstanceRepository{db: db}
|
|
}
|
|
|
|
// Create 创建实例
|
|
func (r *InstanceRepository) Create(ctx context.Context, instance *entity.Instance) error {
|
|
if instance.ID == "" {
|
|
instance.ID = uuid.New().String()
|
|
}
|
|
|
|
// 将 Values 转换为 JSON
|
|
valuesJSON, err := json.Marshal(instance.Values)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal values: %w", err)
|
|
}
|
|
|
|
query := `
|
|
INSERT INTO instances (id, cluster_id, name, namespace, registry_id, repository, chart, version,
|
|
description, values, values_yaml, status, status_reason, last_operation, last_error,
|
|
revision, created_at, updated_at)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
|
|
`
|
|
|
|
_, err = r.db.conn.ExecContext(ctx, query,
|
|
instance.ID,
|
|
instance.ClusterID,
|
|
instance.Name,
|
|
instance.Namespace,
|
|
instance.RegistryID,
|
|
instance.Repository,
|
|
instance.Chart,
|
|
instance.Version,
|
|
instance.Description,
|
|
valuesJSON,
|
|
instance.ValuesYAML,
|
|
instance.Status,
|
|
instance.StatusReason,
|
|
instance.LastOperation,
|
|
instance.LastError,
|
|
instance.Revision,
|
|
instance.CreatedAt,
|
|
instance.UpdatedAt,
|
|
)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create instance: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetByID 根据 ID 获取实例
|
|
func (r *InstanceRepository) GetByID(ctx context.Context, id string) (*entity.Instance, error) {
|
|
query := `
|
|
SELECT id, cluster_id, name, namespace, registry_id, repository, chart, version,
|
|
description, values, values_yaml, status, status_reason, last_operation, last_error,
|
|
revision, created_at, updated_at
|
|
FROM instances
|
|
WHERE id = $1
|
|
`
|
|
|
|
instance := &entity.Instance{}
|
|
var (
|
|
valuesJSON []byte
|
|
statusReason sql.NullString
|
|
lastOperation sql.NullString
|
|
lastError sql.NullString
|
|
)
|
|
|
|
err := r.db.conn.QueryRowContext(ctx, query, id).Scan(
|
|
&instance.ID,
|
|
&instance.ClusterID,
|
|
&instance.Name,
|
|
&instance.Namespace,
|
|
&instance.RegistryID,
|
|
&instance.Repository,
|
|
&instance.Chart,
|
|
&instance.Version,
|
|
&instance.Description,
|
|
&valuesJSON,
|
|
&instance.ValuesYAML,
|
|
&instance.Status,
|
|
&statusReason,
|
|
&lastOperation,
|
|
&lastError,
|
|
&instance.Revision,
|
|
&instance.CreatedAt,
|
|
&instance.UpdatedAt,
|
|
)
|
|
|
|
if err == sql.ErrNoRows {
|
|
return nil, entity.ErrInstanceNotFound
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get instance: %w", err)
|
|
}
|
|
|
|
// 解析 JSON Values
|
|
if len(valuesJSON) > 0 {
|
|
if err := json.Unmarshal(valuesJSON, &instance.Values); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal values: %w", err)
|
|
}
|
|
}
|
|
|
|
if statusReason.Valid {
|
|
instance.StatusReason = statusReason.String
|
|
}
|
|
if lastOperation.Valid {
|
|
instance.LastOperation = entity.InstanceOperation(lastOperation.String)
|
|
}
|
|
if lastError.Valid {
|
|
instance.LastError = lastError.String
|
|
}
|
|
|
|
return instance, nil
|
|
}
|
|
|
|
// GetByClusterAndName 根据集群 ID 和名称获取实例
|
|
func (r *InstanceRepository) GetByClusterAndName(ctx context.Context, clusterID, name string) (*entity.Instance, error) {
|
|
query := `
|
|
SELECT id, cluster_id, name, namespace, registry_id, repository, chart, version,
|
|
description, values, values_yaml, status, status_reason, last_operation, last_error,
|
|
revision, created_at, updated_at
|
|
FROM instances
|
|
WHERE cluster_id = $1 AND name = $2
|
|
`
|
|
|
|
instance := &entity.Instance{}
|
|
var (
|
|
valuesJSON []byte
|
|
statusReason sql.NullString
|
|
lastOperation sql.NullString
|
|
lastError sql.NullString
|
|
)
|
|
|
|
err := r.db.conn.QueryRowContext(ctx, query, clusterID, name).Scan(
|
|
&instance.ID,
|
|
&instance.ClusterID,
|
|
&instance.Name,
|
|
&instance.Namespace,
|
|
&instance.RegistryID,
|
|
&instance.Repository,
|
|
&instance.Chart,
|
|
&instance.Version,
|
|
&instance.Description,
|
|
&valuesJSON,
|
|
&instance.ValuesYAML,
|
|
&instance.Status,
|
|
&statusReason,
|
|
&lastOperation,
|
|
&lastError,
|
|
&instance.Revision,
|
|
&instance.CreatedAt,
|
|
&instance.UpdatedAt,
|
|
)
|
|
|
|
if err == sql.ErrNoRows {
|
|
return nil, entity.ErrInstanceNotFound
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get instance: %w", err)
|
|
}
|
|
|
|
// 解析 JSON Values
|
|
if len(valuesJSON) > 0 {
|
|
if err := json.Unmarshal(valuesJSON, &instance.Values); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal values: %w", err)
|
|
}
|
|
}
|
|
|
|
if statusReason.Valid {
|
|
instance.StatusReason = statusReason.String
|
|
}
|
|
if lastOperation.Valid {
|
|
instance.LastOperation = entity.InstanceOperation(lastOperation.String)
|
|
}
|
|
if lastError.Valid {
|
|
instance.LastError = lastError.String
|
|
}
|
|
|
|
return instance, nil
|
|
}
|
|
|
|
// Update 更新实例
|
|
func (r *InstanceRepository) Update(ctx context.Context, instance *entity.Instance) error {
|
|
instance.UpdatedAt = time.Now()
|
|
|
|
// 将 Values 转换为 JSON
|
|
valuesJSON, err := json.Marshal(instance.Values)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal values: %w", err)
|
|
}
|
|
|
|
query := `
|
|
UPDATE instances
|
|
SET cluster_id = $1, name = $2, namespace = $3, registry_id = $4, repository = $5,
|
|
chart = $6, version = $7, description = $8, values = $9, values_yaml = $10,
|
|
status = $11, status_reason = $12, last_operation = $13, last_error = $14,
|
|
revision = $15, updated_at = $16
|
|
WHERE id = $17
|
|
`
|
|
|
|
result, err := r.db.conn.ExecContext(ctx, query,
|
|
instance.ClusterID,
|
|
instance.Name,
|
|
instance.Namespace,
|
|
instance.RegistryID,
|
|
instance.Repository,
|
|
instance.Chart,
|
|
instance.Version,
|
|
instance.Description,
|
|
valuesJSON,
|
|
instance.ValuesYAML,
|
|
instance.Status,
|
|
instance.StatusReason,
|
|
instance.LastOperation,
|
|
instance.LastError,
|
|
instance.Revision,
|
|
instance.UpdatedAt,
|
|
instance.ID,
|
|
)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update instance: %w", err)
|
|
}
|
|
|
|
rows, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get affected rows: %w", err)
|
|
}
|
|
|
|
if rows == 0 {
|
|
return entity.ErrInstanceNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Delete 删除实例
|
|
func (r *InstanceRepository) Delete(ctx context.Context, id string) error {
|
|
query := `DELETE FROM instances WHERE id = $1`
|
|
|
|
result, err := r.db.conn.ExecContext(ctx, query, id)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete instance: %w", err)
|
|
}
|
|
|
|
rows, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get affected rows: %w", err)
|
|
}
|
|
|
|
if rows == 0 {
|
|
return entity.ErrInstanceNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ListByCluster 列出指定集群的所有实例
|
|
func (r *InstanceRepository) ListByCluster(ctx context.Context, clusterID string) ([]*entity.Instance, error) {
|
|
query := `
|
|
SELECT id, cluster_id, name, namespace, registry_id, repository, chart, version,
|
|
description, values, values_yaml, status, status_reason, last_operation, last_error,
|
|
revision, created_at, updated_at
|
|
FROM instances
|
|
WHERE cluster_id = $1
|
|
ORDER BY created_at DESC
|
|
`
|
|
|
|
rows, err := r.db.conn.QueryContext(ctx, query, clusterID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list instances: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
instances := make([]*entity.Instance, 0)
|
|
for rows.Next() {
|
|
instance := &entity.Instance{}
|
|
var (
|
|
valuesJSON []byte
|
|
statusReason sql.NullString
|
|
lastOperation sql.NullString
|
|
lastError sql.NullString
|
|
)
|
|
|
|
err := rows.Scan(
|
|
&instance.ID,
|
|
&instance.ClusterID,
|
|
&instance.Name,
|
|
&instance.Namespace,
|
|
&instance.RegistryID,
|
|
&instance.Repository,
|
|
&instance.Chart,
|
|
&instance.Version,
|
|
&instance.Description,
|
|
&valuesJSON,
|
|
&instance.ValuesYAML,
|
|
&instance.Status,
|
|
&statusReason,
|
|
&lastOperation,
|
|
&lastError,
|
|
&instance.Revision,
|
|
&instance.CreatedAt,
|
|
&instance.UpdatedAt,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to scan instance: %w", err)
|
|
}
|
|
|
|
// 解析 JSON Values
|
|
if len(valuesJSON) > 0 {
|
|
if err := json.Unmarshal(valuesJSON, &instance.Values); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal values: %w", err)
|
|
}
|
|
}
|
|
|
|
if statusReason.Valid {
|
|
instance.StatusReason = statusReason.String
|
|
}
|
|
if lastOperation.Valid {
|
|
instance.LastOperation = entity.InstanceOperation(lastOperation.String)
|
|
}
|
|
if lastError.Valid {
|
|
instance.LastError = lastError.String
|
|
}
|
|
|
|
instances = append(instances, instance)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("rows iteration error: %w", err)
|
|
}
|
|
|
|
return instances, nil
|
|
}
|
|
|
|
// List 列出所有实例
|
|
func (r *InstanceRepository) List(ctx context.Context) ([]*entity.Instance, error) {
|
|
query := `
|
|
SELECT id, cluster_id, name, namespace, registry_id, repository, chart, version,
|
|
description, values, values_yaml, status, status_reason, last_operation, last_error,
|
|
revision, created_at, updated_at
|
|
FROM instances
|
|
ORDER BY created_at DESC
|
|
`
|
|
|
|
rows, err := r.db.conn.QueryContext(ctx, query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list instances: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
instances := make([]*entity.Instance, 0)
|
|
for rows.Next() {
|
|
instance := &entity.Instance{}
|
|
var (
|
|
valuesJSON []byte
|
|
statusReason sql.NullString
|
|
lastOperation sql.NullString
|
|
lastError sql.NullString
|
|
)
|
|
|
|
err := rows.Scan(
|
|
&instance.ID,
|
|
&instance.ClusterID,
|
|
&instance.Name,
|
|
&instance.Namespace,
|
|
&instance.RegistryID,
|
|
&instance.Repository,
|
|
&instance.Chart,
|
|
&instance.Version,
|
|
&instance.Description,
|
|
&valuesJSON,
|
|
&instance.ValuesYAML,
|
|
&instance.Status,
|
|
&statusReason,
|
|
&lastOperation,
|
|
&lastError,
|
|
&instance.Revision,
|
|
&instance.CreatedAt,
|
|
&instance.UpdatedAt,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to scan instance: %w", err)
|
|
}
|
|
|
|
// 解析 JSON Values
|
|
if len(valuesJSON) > 0 {
|
|
if err := json.Unmarshal(valuesJSON, &instance.Values); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal values: %w", err)
|
|
}
|
|
}
|
|
|
|
if statusReason.Valid {
|
|
instance.StatusReason = statusReason.String
|
|
}
|
|
if lastOperation.Valid {
|
|
instance.LastOperation = entity.InstanceOperation(lastOperation.String)
|
|
}
|
|
if lastError.Valid {
|
|
instance.LastError = lastError.String
|
|
}
|
|
|
|
instances = append(instances, instance)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("rows iteration error: %w", err)
|
|
}
|
|
|
|
return instances, nil
|
|
}
|