yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/apsara/resource_tags.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 apsara
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"yunion.io/x/jsonutils"
    21  	"yunion.io/x/pkg/errors"
    22  
    23  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    24  )
    25  
    26  type STagResource struct {
    27  	ResourceType string `json:"ResourceType"`
    28  	TagValue     string `json:"TagValue"`
    29  	ResourceID   string `json:"ResourceId"`
    30  	TagKey       string `json:"TagKey"`
    31  }
    32  
    33  func (self *SRegion) rawListTagResources(serviceType string, resourceType string, resIds []string, token string) ([]STagResource, string, error) {
    34  	if len(resIds) > 50 {
    35  		return nil, "", errors.Wrap(cloudprovider.ErrNotSupported, "resource count exceed 50 for one request")
    36  	}
    37  	params := make(map[string]string)
    38  	params["ResourceType"] = resourceType
    39  	for i := range resIds {
    40  		params[fmt.Sprintf("ResourceId.%d", i+1)] = resIds[i]
    41  	}
    42  	if len(token) != 0 {
    43  		params["NextToken"] = token
    44  	}
    45  	ret, err := self.tagRequest(serviceType, "ListTagResources", params)
    46  	if err != nil {
    47  		return nil, "", errors.Wrapf(err, `self.tagRequest(%s,"ListTagResources", %s)`, serviceType, jsonutils.Marshal(params).String())
    48  	}
    49  	tagResources := []STagResource{}
    50  	err = ret.Unmarshal(&tagResources, "TagResources", "TagResource")
    51  	if err != nil {
    52  		return nil, "", errors.Wrapf(err, "(%s).Unmarshal(&tagResources)", ret.String())
    53  	}
    54  	nextToken, _ := ret.GetString("NextToken")
    55  	return tagResources, nextToken, nil
    56  }
    57  
    58  func splitStringSlice(resIds []string, stride int) [][]string {
    59  	result := [][]string{}
    60  	i := 0
    61  	for i < len(resIds)/stride {
    62  		result = append(result, resIds[i*stride:i*stride+stride])
    63  		i++
    64  	}
    65  	remainder := len(resIds) % stride
    66  	if remainder != 0 {
    67  		result = append(result, resIds[i*stride:i*stride+remainder])
    68  	}
    69  	return result
    70  }
    71  
    72  func splitTags(tags map[string]string, stride int) []map[string]string {
    73  	tagsGroups := []map[string]string{}
    74  	tagsGroup := map[string]string{}
    75  	for k, v := range tags {
    76  		tagsGroup[k] = v
    77  		if len(tagsGroup) == stride {
    78  			tagsGroups = append(tagsGroups, tagsGroup)
    79  			tagsGroup = map[string]string{}
    80  		}
    81  	}
    82  	if len(tagsGroup) > 0 {
    83  		tagsGroups = append(tagsGroups, tagsGroup)
    84  	}
    85  	return tagsGroups
    86  }
    87  
    88  func (self *SRegion) ListResourceTags(serviceType string, resourceType string, resIds []string) (map[string]*map[string]string, error) {
    89  	tags := make(map[string]*map[string]string)
    90  	tagReources := []STagResource{}
    91  	nextToken := ""
    92  	resIdsGroups := splitStringSlice(resIds, 50)
    93  	for i := range resIdsGroups {
    94  		for {
    95  			_tagResource, nextToken, err := self.rawListTagResources(serviceType, resourceType, resIdsGroups[i], nextToken)
    96  			if err != nil {
    97  				return nil, errors.Wrapf(err, "self.rawListTagResources(%s,%s,%s)", resourceType, resIds, nextToken)
    98  			}
    99  			tagReources = append(tagReources, _tagResource...)
   100  			if len(_tagResource) == 0 || len(nextToken) == 0 {
   101  				break
   102  			}
   103  
   104  		}
   105  	}
   106  	for _, r := range tagReources {
   107  		if tagMapPtr, ok := tags[r.ResourceID]; !ok {
   108  			tagMap := map[string]string{
   109  				r.TagKey: r.TagValue,
   110  			}
   111  			tags[r.ResourceID] = &tagMap
   112  		} else {
   113  			tagMap := *tagMapPtr
   114  			tagMap[r.TagKey] = r.TagValue
   115  		}
   116  	}
   117  	return tags, nil
   118  }
   119  
   120  func (self *SRegion) rawTagResources(serviceType string, resourceType string, resIds []string, tags map[string]string) error {
   121  	if len(resIds) > 50 {
   122  		return errors.Wrap(cloudprovider.ErrNotSupported, "resource count exceed 50 for one request")
   123  	}
   124  	if len(tags) > 20 {
   125  		return errors.Wrap(cloudprovider.ErrNotSupported, "tags count exceed 20 for one request")
   126  	}
   127  	params := make(map[string]string)
   128  	params["ResourceType"] = resourceType
   129  	for i := range resIds {
   130  		params[fmt.Sprintf("ResourceId.%d", i+1)] = resIds[i]
   131  	}
   132  	i := 0
   133  	for k, v := range tags {
   134  		params[fmt.Sprintf("Tag.%d.Key", i+1)] = k
   135  		params[fmt.Sprintf("Tag.%d.Value", i+1)] = v
   136  		i++
   137  	}
   138  	_, err := self.tagRequest(serviceType, "TagResources", params)
   139  	if err != nil {
   140  		return errors.Wrapf(err, `self.tagRequest(%s,"TagResources", %s)`, serviceType, jsonutils.Marshal(params).String())
   141  	}
   142  	return nil
   143  }
   144  
   145  func (self *SRegion) TagResources(serviceType string, resourceType string, resIds []string, tags map[string]string) error {
   146  	if len(resIds) == 0 || len(tags) == 0 {
   147  		return nil
   148  	}
   149  	resIdsGroups := splitStringSlice(resIds, 50)
   150  	tagsGroups := splitTags(tags, 20)
   151  	for i := range resIdsGroups {
   152  		for j := range tagsGroups {
   153  			err := self.rawTagResources(serviceType, resourceType, resIdsGroups[i], tagsGroups[j])
   154  			if err != nil {
   155  				return errors.Wrapf(err, "self.rawTagResources(resourceType, resIdsGroups[i], tagsGroups[i])")
   156  			}
   157  		}
   158  	}
   159  	return nil
   160  }
   161  
   162  func (self *SRegion) rawUntagResources(serviceType string, resourceType string, resIds []string, tags []string) error {
   163  	if len(resIds) > 50 {
   164  		return errors.Wrap(cloudprovider.ErrNotSupported, "resource count exceed 50 for one request")
   165  	}
   166  	if len(tags) > 20 {
   167  		return errors.Wrap(cloudprovider.ErrNotSupported, "tags count exceed 20 for one request")
   168  	}
   169  	params := make(map[string]string)
   170  	params["ResourceType"] = resourceType
   171  	for i := range resIds {
   172  		params[fmt.Sprintf("ResourceId.%d", i+1)] = resIds[i]
   173  	}
   174  	for i := range tags {
   175  		params[fmt.Sprintf("TagKey.%d", i+1)] = tags[i]
   176  	}
   177  	_, err := self.tagRequest(serviceType, "UntagResources", params)
   178  	if err != nil {
   179  		return errors.Wrapf(err, `self.tagRequest(%s,"UntagResources", %s)`, serviceType, jsonutils.Marshal(params).String())
   180  	}
   181  	return nil
   182  }
   183  
   184  func (self *SRegion) UntagResources(serviceType string, resourceType string, resIds []string, tags []string) error {
   185  	if len(resIds) == 0 || len(tags) == 0 {
   186  		return nil
   187  	}
   188  	resIdsGroups := splitStringSlice(resIds, 50)
   189  	tagsGroups := splitStringSlice(tags, 20)
   190  	for i := range resIdsGroups {
   191  		for j := range tagsGroups {
   192  			err := self.rawUntagResources(serviceType, resourceType, resIdsGroups[i], tagsGroups[j])
   193  			if err != nil {
   194  				return errors.Wrapf(err, "self.rawTagResources(resourceType, resIdsGroups[i], tagsGroups[i])")
   195  			}
   196  		}
   197  	}
   198  	return nil
   199  }
   200  
   201  func (self *SRegion) SetResourceTags(serviceType string, resourceType string, resIds []string, tags map[string]string, replace bool) error {
   202  	oldTags, err := self.ListResourceTags(serviceType, resourceType, resIds)
   203  	if err != nil {
   204  		return errors.Wrapf(err, "self.ListResourceTags(%s,%s)", resourceType, resIds)
   205  	}
   206  	for i := range resIds {
   207  		_, ok := oldTags[resIds[i]]
   208  		if !ok {
   209  			err := self.TagResources(serviceType, resourceType, []string{resIds[i]}, tags)
   210  			if err != nil {
   211  				return errors.Wrap(err, "self.TagResources(resourceType, []string{resIds[i]}, tags)")
   212  			}
   213  		} else {
   214  			oldResourceTags := *oldTags[resIds[i]]
   215  			addTags := map[string]string{}
   216  			for k, v := range tags {
   217  				if _, ok := oldResourceTags[k]; !ok {
   218  					addTags[k] = v
   219  				} else {
   220  					if oldResourceTags[k] != v {
   221  						addTags[k] = v
   222  					}
   223  				}
   224  			}
   225  			delTags := []string{}
   226  			if replace {
   227  				for k := range oldResourceTags {
   228  					if _, ok := tags[k]; !ok {
   229  						delTags = append(delTags, k)
   230  					}
   231  				}
   232  			}
   233  			err := self.UntagResources(serviceType, resourceType, []string{resIds[i]}, delTags)
   234  			if err != nil {
   235  				return errors.Wrap(err, "self.UntagResources(resourceType, []string{resIds[i]}, delTags)")
   236  			}
   237  			err = self.TagResources(serviceType, resourceType, []string{resIds[i]}, addTags)
   238  			if err != nil {
   239  				return errors.Wrap(err, "self.TagResources(resourceType, []string{resIds[i]}, addTags)")
   240  			}
   241  		}
   242  	}
   243  	return nil
   244  }