From 785a6146300a84d3751f44c22faa4754664f6e6c Mon Sep 17 00:00:00 2001 From: dev4unet Date: Wed, 10 Jul 2024 15:23:17 +0900 Subject: [PATCH 1/4] TagHandler in Development --- .../cloud-driver/drivers/aws/AwsDriver.go | 3 + .../drivers/aws/connect/AwsCloudConnection.go | 12 +- .../drivers/aws/main/Test_Resources.go | 104 +++++++++++++- .../drivers/aws/resources/TagHandler.go | 133 ++++++++++++++++++ 4 files changed, 242 insertions(+), 10 deletions(-) create mode 100644 cloud-control-manager/cloud-driver/drivers/aws/resources/TagHandler.go diff --git a/cloud-control-manager/cloud-driver/drivers/aws/AwsDriver.go b/cloud-control-manager/cloud-driver/drivers/aws/AwsDriver.go index 8c2bdd22a..186d881ee 100644 --- a/cloud-control-manager/cloud-driver/drivers/aws/AwsDriver.go +++ b/cloud-control-manager/cloud-driver/drivers/aws/AwsDriver.go @@ -60,6 +60,7 @@ func (AwsDriver) GetDriverCapability() idrv.DriverCapabilityInfo { drvCapabilityInfo.NLBHandler = true drvCapabilityInfo.RegionZoneHandler = true drvCapabilityInfo.PriceInfoHandler = true + drvCapabilityInfo.TagHandler = true return drvCapabilityInfo } @@ -254,6 +255,8 @@ func (driver *AwsDriver) ConnectCloud(connectionInfo idrv.ConnectionInfo) (icon. // Connection for AnyCall AnyCallClient: vmClient, + + TagClient: vmClient, } return &iConn, nil // return type: (icon.CloudConnection, error) diff --git a/cloud-control-manager/cloud-driver/drivers/aws/connect/AwsCloudConnection.go b/cloud-control-manager/cloud-driver/drivers/aws/connect/AwsCloudConnection.go index 55e366f6a..bf39d9e64 100644 --- a/cloud-control-manager/cloud-driver/drivers/aws/connect/AwsCloudConnection.go +++ b/cloud-control-manager/cloud-driver/drivers/aws/connect/AwsCloudConnection.go @@ -11,8 +11,6 @@ package connect import ( - "errors" - cblog "github.com/cloud-barista/cb-log" idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" @@ -63,6 +61,7 @@ type AwsCloudConnection struct { AutoScalingClient *autoscaling.AutoScaling AnyCallClient *ec2.EC2 + TagClient *ec2.EC2 } var cblogger *logrus.Logger @@ -111,6 +110,11 @@ func (cloudConn *AwsCloudConnection) CreateSecurityHandler() (irs.SecurityHandle return &handler, nil } +func (cloudConn *AwsCloudConnection) CreateTagHandler() (irs.TagHandler, error) { + handler := ars.AwsTagHandler{cloudConn.Region, cloudConn.VMClient} + return &handler, nil +} + /* func (cloudConn *AwsCloudConnection) CreateVNicHandler() (irs.VNicHandler, error) { cblogger.Info("Start") @@ -182,7 +186,3 @@ func (cloudConn *AwsCloudConnection) CreatePriceInfoHandler() (irs.PriceInfoHand handler := ars.AwsPriceInfoHandler{cloudConn.Region, cloudConn.PriceInfoClient} return &handler, nil } - -func (cloudConn *AwsCloudConnection) CreateTagHandler() (irs.TagHandler, error) { - return nil, errors.New("AWS Driver: not implemented") -} diff --git a/cloud-control-manager/cloud-driver/drivers/aws/main/Test_Resources.go b/cloud-control-manager/cloud-driver/drivers/aws/main/Test_Resources.go index 68893d1fc..5b80ff083 100644 --- a/cloud-control-manager/cloud-driver/drivers/aws/main/Test_Resources.go +++ b/cloud-control-manager/cloud-driver/drivers/aws/main/Test_Resources.go @@ -1777,6 +1777,100 @@ func handlePriceInfo() { } } +// Test AMI +func handleTag() { + cblogger.Debug("Start TagHandler Resource Test") + + ResourceHandler, err := getResourceHandler("Tag") + if err != nil { + panic(err) + } + handler := ResourceHandler.(irs.TagHandler) + + var reqType irs.RSType = irs.VM + reqIID := irs.IID{SystemId: "i-02ac1c4ff1d40815c"} + reqTag := irs.KeyValue{Key: "tag3", Value: "태그3"} + reqKey := "Name" + + for { + fmt.Println("TagHandler Management") + fmt.Println("0. Quit") + fmt.Println("1. Tag List") + fmt.Println("2. Tag Add") + fmt.Println("3. Tag Get") + fmt.Println("4. Tag Delete") + fmt.Println("5. Tag Find") + fmt.Println("6. Tag Create") + + var commandNum int + inputCnt, err := fmt.Scan(&commandNum) + if err != nil { + panic(err) + } + + if inputCnt == 1 { + switch commandNum { + case 0: + return + + case 1: + cblogger.Infof("조회 요청 태그 타입 : [%s]", reqType) + if reqType == irs.VM { + cblogger.Debug("VM 요청됨") + } + + result, err := handler.ListTag(reqType, reqIID) + if err != nil { + cblogger.Info(" Tag 목록 조회 실패 : ", err) + } else { + cblogger.Info("Tag 목록 조회 결과") + cblogger.Debug(result) + cblogger.Infof("로그 레벨 : [%s]", cblog.GetLevel()) + //spew.Dump(result) + cblogger.Info("출력 결과 수 : ", len(result)) + + //조회및 삭제 테스트를 위해 리스트의 첫번째 정보의 ID를 요청ID로 자동 갱신함. + if result != nil { + //tagReqInfo.IId = result[0].IId // 조회 및 삭제를 위해 생성된 ID로 변경 + } + } + + case 2: + cblogger.Infof("[%s] Tag 추가 테스트", reqIID.SystemId) + result, err := handler.AddTag(reqType, reqIID, reqTag) + if err != nil { + cblogger.Infof(reqIID.SystemId, " Tag 생성 실패 : ", err) + } else { + cblogger.Info("Tag 생성 결과 : ", result) + spew.Dump(result) + } + + case 3: + cblogger.Infof("[%s] Tag 조회 테스트 - Key[%s]", reqIID.SystemId, reqKey) + result, err := handler.GetTag(reqType, reqIID, reqKey) + if err != nil { + cblogger.Infof("[%s] Tag 조회 실패 : [%v]", reqKey, err) + } else { + cblogger.Infof("[%s] Tag 조회 결과 : [%s]", reqKey, result) + spew.Dump(result) + } + + case 4: + cblogger.Infof("[%s] Tag 삭제 테스트 - Key[%s]", reqIID.SystemId, reqKey) + result, err := handler.RemoveTag(reqType, reqIID, reqKey) + if err != nil { + cblogger.Infof("[%s] Tag 삭제 실패 : [%v]", reqKey, err) + } else { + cblogger.Infof("[%s] Tag 삭제 결과 : [%v]", reqKey, result) + } + + case 7: + + } + } + } +} + // handlerType : resources폴더의 xxxHandler.go에서 Handler이전까지의 문자열 // (예) ImageHandler.go -> "Image" func getResourceHandler(handlerType string) (interface{}, error) { @@ -1826,6 +1920,8 @@ func getResourceHandler(handlerType string) (interface{}, error) { resourceHandler, err = cloudConnection.CreateRegionZoneHandler() case "PriceInfo": resourceHandler, err = cloudConnection.CreatePriceInfoHandler() + case "Tag": + resourceHandler, err = cloudConnection.CreateTagHandler() } if err != nil { @@ -1933,7 +2029,7 @@ func readConfigFile() Config { //cblogger.Infof("최종 환경 설정파일 경로 : [%s]", rootPath+"/config/config.yaml") //data, err := ioutil.ReadFile(rootPath + "/config/config.yaml") //data, err = ioutil.ReadFile("/Users/mzc01-swy/projects/feature_aws_filter_swy_240130/cloud-control-manager/cloud-driver/drivers/aws/main/Sample/config/config.yaml") - + data, err := ioutil.ReadFile(confPath) if err != nil { panic(err) @@ -1951,8 +2047,7 @@ func readConfigFile() Config { } func main() { - cblogger.Info("AWS Resource Test") - // handleVPC() + //handleVPC() // handleKeyPair() // handlePublicIP() // PublicIP 생성 후 conf // handleSecurity() @@ -1963,5 +2058,6 @@ func main() { // handleNLB() // handleCluster() //handleRegionZone() - handlePriceInfo() + //handlePriceInfo() + handleTag() } diff --git a/cloud-control-manager/cloud-driver/drivers/aws/resources/TagHandler.go b/cloud-control-manager/cloud-driver/drivers/aws/resources/TagHandler.go new file mode 100644 index 000000000..b46de28e3 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/aws/resources/TagHandler.go @@ -0,0 +1,133 @@ +// Cloud Driver Interface of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is Resouces interfaces of Cloud Driver. +// +// by devunet@mz.co.kr + +// https://github.com/cloud-barista/cb-spider/wiki/Tag-and-Cloud-Driver-API +package resources + +import ( + //"errors" + //"reflect" + //"strconv" + + "errors" + + "github.com/aws/aws-sdk-go/aws" + //"github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/ec2" + call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" +) + +type AwsTagHandler struct { + Region idrv.RegionInfo + Client *ec2.EC2 +} + +func (tagHandler *AwsTagHandler) AddTag(resType irs.RSType, resIID irs.IID, tag irs.KeyValue) (irs.KeyValue, error) { + cblogger.Debugf("Req resTyp:[%s] / resIID:[%s] / Tag Key:[%s] / Tag Value:[%s]", resType, resIID, tag.Key, tag.Value) + + if resIID.SystemId == "" { + msg := "tag will not be add because resIID.SystemId is not provided" + cblogger.Error(msg) + return irs.KeyValue{}, errors.New(msg) + } + + hiscallInfo := GetCallLogScheme(tagHandler.Region, call.TAG, resIID.SystemId, "DescribeTags()") + start := call.Start() + + // 리소스에 신규 태그 추가 + result, errtag := tagHandler.Client.CreateTags(&ec2.CreateTagsInput{ + Resources: []*string{&resIID.SystemId}, + Tags: []*ec2.Tag{ + { + Key: aws.String(tag.Key), + Value: aws.String(tag.Value), + }, + }, + }) + + if errtag != nil { + cblogger.Errorf("Failed to set Name Tag for [%s] VM", resIID.NameId) + cblogger.Error(errtag) + return irs.KeyValue{}, errtag + } + LoggingInfo(hiscallInfo, start) + + if cblogger.Level.String() == "debug" { + cblogger.Info(result) + } + + return tag, nil +} + +func (tagHandler *AwsTagHandler) ListTag(resType irs.RSType, resIID irs.IID) ([]irs.KeyValue, error) { + cblogger.Debugf("Req resTyp:[%s] / resIID:[%s]", resType, resIID) + + input := &ec2.DescribeTagsInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("resource-id"), + Values: []*string{ + aws.String(resIID.SystemId), + }, + }, + }, + } + if cblogger.Level.String() == "debug" { + cblogger.Debug(input) + } + + hiscallInfo := GetCallLogScheme(tagHandler.Region, call.TAG, resIID.SystemId, "DescribeTags()") + start := call.Start() + + result, errtag := tagHandler.Client.DescribeTags(input) + if errtag != nil { + cblogger.Errorf("Failed to set Name Tag for [%s] VM", resIID.NameId) + cblogger.Error(errtag) + LoggingError(hiscallInfo, errtag) + return nil, errtag + } + LoggingInfo(hiscallInfo, start) + + if cblogger.Level.String() == "debug" { + cblogger.Info(result) + } + + return nil, nil +} + +// describetags +func (tagHandler *AwsTagHandler) GetTag(resType irs.RSType, resIID irs.IID, key string) (irs.KeyValue, error) { + cblogger.Info("resTyp : ", resType) + cblogger.Info("resIID : ", resIID) + cblogger.Info("key : ", key) + + return irs.KeyValue{}, nil +} + +// deletetags +func (tagHandler *AwsTagHandler) RemoveTag(resType irs.RSType, resIID irs.IID, key string) (bool, error) { + cblogger.Info("resTyp : ", resType) + cblogger.Info("resIID : ", resIID) + cblogger.Info("key : ", key) + + return false, nil +} + +// Find tags by tag key or value +// resType: ALL | VPC, SUBNET, etc.,. +// keyword: The keyword to search for in the tag key or value. +// if you want to find all tags, set keyword to "" or "*". +func (tagHandler *AwsTagHandler) FindTag(resType irs.RSType, keyword string) ([]*irs.TagInfo, error) { + cblogger.Info("resTyp : ", resType) + cblogger.Info("keyword : ", keyword) + return nil, nil +} From cc471902c0b19e9ea76d287b69d9d124c174c0c2 Mon Sep 17 00:00:00 2001 From: dev4unet Date: Wed, 10 Jul 2024 15:24:07 +0900 Subject: [PATCH 2/4] Reflecting TagHandler return type changes --- .../cloud-driver/interfaces/resources/TagHandler.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cloud-control-manager/cloud-driver/interfaces/resources/TagHandler.go b/cloud-control-manager/cloud-driver/interfaces/resources/TagHandler.go index bbad05efe..f7e356ccc 100644 --- a/cloud-control-manager/cloud-driver/interfaces/resources/TagHandler.go +++ b/cloud-control-manager/cloud-driver/interfaces/resources/TagHandler.go @@ -19,9 +19,9 @@ type TagInfo struct { } type TagHandler interface { - AddTag(resType RSType, resIID IID, tag KeyValue) (TagInfo, error) - ListTag(resType RSType, resIID IID) ([]*TagInfo, error) - GetTag(resType RSType, resIID IID, key string) (TagInfo, error) + AddTag(resType RSType, resIID IID, tag KeyValue) (KeyValue, error) + ListTag(resType RSType, resIID IID) ([]KeyValue, error) + GetTag(resType RSType, resIID IID, key string) (KeyValue, error) RemoveTag(resType RSType, resIID IID, key string) (bool, error) // Find tags by tag key or value From be9acded3b96c65ef6e46c01d1089dde6db24332 Mon Sep 17 00:00:00 2001 From: dev4unet Date: Wed, 10 Jul 2024 08:34:22 +0000 Subject: [PATCH 3/4] Features implemented except for FindTag. --- .../drivers/aws/main/Test_Resources.go | 9 +- .../drivers/aws/resources/TagHandler.go | 136 +++++++++++++++--- 2 files changed, 125 insertions(+), 20 deletions(-) diff --git a/cloud-control-manager/cloud-driver/drivers/aws/main/Test_Resources.go b/cloud-control-manager/cloud-driver/drivers/aws/main/Test_Resources.go index 5b80ff083..a1072fbf7 100644 --- a/cloud-control-manager/cloud-driver/drivers/aws/main/Test_Resources.go +++ b/cloud-control-manager/cloud-driver/drivers/aws/main/Test_Resources.go @@ -1777,7 +1777,7 @@ func handlePriceInfo() { } } -// Test AMI +// Test Tag func handleTag() { cblogger.Debug("Start TagHandler Resource Test") @@ -1790,7 +1790,7 @@ func handleTag() { var reqType irs.RSType = irs.VM reqIID := irs.IID{SystemId: "i-02ac1c4ff1d40815c"} reqTag := irs.KeyValue{Key: "tag3", Value: "태그3"} - reqKey := "Name" + reqKey := "tag3" for { fmt.Println("TagHandler Management") @@ -1800,7 +1800,6 @@ func handleTag() { fmt.Println("3. Tag Get") fmt.Println("4. Tag Delete") fmt.Println("5. Tag Find") - fmt.Println("6. Tag Create") var commandNum int inputCnt, err := fmt.Scan(&commandNum) @@ -1842,6 +1841,8 @@ func handleTag() { cblogger.Infof(reqIID.SystemId, " Tag 생성 실패 : ", err) } else { cblogger.Info("Tag 생성 결과 : ", result) + reqKey = result.Key + cblogger.Infof("요청 대상 Tag Key가 [%s]로 변경 됨", reqKey) spew.Dump(result) } @@ -1864,7 +1865,7 @@ func handleTag() { cblogger.Infof("[%s] Tag 삭제 결과 : [%v]", reqKey, result) } - case 7: + case 5: } } diff --git a/cloud-control-manager/cloud-driver/drivers/aws/resources/TagHandler.go b/cloud-control-manager/cloud-driver/drivers/aws/resources/TagHandler.go index b46de28e3..54c0b9742 100644 --- a/cloud-control-manager/cloud-driver/drivers/aws/resources/TagHandler.go +++ b/cloud-control-manager/cloud-driver/drivers/aws/resources/TagHandler.go @@ -34,13 +34,13 @@ type AwsTagHandler struct { func (tagHandler *AwsTagHandler) AddTag(resType irs.RSType, resIID irs.IID, tag irs.KeyValue) (irs.KeyValue, error) { cblogger.Debugf("Req resTyp:[%s] / resIID:[%s] / Tag Key:[%s] / Tag Value:[%s]", resType, resIID, tag.Key, tag.Value) - if resIID.SystemId == "" { - msg := "tag will not be add because resIID.SystemId is not provided" + if resIID.SystemId == "" || tag.Key == "" { + msg := "tag will not be add because resIID.SystemId or tag.Key is not provided" cblogger.Error(msg) return irs.KeyValue{}, errors.New(msg) } - hiscallInfo := GetCallLogScheme(tagHandler.Region, call.TAG, resIID.SystemId, "DescribeTags()") + hiscallInfo := GetCallLogScheme(tagHandler.Region, call.TAG, resIID.SystemId, "CreateTags()") start := call.Start() // 리소스에 신규 태그 추가 @@ -55,7 +55,7 @@ func (tagHandler *AwsTagHandler) AddTag(resType irs.RSType, resIID irs.IID, tag }) if errtag != nil { - cblogger.Errorf("Failed to set Name Tag for [%s] VM", resIID.NameId) + cblogger.Errorf("Failed to add [%s] Tag for [%s]", tag.Key, resIID.SystemId) cblogger.Error(errtag) return irs.KeyValue{}, errtag } @@ -71,6 +71,12 @@ func (tagHandler *AwsTagHandler) AddTag(resType irs.RSType, resIID irs.IID, tag func (tagHandler *AwsTagHandler) ListTag(resType irs.RSType, resIID irs.IID) ([]irs.KeyValue, error) { cblogger.Debugf("Req resTyp:[%s] / resIID:[%s]", resType, resIID) + if resIID.SystemId == "" { + msg := "resIID.SystemId is not provided" + cblogger.Error(msg) + return nil, errors.New(msg) + } + input := &ec2.DescribeTagsInput{ Filters: []*ec2.Filter{ { @@ -90,7 +96,7 @@ func (tagHandler *AwsTagHandler) ListTag(resType irs.RSType, resIID irs.IID) ([] result, errtag := tagHandler.Client.DescribeTags(input) if errtag != nil { - cblogger.Errorf("Failed to set Name Tag for [%s] VM", resIID.NameId) + cblogger.Errorf("Failed to look up tags for [%s]", resIID.NameId) cblogger.Error(errtag) LoggingError(hiscallInfo, errtag) return nil, errtag @@ -101,25 +107,123 @@ func (tagHandler *AwsTagHandler) ListTag(resType irs.RSType, resIID irs.IID) ([] cblogger.Info(result) } - return nil, nil + var retTagList []irs.KeyValue + for _, tag := range result.Tags { + retTagList = append(retTagList, irs.KeyValue{ + Key: aws.StringValue(tag.Key), + Value: aws.StringValue(tag.Value), + }) + } + + return retTagList, nil } // describetags func (tagHandler *AwsTagHandler) GetTag(resType irs.RSType, resIID irs.IID, key string) (irs.KeyValue, error) { - cblogger.Info("resTyp : ", resType) - cblogger.Info("resIID : ", resIID) - cblogger.Info("key : ", key) + cblogger.Debugf("Req resTyp:[%s] / resIID:[%s] / key:[%s]", resType, resIID, key) + + if resIID.SystemId == "" || key == "" { + msg := "resIID.SystemId or key is not provided" + cblogger.Error(msg) + return irs.KeyValue{}, errors.New(msg) + } + + input := &ec2.DescribeTagsInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("resource-id"), + Values: []*string{ + aws.String(resIID.SystemId), + }, + }, + { + Name: aws.String("key"), + Values: []*string{ + aws.String(key), + }, + }, + }, + } + + if cblogger.Level.String() == "debug" { + cblogger.Debug(input) + } + + hiscallInfo := GetCallLogScheme(tagHandler.Region, call.TAG, resIID.SystemId, "DescribeTags()") + start := call.Start() + + result, errtag := tagHandler.Client.DescribeTags(input) + if errtag != nil { + cblogger.Errorf("Failed to lookup the [%s] tag key of an [%s] object", key, resIID.NameId) + cblogger.Error(errtag) + LoggingError(hiscallInfo, errtag) + return irs.KeyValue{}, errtag + } + LoggingInfo(hiscallInfo, start) + + if cblogger.Level.String() == "debug" { + cblogger.Info(result) + } + + if len(result.Tags) == 0 { + msg := "tag with key " + key + " not found" + cblogger.Error(msg) + return irs.KeyValue{}, errors.New(msg) + } + + var retTag irs.KeyValue + for _, tag := range result.Tags { + if aws.StringValue(tag.Key) == key { + retTag.Key = aws.StringValue(tag.Key) + retTag.Value = aws.StringValue(tag.Value) + break + } + } - return irs.KeyValue{}, nil + return retTag, nil } -// deletetags func (tagHandler *AwsTagHandler) RemoveTag(resType irs.RSType, resIID irs.IID, key string) (bool, error) { - cblogger.Info("resTyp : ", resType) - cblogger.Info("resIID : ", resIID) - cblogger.Info("key : ", key) + cblogger.Debugf("Req resTyp:[%s] / resIID:[%s] / key:[%s]", resType, resIID, key) + + if resIID.SystemId == "" || key == "" { + msg := "resIID.SystemId or key is not provided" + cblogger.Error(msg) + return false, errors.New(msg) + } + + input := &ec2.DeleteTagsInput{ + Resources: []*string{ + aws.String(resIID.SystemId), + }, + Tags: []*ec2.Tag{ + { + Key: aws.String(key), + }, + }, + } + + if cblogger.Level.String() == "debug" { + cblogger.Debug(input) + } + + hiscallInfo := GetCallLogScheme(tagHandler.Region, call.TAG, resIID.SystemId, "DeleteTags()") + start := call.Start() + + result, errtag := tagHandler.Client.DeleteTags(input) + if errtag != nil { + cblogger.Errorf("Failed to delete [%s] tag key of an [%s] object", key, resIID.NameId) + cblogger.Error(errtag) + LoggingError(hiscallInfo, errtag) + return false, errtag + } + LoggingInfo(hiscallInfo, start) + + if cblogger.Level.String() == "debug" { + cblogger.Info(result) + } - return false, nil + return true, nil } // Find tags by tag key or value @@ -129,5 +233,5 @@ func (tagHandler *AwsTagHandler) RemoveTag(resType irs.RSType, resIID irs.IID, k func (tagHandler *AwsTagHandler) FindTag(resType irs.RSType, keyword string) ([]*irs.TagInfo, error) { cblogger.Info("resTyp : ", resType) cblogger.Info("keyword : ", keyword) - return nil, nil + return nil, errors.New("not yet implemented") } From 95f35ce7f52c9cac0028349e9b6812caa4a3aa17 Mon Sep 17 00:00:00 2001 From: dev4unet Date: Thu, 11 Jul 2024 11:51:53 +0000 Subject: [PATCH 4/4] Implementing the FindTag feature --- .../drivers/aws/main/Test_Resources.go | 12 +- .../drivers/aws/resources/TagHandler.go | 151 +++++++++++++++++- 2 files changed, 159 insertions(+), 4 deletions(-) diff --git a/cloud-control-manager/cloud-driver/drivers/aws/main/Test_Resources.go b/cloud-control-manager/cloud-driver/drivers/aws/main/Test_Resources.go index a1072fbf7..4041d0775 100644 --- a/cloud-control-manager/cloud-driver/drivers/aws/main/Test_Resources.go +++ b/cloud-control-manager/cloud-driver/drivers/aws/main/Test_Resources.go @@ -1791,6 +1791,8 @@ func handleTag() { reqIID := irs.IID{SystemId: "i-02ac1c4ff1d40815c"} reqTag := irs.KeyValue{Key: "tag3", Value: "태그3"} reqKey := "tag3" + reqKey = "" + reqType = irs.ALL for { fmt.Println("TagHandler Management") @@ -1866,7 +1868,15 @@ func handleTag() { } case 5: - + cblogger.Infof("[%s] Tag 찾기 테스트 - Key[%s]", reqType, reqKey) + result, err := handler.FindTag(reqType, reqKey) + if err != nil { + cblogger.Infof("[%s] Tag 검색 실패 : [%s]", reqKey, err) + } else { + cblogger.Infof("[%s] Tag 검색 결과 : [%d]건", reqKey, len(result)) + spew.Dump(result) + cblogger.Infof("Tag 검색 결과 : [%d]건", len(result)) + } } } } diff --git a/cloud-control-manager/cloud-driver/drivers/aws/resources/TagHandler.go b/cloud-control-manager/cloud-driver/drivers/aws/resources/TagHandler.go index 54c0b9742..a5abf02bc 100644 --- a/cloud-control-manager/cloud-driver/drivers/aws/resources/TagHandler.go +++ b/cloud-control-manager/cloud-driver/drivers/aws/resources/TagHandler.go @@ -17,8 +17,10 @@ import ( //"strconv" "errors" + "fmt" "github.com/aws/aws-sdk-go/aws" + //"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ec2" call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" @@ -31,6 +33,36 @@ type AwsTagHandler struct { Client *ec2.EC2 } +// Map of RSType to AWS resource types +var rsTypeToAwsResourceTypeMap = map[irs.RSType]string{ + irs.IMAGE: "image", + irs.VPC: "vpc", + irs.SUBNET: "subnet", + irs.SG: "security-group", + irs.KEY: "key-pair", + irs.VM: "instance", + irs.NLB: "network-load-balancer", + irs.DISK: "volume", + irs.MYIMAGE: "image", + irs.CLUSTER: "cluster", + irs.NODEGROUP: "nodegroup", +} + +// Map of AWS resource types to RSType for response handling +var awsResourceTypeToRSTypeMap = map[string]irs.RSType{ + "image": irs.IMAGE, + "vpc": irs.VPC, + "subnet": irs.SUBNET, + "security-group": irs.SG, + "key-pair": irs.KEY, + "instance": irs.VM, + "network-load-balancer": irs.NLB, + "volume": irs.DISK, + //"image": irs.MYIMAGE, + "cluster": irs.CLUSTER, + "nodegroup": irs.NODEGROUP, +} + func (tagHandler *AwsTagHandler) AddTag(resType irs.RSType, resIID irs.IID, tag irs.KeyValue) (irs.KeyValue, error) { cblogger.Debugf("Req resTyp:[%s] / resIID:[%s] / Tag Key:[%s] / Tag Value:[%s]", resType, resIID, tag.Key, tag.Value) @@ -162,7 +194,9 @@ func (tagHandler *AwsTagHandler) GetTag(resType irs.RSType, resIID irs.IID, key LoggingInfo(hiscallInfo, start) if cblogger.Level.String() == "debug" { + cblogger.Info("---------------------") cblogger.Info(result) + cblogger.Info("---------------------") } if len(result.Tags) == 0 { @@ -231,7 +265,118 @@ func (tagHandler *AwsTagHandler) RemoveTag(resType irs.RSType, resIID irs.IID, k // keyword: The keyword to search for in the tag key or value. // if you want to find all tags, set keyword to "" or "*". func (tagHandler *AwsTagHandler) FindTag(resType irs.RSType, keyword string) ([]*irs.TagInfo, error) { - cblogger.Info("resTyp : ", resType) - cblogger.Info("keyword : ", keyword) - return nil, errors.New("not yet implemented") + cblogger.Debugf("resType : [%s] / keyword : [%s]", resType, keyword) + + var filters []*ec2.Filter + + // Add resource type filter if resType is not ALL + if resType != irs.ALL { + if awsResType, ok := rsTypeToAwsResourceTypeMap[resType]; ok { + filters = append(filters, &ec2.Filter{ + Name: aws.String("resource-type"), + Values: []*string{ + aws.String(awsResType), + }, + }) + } else { + return nil, fmt.Errorf("unsupported resource type: %s", resType) + } + } + + tagInfoMap := make(map[string]*irs.TagInfo) + + // Function to process tags and add them to tagInfoMap + processTags := func(result *ec2.DescribeTagsOutput) { + if cblogger.Level.String() == "debug" { + cblogger.Debug(result) + //cblogger.Debug("=================================") + //spew.Dump(result) + //cblogger.Debug("=================================") + } + + for _, tag := range result.Tags { + resID := aws.StringValue(tag.ResourceId) + + awsResType := aws.StringValue(tag.ResourceType) + rType, exists := awsResourceTypeToRSTypeMap[awsResType] + if !exists { + //@TODO - 변환 실패한 리소스의 경우 UNKNOWN을 만들거나 에러 로그만 찍거나 결정 필요할 듯 + cblogger.Errorf("No RSType matching [%s] found.", awsResType) + + rType = irs.RSType(awsResType) // Use the raw AWS resource type if not mapped + } + + if _, exists := tagInfoMap[resID]; !exists { + tagInfoMap[resID] = &irs.TagInfo{ + ResType: rType, + ResIId: irs.IID{ + SystemId: resID, + }, + } + } + tagInfoMap[resID].TagList = append(tagInfoMap[resID].TagList, irs.KeyValue{ + Key: aws.StringValue(tag.Key), + Value: aws.StringValue(tag.Value), + }) + } + } + + // Search by tag-key if keyword is not empty or "*" + if keyword != "" && keyword != "*" { + keyInput := &ec2.DescribeTagsInput{ + Filters: append(filters, &ec2.Filter{ + Name: aws.String("tag-key"), + Values: []*string{ + aws.String(keyword), + }, + }), + } + + if cblogger.Level.String() == "debug" { + cblogger.Debug(keyInput) + } + + keyResult, err := tagHandler.Client.DescribeTags(keyInput) + if err != nil { + return nil, fmt.Errorf("failed to describe tags by key: %w", err) + } + processTags(keyResult) + + valueInput := &ec2.DescribeTagsInput{ + Filters: append(filters, &ec2.Filter{ + Name: aws.String("tag-value"), + Values: []*string{ + aws.String(keyword), + }, + }), + } + + if cblogger.Level.String() == "debug" { + cblogger.Debug(valueInput) + } + + valueResult, err := tagHandler.Client.DescribeTags(valueInput) + if err != nil { + return nil, fmt.Errorf("failed to describe tags by value: %w", err) + } + processTags(valueResult) + } else { + // Search all tags if keyword is empty or "*" + input := &ec2.DescribeTagsInput{ + Filters: filters, + } + + result, err := tagHandler.Client.DescribeTags(input) + if err != nil { + return nil, fmt.Errorf("failed to describe tags: %w", err) + } + processTags(result) + } + + var tagInfos []*irs.TagInfo + for _, tagInfo := range tagInfoMap { + tagInfos = append(tagInfos, tagInfo) + } + + return tagInfos, nil }