github.com/oam-dev/kubevela@v1.9.11/pkg/utils/env/env.go (about)

     1  /*
     2  Copyright 2021 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package env
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"os"
    24  	"path/filepath"
    25  
    26  	"github.com/AlecAivazis/survey/v2"
    27  	"github.com/kubevela/pkg/util/singleton"
    28  	"github.com/pkg/errors"
    29  	v1 "k8s.io/api/core/v1"
    30  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    31  	"k8s.io/apimachinery/pkg/labels"
    32  	"sigs.k8s.io/controller-runtime/pkg/client"
    33  
    34  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    35  	"github.com/oam-dev/kubevela/apis/types"
    36  	"github.com/oam-dev/kubevela/pkg/oam"
    37  	"github.com/oam-dev/kubevela/pkg/oam/util"
    38  	"github.com/oam-dev/kubevela/pkg/utils"
    39  	"github.com/oam-dev/kubevela/pkg/utils/common"
    40  	"github.com/oam-dev/kubevela/pkg/utils/system"
    41  )
    42  
    43  const (
    44  	// DefaultEnvNamespace is namespace of default env
    45  	DefaultEnvNamespace = "default"
    46  )
    47  
    48  // following functions are CRUD of env
    49  
    50  // CreateEnv will create e env.
    51  // Because Env equals to namespace, one env should not be updated
    52  func CreateEnv(envArgs *types.EnvMeta) (err error) {
    53  	c := singleton.KubeClient.Get()
    54  	if envArgs.Namespace == "" {
    55  		err = common.AskToChooseOneNamespace(c, envArgs)
    56  		if err != nil {
    57  			return err
    58  		}
    59  	}
    60  	if envArgs.Name == "" {
    61  		prompt := &survey.Input{
    62  			Message: "Please name your new env:",
    63  		}
    64  		err = survey.AskOne(prompt, &envArgs.Name)
    65  		if err != nil {
    66  			return err
    67  		}
    68  	}
    69  	ctx := context.TODO()
    70  
    71  	var nsList v1.NamespaceList
    72  	err = c.List(ctx, &nsList, client.MatchingLabels{oam.LabelControlPlaneNamespaceUsage: oam.VelaNamespaceUsageEnv,
    73  		oam.LabelNamespaceOfEnvName: envArgs.Name})
    74  	if err != nil {
    75  		return err
    76  	}
    77  	if len(nsList.Items) > 0 {
    78  		return fmt.Errorf("env name %s already exists", envArgs.Name)
    79  	}
    80  
    81  	namespace, err := utils.GetNamespace(ctx, c, envArgs.Namespace)
    82  	if err != nil && !apierrors.IsNotFound(err) {
    83  		return err
    84  	}
    85  	if namespace != nil {
    86  		existedEnv := namespace.GetLabels()[oam.LabelNamespaceOfEnvName]
    87  		if existedEnv != "" && existedEnv != envArgs.Name {
    88  			return fmt.Errorf("the namespace %s was already assigned to env %s", envArgs.Namespace, existedEnv)
    89  		}
    90  	}
    91  	err = utils.CreateOrUpdateNamespace(ctx, c, envArgs.Namespace, utils.MergeOverrideLabels(map[string]string{
    92  		oam.LabelControlPlaneNamespaceUsage: oam.VelaNamespaceUsageEnv,
    93  	}), utils.MergeNoConflictLabels(map[string]string{
    94  		oam.LabelNamespaceOfEnvName: envArgs.Name,
    95  	}))
    96  	if err != nil {
    97  		return err
    98  	}
    99  	return nil
   100  
   101  }
   102  
   103  // GetEnvByName will get env info by name
   104  func GetEnvByName(name string) (*types.EnvMeta, error) {
   105  	if name == DefaultEnvNamespace {
   106  		return &types.EnvMeta{Name: DefaultEnvNamespace, Namespace: DefaultEnvNamespace}, nil
   107  	}
   108  	namespace, err := getEnvNamespaceByName(name)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	return &types.EnvMeta{
   113  		Name:      name,
   114  		Namespace: namespace.Name,
   115  	}, nil
   116  }
   117  
   118  // ListEnvs will list all envs
   119  // if envName specified, return list that only contains one env
   120  func ListEnvs(envName string) ([]*types.EnvMeta, error) {
   121  	var envList []*types.EnvMeta
   122  	if envName != "" {
   123  		env, err := GetEnvByName(envName)
   124  		if err != nil {
   125  			if os.IsNotExist(err) {
   126  				err = fmt.Errorf("env %s not exist", envName)
   127  			}
   128  			return envList, err
   129  		}
   130  		envList = append(envList, env)
   131  		return envList, err
   132  	}
   133  	clt := singleton.KubeClient.Get()
   134  
   135  	ctx := context.Background()
   136  	var nsList v1.NamespaceList
   137  	err := clt.List(ctx, &nsList, client.MatchingLabels{oam.LabelControlPlaneNamespaceUsage: oam.VelaNamespaceUsageEnv})
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	for _, it := range nsList.Items {
   142  		envList = append(envList, &types.EnvMeta{
   143  			Name:      it.Labels[oam.LabelNamespaceOfEnvName],
   144  			Namespace: it.Name,
   145  		})
   146  	}
   147  	if len(envList) < 1 {
   148  		return envList, nil
   149  	}
   150  	cur, err := GetCurrentEnv()
   151  	if err != nil {
   152  		_ = SetCurrentEnv(envList[0])
   153  		envList[0].Current = "*"
   154  		// we set a current env if not exist
   155  		// nolint:nilerr
   156  		return envList, nil
   157  	}
   158  	for i := range envList {
   159  		if envList[i].Name == cur.Name {
   160  			envList[i].Current = "*"
   161  		}
   162  	}
   163  	return envList, nil
   164  }
   165  
   166  // DeleteEnv will delete env and its application
   167  func DeleteEnv(envName string) (string, error) {
   168  	var message string
   169  	var err error
   170  	envMeta, err := GetEnvByName(envName)
   171  	if err != nil {
   172  		return "", err
   173  	}
   174  	clt := singleton.KubeClient.Get()
   175  	var appList v1beta1.ApplicationList
   176  	err = clt.List(context.TODO(), &appList, client.InNamespace(envMeta.Namespace))
   177  	if err != nil {
   178  		return "", err
   179  	}
   180  	if len(appList.Items) > 0 {
   181  		err = fmt.Errorf("you can't delete this environment(namespace=%s) that has %d application inside", envMeta.Namespace, len(appList.Items))
   182  		return message, err
   183  	}
   184  	// reset the labels
   185  	err = utils.UpdateNamespace(context.TODO(), clt, envMeta.Namespace, utils.MergeOverrideLabels(map[string]string{
   186  		oam.LabelNamespaceOfEnvName:         "",
   187  		oam.LabelControlPlaneNamespaceUsage: "",
   188  	}))
   189  	if err != nil {
   190  		return "", err
   191  	}
   192  	message = "env " + envName + " deleted"
   193  	return message, err
   194  }
   195  
   196  // GetCurrentEnv will get current env
   197  func GetCurrentEnv() (*types.EnvMeta, error) {
   198  	currentEnvPath, err := system.GetCurrentEnvPath()
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	data, err := os.ReadFile(filepath.Clean(currentEnvPath))
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  	var envMeta types.EnvMeta
   207  	err = json.Unmarshal(data, &envMeta)
   208  	if err != nil {
   209  		em, err := GetEnvByName(string(data))
   210  		if err != nil {
   211  			return nil, err
   212  		}
   213  		_ = SetCurrentEnv(em)
   214  		return em, nil
   215  	}
   216  	return &envMeta, nil
   217  }
   218  
   219  // SetCurrentEnv will set the current env to the specified one
   220  func SetCurrentEnv(meta *types.EnvMeta) error {
   221  	currentEnvPath, err := system.GetCurrentEnvPath()
   222  	if err != nil {
   223  		return err
   224  	}
   225  	data, err := json.Marshal(meta)
   226  	if err != nil {
   227  		return err
   228  	}
   229  	//nolint:gosec
   230  	if err = os.WriteFile(currentEnvPath, data, 0644); err != nil {
   231  		return err
   232  	}
   233  	return nil
   234  }
   235  
   236  // getEnvNamespaceByName get v1.Namespace object by env name
   237  func getEnvNamespaceByName(name string) (*v1.Namespace, error) {
   238  	ctx := context.Background()
   239  	var nsList v1.NamespaceList
   240  	err := singleton.KubeClient.Get().List(ctx, &nsList, client.MatchingLabels{oam.LabelNamespaceOfEnvName: name})
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  	if len(nsList.Items) < 1 {
   245  		return nil, errors.Errorf("Env %s not exist", name)
   246  	}
   247  
   248  	return &nsList.Items[0], nil
   249  }
   250  
   251  // SetEnvLabels set labels for namespace
   252  func SetEnvLabels(envArgs *types.EnvMeta) error {
   253  	namespace, err := getEnvNamespaceByName(envArgs.Name)
   254  	if err != nil {
   255  		return err
   256  	}
   257  	labelsMap, err := labels.ConvertSelectorToLabelsMap(envArgs.Labels)
   258  	if err != nil {
   259  		return err
   260  	}
   261  
   262  	namespace.Labels = util.MergeMapOverrideWithDst(namespace.GetLabels(), labelsMap)
   263  
   264  	err = singleton.KubeClient.Get().Update(context.Background(), namespace)
   265  	if err != nil {
   266  		return errors.Wrapf(err, "fail to set env labelsMap")
   267  	}
   268  	return nil
   269  }