blob: b104d3ddb6b2e10842b82a0498e039cb7e17ab12 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 parser
import (
"strconv"
"strings"
)
import (
"github.com/magiconair/properties"
perrors "github.com/pkg/errors"
"gopkg.in/yaml.v2"
)
import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/logger"
)
const (
// ScopeApplication : scope application
ScopeApplication = "application"
// GeneralType defines the general type
GeneralType = "general"
)
// ConfigurationParser interface
type ConfigurationParser interface {
Parse(string) (map[string]string, error)
ParseToUrls(content string) ([]*common.URL, error)
}
// DefaultConfigurationParser for supporting properties file in config center
type DefaultConfigurationParser struct{}
// ConfiguratorConfig defines configurator config
type ConfiguratorConfig struct {
ConfigVersion string `yaml:"configVersion"`
Scope string `yaml:"scope"`
Key string `yaml:"key"`
Enabled bool `yaml:"enabled"`
Configs []ConfigItem `yaml:"configs"`
}
// ConfigItem defines config item
type ConfigItem struct {
Type string `yaml:"type"`
Enabled bool `yaml:"enabled"`
Addresses []string `yaml:"addresses"`
ProviderAddresses []string `yaml:"providerAddresses"`
Services []string `yaml:"services"`
Applications []string `yaml:"applications"`
Parameters map[string]string `yaml:"parameters"`
Side string `yaml:"side"`
}
// Parse load content
func (parser *DefaultConfigurationParser) Parse(content string) (map[string]string, error) {
pps, err := properties.LoadString(content)
if err != nil {
logger.Errorf("Parse the content {%v} in DefaultConfigurationParser error ,error message is {%v}", content, err)
return nil, err
}
return pps.Map(), nil
}
// ParseToUrls is used to parse content to urls
func (parser *DefaultConfigurationParser) ParseToUrls(content string) ([]*common.URL, error) {
config := ConfiguratorConfig{}
if err := yaml.Unmarshal([]byte(content), &config); err != nil {
return nil, err
}
scope := config.Scope
items := config.Configs
var allUrls []*common.URL
if scope == ScopeApplication {
for _, v := range items {
urls, err := appItemToUrls(v, config)
if err != nil {
return nil, err
}
allUrls = append(allUrls, urls...)
}
} else {
for _, v := range items {
urls, err := serviceItemToUrls(v, config)
if err != nil {
return nil, err
}
allUrls = append(allUrls, urls...)
}
}
return allUrls, nil
}
// serviceItemToUrls is used to transfer item and config to urls
func serviceItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.URL, error) {
var addresses = item.Addresses
if len(addresses) == 0 {
addresses = append(addresses, constant.ANYHOST_VALUE)
}
var urls []*common.URL
for _, v := range addresses {
urlStr := constant.OVERRIDE_PROTOCOL + "://" + v + "/"
serviceStr, err := getServiceString(config.Key)
if err != nil {
return nil, perrors.WithStack(err)
}
urlStr = urlStr + serviceStr
paramStr, err := getParamString(item)
if err != nil {
return nil, perrors.WithStack(err)
}
urlStr = urlStr + paramStr
urlStr = urlStr + getEnabledString(item, config)
urlStr = urlStr + "&category="
urlStr = urlStr + constant.DYNAMIC_CONFIGURATORS_CATEGORY
urlStr = urlStr + "&configVersion="
urlStr = urlStr + config.ConfigVersion
apps := item.Applications
if len(apps) > 0 {
for _, v := range apps {
newUrlStr := urlStr
newUrlStr = newUrlStr + "&application"
newUrlStr = newUrlStr + v
url, err := common.NewURL(newUrlStr)
if err != nil {
return nil, perrors.WithStack(err)
}
urls = append(urls, url)
}
} else {
url, err := common.NewURL(urlStr)
if err != nil {
return nil, perrors.WithStack(err)
}
urls = append(urls, url)
}
}
return urls, nil
}
// nolint
func appItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.URL, error) {
var addresses = item.Addresses
if len(addresses) == 0 {
addresses = append(addresses, constant.ANYHOST_VALUE)
}
var urls []*common.URL
for _, v := range addresses {
urlStr := constant.OVERRIDE_PROTOCOL + "://" + v + "/"
services := item.Services
if len(services) == 0 {
services = append(services, constant.ANY_VALUE)
}
for _, vs := range services {
serviceStr, err := getServiceString(vs)
if err != nil {
return nil, perrors.WithStack(err)
}
urlStr = urlStr + serviceStr
paramStr, err := getParamString(item)
if err != nil {
return nil, perrors.WithStack(err)
}
urlStr = urlStr + paramStr
urlStr = urlStr + "&application="
urlStr = urlStr + config.Key
urlStr = urlStr + getEnabledString(item, config)
urlStr = urlStr + "&category="
urlStr = urlStr + constant.APP_DYNAMIC_CONFIGURATORS_CATEGORY
urlStr = urlStr + "&configVersion="
urlStr = urlStr + config.ConfigVersion
url, err := common.NewURL(urlStr)
if err != nil {
return nil, perrors.WithStack(err)
}
urls = append(urls, url)
}
}
return urls, nil
}
// getServiceString returns service string
func getServiceString(service string) (string, error) {
if len(service) == 0 {
return "", perrors.New("service field in configuration is null.")
}
var serviceStr string
i := strings.Index(service, "/")
if i > 0 {
serviceStr = serviceStr + "group="
serviceStr = serviceStr + service[0:i]
serviceStr = serviceStr + "&"
service = service[i+1:]
}
j := strings.Index(service, ":")
if j > 0 {
serviceStr = serviceStr + "version="
serviceStr = serviceStr + service[j+1:]
serviceStr = serviceStr + "&"
service = service[0:j]
}
serviceStr = service + "?" + serviceStr
return serviceStr, nil
}
// nolint
func getParamString(item ConfigItem) (string, error) {
var retStr string
retStr = retStr + "category="
retStr = retStr + constant.DYNAMIC_CONFIGURATORS_CATEGORY
if len(item.Side) > 0 {
retStr = retStr + "&side="
retStr = retStr + item.Side
}
params := item.Parameters
if len(params) <= 0 {
return "", perrors.New("Invalid configurator rule, please specify at least one parameter " +
"you want to change in the rule.")
}
for k, v := range params {
retStr += "&" + k + "=" + v
}
retStr += "&" + constant.OVERRIDE_PROVIDERS_KEY + "=" + strings.Join(item.ProviderAddresses, ",")
return retStr, nil
}
// getEnabledString returns enabled string
func getEnabledString(item ConfigItem, config ConfiguratorConfig) string {
retStr := "&enabled="
if len(item.Type) == 0 || item.Type == GeneralType {
retStr = retStr + strconv.FormatBool(config.Enabled)
} else {
retStr = retStr + strconv.FormatBool(item.Enabled)
}
return retStr
}