github.com/oam-dev/kubevela@v1.9.11/pkg/config/writer/nacos.go (about)

     1  /*
     2  Copyright 2022 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 writer
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"runtime/debug"
    23  	"strings"
    24  
    25  	"github.com/nacos-group/nacos-sdk-go/v2/clients"
    26  	"github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"
    27  	"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
    28  	"github.com/nacos-group/nacos-sdk-go/v2/vo"
    29  	"k8s.io/klog/v2"
    30  
    31  	"github.com/kubevela/workflow/pkg/cue/model/value"
    32  
    33  	"github.com/oam-dev/kubevela/apis/types"
    34  	icontext "github.com/oam-dev/kubevela/pkg/config/context"
    35  	"github.com/oam-dev/kubevela/pkg/cue"
    36  	"github.com/oam-dev/kubevela/pkg/cue/script"
    37  )
    38  
    39  // NacosConfig defines the nacos output
    40  type NacosConfig struct {
    41  	Endpoint ConfigRef `json:"endpoint"`
    42  	// Format defines the format in which Data will be output.
    43  	Format   string              `json:"format"`
    44  	Metadata NacosConfigMetadata `json:"metadata"`
    45  }
    46  
    47  // NacosConfigMetadata the metadata of the nacos config
    48  type NacosConfigMetadata struct {
    49  	DataID      string `json:"dataId"`
    50  	Group       string `json:"group"`
    51  	NamespaceID string `json:"namespaceId"`
    52  	AppName     string `json:"appName,omitempty"`
    53  	Tenant      string `json:"tenant,omitempty"`
    54  	Tag         string `json:"tag,omitempty"`
    55  }
    56  
    57  // NacosData merge the nacos endpoint config and the rendered data
    58  type NacosData struct {
    59  	NacosConfig
    60  	Content []byte                      `json:"-"`
    61  	Client  config_client.IConfigClient `json:"-"`
    62  }
    63  
    64  // parseNacosConfig parse the nacos server config
    65  func parseNacosConfig(templateField *value.Value, wc *ExpandedWriterConfig) {
    66  	nacos, _ := templateField.LookupValue("nacos")
    67  	if nacos != nil {
    68  		format, err := nacos.GetString("format")
    69  		if err != nil && !cue.IsFieldNotExist(err) {
    70  			klog.Warningf("fail to get the format from the nacos config: %s", err.Error())
    71  		}
    72  		endpoint, err := nacos.GetString("endpoint", "name")
    73  		if err != nil && !cue.IsFieldNotExist(err) {
    74  			klog.Warningf("fail to get the endpoint name from the nacos config: %s", err.Error())
    75  		}
    76  		wc.Nacos = &NacosConfig{
    77  			Format: format,
    78  			Endpoint: ConfigRef{
    79  				Name: endpoint,
    80  			},
    81  		}
    82  	}
    83  }
    84  
    85  func renderNacos(config *NacosConfig, template script.CUE, context icontext.ConfigRenderContext, properties map[string]interface{}) (*NacosData, error) {
    86  	nacos, err := template.RunAndOutput(context, properties, "template", "nacos")
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	format, err := nacos.GetString("format")
    91  	if err != nil {
    92  		format = config.Format
    93  	}
    94  	var nacosData NacosData
    95  	if err := nacos.UnmarshalTo(&nacosData); err != nil {
    96  		return nil, err
    97  	}
    98  	content, err := nacos.LookupValue("content")
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	out, err := encodingOutput(content, format)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	nacosData.Content = out
   108  	if nacosData.Endpoint.Namespace == "" {
   109  		nacosData.Endpoint.Namespace = types.DefaultKubeVelaNS
   110  	}
   111  
   112  	return &nacosData, nil
   113  }
   114  
   115  func (n *NacosData) write(ctx context.Context, configReader icontext.ReadConfigProvider) (err error) {
   116  	defer func() {
   117  		if rec := recover(); rec != nil {
   118  			err = fmt.Errorf("panic when writing the data to nacos:%v", rec)
   119  			debug.PrintStack()
   120  		}
   121  	}()
   122  	// the config of the nacos server saving in the default system namespace
   123  	config, err := configReader(ctx, n.Endpoint.Namespace, n.Endpoint.Name)
   124  	if err != nil {
   125  		return fmt.Errorf("fail to read the config of the nacos server:%w", err)
   126  	}
   127  	readString := func(data map[string]interface{}, key string) string {
   128  		if v, ok := data[key]; ok {
   129  			str, _ := v.(string)
   130  			return str
   131  		}
   132  		return ""
   133  	}
   134  	readUint64 := func(data map[string]interface{}, key string) uint64 {
   135  		if v, ok := data[key]; ok {
   136  			vu, _ := v.(float64)
   137  			return uint64(vu)
   138  		}
   139  		return 0
   140  	}
   141  	readBool := func(data map[string]interface{}, key string) bool {
   142  		if v, ok := data[key]; ok {
   143  			vu, _ := v.(bool)
   144  			return vu
   145  		}
   146  		return false
   147  	}
   148  	var nacosParam vo.NacosClientParam
   149  	if serverConfigs, ok := config["servers"]; ok {
   150  		var servers []constant.ServerConfig
   151  		serverEndpoints, _ := serverConfigs.([]interface{})
   152  		for _, s := range serverEndpoints {
   153  			sm, ok := s.(map[string]interface{})
   154  			if ok && sm != nil {
   155  				servers = append(servers, constant.ServerConfig{
   156  					IpAddr:   readString(sm, "ipAddr"),
   157  					Port:     readUint64(sm, "port"),
   158  					GrpcPort: readUint64(sm, "grpcPort"),
   159  				})
   160  			}
   161  		}
   162  		nacosParam.ServerConfigs = servers
   163  	}
   164  	// Discover the server endpoint
   165  	if clientConfigs, ok := config["client"]; ok {
   166  		client, _ := clientConfigs.(map[string]interface{})
   167  		if client != nil {
   168  			nacosParam.ClientConfig = constant.NewClientConfig(
   169  				constant.WithEndpoint(readString(client, "endpoint")),
   170  				constant.WithAppName(n.Metadata.AppName),
   171  				constant.WithNamespaceId(n.Metadata.NamespaceID),
   172  				constant.WithUsername(readString(client, "username")),
   173  				constant.WithPassword(readString(client, "password")),
   174  				constant.WithRegionId(readString(client, "regionId")),
   175  				constant.WithOpenKMS(readBool(client, "openKMS")),
   176  				constant.WithAccessKey(readString(client, "accessKey")),
   177  				constant.WithSecretKey(readString(client, "secretKey")),
   178  			)
   179  		}
   180  	}
   181  	// The mock client creates on the outer.
   182  	if n.Client == nil {
   183  		nacosClient, err := clients.NewConfigClient(nacosParam)
   184  		if err != nil {
   185  			return err
   186  		}
   187  		defer nacosClient.CloseClient()
   188  		n.Client = nacosClient
   189  	}
   190  	_, err = n.Client.PublishConfig(vo.ConfigParam{
   191  		DataId:  n.Metadata.DataID,
   192  		Group:   n.Metadata.Group,
   193  		Content: string(n.Content),
   194  		AppName: n.Metadata.AppName,
   195  		Tag:     n.Metadata.Tag,
   196  		Type:    strings.ToLower(n.Format),
   197  	})
   198  	if err != nil {
   199  		return fmt.Errorf("fail to publish the config to the nacos server:%w", err)
   200  	}
   201  	return nil
   202  }