/*
Copyright 2021 The KodeRover Authors.
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 service
import (
"context"
"fmt"
"path/filepath"
"strings"
"sync"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/hashicorp/go-multierror"
helmclient "github.com/mittwald/go-helm-client"
"github.com/pkg/errors"
"go.uber.org/zap"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/releaseutil"
"helm.sh/helm/v3/pkg/strvals"
versionedclient "istio.io/client-go/pkg/clientset/versioned"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/informers"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/koderover/zadig/pkg/microservice/aslan/config"
"github.com/koderover/zadig/pkg/microservice/aslan/core/common/repository/models"
commonmodels "github.com/koderover/zadig/pkg/microservice/aslan/core/common/repository/models"
"github.com/koderover/zadig/pkg/microservice/aslan/core/common/repository/models/template"
templatemodels "github.com/koderover/zadig/pkg/microservice/aslan/core/common/repository/models/template"
"github.com/koderover/zadig/pkg/microservice/aslan/core/common/repository/mongodb"
commonrepo "github.com/koderover/zadig/pkg/microservice/aslan/core/common/repository/mongodb"
mongotemplate "github.com/koderover/zadig/pkg/microservice/aslan/core/common/repository/mongodb/template"
templaterepo "github.com/koderover/zadig/pkg/microservice/aslan/core/common/repository/mongodb/template"
commonservice "github.com/koderover/zadig/pkg/microservice/aslan/core/common/service"
"github.com/koderover/zadig/pkg/microservice/aslan/core/common/service/collaboration"
"github.com/koderover/zadig/pkg/microservice/aslan/core/common/service/kube"
"github.com/koderover/zadig/pkg/microservice/aslan/core/common/service/render"
commonutil "github.com/koderover/zadig/pkg/microservice/aslan/core/common/util"
"github.com/koderover/zadig/pkg/setting"
kubeclient "github.com/koderover/zadig/pkg/shared/kube/client"
"github.com/koderover/zadig/pkg/shared/kube/wrapper"
e "github.com/koderover/zadig/pkg/tool/errors"
helmtool "github.com/koderover/zadig/pkg/tool/helmclient"
"github.com/koderover/zadig/pkg/tool/kube/getter"
"github.com/koderover/zadig/pkg/tool/kube/informer"
"github.com/koderover/zadig/pkg/tool/kube/serializer"
"github.com/koderover/zadig/pkg/tool/log"
"github.com/koderover/zadig/pkg/types"
"github.com/koderover/zadig/pkg/util"
"github.com/koderover/zadig/pkg/util/converter"
"github.com/koderover/zadig/pkg/util/fs"
yamlutil "github.com/koderover/zadig/pkg/util/yaml"
)
func GetProductDeployType(projectName string) (string, error) {
projectInfo, err := templaterepo.NewProductColl().Find(projectName)
if err != nil {
return "", err
}
if projectInfo.IsCVMProduct() {
return setting.PMDeployType, nil
}
if projectInfo.IsHelmProduct() {
return setting.HelmDeployType, nil
}
return setting.K8SDeployType, nil
}
func ListProducts(userID, projectName string, envNames []string, production bool, log *zap.SugaredLogger) ([]*EnvResp, error) {
envs, err := commonrepo.NewProductColl().List(&commonrepo.ProductListOptions{
Name: projectName,
InEnvs: envNames,
IsSortByProductName: true,
Production: util.GetBoolPointer(production),
})
if err != nil {
log.Errorf("Failed to list envs, err: %s", err)
return nil, e.ErrListEnvs.AddDesc(err.Error())
}
var res []*EnvResp
reg, _, err := commonservice.FindDefaultRegistry(false, log)
if err != nil {
log.Errorf("FindDefaultRegistry error: %v", err)
return nil, e.ErrListEnvs.AddErr(err)
}
clusters, err := commonrepo.NewK8SClusterColl().List(&commonrepo.ClusterListOpts{})
if err != nil {
log.Errorf("failed to list clusters, err: %s", err)
return nil, e.ErrListEnvs.AddErr(err)
}
clusterMap := make(map[string]*models.K8SCluster)
for _, cluster := range clusters {
clusterMap[cluster.ID.Hex()] = cluster
}
getClusterName := func(clusterID string) string {
cluster, ok := clusterMap[clusterID]
if ok {
return cluster.Name
}
return ""
}
list, err := commonservice.ListFavorites(&mongodb.FavoriteArgs{
UserID: userID,
ProductName: projectName,
Type: commonservice.FavoriteTypeEnv,
})
if err != nil {
return nil, errors.Wrap(err, "list favorite environments")
}
// add personal favorite data in response
favSet := sets.NewString(func() []string {
var nameList []string
for _, fav := range list {
nameList = append(nameList, fav.Name)
}
return nameList
}()...)
envCMMap, err := collaboration.GetEnvCMMap([]string{projectName}, log)
if err != nil {
return nil, err
}
for _, env := range envs {
if len(env.RegistryID) == 0 {
env.RegistryID = reg.ID.Hex()
}
var baseRefs []string
if cmSet, ok := envCMMap[collaboration.BuildEnvCMMapKey(env.ProductName, env.EnvName)]; ok {
baseRefs = append(baseRefs, cmSet.List()...)
}
res = append(res, &EnvResp{
ProjectName: projectName,
Name: env.EnvName,
IsPublic: env.IsPublic,
IsExisted: env.IsExisted,
ClusterName: getClusterName(env.ClusterID),
Source: env.Source,
Production: env.Production,
Status: env.Status,
Error: env.Error,
UpdateTime: env.UpdateTime,
UpdateBy: env.UpdateBy,
RegistryID: env.RegistryID,
ClusterID: env.ClusterID,
Namespace: env.Namespace,
Alias: env.Alias,
BaseRefs: baseRefs,
BaseName: env.BaseName,
ShareEnvEnable: env.ShareEnv.Enable,
ShareEnvIsBase: env.ShareEnv.IsBase,
ShareEnvBaseEnv: env.ShareEnv.BaseEnv,
IsFavorite: favSet.Has(env.EnvName),
})
}
return res, nil
}
var mutexAutoCreate sync.RWMutex
func AutoCreateProduct(productName, envType, requestID string, log *zap.SugaredLogger) []*EnvStatus {
mutexAutoCreate.Lock()
defer func() {
mutexAutoCreate.Unlock()
}()
envStatus := make([]*EnvStatus, 0)
envNames := []string{"dev", "qa"}
for _, envName := range envNames {
devStatus := &EnvStatus{
EnvName: envName,
}
status, err := autoCreateProduct(envType, envName, productName, requestID, setting.SystemUser, log)
devStatus.Status = status
if err != nil {
devStatus.ErrMessage = err.Error()
}
envStatus = append(envStatus, devStatus)
}
return envStatus
}
var mutexAutoUpdate sync.RWMutex
type UpdateServiceArg struct {
ServiceName string `json:"service_name"`
DeployStrategy string `json:"deploy_strategy"`
VariableYaml string `json:"variable_yaml"`
}
type UpdateEnv struct {
EnvName string `json:"env_name"`
Services []*UpdateServiceArg `json:"services"`
}
func UpdateMultipleK8sEnv(args []*UpdateEnv, envNames []string, productName, requestID string, force bool, log *zap.SugaredLogger) ([]*EnvStatus, error) {
mutexAutoUpdate.Lock()
defer func() {
mutexAutoUpdate.Unlock()
}()
envStatuses := make([]*EnvStatus, 0)
productsRevision, err := ListProductsRevision(productName, "", log)
if err != nil {
log.Errorf("UpdateMultipleK8sEnv ListProductsRevision err:%v", err)
return envStatuses, err
}
productMap := make(map[string]*ProductRevision)
for _, productRevision := range productsRevision {
if productRevision.ProductName == productName && sets.NewString(envNames...).Has(productRevi