/* * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * * 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 services import ( "context" "errors" "strings" "github.com/dynemo-ai/dynemo/deploy/compoundai/api-server/api/common/consts" "github.com/dynemo-ai/dynemo/deploy/compoundai/api-server/api/database" "github.com/dynemo-ai/dynemo/deploy/compoundai/api-server/api/models" "k8s.io/apimachinery/pkg/util/validation" "github.com/rs/zerolog/log" "gorm.io/gorm" ) type clusterService struct{} var ClusterService = clusterService{} type CreateClusterOption struct { CreatorId string OrganizationId string Name string Description string KubeConfig string } type UpdateClusterOption struct { Description *string KubeConfig *string } type ListClusterOption struct { BaseListOption OrganizationId *string Ids *[]uint Names *[]string CreatorIds *[]uint Order *string } func (s *clusterService) Create(ctx context.Context, opt CreateClusterOption) (*models.Cluster, error) { errs := validation.IsDNS1035Label(opt.Name) if len(errs) > 0 { return nil, errors.New(strings.Join(errs, ";")) } db := s.getDB(ctx) log.Info().Msg("Starting create cluster transaction") cluster := models.Cluster{ Resource: models.Resource{ Name: opt.Name, }, OrganizationAssociate: models.OrganizationAssociate{ OrganizationId: opt.OrganizationId, }, CreatorAssociate: models.CreatorAssociate{ UserId: opt.CreatorId, }, Description: opt.Description, KubeConfig: opt.KubeConfig, } if err := db.Create(&cluster).Error; err != nil { return nil, err } log.Info().Msg("Finished create cluster transaction") return &cluster, nil } func (s *clusterService) Update(ctx context.Context, c *models.Cluster, opt UpdateClusterOption) (*models.Cluster, error) { var err error updaters := make(map[string]interface{}) if opt.Description != nil { updaters["description"] = *opt.Description defer func() { if err == nil { c.Description = *opt.Description } }() } if opt.KubeConfig != nil { updaters["kube_config"] = *opt.KubeConfig defer func() { if err == nil { c.KubeConfig = *opt.KubeConfig } }() } if len(updaters) == 0 { return c, nil } db := s.getDB(ctx) log.Info().Msgf("Updating cluster with updaters: %+v", updaters) err = db.Where("id = ?", c.ID).Updates(updaters).Error if err != nil { log.Error().Msgf("Failed to update cluster: %s", err.Error()) return nil, err } return c, err } func (s *clusterService) Get(ctx context.Context, id uint) (*models.Cluster, error) { var cluster models.Cluster db := s.getDB(ctx) err := db.Where("id = ?", id).First(&cluster).Error if err != nil { log.Error().Msgf("Failed to get cluster by id %d: %s", id, err.Error()) return nil, err } if cluster.ID == 0 { return nil, consts.ErrNotFound } return &cluster, nil } func (s *clusterService) GetByUid(ctx context.Context, uid string) (*models.Cluster, error) { var cluster models.Cluster db := s.getDB(ctx) err := db.Where("uid = ?", uid).First(&cluster).Error if err != nil { log.Error().Msgf("Failed to get cluster by uid %s: %s", uid, err.Error()) return nil, err } if cluster.ID == 0 { return nil, consts.ErrNotFound } return &cluster, nil } func (s *clusterService) GetByName(ctx context.Context, organizationId string, name string) (*models.Cluster, error) { var cluster models.Cluster db := s.getDB(ctx) err := db.Where("organization_id = ?", organizationId).Where("name = ?", name).First(&cluster).Error if err != nil { log.Error().Msgf("Failed to get cluster by name %s: %s", name, err.Error()) return nil, err } if cluster.ID == 0 { return nil, consts.ErrNotFound } return &cluster, nil } func (s *clusterService) GetIdByName(ctx context.Context, organizationId uint, name string) (uint, error) { var cluster models.Cluster db := s.getDB(ctx) err := db.Select("id").Where("organization_id = ?", organizationId).Where("name = ?", name).First(&cluster).Error return cluster.ID, err } func (s *clusterService) List(ctx context.Context, opt ListClusterOption) ([]*models.Cluster, uint, error) { clusters := make([]*models.Cluster, 0) query := s.getDB(ctx) if opt.Ids != nil { if len(*opt.Ids) == 0 { return clusters, 0, nil } query = query.Where("id in (?)", *opt.Ids) } if opt.Names != nil { if len(*opt.Names) == 0 { return clusters, 0, nil } query = query.Where("name in (?)", *opt.Names) } if opt.OrganizationId != nil { query = query.Where("organization_id = ?", *opt.OrganizationId) } var total int64 err := query.Count(&total).Error if err != nil { return nil, 0, err } query = opt.BindQueryWithLimit(query) if opt.Ids == nil { if opt.Order == nil { query = query.Order("id DESC") } else { query = query.Order(*opt.Order) } } err = query.Find(&clusters).Error if err != nil { return nil, 0, err } return clusters, uint(total), err } func (s *clusterService) getDB(ctx context.Context) *gorm.DB { db := database.DatabaseUtil.GetDBSession(ctx).Model(&models.Cluster{}) return db }