scripts/compareVersion/compare.go (337 lines of code) (raw):

package main import ( "encoding/json" "fmt" "go/ast" "go/parser" "go/token" "os" "path/filepath" "strings" ) type Method struct { Name string Request string Response string } type Result struct { Name string Exist bool Requests []string Responses []string } // Rpc 结构体定义 type Rpc struct { Name string // Rpc名称 State string // 风险、安全、无 } // Version 结构体定义 type Version struct { Version string // 版本 Rpcs []Rpc // 所有的Rpc } func getMethodInfo(field *ast.Field) *Method { var method *Method = &Method{} method.Name = field.Names[0].Name var lock1 bool var lock2 bool if tp, ok := field.Type.(*ast.FuncType); ok { lists := tp.Params.List if startExpr, ok := lists[1].Type.(*ast.StarExpr); ok { if request, ok := startExpr.X.(*ast.Ident); ok { method.Request = request.Name lock1 = true } } results := tp.Results.List if startExpr, ok := results[0].Type.(*ast.StarExpr); ok { if response, ok := startExpr.X.(*ast.Ident); ok { method.Response = response.Name lock2 = true } } if lock1 && lock2 { return method } } return nil } func getMethodsInfo(interfacetype *ast.InterfaceType) []*Method { list := interfacetype.Methods.List methods := make([]*Method, 0) for _, field := range list { method := getMethodInfo(field) if method != nil { methods = append(methods, method) } } return methods } func GetAllRpcAndRequest() ([]*Method, map[string][]string) { filename := "terraform/internal/tfplugin5/tfplugin5.pb.go" fs := token.NewFileSet() node, err := parser.ParseFile(fs, filename, nil, parser.ParseComments) if err != nil { fmt.Println(err) return nil, nil } //扫描得到所有的RPC调用 var methods []*Method ast.Inspect(node, func(n ast.Node) bool { switch x := n.(type) { case *ast.TypeSpec: if x.Name.Name == "ProviderServer" { if interfaceType, ok := x.Type.(*ast.InterfaceType); ok { methods = getMethodsInfo(interfaceType) // for _, m := range methods { // fmt.Println(m) // } } } } return true }) //扫描得到所有的Requests 和 Response结构体 Params := make(map[string][]string) ast.Inspect(node, func(n ast.Node) bool { structType, ok := n.(*ast.TypeSpec) if !ok { return true } // Check if the name ends with "Response" if strings.HasSuffix(structType.Name.Name, "Response") || strings.HasSuffix(structType.Name.Name, "Request") { if _, ok := Params[structType.Name.Name]; !ok { Params[structType.Name.Name] = make([]string, 0) } // Check the struct type if structDecl, ok := structType.Type.(*ast.StructType); ok { for _, field := range structDecl.Fields.List { // Check if the field has a tag if field.Tag != nil { for _, fieldName := range field.Names { Params[structType.Name.Name] = append(Params[structType.Name.Name], fieldName.Name) } } } } } return true }) return methods, Params } func GetAllGrpcProvider() ([]*Method, map[string][]string) { //filename := "terraform-provider-apsarastack/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/grpc_provider.go" filename := "../../vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server/server.go" // your directory containing Go source files fs := token.NewFileSet() node, err := parser.ParseFile(fs, filename, nil, parser.ParseComments) if err != nil { fmt.Println(err) return nil, nil } var methods []*Method ast.Inspect(node, func(n ast.Node) bool { funcDecl, ok := n.(*ast.FuncDecl) if !ok { return true } // Check if the function has exactly two parameters and two results. if len(funcDecl.Type.Params.List) != 2 || len(funcDecl.Type.Results.List) != 2 { return true } // Extract second parameter and first result. secondParam := funcDecl.Type.Params.List[1] firstResult := funcDecl.Type.Results.List[0] // Ensure second parameter is *ast.StarExpr and X is *ast.SelectorExpr. secondParamType, ok := secondParam.Type.(*ast.StarExpr) if !ok { return true } secondParamSelector, ok := secondParamType.X.(*ast.SelectorExpr) if !ok { return true } // Ensure first result is *ast.StarExpr and X is *ast.SelectorExpr. firstResultType, ok := firstResult.Type.(*ast.StarExpr) if !ok { return true } firstResultSelector, ok := firstResultType.X.(*ast.SelectorExpr) if !ok { return true } // Print relevant information. var method = new(Method) method.Name = funcDecl.Name.Name method.Request = secondParamSelector.Sel.Name method.Response = firstResultSelector.Sel.Name methods = append(methods, method) return true }) // for _, m := range methods { // fmt.Println(m) // } //扫描provider中的所有request 和 response dirname := "../../vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/" // your directory containing Go source files Params := make(map[string][]string) filepath.Walk(dirname, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() && strings.HasSuffix(path, ".go") && !strings.HasSuffix(path, "test.go") { // process only Go files filename = path fs = token.NewFileSet() node, err = parser.ParseFile(fs, filename, nil, parser.ParseComments) if err != nil { fmt.Println(err) return nil } //扫描得到所有的Requests 和 Response结构体 ast.Inspect(node, func(n ast.Node) bool { structType, ok := n.(*ast.TypeSpec) if !ok { return true } // Check if the name ends with "Response" if strings.HasSuffix(structType.Name.Name, "Response") || strings.HasSuffix(structType.Name.Name, "Request") { if _, ok := Params[structType.Name.Name]; !ok { Params[structType.Name.Name] = make([]string, 0) } // Check the struct type if structDecl, ok := structType.Type.(*ast.StructType); ok { for _, field := range structDecl.Fields.List { // Check if the field has a tag for _, fieldName := range field.Names { Params[structType.Name.Name] = append(Params[structType.Name.Name], fieldName.Name) } } } } return true }) } return nil }) return methods, Params } func Tojson(v1 Version) { // 创建或打开一个JSON文件 file, err := os.OpenFile("versions.json", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { fmt.Println(err) return } defer file.Close() // 创建一个新的Version实例 // 将Version实例追加到JSON文件中 enc := json.NewEncoder(file) err = enc.Encode(v1) if err != nil { fmt.Println(err) return } } func GetAllRpcAndRequest2() ([]*Method, map[string][]string) { filename := "opentofu/internal/tfplugin5/tfplugin5.pb.go" fs := token.NewFileSet() node, err := parser.ParseFile(fs, filename, nil, parser.ParseComments) if err != nil { fmt.Println(err) return nil, nil } //扫描得到所有的RPC调用 var methods []*Method ast.Inspect(node, func(n ast.Node) bool { switch x := n.(type) { case *ast.TypeSpec: if x.Name.Name == "ProviderServer" { if interfaceType, ok := x.Type.(*ast.InterfaceType); ok { methods = getMethodsInfo(interfaceType) // for _, m := range methods { // fmt.Println(m) // } } } } return true }) //扫描得到所有的Requests 和 Response结构体 Params := make(map[string][]string) ast.Inspect(node, func(n ast.Node) bool { structType, ok := n.(*ast.TypeSpec) if !ok { return true } // Check if the name ends with "Response" if strings.HasSuffix(structType.Name.Name, "Response") || strings.HasSuffix(structType.Name.Name, "Request") { if _, ok := Params[structType.Name.Name]; !ok { Params[structType.Name.Name] = make([]string, 0) } // Check the struct type if structDecl, ok := structType.Type.(*ast.StructType); ok { for _, field := range structDecl.Fields.List { // Check if the field has a tag if field.Tag != nil { for _, fieldName := range field.Names { Params[structType.Name.Name] = append(Params[structType.Name.Name], fieldName.Name) } } } } } return true }) return methods, Params } func main() { if len(os.Args) < 2 { fmt.Println("Please provide at least one argument.") return } var coreMethods []*Method var coreParms map[string][]string var version Version version.Version = os.Args[1] if strings.HasPrefix(os.Args[1], "terraform") { coreMethods, coreParms = GetAllRpcAndRequest() } else if strings.HasPrefix(os.Args[1], "opentofu") { coreMethods, coreParms = GetAllRpcAndRequest2() } else { fmt.Println("input error") return } providerMethods, providerParms := GetAllGrpcProvider() methodMap := make(map[string]*Method) for _, m := range providerMethods { methodMap[m.Name] = m } okRpcs := make([]*Result, 0) notExistRpcs := make([]*Result, 0) riskRpcs := make([]*Result, 0) for _, m := range coreMethods { result := new(Result) result.Name = m.Name if _, ok := methodMap[m.Name]; !ok { result.Exist = false notExistRpcs = append(notExistRpcs, result) } else { result.Exist = true cm := methodMap[m.Name] requestMap := make(map[string]interface{}) responseMap := make(map[string]interface{}) for _, s := range providerParms[cm.Request] { requestMap[s] = struct{}{} } for _, s := range providerParms[cm.Response] { responseMap[s] = struct{}{} } for _, s := range coreParms[m.Request] { if _, ok := requestMap[s]; !ok { result.Requests = append(result.Requests, s) } } for _, s := range coreParms[m.Response] { if _, ok := responseMap[s]; !ok { result.Responses = append(result.Responses, s) } } if len(result.Requests) == 0 && len(result.Responses) == 0 { okRpcs = append(okRpcs, result) } else { riskRpcs = append(riskRpcs, result) } } } for _, result := range okRpcs { fmt.Println("无风险RPC:", result.Name) version.Rpcs = append(version.Rpcs, Rpc{Name: result.Name, State: ":white_check_mark:"}) } fmt.Println() for _, result := range notExistRpcs { fmt.Println("未实现RPC:", result.Name) version.Rpcs = append(version.Rpcs, Rpc{Name: result.Name, State: ":no_entry_sign:"}) } fmt.Println() for _, result := range riskRpcs { fmt.Println("风险RPC:", result.Name) version.Rpcs = append(version.Rpcs, Rpc{Name: result.Name, State: ":x:"}) if len(result.Requests) != 0 { fmt.Println("缺失入参:") for _, request := range result.Requests { fmt.Println(request) } } if len(result.Responses) != 0 { fmt.Println("缺失出餐:") for _, response := range result.Responses { fmt.Println(response) } } fmt.Println() } Tojson(version) }