yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aws/loadbalancerlistenerrule.go (about)

     1  // Copyright 2019 Yunion
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package aws
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"fmt"
    21  	"strings"
    22  
    23  	"github.com/aws/aws-sdk-go/service/elbv2"
    24  
    25  	"yunion.io/x/jsonutils"
    26  	"yunion.io/x/log"
    27  	"yunion.io/x/pkg/errors"
    28  
    29  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    30  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    31  	"yunion.io/x/cloudmux/pkg/multicloud"
    32  )
    33  
    34  type SElbListenerRule struct {
    35  	multicloud.SResourceBase
    36  	multicloud.SLoadbalancerRedirectBase
    37  	AwsTags
    38  	listener *SElbListener
    39  	region   *SRegion
    40  
    41  	Priority      string      `json:"Priority"`
    42  	IsDefaultRule bool        `json:"IsDefault"`
    43  	Actions       []Action    `json:"Actions"`
    44  	RuleArn       string      `json:"RuleArn"`
    45  	Conditions    []Condition `json:"Conditions"`
    46  }
    47  
    48  type Action struct {
    49  	TargetGroupArn string `json:"TargetGroupArn"`
    50  	Type           string `json:"Type"`
    51  }
    52  
    53  type Condition struct {
    54  	Field                   string             `json:"field"`
    55  	HTTPRequestMethodConfig *Config            `json:"httpRequestMethodConfig,omitempty"`
    56  	Values                  []string           `json:"values"`
    57  	SourceIPConfig          *Config            `json:"sourceIpConfig,omitempty"`
    58  	QueryStringConfig       *QueryStringConfig `json:"queryStringConfig,omitempty"`
    59  	HTTPHeaderConfig        *HTTPHeaderConfig  `json:"httpHeaderConfig,omitempty"`
    60  	PathPatternConfig       *Config            `json:"pathPatternConfig,omitempty"`
    61  	HostHeaderConfig        *Config            `json:"hostHeaderConfig,omitempty"`
    62  }
    63  
    64  type HTTPHeaderConfig struct {
    65  	HTTPHeaderName string   `json:"HttpHeaderName"`
    66  	Values         []string `json:"values"`
    67  }
    68  
    69  type Config struct {
    70  	Values []string `json:"values"`
    71  }
    72  
    73  type QueryStringConfig struct {
    74  	Values []Query `json:"values"`
    75  }
    76  
    77  type Query struct {
    78  	Key   string `json:"key"`
    79  	Value string `json:"value"`
    80  }
    81  
    82  func (self *SElbListenerRule) GetId() string {
    83  	return self.RuleArn
    84  }
    85  
    86  func (self *SElbListenerRule) GetName() string {
    87  	segs := strings.Split(self.RuleArn, "/")
    88  	return segs[len(segs)-1]
    89  }
    90  
    91  func (self *SElbListenerRule) GetGlobalId() string {
    92  	return self.GetId()
    93  }
    94  
    95  func (self *SElbListenerRule) GetStatus() string {
    96  	return api.LB_STATUS_ENABLED
    97  }
    98  
    99  func (self *SElbListenerRule) Refresh() error {
   100  	rule, err := self.region.GetElbListenerRuleById(self.GetId())
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	err = jsonutils.Update(self, rule)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  func (self *SElbListenerRule) IsDefault() bool {
   114  	return self.IsDefaultRule
   115  }
   116  
   117  func (self *SElbListenerRule) IsEmulated() bool {
   118  	return false
   119  }
   120  
   121  func (self *SElbListenerRule) GetProjectId() string {
   122  	return ""
   123  }
   124  
   125  func (self *SElbListenerRule) GetDomain() string {
   126  	for _, condition := range self.Conditions {
   127  		if condition.Field == "host-header" {
   128  			return strings.Join(condition.Values, ",")
   129  		}
   130  	}
   131  
   132  	return ""
   133  }
   134  
   135  func (self *SElbListenerRule) GetCondition() string {
   136  	conditon, err := json.Marshal(self.Conditions)
   137  	if err != nil {
   138  		log.Errorf("GetCondition %s", err)
   139  		return ""
   140  	}
   141  
   142  	return string(conditon)
   143  }
   144  
   145  func (self *SElbListenerRule) GetPath() string {
   146  	for _, condition := range self.Conditions {
   147  		if condition.Field == "path-pattern" {
   148  			return strings.Join(condition.Values, ",")
   149  		}
   150  	}
   151  
   152  	return ""
   153  }
   154  
   155  func (self *SElbListenerRule) GetBackendGroupId() string {
   156  	for _, action := range self.Actions {
   157  		if action.Type == "forward" {
   158  			return action.TargetGroupArn
   159  		}
   160  	}
   161  
   162  	return ""
   163  }
   164  
   165  func (self *SElbListenerRule) Delete(ctx context.Context) error {
   166  	return self.region.DeleteElbListenerRule(self.GetId())
   167  }
   168  
   169  func (self *SRegion) DeleteElbListenerRule(ruleId string) error {
   170  	client, err := self.GetElbV2Client()
   171  	if err != nil {
   172  		return err
   173  	}
   174  
   175  	params := &elbv2.DeleteRuleInput{}
   176  	params.SetRuleArn(ruleId)
   177  	_, err = client.DeleteRule(params)
   178  	if err != nil {
   179  		return err
   180  	}
   181  
   182  	return nil
   183  }
   184  
   185  func (self *SRegion) CreateElbListenerRule(listenerId string, config *cloudprovider.SLoadbalancerListenerRule) (*SElbListenerRule, error) {
   186  	client, err := self.GetElbV2Client()
   187  	if err != nil {
   188  		return nil, errors.Wrap(err, "GetElbV2Client")
   189  	}
   190  
   191  	forward := "forward"
   192  	action := &elbv2.Action{
   193  		TargetGroupArn: &config.BackendGroupID,
   194  		Type:           &forward,
   195  	}
   196  
   197  	condtions, err := parseConditions(config.Condition)
   198  	if err != nil {
   199  		return nil, errors.Wrap(err, "parseConditions")
   200  	}
   201  
   202  	params := &elbv2.CreateRuleInput{}
   203  	params.SetListenerArn(listenerId)
   204  	params.SetActions([]*elbv2.Action{action})
   205  	params.SetConditions(condtions)
   206  	params.SetPriority(int64(1))
   207  	ret, err := client.CreateRule(params)
   208  	if err != nil {
   209  		return nil, errors.Wrap(err, "CreateRule")
   210  	}
   211  
   212  	if len(ret.Rules) == 0 {
   213  		return nil, errors.Wrap(fmt.Errorf("empty rules"), "Region.CreateElbListenerRule.len")
   214  	}
   215  
   216  	rule := SElbListenerRule{}
   217  	err = unmarshalAwsOutput(ret.Rules[0], "", &rule)
   218  	if err != nil {
   219  		return nil, errors.Wrap(err, "unmarshalAwsOutput.rule")
   220  	}
   221  
   222  	rule.region = self
   223  	return &rule, nil
   224  }
   225  
   226  func parseConditions(conditions string) ([]*elbv2.RuleCondition, error) {
   227  	obj, err := jsonutils.ParseString(conditions)
   228  	if err != nil {
   229  		return nil, errors.Wrap(err, "ParseString.conditions")
   230  	}
   231  
   232  	conditionArray, ok := obj.(*jsonutils.JSONArray)
   233  	if !ok {
   234  		return nil, fmt.Errorf("parseConditions invalid condition fromat.")
   235  	}
   236  
   237  	ret := []*elbv2.RuleCondition{}
   238  	cs, _ := conditionArray.GetArray()
   239  	for i := range cs {
   240  		c, err := parseCondition(cs[i])
   241  		if err != nil {
   242  			return nil, errors.Wrap(err, "parseCondition")
   243  		}
   244  
   245  		ret = append(ret, c)
   246  	}
   247  
   248  	return ret, nil
   249  }
   250  
   251  func parseCondition(condition jsonutils.JSONObject) (*elbv2.RuleCondition, error) {
   252  	conditionDict, ok := condition.(*jsonutils.JSONDict)
   253  	if !ok {
   254  		return nil, fmt.Errorf("parseCondition invalid condition fromat.")
   255  	}
   256  
   257  	dict, _ := conditionDict.GetMap()
   258  	field, ok := dict["field"]
   259  	if !ok {
   260  		return nil, fmt.Errorf("parseCondition invalid condition, missing field: %#v", condition)
   261  	}
   262  
   263  	f, _ := field.GetString()
   264  	switch f {
   265  	case "http-header":
   266  		return parseHttpHeaderCondition(conditionDict)
   267  	case "path-pattern":
   268  		return parsePathPatternCondition(conditionDict)
   269  	case "http-request-method":
   270  		return parseRequestModthdCondition(conditionDict)
   271  	case "host-header":
   272  		return parseHostHeaderCondition(conditionDict)
   273  	case "query-string":
   274  		return parseQueryStringCondition(conditionDict)
   275  	case "source-ip":
   276  		return parseSourceIpCondition(conditionDict)
   277  	default:
   278  		return nil, fmt.Errorf("parseCondition invalid condition key %#v", field)
   279  	}
   280  }
   281  
   282  func parseHttpHeaderCondition(conditon *jsonutils.JSONDict) (*elbv2.RuleCondition, error) {
   283  	ret := &elbv2.RuleCondition{}
   284  	ret.SetField("http-header")
   285  
   286  	values, err := conditon.GetMap("httpHeaderConfig")
   287  	if err != nil {
   288  		return nil, errors.Wrap(err, "GetMap")
   289  	}
   290  
   291  	name, ok := values["HttpHeaderName"]
   292  	if !ok {
   293  		return nil, fmt.Errorf("parseHttpHeaderCondition missing filed HttpHeaderName")
   294  	}
   295  
   296  	nameObj, ok := name.(*jsonutils.JSONString)
   297  	if !ok {
   298  		return nil, fmt.Errorf("parseHttpHeaderCondition missing invalid data %#v", name)
   299  	}
   300  
   301  	headname, _ := nameObj.GetString()
   302  	config := &elbv2.HttpHeaderConditionConfig{}
   303  	config.SetHttpHeaderName(headname)
   304  
   305  	vs, ok := values["values"]
   306  	if !ok {
   307  		return nil, fmt.Errorf("parseHttpHeaderCondition missing filed values")
   308  	}
   309  
   310  	_vs, err := parseConditionStringArrayValues(vs)
   311  	if err != nil {
   312  		return nil, errors.Wrap(err, "parseConditionStringArrayValues")
   313  	}
   314  	config.SetValues(_vs)
   315  	ret.SetHttpHeaderConfig(config)
   316  	return ret, nil
   317  }
   318  
   319  func parsePathPatternCondition(condition *jsonutils.JSONDict) (*elbv2.RuleCondition, error) {
   320  	ret := &elbv2.RuleCondition{}
   321  	ret.SetField("path-pattern")
   322  
   323  	values, err := condition.GetMap("pathPatternConfig")
   324  	if err != nil {
   325  		return nil, errors.Wrap(err, "GetMap")
   326  	}
   327  
   328  	config := &elbv2.PathPatternConditionConfig{}
   329  	vs, ok := values["values"]
   330  	if !ok {
   331  		return nil, fmt.Errorf("parsePathPatternCondition missing filed values")
   332  	}
   333  
   334  	_vs, err := parseConditionStringArrayValues(vs)
   335  	if err != nil {
   336  		return nil, errors.Wrap(err, "parseConditionStringArrayValues")
   337  	}
   338  	config.SetValues(_vs)
   339  	ret.SetPathPatternConfig(config)
   340  	return ret, nil
   341  
   342  }
   343  
   344  func parseRequestModthdCondition(condition *jsonutils.JSONDict) (*elbv2.RuleCondition, error) {
   345  	ret := &elbv2.RuleCondition{}
   346  	ret.SetField("http-request-method")
   347  
   348  	values, err := condition.GetMap("httpRequestMethodConfig")
   349  	if err != nil {
   350  		return nil, errors.Wrap(err, "GetMap.httpRequestMethodConfig")
   351  	}
   352  
   353  	config := &elbv2.HttpRequestMethodConditionConfig{}
   354  	vs, ok := values["values"]
   355  	if !ok {
   356  		return nil, fmt.Errorf("parseRequestModthdCondition missing filed values")
   357  	}
   358  
   359  	_vs, err := parseConditionStringArrayValues(vs)
   360  	if err != nil {
   361  		return nil, errors.Wrap(err, "parseConditionStringArrayValues")
   362  	}
   363  	config.SetValues(_vs)
   364  	return ret, nil
   365  }
   366  
   367  func parseHostHeaderCondition(condition *jsonutils.JSONDict) (*elbv2.RuleCondition, error) {
   368  	ret := &elbv2.RuleCondition{}
   369  	ret.SetField("host-header")
   370  
   371  	values, err := condition.GetMap("hostHeaderConfig")
   372  	if err != nil {
   373  		return nil, errors.Wrap(err, "GetMap.hostHeaderConfig")
   374  	}
   375  
   376  	config := &elbv2.HostHeaderConditionConfig{}
   377  	vs, ok := values["values"]
   378  	if !ok {
   379  		return nil, fmt.Errorf("parseHostHeaderCondition missing filed values")
   380  	}
   381  
   382  	_vs, err := parseConditionStringArrayValues(vs)
   383  	if err != nil {
   384  		return nil, errors.Wrap(err, "parseConditionStringArrayValues")
   385  	}
   386  	config.SetValues(_vs)
   387  	ret.SetHostHeaderConfig(config)
   388  	return ret, nil
   389  }
   390  
   391  func parseQueryStringCondition(condition *jsonutils.JSONDict) (*elbv2.RuleCondition, error) {
   392  	ret := &elbv2.RuleCondition{}
   393  	ret.SetField("query-string")
   394  
   395  	values, err := condition.GetMap("queryStringConfig")
   396  	if err != nil {
   397  		return nil, errors.Wrap(err, "GetMap.queryStringConfig")
   398  	}
   399  
   400  	config := &elbv2.QueryStringConditionConfig{}
   401  	vs, ok := values["values"]
   402  	if !ok {
   403  		return nil, fmt.Errorf("parseQueryStringCondition missing filed values")
   404  	}
   405  
   406  	_vs, err := parseConditionDictArrayValues(vs)
   407  	if err != nil {
   408  		return nil, errors.Wrap(err, "parseConditionDictArrayValues")
   409  	}
   410  	config.SetValues(_vs)
   411  	ret.SetQueryStringConfig(config)
   412  	return ret, nil
   413  }
   414  
   415  func parseSourceIpCondition(condition *jsonutils.JSONDict) (*elbv2.RuleCondition, error) {
   416  	ret := &elbv2.RuleCondition{}
   417  	ret.SetField("source-ip")
   418  
   419  	values, err := condition.GetMap("sourceIpConfig")
   420  	if err != nil {
   421  		return nil, errors.Wrap(err, "GetMap.sourceIpConfig")
   422  	}
   423  
   424  	config := &elbv2.SourceIpConditionConfig{}
   425  	vs, ok := values["values"]
   426  	if !ok {
   427  		return nil, fmt.Errorf("parseSourceIpCondition missing filed values")
   428  	}
   429  
   430  	_vs, err := parseConditionStringArrayValues(vs)
   431  	if err != nil {
   432  		return nil, errors.Wrap(err, "parseConditionStringArrayValues")
   433  	}
   434  	config.SetValues(_vs)
   435  	return ret, nil
   436  }
   437  
   438  func parseConditionStringArrayValues(values jsonutils.JSONObject) ([]*string, error) {
   439  	objs, ok := values.(*jsonutils.JSONArray)
   440  	if !ok {
   441  		return nil, fmt.Errorf("parseConditionStringArrayValues invalid values format, required array: %#v", values)
   442  	}
   443  
   444  	ret := []*string{}
   445  	vs, _ := objs.GetArray()
   446  	for i := range vs {
   447  		v, ok := vs[i].(*jsonutils.JSONString)
   448  		if !ok {
   449  			return nil, fmt.Errorf("parseConditionStringArrayValues invalid value, required string: %#v", v)
   450  		}
   451  
   452  		_v, _ := v.GetString()
   453  		ret = append(ret, &_v)
   454  	}
   455  
   456  	return ret, nil
   457  }
   458  
   459  func parseConditionDictArrayValues(values jsonutils.JSONObject) ([]*elbv2.QueryStringKeyValuePair, error) {
   460  	objs, ok := values.(*jsonutils.JSONArray)
   461  	if !ok {
   462  		return nil, fmt.Errorf("parseConditionDictArrayValues invalid values format, required array: %#v", values)
   463  	}
   464  
   465  	ret := []*elbv2.QueryStringKeyValuePair{}
   466  	vs, _ := objs.GetArray()
   467  	for i := range vs {
   468  		v, ok := vs[i].(*jsonutils.JSONDict)
   469  		if !ok {
   470  			return nil, fmt.Errorf("parseConditionDictArrayValues invalid value, required dict: %#v", v)
   471  		}
   472  
   473  		key, err := v.GetString("key")
   474  		if err != nil {
   475  			return nil, errors.Wrap(err, "GetString.key")
   476  		}
   477  
   478  		value, err := v.GetString("value")
   479  		if err != nil {
   480  			return nil, errors.Wrap(err, "GetString.value")
   481  		}
   482  
   483  		pair := &elbv2.QueryStringKeyValuePair{}
   484  		pair.SetKey(key)
   485  		pair.SetValue(value)
   486  		ret = append(ret, pair)
   487  	}
   488  
   489  	return ret, nil
   490  }