From 7ef6c2756d2e5e9124b81a066cc37d768a4ddd2c Mon Sep 17 00:00:00 2001 From: ioito Date: Wed, 16 Oct 2019 11:10:28 +0800 Subject: [PATCH 01/25] =?UTF-8?q?optimized:=20=E5=AF=BC=E5=87=BAGetPorts?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- util/secrules/secrules.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/secrules/secrules.go b/util/secrules/secrules.go index 86b9649..b5f4239 100644 --- a/util/secrules/secrules.go +++ b/util/secrules/secrules.go @@ -362,7 +362,7 @@ func (rule *SecurityRule) ValidateRule() error { return nil } -func (rule *SecurityRule) getPort() string { +func (rule *SecurityRule) GetPortsString() string { if rule.PortStart > 0 && rule.PortEnd > 0 { if rule.PortStart < rule.PortEnd { return fmt.Sprintf("%d-%d", rule.PortStart, rule.PortEnd) @@ -398,7 +398,7 @@ func (rule *SecurityRule) String() (result string) { s = append(s, rule.Protocol) if rule.Protocol == PROTO_TCP || rule.Protocol == PROTO_UDP { - port := rule.getPort() + port := rule.GetPortsString() if len(port) > 0 { s = append(s, port) } From 1453c5ebc426deb51f99b1d7e34ba7a5496c6d73 Mon Sep 17 00:00:00 2001 From: ioito Date: Wed, 23 Oct 2019 17:39:24 +0800 Subject: [PATCH 02/25] =?UTF-8?q?fix:=20=E9=81=BF=E5=85=8D=E5=86=99log?= =?UTF-8?q?=E6=97=B6=E7=9B=B4=E6=8E=A5=E4=BC=A0err=E6=97=B6=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=AE=B0=E5=BD=95=E4=B8=BA{}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- util/stringutils/stringutils.go | 2 ++ util/stringutils/stringutils_test.go | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/util/stringutils/stringutils.go b/util/stringutils/stringutils.go index df65e2f..4fccc70 100644 --- a/util/stringutils/stringutils.go +++ b/util/stringutils/stringutils.go @@ -64,6 +64,8 @@ func Interface2String(val interface{}) string { return fmt.Sprintf("%f", vval) case bool: return fmt.Sprintf("%v", vval) + case error: + return vval.Error() case time.Time: return timeutils.FullIsoTime(vval) case fmt.Stringer: diff --git a/util/stringutils/stringutils_test.go b/util/stringutils/stringutils_test.go index 31f5412..ab6f01e 100644 --- a/util/stringutils/stringutils_test.go +++ b/util/stringutils/stringutils_test.go @@ -15,14 +15,24 @@ package stringutils import ( + "fmt" "testing" ) func TestStringUtils(t *testing.T) { t.Logf("%s", UUID4()) - t.Logf("%s", Interface2String(nil)) - t.Logf("%s", Interface2String(2)) - t.Logf("%s", Interface2String("test string")) + if Interface2String(nil) != "" { + t.Errorf("Interface2String(nil) should be empty") + } + if Interface2String(2) != "2" { + t.Errorf(`Interface2String(2) should be "2"`) + } + if Interface2String("test string") != "test string" { + t.Errorf(`Interface2String("test string") should be "test string"`) + } + if Interface2String(fmt.Errorf("test error")) != "test error" { + t.Errorf(`Interface2String(fmt.Errorf("test error")) should be "test error"`) + } type TestStruct struct { Name string Age int From d832772313945dbd6372496dc1c5d154e6e90250 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 8 Nov 2019 10:42:28 +0800 Subject: [PATCH 03/25] prometheus: use promhttp.Handler (#33) The old method was deprecated in upstream commit 761a2ff07c763475bff727730e823a8f4d501f36 ("Remove all deprecated features"). It's a commit for preparation of module version v1.0.0 --- util/prometheus/gin_handler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/prometheus/gin_handler.go b/util/prometheus/gin_handler.go index b9c2abb..f6fbe4f 100644 --- a/util/prometheus/gin_handler.go +++ b/util/prometheus/gin_handler.go @@ -16,9 +16,9 @@ package prometheus import ( "github.com/gin-gonic/gin" - "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" ) func InstallHandler(r *gin.Engine) { - r.Any("/metrics", gin.WrapH(prometheus.Handler())) + r.Any("/metrics", gin.WrapH(promhttp.Handler())) } From 909a1322f6bdbe89c8db88f319f7f844699b5971 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Mon, 11 Nov 2019 00:47:08 +0800 Subject: [PATCH 04/25] feature: add errors consts --- errors/consts.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 errors/consts.go diff --git a/errors/consts.go b/errors/consts.go new file mode 100644 index 0000000..02ca1f9 --- /dev/null +++ b/errors/consts.go @@ -0,0 +1,14 @@ +package errors + +const ( + ErrServer = Error("ServerError") + ErrClient = Error("ClientError") + ErrUnclassified = Error("UnclassifiedError") + + ErrNotFound = Error("NotFoundError") + ErrDuplicateId = Error("DuplicateIdError") + ErrInvalidStatus = Error("InvalidStatusError") + ErrTimeout = Error("TimeoutError") + ErrNotImplemented = Error("NotImplementedError") + ErrNotSupported = Error("NotSupportedError") +) From 1428145808e2ac8cc2e0bc5f9d63e7027a7a2012 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Mon, 11 Nov 2019 19:06:11 +0800 Subject: [PATCH 05/25] feature: implement cause of aggregate error --- errors/aggregate.go | 13 +++++++++++-- errors/aggregate_test.go | 12 ++++++++++++ errors/consts.go | 2 ++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/errors/aggregate.go b/errors/aggregate.go index 4be924e..42163b1 100644 --- a/errors/aggregate.go +++ b/errors/aggregate.go @@ -15,7 +15,6 @@ package errors import ( - "errors" "fmt" ) @@ -78,6 +77,16 @@ func (agg aggregate) Errors() []error { return []error(agg) } +func (agg aggregate) Cause() error { + if len(agg) == 0 { + return nil + } + if len(agg) == 1 { + return Cause(agg[0]) + } + return ErrAggregate +} + // Matcher is used to match errors. Returns true if the error matches. type Matcher func(error) bool @@ -196,4 +205,4 @@ func AggregateGoroutines(funcs ...func() error) Aggregate { } // ErrPreconditionViolated is returned when the precondition is violated -var ErrPreconditionViolated = errors.New("precondition is violated") +// var ErrPreconditionViolated = errors.New("precondition is violated") diff --git a/errors/aggregate_test.go b/errors/aggregate_test.go index 71076b1..8c9238d 100644 --- a/errors/aggregate_test.go +++ b/errors/aggregate_test.go @@ -107,6 +107,9 @@ func TestSingularAggregate(t *testing.T) { if s := agg.Errors()[0].Error(); s != "err" { t.Errorf("expected 'err', got %q", s) } + if s := Cause(agg).Error(); s != "err" { + t.Errorf("Cause(agg) expected 'err', got %q", s) + } err = agg.(error) if err == nil { @@ -115,6 +118,9 @@ func TestSingularAggregate(t *testing.T) { if s := err.Error(); s != "err" { t.Errorf("expected 'err', got %q", s) } + if s := Cause(err).Error(); s != "err" { + t.Errorf("Cause(err) expected 'err', got %q", s) + } } func TestPluralAggregate(t *testing.T) { @@ -135,6 +141,9 @@ func TestPluralAggregate(t *testing.T) { if s := agg.Errors()[0].Error(); s != "abc" { t.Errorf("expected '[abc, 123]', got %q", s) } + if s := Cause(agg); s != ErrAggregate { + t.Errorf("Cause(agg) expect %q, got %q", ErrAggregate, s) + } err = agg.(error) if err == nil { @@ -143,6 +152,9 @@ func TestPluralAggregate(t *testing.T) { if s := err.Error(); s != "[abc, 123]" { t.Errorf("expected '[abc, 123]', got %q", s) } + if s := Cause(err); s != ErrAggregate { + t.Errorf("Cause(err) expect %q, got %q", ErrAggregate, s) + } } func TestFilterOut(t *testing.T) { diff --git a/errors/consts.go b/errors/consts.go index 02ca1f9..4ac451d 100644 --- a/errors/consts.go +++ b/errors/consts.go @@ -11,4 +11,6 @@ const ( ErrTimeout = Error("TimeoutError") ErrNotImplemented = Error("NotImplementedError") ErrNotSupported = Error("NotSupportedError") + + ErrAggregate = Error("AggregateError") ) From a1b142687fa102a5c2c7199a2d1e35ec224d6e5a Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Wed, 13 Nov 2019 22:01:09 +0800 Subject: [PATCH 06/25] fix: optimized rules allowList count --- util/netutils/netutils.go | 13 +++++ util/secrules/secruleset.go | 81 +++++++++++++++++++++++++++++++- util/secrules/secruleset_test.go | 23 +++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/util/netutils/netutils.go b/util/netutils/netutils.go index 24a419d..609fe75 100644 --- a/util/netutils/netutils.go +++ b/util/netutils/netutils.go @@ -206,6 +206,19 @@ func (ar IPV4AddrRange) EndIp() IPV4Addr { return ar.end } +func (ar IPV4AddrRange) Merge(ar2 IPV4AddrRange) (*IPV4AddrRange, bool) { + if ar.IsOverlap(ar2) || ar.end+1 == ar2.start || ar2.end+1 == ar.start { + if ar2.start < ar.start { + ar.start = ar2.start + } + if ar2.end > ar.end { + ar.end = ar2.end + } + return &ar, true + } + return nil, false +} + func (ar IPV4AddrRange) IsOverlap(ar2 IPV4AddrRange) bool { if ar.start > ar2.end || ar.end < ar2.start { return false diff --git a/util/secrules/secruleset.go b/util/secrules/secruleset.go index ba65aaf..511591e 100644 --- a/util/secrules/secruleset.go +++ b/util/secrules/secruleset.go @@ -16,7 +16,10 @@ package secrules import ( "bytes" + "net" "sort" + + "yunion.io/x/pkg/util/netutils" ) type SecurityRuleSet []SecurityRule @@ -55,6 +58,12 @@ func (srs SecurityRuleSet) String() string { return buf.String() } +func (srs SecurityRuleSet) Equals(srs1 SecurityRuleSet) bool { + sort.Sort(srs) + sort.Sort(srs1) + return srs.equals(srs1) +} + func (srs SecurityRuleSet) equals(srs1 SecurityRuleSet) bool { if len(srs) != len(srs1) { return false @@ -191,5 +200,75 @@ func (srs SecurityRuleSet) collapse() SecurityRuleSet { // save that contains, intersects } } - return srs1 + + //merge cidr + sort.Slice(srs1, func(i, j int) bool { + sr0 := &srs1[i] + sr1 := &srs1[j] + if sr0.Protocol != sr1.Protocol { + return sr0.Protocol < sr1.Protocol + } + + if sr0.GetPortsString() != sr1.GetPortsString() { + return sr0.GetPortsString() < sr1.GetPortsString() + } + range0 := netutils.NewIPV4AddrRangeFromIPNet(sr0.IPNet) + range1 := netutils.NewIPV4AddrRangeFromIPNet(sr1.IPNet) + if range0.StartIp() != range1.StartIp() { + return range0.StartIp() < range1.StartIp() + } + if range0.EndIp() != range1.EndIp() { + return range0.EndIp() < range1.EndIp() + } + return sr0.Priority < sr1.Priority + }) + + // 将端口和协议相同的规则归类 + needMerged := []SecurityRuleSet{} + for i, j := 0, 0; i < len(srs1); i++ { + if i == 0 { + needMerged = append(needMerged, SecurityRuleSet{srs1[i]}) + continue + } + last := needMerged[j][len(needMerged[j])-1] + if last.Protocol == srs1[i].Protocol && last.GetPortsString() == srs1[i].GetPortsString() { + needMerged[j] = append(needMerged[j], srs1[i]) + continue + } + needMerged = append(needMerged, SecurityRuleSet{srs1[i]}) + j++ + } + + result := SecurityRuleSet{} + for _, srs := range needMerged { + result = append(result, srs.mergeNet()...) + } + return result +} + +func (srs SecurityRuleSet) mergeNet() SecurityRuleSet { + result := SecurityRuleSet{} + ranges := []netutils.IPV4AddrRange{} + for i := 0; i < len(srs); i++ { + if i == 0 { + ranges = append(ranges, netutils.NewIPV4AddrRangeFromIPNet(srs[i].IPNet)) + continue + } + preNet := ranges[len(ranges)-1] + nextNet := netutils.NewIPV4AddrRangeFromIPNet(srs[i].IPNet) + if net, ok := preNet.Merge(nextNet); ok { + ranges[len(ranges)-1] = *net + continue + } + ranges = append(ranges, nextNet) + } + nets := []*net.IPNet{} + for _, addr := range ranges { + nets = append(nets, addr.ToIPNets()...) + } + for _, net := range nets { + srs[0].IPNet = net + result = append(result, srs[0]) + } + return result } diff --git a/util/secrules/secruleset_test.go b/util/secrules/secruleset_test.go index 55ea5f2..f79063b 100644 --- a/util/secrules/secruleset_test.go +++ b/util/secrules/secruleset_test.go @@ -15,13 +15,36 @@ package secrules import ( + "sort" "testing" + "yunion.io/x/log" "yunion.io/x/pkg/util/netutils" ) func TestSecRuleSet_AllowList(t *testing.T) { + srs0 := SecurityRuleSet{ + *MustParseSecurityRule("out:deny 192.168.222.2 tcp 3389"), + *MustParseSecurityRule("out:allow any"), + } + rules := srs0.AllowList() + a, _ := netutils.NewIPV4Addr("192.168.222.2") + for _, rule := range rules { + switch rule.Protocol { + case PROTO_TCP: + ar := netutils.NewIPV4AddrRangeFromIPNet(rule.IPNet) + if ar.Contains(a) && rule.PortStart <= 3389 && rule.PortEnd >= 3389 { + log.Fatalf("allow list should not contain 192.168.222.2 tcp 3389") + } + case PROTO_ICMP, PROTO_UDP: + if rule.IPNet.String() != "0.0.0.0/0" { + log.Fatalf("proto %s shoud be merged", rule.Protocol) + } + } + } dieIf := func(t *testing.T, srs0, srs1 SecurityRuleSet) { + sort.Sort(srs0) + sort.Sort(srs1) if !srs0.equals(srs1) { t.Fatalf("not equal:\n%s\n%s", srs0, srs1) } From 90aa48847b4502e5d51dc8fab47aac09630e7324 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 18 Nov 2019 01:28:46 +0000 Subject: [PATCH 07/25] secrules: use sub-tests --- util/secrules/secruleset_test.go | 66 ++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/util/secrules/secruleset_test.go b/util/secrules/secruleset_test.go index f79063b..c637e98 100644 --- a/util/secrules/secruleset_test.go +++ b/util/secrules/secruleset_test.go @@ -18,30 +18,10 @@ import ( "sort" "testing" - "yunion.io/x/log" "yunion.io/x/pkg/util/netutils" ) func TestSecRuleSet_AllowList(t *testing.T) { - srs0 := SecurityRuleSet{ - *MustParseSecurityRule("out:deny 192.168.222.2 tcp 3389"), - *MustParseSecurityRule("out:allow any"), - } - rules := srs0.AllowList() - a, _ := netutils.NewIPV4Addr("192.168.222.2") - for _, rule := range rules { - switch rule.Protocol { - case PROTO_TCP: - ar := netutils.NewIPV4AddrRangeFromIPNet(rule.IPNet) - if ar.Contains(a) && rule.PortStart <= 3389 && rule.PortEnd >= 3389 { - log.Fatalf("allow list should not contain 192.168.222.2 tcp 3389") - } - case PROTO_ICMP, PROTO_UDP: - if rule.IPNet.String() != "0.0.0.0/0" { - log.Fatalf("proto %s shoud be merged", rule.Protocol) - } - } - } dieIf := func(t *testing.T, srs0, srs1 SecurityRuleSet) { sort.Sort(srs0) sort.Sort(srs1) @@ -189,4 +169,50 @@ func TestSecRuleSet_AllowList(t *testing.T) { } dieIf(t, srs1, srs1_) }) + t.Run("cidr: merge", func(t *testing.T) { + srs0 := SecurityRuleSet{ + *MustParseSecurityRule("out:deny 192.168.222.2 tcp 3389"), + *MustParseSecurityRule("out:allow any"), + } + srs1 := srs0.AllowList() + srs1_ := SecurityRuleSet{ + *MustParseSecurityRule("out:allow 0.0.0.0/1 tcp 1-65535"), + *MustParseSecurityRule("out:allow 128.0.0.0/2 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.0.0.0/9 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.128.0.0/11 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.160.0.0/13 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.0.0/17 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.128.0/18 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.192.0/20 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.208.0/21 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.216.0/22 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.220.0/23 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.222.0/31 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.222.128/25 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.222.16/28 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.222.2 tcp 1-3388"), + *MustParseSecurityRule("out:allow 192.168.222.2 tcp 3390-65535"), + *MustParseSecurityRule("out:allow 192.168.222.3 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.222.32/27 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.222.4/30 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.222.64/26 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.222.8/29 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.223.0/24 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.224.0/19 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.169.0.0/16 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.170.0.0/15 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.172.0.0/14 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.176.0.0/12 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.192.0.0/10 tcp 1-65535"), + *MustParseSecurityRule("out:allow 193.0.0.0/8 tcp 1-65535"), + *MustParseSecurityRule("out:allow 194.0.0.0/7 tcp 1-65535"), + *MustParseSecurityRule("out:allow 196.0.0.0/6 tcp 1-65535"), + *MustParseSecurityRule("out:allow 200.0.0.0/5 tcp 1-65535"), + *MustParseSecurityRule("out:allow 208.0.0.0/4 tcp 1-65535"), + *MustParseSecurityRule("out:allow 224.0.0.0/3 tcp 1-65535"), + *MustParseSecurityRule("out:allow icmp"), + *MustParseSecurityRule("out:allow udp 1-65535"), + } + dieIf(t, srs1, srs1_) + }) } From 987750a7f11239e6b622fe177f50fb85599a3494 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 18 Nov 2019 01:33:45 +0000 Subject: [PATCH 08/25] secrules: eliminate port range 1-65535 --- util/secrules/secruleset.go | 7 ++++ util/secrules/secruleset_test.go | 68 ++++++++++++++++---------------- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/util/secrules/secruleset.go b/util/secrules/secruleset.go index 511591e..ba4a124 100644 --- a/util/secrules/secruleset.go +++ b/util/secrules/secruleset.go @@ -200,6 +200,13 @@ func (srs SecurityRuleSet) collapse() SecurityRuleSet { // save that contains, intersects } } + for i := range srs1 { + sr := &srs1[i] + if sr.PortStart <= 1 && sr.PortEnd >= 65535 { + sr.PortStart = -1 + sr.PortEnd = -1 + } + } //merge cidr sort.Slice(srs1, func(i, j int) bool { diff --git a/util/secrules/secruleset_test.go b/util/secrules/secruleset_test.go index c637e98..ca45341 100644 --- a/util/secrules/secruleset_test.go +++ b/util/secrules/secruleset_test.go @@ -113,7 +113,7 @@ func TestSecRuleSet_AllowList(t *testing.T) { srs1_ := SecurityRuleSet{ *MustParseSecurityRule("in:allow 192.168.2.0/23 icmp"), *MustParseSecurityRule("in:allow 192.168.2.0/23 tcp 1025-65535"), - *MustParseSecurityRule("in:allow 192.168.2.0/23 udp 1-65535"), + *MustParseSecurityRule("in:allow 192.168.2.0/23 udp"), } dieIf(t, srs1, srs1_) }) @@ -176,42 +176,42 @@ func TestSecRuleSet_AllowList(t *testing.T) { } srs1 := srs0.AllowList() srs1_ := SecurityRuleSet{ - *MustParseSecurityRule("out:allow 0.0.0.0/1 tcp 1-65535"), - *MustParseSecurityRule("out:allow 128.0.0.0/2 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.0.0.0/9 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.128.0.0/11 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.160.0.0/13 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.0.0/17 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.128.0/18 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.192.0/20 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.208.0/21 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.216.0/22 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.220.0/23 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.222.0/31 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.222.128/25 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.222.16/28 tcp 1-65535"), + *MustParseSecurityRule("out:allow 0.0.0.0/1 tcp"), + *MustParseSecurityRule("out:allow 128.0.0.0/2 tcp"), + *MustParseSecurityRule("out:allow 192.0.0.0/9 tcp"), + *MustParseSecurityRule("out:allow 192.128.0.0/11 tcp"), + *MustParseSecurityRule("out:allow 192.160.0.0/13 tcp"), + *MustParseSecurityRule("out:allow 192.168.0.0/17 tcp"), + *MustParseSecurityRule("out:allow 192.168.128.0/18 tcp"), + *MustParseSecurityRule("out:allow 192.168.192.0/20 tcp"), + *MustParseSecurityRule("out:allow 192.168.208.0/21 tcp"), + *MustParseSecurityRule("out:allow 192.168.216.0/22 tcp"), + *MustParseSecurityRule("out:allow 192.168.220.0/23 tcp"), + *MustParseSecurityRule("out:allow 192.168.222.0/31 tcp"), + *MustParseSecurityRule("out:allow 192.168.222.128/25 tcp"), + *MustParseSecurityRule("out:allow 192.168.222.16/28 tcp"), *MustParseSecurityRule("out:allow 192.168.222.2 tcp 1-3388"), *MustParseSecurityRule("out:allow 192.168.222.2 tcp 3390-65535"), - *MustParseSecurityRule("out:allow 192.168.222.3 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.222.32/27 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.222.4/30 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.222.64/26 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.222.8/29 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.223.0/24 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.168.224.0/19 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.169.0.0/16 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.170.0.0/15 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.172.0.0/14 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.176.0.0/12 tcp 1-65535"), - *MustParseSecurityRule("out:allow 192.192.0.0/10 tcp 1-65535"), - *MustParseSecurityRule("out:allow 193.0.0.0/8 tcp 1-65535"), - *MustParseSecurityRule("out:allow 194.0.0.0/7 tcp 1-65535"), - *MustParseSecurityRule("out:allow 196.0.0.0/6 tcp 1-65535"), - *MustParseSecurityRule("out:allow 200.0.0.0/5 tcp 1-65535"), - *MustParseSecurityRule("out:allow 208.0.0.0/4 tcp 1-65535"), - *MustParseSecurityRule("out:allow 224.0.0.0/3 tcp 1-65535"), + *MustParseSecurityRule("out:allow 192.168.222.3 tcp"), + *MustParseSecurityRule("out:allow 192.168.222.32/27 tcp"), + *MustParseSecurityRule("out:allow 192.168.222.4/30 tcp"), + *MustParseSecurityRule("out:allow 192.168.222.64/26 tcp"), + *MustParseSecurityRule("out:allow 192.168.222.8/29 tcp"), + *MustParseSecurityRule("out:allow 192.168.223.0/24 tcp"), + *MustParseSecurityRule("out:allow 192.168.224.0/19 tcp"), + *MustParseSecurityRule("out:allow 192.169.0.0/16 tcp"), + *MustParseSecurityRule("out:allow 192.170.0.0/15 tcp"), + *MustParseSecurityRule("out:allow 192.172.0.0/14 tcp"), + *MustParseSecurityRule("out:allow 192.176.0.0/12 tcp"), + *MustParseSecurityRule("out:allow 192.192.0.0/10 tcp"), + *MustParseSecurityRule("out:allow 193.0.0.0/8 tcp"), + *MustParseSecurityRule("out:allow 194.0.0.0/7 tcp"), + *MustParseSecurityRule("out:allow 196.0.0.0/6 tcp"), + *MustParseSecurityRule("out:allow 200.0.0.0/5 tcp"), + *MustParseSecurityRule("out:allow 208.0.0.0/4 tcp"), + *MustParseSecurityRule("out:allow 224.0.0.0/3 tcp"), *MustParseSecurityRule("out:allow icmp"), - *MustParseSecurityRule("out:allow udp 1-65535"), + *MustParseSecurityRule("out:allow udp"), } dieIf(t, srs1, srs1_) }) From 5513f23a8f790c53ce6b628456bab5d425879647 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Tue, 19 Nov 2019 22:32:01 +0800 Subject: [PATCH 09/25] feature: filterclause support multiple params. equivalent to the union of multiple single param --- util/filterclause/filterclause.go | 36 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/util/filterclause/filterclause.go b/util/filterclause/filterclause.go index 5fddf4a..2365565 100644 --- a/util/filterclause/filterclause.go +++ b/util/filterclause/filterclause.go @@ -46,6 +46,20 @@ func (jfc *SJointFilterClause) GetJointModelName() string { return jfc.JointModel[:len(jfc.JointModel)-1] } +func condFunc(field sqlchemy.IQueryField, params []string, cond func(field sqlchemy.IQueryField, val string) sqlchemy.ICondition) sqlchemy.ICondition { + if len(params) == 1 { + return cond(field, params[0]) + } else if len(params) > 1 { + conds := make([]sqlchemy.ICondition, len(params)) + for i := range params { + conds[i] = cond(field, params[i]) + } + return sqlchemy.OR(conds...) + } else { + return nil + } +} + func (fc *SFilterClause) QueryCondition(q *sqlchemy.SQuery) sqlchemy.ICondition { field := q.Field(fc.field) if field == nil { @@ -78,25 +92,17 @@ func (fc *SFilterClause) QueryCondition(q *sqlchemy.SQuery) sqlchemy.ICondition return sqlchemy.LT(field, fc.params[0]) } case "like": - if len(fc.params) == 1 { - return sqlchemy.Like(field, fc.params[0]) - } + return condFunc(field, fc.params, sqlchemy.Like) case "contains": - if len(fc.params) == 1 { - return sqlchemy.Contains(field, fc.params[0]) - } + return condFunc(field, fc.params, sqlchemy.Contains) case "startswith": - if len(fc.params) == 1 { - return sqlchemy.Startswith(field, fc.params[0]) - } + return condFunc(field, fc.params, sqlchemy.Startswith) case "endswith": - if len(fc.params) == 1 { - return sqlchemy.Endswith(field, fc.params[0]) - } + return condFunc(field, fc.params, sqlchemy.Endswith) case "equals": - if len(fc.params) == 1 { - return sqlchemy.Equals(field, fc.params[0]) - } + return condFunc(field, fc.params, func(f sqlchemy.IQueryField, p string) sqlchemy.ICondition { + return sqlchemy.Equals(f, p) + }) case "notequals": if len(fc.params) == 1 { return sqlchemy.NOT(sqlchemy.Equals(field, fc.params[0])) From d705d1ae938fd6c485f6caf7acb53944689b41ea Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Wed, 20 Nov 2019 15:02:27 +0800 Subject: [PATCH 10/25] =?UTF-8?q?fix:=20=E6=94=AF=E6=8C=81=E5=8C=B9?= =?UTF-8?q?=E9=85=8Dgoogle=20cloud=E6=97=B6=E9=97=B4=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- util/timeutils/timeutils.go | 15 +++++++--- util/timeutils/timeutils_test.go | 48 +++++++++++++++----------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/util/timeutils/timeutils.go b/util/timeutils/timeutils.go index 0db70f0..48ec5ee 100644 --- a/util/timeutils/timeutils.go +++ b/util/timeutils/timeutils.go @@ -16,8 +16,8 @@ package timeutils import ( "fmt" - "time" "strings" + "time" "yunion.io/x/pkg/util/regutils" ) @@ -108,10 +108,17 @@ func ParseIsoNoSecondTime(str string) (time.Time, error) { func toFullIsoNanoTimeFormat(str string) string { // 2019-09-17T20:50:17.66667134+08:00 + // 2019-11-19T18:54:48.084-08:00 subsecStr := str[20:] - pos := strings.IndexByte(subsecStr, 'Z') - if pos < 0 { - pos = strings.IndexByte(subsecStr, '+') + pos := -1 + for _, s := range []byte{'Z', '+', '-'} { + pos = strings.IndexByte(subsecStr, s) + if pos >= 0 { + break + } + } + if pos < 0 { //避免-1越界 + return str } leftOver := subsecStr[pos:] subsecStr = subsecStr[:pos] diff --git a/util/timeutils/timeutils_test.go b/util/timeutils/timeutils_test.go index 5ed9c33..119cb68 100644 --- a/util/timeutils/timeutils_test.go +++ b/util/timeutils/timeutils_test.go @@ -66,42 +66,38 @@ func TestTimeUtils(t *testing.T) { t.Errorf("Parse ZStack time error! %s %s %s", tmLocal, tm5, tmLocal.Sub(tm6)) } - tm7, err := ParseTimeStr("2019-09-17T03:02:45.709546502+08:00") - if err != nil { - t.Fatalf("2019-09-17T03:02:45.709546502+08:00 fail: %s", err) - } - t.Logf("%s", tm7.String()) - - tm8, err := ParseTimeStr("2019-09-17T03:15:42.480940759+08:00\n") - if err != nil { - t.Fatalf("2019-09-17T03:15:42.480940759+08:00 err %s", err) - } - t.Logf("%s", tm8.String()) - - tm9, err := ParseTimeStr("2019-09-03T11:25:26.81415Z\n") - if err != nil { - t.Fatalf("2019-09-03T11:25:26.81415Z err %s", err) + cases := []struct { + in string + want string + }{ + {in: "2019-09-17T03:02:45.709546502+08:00", want: "2019-09-17 03:02:45.709546502 +0800 CST"}, + {in: "2019-09-17T03:15:42.480940759+08:00\n", want: "2019-09-17 03:15:42.480940759 +0800 CST"}, + {in: "2019-09-03T11:25:26.81415Z\n", want: "2019-09-03 11:25:26.81415 +0000 UTC"}, + {in: "2019-09-03T11:25:26.8141523Z\n", want: "2019-09-03 11:25:26.8141523 +0000 UTC"}, + {in: "2019-11-19T18:54:48.084-08:00", want: "2019-11-19 18:54:48.084 -0800 -0800"}, } - t.Logf("%s", tm9.String()) - - tm10, err := ParseTimeStr("2019-09-03T11:25:26.8141523Z\n") - if err != nil { - t.Fatalf("2019-09-03T11:25:26.8141523Z err %s", err) + for _, c := range cases { + tm, err := ParseTimeStr(c.in) + if err != nil { + t.Fatalf("%s fail: %v", c.in, err) + } + if tm.String() != c.want { + t.Fatalf("%s != %s", tm.String(), c.want) + } } - t.Logf("%s", tm10.String()) } func TestToFullIsoNanoTimeFormat(t *testing.T) { cases := []struct { - in string + in string want string - } { + }{ { - in: "2019-09-17T20:50:17.66667134+08:00", + in: "2019-09-17T20:50:17.66667134+08:00", want: "2019-09-17T20:50:17.666671340+08:00", }, { - in: "2019-09-17T20:50:17.66134+08:00", + in: "2019-09-17T20:50:17.66134+08:00", want: "2019-09-17T20:50:17.661340000+08:00", }, } @@ -111,4 +107,4 @@ func TestToFullIsoNanoTimeFormat(t *testing.T) { t.Errorf("want %s != got %s", c.want, got) } } -} \ No newline at end of file +} From 1c834994b1858210634c424ec6b3703ea29ba0ad Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 9 Dec 2019 12:16:44 +0000 Subject: [PATCH 11/25] reflectutils: ignore fields properly --- util/reflectutils/jsonfield.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/util/reflectutils/jsonfield.go b/util/reflectutils/jsonfield.go index e8f5748..a35be63 100644 --- a/util/reflectutils/jsonfield.go +++ b/util/reflectutils/jsonfield.go @@ -144,10 +144,12 @@ func fetchStructFieldValueSet(dataValue reflect.Value, allocatePtr bool) SStruct } } jsonInfo := ParseStructFieldJsonInfo(sf) - fields = append(fields, SStructFieldValue{ - Info: jsonInfo, - Value: fv, - }) + if !jsonInfo.Ignore { + fields = append(fields, SStructFieldValue{ + Info: jsonInfo, + Value: fv, + }) + } } return fields } From a6c4a2281e0c8ec8e9e8098668421cd95b6010ea Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Tue, 10 Dec 2019 04:05:21 +0000 Subject: [PATCH 12/25] gotypes: add constraint notes for DeepCopy --- gotypes/deepcopy.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gotypes/deepcopy.go b/gotypes/deepcopy.go index 93030a8..ddd38c4 100644 --- a/gotypes/deepcopy.go +++ b/gotypes/deepcopy.go @@ -24,6 +24,13 @@ type IDeepCopy interface { type DeepCopyFlags uintptr +// DeepCopy copies the passed argument recursively +// +// There are a few constraint +// +// - It cannot handle un-exported fields +// - It cannot handle circular references. Doing so will cause runtime stack +// overflow panic func DeepCopy(v interface{}) interface{} { rv := reflect.ValueOf(v) cpRv := DeepCopyRv(rv) From 20f2374af945faf09c1fccd6a4fd30c485e9d922 Mon Sep 17 00:00:00 2001 From: Rain Date: Tue, 17 Dec 2019 11:45:49 +0800 Subject: [PATCH 13/25] test: Benchmark for FetchStructFieldValueSet --- util/reflectutils/jsonfield_test.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/util/reflectutils/jsonfield_test.go b/util/reflectutils/jsonfield_test.go index 6f50331..2fcbdc3 100644 --- a/util/reflectutils/jsonfield_test.go +++ b/util/reflectutils/jsonfield_test.go @@ -54,3 +54,30 @@ func TestParseStructFieldJsonInfo_Name(t *testing.T) { } } } + +func BenchmarkFetchStructFieldValueSet(b *testing.B) { + type GuestIp struct { + GuestIpStart string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"` + GuestIpEnd string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"` + GuestIpMask int8 `nullable:"false" list:"user" update:"user" create:"required"` + } + type Network struct { + GuestIp + VlanId int `nullable:"false" default:"1" list:"user" update:"user" create:"optional"` + WireId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"` + } + j := Network{ + GuestIp: GuestIp{ + GuestIpStart: "10.168.10.1", + GuestIpEnd: "10.168.10.244", + GuestIpMask: 24, + }, + VlanId: 123, + WireId: "8324234723a", + } + v := reflect.ValueOf(j) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = FetchStructFieldValueSet(v) + } +} From 3532215789cb603a02a02152e7bacc1dac2f2bbe Mon Sep 17 00:00:00 2001 From: Rain Date: Tue, 17 Dec 2019 13:17:45 +0800 Subject: [PATCH 14/25] Add StructFieldInfo Cache with FieldInfo's Copy --- util/reflectutils/jsonfield.go | 57 +++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/util/reflectutils/jsonfield.go b/util/reflectutils/jsonfield.go index a35be63..03e608e 100644 --- a/util/reflectutils/jsonfield.go +++ b/util/reflectutils/jsonfield.go @@ -17,6 +17,7 @@ package reflectutils import ( "reflect" "strings" + "sync" "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/utils" @@ -78,14 +79,14 @@ func ParseStructFieldJsonInfo(sf reflect.StructField) SStructFieldInfo { if val, ok := info.Tags["name"]; ok { info.Name = val } + if len(info.Name) == 0 { + info.Name = utils.CamelSplit(info.FieldName, "_") + } return info } func (info *SStructFieldInfo) MarshalName() string { - if len(info.Name) > 0 { - return info.Name - } - return utils.CamelSplit(info.FieldName, "_") + return info.Name } type SStructFieldValue struct { @@ -103,9 +104,52 @@ func FetchStructFieldValueSetForWrite(dataValue reflect.Value) SStructFieldValue return fetchStructFieldValueSet(dataValue, true) } +type sStructFieldInfoMap map[string]SStructFieldInfo + +func newStructFieldInfoMap(cap int) sStructFieldInfoMap { + return make(map[string]SStructFieldInfo, cap) +} + +var structFieldInfoCache sync.Map + +func fetchCacheStructFieldInfos(dataType reflect.Type) sStructFieldInfoMap { + if r, ok := structFieldInfoCache.Load(dataType); ok { + return r.(sStructFieldInfoMap) + } + infos := fetchStructFieldInfos(dataType) + structFieldInfoCache.Store(dataType, infos) + return infos +} + +func fetchStructFieldInfos(dataType reflect.Type) sStructFieldInfoMap { + smap := newStructFieldInfoMap(dataType.NumField()) + for i := 0; i < dataType.NumField(); i += 1 { + sf := dataType.Field(i) + if !gotypes.IsFieldExportable(sf.Name) { + continue + } + if sf.Anonymous { + // call ParseStructFieldJsonInfo for sf if sft.Kind() is reflect.Interface: + // if the corresponding value is reflect.Struct, this item in fieldInfos will be ignored, + // otherwise this item in fieldInfos will be used correctly + sft := sf.Type + if sft.Kind() == reflect.Ptr { + sft = sft.Elem() + } + if sft.Kind() == reflect.Struct && sft != gotypes.TimeType { + continue + } + } + smap[sf.Name] = ParseStructFieldJsonInfo(sf) + } + return smap +} + + func fetchStructFieldValueSet(dataValue reflect.Value, allocatePtr bool) SStructFieldValueSet { fields := SStructFieldValueSet{} dataType := dataValue.Type() + fieldInfos := fetchCacheStructFieldInfos(dataType) for i := 0; i < dataType.NumField(); i += 1 { sf := dataType.Field(i) @@ -143,10 +187,9 @@ func fetchStructFieldValueSet(dataValue reflect.Value, allocatePtr bool) SStruct continue } } - jsonInfo := ParseStructFieldJsonInfo(sf) - if !jsonInfo.Ignore { + if !fieldInfos[sf.Name].Ignore { fields = append(fields, SStructFieldValue{ - Info: jsonInfo, + Info: fieldInfos[sf.Name], Value: fv, }) } From 41dda45df9830cabbfa280b42fbca2caa75531ee Mon Sep 17 00:00:00 2001 From: Rain Date: Tue, 17 Dec 2019 13:19:34 +0800 Subject: [PATCH 15/25] Add StructFieldInfo Cache with Wrapped Map without FieldInfo's Copy --- util/reflectutils/jsonfield.go | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/util/reflectutils/jsonfield.go b/util/reflectutils/jsonfield.go index 03e608e..55a6b42 100644 --- a/util/reflectutils/jsonfield.go +++ b/util/reflectutils/jsonfield.go @@ -90,7 +90,7 @@ func (info *SStructFieldInfo) MarshalName() string { } type SStructFieldValue struct { - Info SStructFieldInfo + Info *SStructFieldInfo Value reflect.Value } @@ -104,10 +104,30 @@ func FetchStructFieldValueSetForWrite(dataValue reflect.Value) SStructFieldValue return fetchStructFieldValueSet(dataValue, true) } -type sStructFieldInfoMap map[string]SStructFieldInfo +type sStructFieldInfoMap struct { + indexMap map[string]int + infos []SStructFieldInfo +} func newStructFieldInfoMap(cap int) sStructFieldInfoMap { - return make(map[string]SStructFieldInfo, cap) + return sStructFieldInfoMap{ + indexMap: make(map[string]int, cap), + infos: make([]SStructFieldInfo, 0, cap), + } +} + +func (m *sStructFieldInfoMap) shrink() { + m.infos = m.infos[:len(m.infos):len(m.infos)] +} + +func (m *sStructFieldInfoMap) get(name string) *SStructFieldInfo { + return &m.infos[m.indexMap[name]] +} + +func (m *sStructFieldInfoMap) set(name string, info SStructFieldInfo) bool { + m.indexMap[name] = len(m.infos) + m.infos = append(m.infos, info) + return true } var structFieldInfoCache sync.Map @@ -140,8 +160,9 @@ func fetchStructFieldInfos(dataType reflect.Type) sStructFieldInfoMap { continue } } - smap[sf.Name] = ParseStructFieldJsonInfo(sf) + smap.set(sf.Name, ParseStructFieldJsonInfo(sf)) } + smap.shrink() return smap } @@ -187,9 +208,10 @@ func fetchStructFieldValueSet(dataValue reflect.Value, allocatePtr bool) SStruct continue } } - if !fieldInfos[sf.Name].Ignore { + fieldInfo := fieldInfos.get(sf.Name) + if !fieldInfo.Ignore { fields = append(fields, SStructFieldValue{ - Info: fieldInfos[sf.Name], + Info: fieldInfo, Value: fv, }) } From 44988cc2029b9c0bb774409b544aa4107c14af12 Mon Sep 17 00:00:00 2001 From: Rain Date: Sat, 21 Dec 2019 14:36:02 +0800 Subject: [PATCH 16/25] fix: Fix the bug in case of adding FieldInfoCache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 首先,添加了FieldInfoCache之后,无论是往外传struct还是指针, 都没有对SStructFieldInfo中的Tags (一个map)进行deepcopy。 而这个map甚至整个SStructFieldInfo是readonly的。所以整个 SStructFieldInfo都应该deepcopy一份出来。 因此,SStructFieldValue 中的SStructFieldInfo也作为结构体传递出来。 --- util/reflectutils/jsonfield.go | 50 ++++++++++++++++------------------ 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/util/reflectutils/jsonfield.go b/util/reflectutils/jsonfield.go index 55a6b42..f46467f 100644 --- a/util/reflectutils/jsonfield.go +++ b/util/reflectutils/jsonfield.go @@ -34,6 +34,24 @@ type SStructFieldInfo struct { Tags map[string]string } +func (s SStructFieldInfo) deepCopy() *SStructFieldInfo { + scopy := SStructFieldInfo{ + Ignore: s.Ignore, + OmitEmpty: s.OmitEmpty, + OmitFalse: s.OmitFalse, + OmitZero: s.OmitZero, + Name: s.Name, + FieldName: s.FieldName, + ForceString: s.ForceString, + } + tags := make(map[string]string, len(s.Tags)) + for k, v := range s.Tags { + tags[k] = v + } + scopy.Tags = tags + return &scopy +} + func ParseStructFieldJsonInfo(sf reflect.StructField) SStructFieldInfo { info := SStructFieldInfo{} info.FieldName = sf.Name @@ -104,30 +122,10 @@ func FetchStructFieldValueSetForWrite(dataValue reflect.Value) SStructFieldValue return fetchStructFieldValueSet(dataValue, true) } -type sStructFieldInfoMap struct { - indexMap map[string]int - infos []SStructFieldInfo -} +type sStructFieldInfoMap map[string]SStructFieldInfo -func newStructFieldInfoMap(cap int) sStructFieldInfoMap { - return sStructFieldInfoMap{ - indexMap: make(map[string]int, cap), - infos: make([]SStructFieldInfo, 0, cap), - } -} - -func (m *sStructFieldInfoMap) shrink() { - m.infos = m.infos[:len(m.infos):len(m.infos)] -} - -func (m *sStructFieldInfoMap) get(name string) *SStructFieldInfo { - return &m.infos[m.indexMap[name]] -} - -func (m *sStructFieldInfoMap) set(name string, info SStructFieldInfo) bool { - m.indexMap[name] = len(m.infos) - m.infos = append(m.infos, info) - return true +func newStructFieldInfoMap(caps int) sStructFieldInfoMap { + return make(map[string]SStructFieldInfo, caps) } var structFieldInfoCache sync.Map @@ -160,13 +158,11 @@ func fetchStructFieldInfos(dataType reflect.Type) sStructFieldInfoMap { continue } } - smap.set(sf.Name, ParseStructFieldJsonInfo(sf)) + smap[sf.Name] = ParseStructFieldJsonInfo(sf) } - smap.shrink() return smap } - func fetchStructFieldValueSet(dataValue reflect.Value, allocatePtr bool) SStructFieldValueSet { fields := SStructFieldValueSet{} dataType := dataValue.Type() @@ -208,7 +204,7 @@ func fetchStructFieldValueSet(dataValue reflect.Value, allocatePtr bool) SStruct continue } } - fieldInfo := fieldInfos.get(sf.Name) + fieldInfo := fieldInfos[sf.Name].deepCopy() if !fieldInfo.Ignore { fields = append(fields, SStructFieldValue{ Info: fieldInfo, From 4fc0c3748cf33dfa866840a0f2f0b7545d9c295f Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 4 Oct 2019 11:36:17 +0000 Subject: [PATCH 17/25] github: work around "[error]No such file or directory"" --- .github/workflows/build-and-test.yaml | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 2036f41..e423222 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v1 with: fetch-depth: 1 - - uses: actions/setup-go@v1 + - uses: actions/setup-go@v1.1.2 with: go-version: '1.13' - name: prepare GOPATH @@ -25,19 +25,8 @@ jobs: workdir="$HOME/go/src/yunion.io/x" mkdir -p "$workdir" mv "$HOME/work/pkg/pkg" "$workdir/" - - name: go get dependencies - shell: bash - run: | - set -o xtrace - workdir="$HOME/go/src/yunion.io/x" + export GOPATH="$HOME/go" cd "$workdir/pkg" go get -v -t -d ./... - - name: run test - shell: bash - run: | - set -o xtrace - workdir="$HOME/go/src/yunion.io/x" - export GOPATH="$HOME/go" - cd "$workdir/pkg" go test -v ./... From 7d3e74cbf1ccde2d3ede93974b60f1ae72222a0c Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 30 Dec 2019 13:07:53 +0800 Subject: [PATCH 18/25] reflectutils: fix TestParseStructFieldJsonInfo_Name Ref 3532215 ("Add StructFieldInfo Cache with FieldInfo's Copy") --- util/reflectutils/jsonfield.go | 2 +- util/reflectutils/jsonfield_test.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/util/reflectutils/jsonfield.go b/util/reflectutils/jsonfield.go index f46467f..45e4e55 100644 --- a/util/reflectutils/jsonfield.go +++ b/util/reflectutils/jsonfield.go @@ -97,7 +97,7 @@ func ParseStructFieldJsonInfo(sf reflect.StructField) SStructFieldInfo { if val, ok := info.Tags["name"]; ok { info.Name = val } - if len(info.Name) == 0 { + if !info.Ignore && len(info.Name) == 0 { info.Name = utils.CamelSplit(info.FieldName, "_") } return info diff --git a/util/reflectutils/jsonfield_test.go b/util/reflectutils/jsonfield_test.go index 2fcbdc3..83aaa82 100644 --- a/util/reflectutils/jsonfield_test.go +++ b/util/reflectutils/jsonfield_test.go @@ -22,8 +22,8 @@ import ( func TestParseStructFieldJsonInfo_Name(t *testing.T) { type T struct { - FiCamel int `validate:"name=,marshal_name=fi_camel"` - FiIgnore int `json:"-" validate:"name=,marshal_name=fi_ignore"` + FiCamel int `validate:"name=fi_camel,marshal_name=fi_camel"` + FiIgnore int `json:"-" validate:"name=,marshal_name="` FiDash int `json:"-," validate:"name=-,marshal_name=-"` FiJson int `json:"json" validate:"name=json,marshal_name=json"` FiName int `json:"json" name:"name" validate:"name=name,marshal_name=name"` @@ -63,14 +63,14 @@ func BenchmarkFetchStructFieldValueSet(b *testing.B) { } type Network struct { GuestIp - VlanId int `nullable:"false" default:"1" list:"user" update:"user" create:"optional"` + VlanId int `nullable:"false" default:"1" list:"user" update:"user" create:"optional"` WireId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"` } j := Network{ GuestIp: GuestIp{ GuestIpStart: "10.168.10.1", - GuestIpEnd: "10.168.10.244", - GuestIpMask: 24, + GuestIpEnd: "10.168.10.244", + GuestIpMask: 24, }, VlanId: 123, WireId: "8324234723a", From c644d9e396d8829e99eb36b374b3fd0c5a9bb1ab Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 30 Dec 2019 11:38:52 +0800 Subject: [PATCH 19/25] netutils: add tests for NewIPV4Addr --- util/netutils/errors.go | 12 ++++++------ util/netutils/netutils.go | 3 +-- util/netutils/netutils_test.go | 31 +++++++++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/util/netutils/errors.go b/util/netutils/errors.go index 6ec8aa6..e255538 100644 --- a/util/netutils/errors.go +++ b/util/netutils/errors.go @@ -15,13 +15,13 @@ package netutils import ( - "github.com/pkg/errors" + "yunion.io/x/pkg/errors" ) var ( - ErrInvalidNumber = errors.New("invalid number") - ErrOutOfRange = errors.New("ip number out of range [0-255]") - ErrInvalidIPAddr = errors.New("invalid ip address") - ErrInvalidMask = errors.New("invalid mask") - ErrOutOfRangeMask = errors.New("out of range masklen [0-32]") + ErrInvalidNumber = errors.Error("invalid number") + ErrOutOfRange = errors.Error("ip number out of range [0-255]") + ErrInvalidIPAddr = errors.Error("invalid ip address") + ErrInvalidMask = errors.Error("invalid mask") + ErrOutOfRangeMask = errors.Error("out of range masklen [0-32]") ) diff --git a/util/netutils/netutils.go b/util/netutils/netutils.go index 609fe75..d042158 100644 --- a/util/netutils/netutils.go +++ b/util/netutils/netutils.go @@ -21,8 +21,7 @@ import ( "strconv" "strings" - "github.com/pkg/errors" - + "yunion.io/x/pkg/errors" "yunion.io/x/pkg/util/regutils" ) diff --git a/util/netutils/netutils_test.go b/util/netutils/netutils_test.go index 935dce8..e4391ad 100644 --- a/util/netutils/netutils_test.go +++ b/util/netutils/netutils_test.go @@ -58,8 +58,36 @@ func TestIPV4Addr_StepDown(t *testing.T) { t.Logf("Stepdown: %s", ipaddr.StepDown()) } -func TestMasklen2Mask(t *testing.T) { +func TestNewIPV4Addr(t *testing.T) { + cases := []struct { + in string + out IPV4Addr + }{ + { + in: "", // maybe used by ":8080" + out: IPV4Addr(0), + }, + { + in: "0.0.0.0", + out: IPV4Addr(0), + }, + { + in: "192.168.1.0", + out: IPV4Addr(192<<24 | 168<<16 | 1<<8), + }, + } + for _, c := range cases { + got, err := NewIPV4Addr(c.in) + if err != nil { + t.Fatalf("(%q): err : %v", c.in, err) + } + if got != c.out { + t.Fatalf("(%q): got %s, want %s", c.in, got, c.out) + } + } +} +func TestMasklen2Mask(t *testing.T) { t.Logf("%s", Masklen2Mask(0)) t.Logf("%s", Masklen2Mask(1)) t.Logf("%s", Masklen2Mask(23)) @@ -70,7 +98,6 @@ func TestMasklen2Mask(t *testing.T) { t.Logf("%d", Mask2Len(Masklen2Mask(32))) t.Logf("%d", Mask2Len(Masklen2Mask(24))) t.Logf("%d", Mask2Len(Masklen2Mask(1))) - } func TestIPRangeRandom(t *testing.T) { From 47e2c3992843b19658b5cac52db88636faa01773 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 30 Dec 2019 11:49:25 +0800 Subject: [PATCH 20/25] netutils: use assert test in TestMasklen2Mask --- util/netutils/netutils_test.go | 45 ++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/util/netutils/netutils_test.go b/util/netutils/netutils_test.go index e4391ad..335f331 100644 --- a/util/netutils/netutils_test.go +++ b/util/netutils/netutils_test.go @@ -88,16 +88,41 @@ func TestNewIPV4Addr(t *testing.T) { } func TestMasklen2Mask(t *testing.T) { - t.Logf("%s", Masklen2Mask(0)) - t.Logf("%s", Masklen2Mask(1)) - t.Logf("%s", Masklen2Mask(23)) - t.Logf("%s", Masklen2Mask(24)) - t.Logf("%s", Masklen2Mask(32)) - - t.Logf("%d", Mask2Len(Masklen2Mask(0))) - t.Logf("%d", Mask2Len(Masklen2Mask(32))) - t.Logf("%d", Mask2Len(Masklen2Mask(24))) - t.Logf("%d", Mask2Len(Masklen2Mask(1))) + cases := []struct { + in int8 + out IPV4Addr + }{ + { + in: 0, + out: IPV4Addr(0), + }, + { + in: 1, + out: IPV4Addr(1 << 31), + }, + { + in: 23, + out: IPV4Addr(^(uint32(1<<9) - 1)), + }, + { + in: 24, + out: IPV4Addr(^(uint32(1<<8) - 1)), + }, + { + in: 32, + out: IPV4Addr(^(uint32(1<<0) - 1)), + }, + } + for _, c := range cases { + got := Masklen2Mask(c.in) + if got != c.out { + t.Fatalf("(%2d): got %s, want %s", c.in, got, c.out) + } + in2 := Mask2Len(got) + if in2 != c.in { + t.Fatalf("(%2d): got %d", c.in, in2) + } + } } func TestIPRangeRandom(t *testing.T) { From e211627753fdbe56078bd5beca91c328eca4b3d4 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 30 Dec 2019 11:51:13 +0800 Subject: [PATCH 21/25] netutils: simplify Masklen2Mask --- util/netutils/netutils.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/util/netutils/netutils.go b/util/netutils/netutils.go index d042158..403afb0 100644 --- a/util/netutils/netutils.go +++ b/util/netutils/netutils.go @@ -320,11 +320,7 @@ func (ar IPV4AddrRange) equals(ar2 IPV4AddrRange) bool { } func Masklen2Mask(maskLen int8) IPV4Addr { - var mask uint32 = 0 - for i := 0; i < int(maskLen); i += 1 { - mask |= 1 << uint(31-i) - } - return IPV4Addr(mask) + return IPV4Addr(^(uint32(1<<(32-maskLen)) - 1)) } type IPV4Prefix struct { From 7c8e0ad803130ec87a38e2b10d13ed1eb4ae19e5 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 30 Dec 2019 11:55:02 +0800 Subject: [PATCH 22/25] netutils: simplify Mask2Len --- util/netutils/netutils.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/util/netutils/netutils.go b/util/netutils/netutils.go index 403afb0..4c0c9b6 100644 --- a/util/netutils/netutils.go +++ b/util/netutils/netutils.go @@ -16,6 +16,7 @@ package netutils import ( "fmt" + "math/bits" "math/rand" "net" "strconv" @@ -345,14 +346,7 @@ func (pref *IPV4Prefix) Equals(pref1 *IPV4Prefix) bool { } func Mask2Len(mask IPV4Addr) int8 { - var maskLen int8 = 0 - for { - if (mask & (1 << uint(31-maskLen))) == 0 { - break - } - maskLen += 1 - } - return maskLen + return int8(bits.LeadingZeros32(^uint32(mask))) } func ParsePrefix(prefix string) (IPV4Addr, int8, error) { From 2c859f34637e2ffa30f41922bce79f3e45a9b029 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 30 Dec 2019 13:19:14 +0800 Subject: [PATCH 23/25] tristate: use assert tests --- tristate/tristate_test.go | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tristate/tristate_test.go b/tristate/tristate_test.go index 20d5cb0..4e491aa 100644 --- a/tristate/tristate_test.go +++ b/tristate/tristate_test.go @@ -17,18 +17,15 @@ package tristate import "testing" func TestTriState(t *testing.T) { - if True.Bool() { - t.Logf("True == true") - } - if !False.Bool() { - t.Logf("False == false") - } - if !None.Bool() { - t.Logf("None == false") + assert := func(hold bool, fmtStr string, args ...interface{}) { + if !hold { + t.Fatalf(fmtStr, args...) + } } + assert(True.Bool(), "True != true") + assert(!False.Bool(), "False != false") + assert(!None.Bool(), "None != false") var val TriState - if val.IsNone() { - t.Logf("val is None") - } + assert(val.IsNone(), "val is None") } From 591b186e87a4f2caf20f57982b66c3908a023941 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 30 Dec 2019 13:23:14 +0800 Subject: [PATCH 24/25] gotypes: use assert tests --- gotypes/gotypes_test.go | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/gotypes/gotypes_test.go b/gotypes/gotypes_test.go index 1ee04cf..fc552f6 100644 --- a/gotypes/gotypes_test.go +++ b/gotypes/gotypes_test.go @@ -246,14 +246,29 @@ func TestInCollection(t *testing.T) { } func TestGetInstanceTypeName(t *testing.T) { - var a int32 - t.Logf("%s", GetInstanceTypeName(a)) - - type STestStruct struct { + { + var ( + a int32 + want = "int32" + ) + if got := GetInstanceTypeName(a); got != want { + t.Fatalf("want %s, got %s", want, got) + } } - t.Logf("%s", GetInstanceTypeName(STestStruct{})) - t.Logf("%s", GetInstanceTypeName(&STestStruct{})) + { + type STestStruct struct{} + var ( + want = "STestStruct" + a = STestStruct{} + ) + if got := GetInstanceTypeName(a); got != want { + t.Fatalf("want %s, got %s", want, got) + } + if got := GetInstanceTypeName(&a); got != want { + t.Fatalf("ptr type name: want %s, got %s", want, got) + } + } } func TestIsNil(t *testing.T) { From 174d96260cd000ebd3da6fa9ce028b6578521496 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 30 Dec 2019 13:40:18 +0800 Subject: [PATCH 25/25] timeutils: test equalness with time.Equal --- util/timeutils/timeutils_test.go | 59 ++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/util/timeutils/timeutils_test.go b/util/timeutils/timeutils_test.go index 119cb68..7f3a57f 100644 --- a/util/timeutils/timeutils_test.go +++ b/util/timeutils/timeutils_test.go @@ -23,16 +23,16 @@ func TestTimeUtils(t *testing.T) { tm := time.Now().UTC() tmLocal := time.Now() - t.Logf("isoTime: %s", IsoTime(tm)) - t.Logf("isoNoSecondTime: %s", IsoNoSecondTime(time.Time{})) - t.Logf("FullIsoTime: %s", FullIsoTime(tm)) - t.Logf("mysqlTime: %s", MysqlTime(tm)) - t.Logf("CompactTime: %s", CompactTime(tm)) - t.Logf("ShortDate: %s", ShortDate(tm)) - t.Logf("Date: %s", DateStr(tm)) - t.Logf("RFC2882: %s", RFC2882Time(tm)) - t.Logf("ZStack: %s", ZStackTime(tmLocal)) - t.Logf("FullIsoNanoTime: %s", FullIsoNanoTime(tmLocal)) + t.Logf("example: isoTime: %s", IsoTime(tm)) + t.Logf("example: isoNoSecondTime: %s", IsoNoSecondTime(time.Time{})) + t.Logf("example: FullIsoTime: %s", FullIsoTime(tm)) + t.Logf("example: mysqlTime: %s", MysqlTime(tm)) + t.Logf("example: CompactTime: %s", CompactTime(tm)) + t.Logf("example: ShortDate: %s", ShortDate(tm)) + t.Logf("example: Date: %s", DateStr(tm)) + t.Logf("example: RFC2882: %s", RFC2882Time(tm)) + t.Logf("example: ZStack: %s", ZStackTime(tmLocal)) + t.Logf("example: FullIsoNanoTime: %s", FullIsoNanoTime(tmLocal)) tm2, err := ParseTimeStr(IsoTime(tm)) if err != nil { @@ -66,23 +66,46 @@ func TestTimeUtils(t *testing.T) { t.Errorf("Parse ZStack time error! %s %s %s", tmLocal, tm5, tmLocal.Sub(tm6)) } + wantParse := func(s string) time.Time { + tmStringFmt := "2006-01-02 15:04:05.999999999 -0700 MST" + r, err := time.Parse(tmStringFmt, s) + if err != nil { + t.Fatalf("error when mustParse %s: %v", s, err) + } + return r + } cases := []struct { in string - want string + want time.Time }{ - {in: "2019-09-17T03:02:45.709546502+08:00", want: "2019-09-17 03:02:45.709546502 +0800 CST"}, - {in: "2019-09-17T03:15:42.480940759+08:00\n", want: "2019-09-17 03:15:42.480940759 +0800 CST"}, - {in: "2019-09-03T11:25:26.81415Z\n", want: "2019-09-03 11:25:26.81415 +0000 UTC"}, - {in: "2019-09-03T11:25:26.8141523Z\n", want: "2019-09-03 11:25:26.8141523 +0000 UTC"}, - {in: "2019-11-19T18:54:48.084-08:00", want: "2019-11-19 18:54:48.084 -0800 -0800"}, + { + in: "2019-09-17T03:02:45.709546502+08:00", + want: wantParse("2019-09-17 03:02:45.709546502 +0800 CST"), + }, + { + in: "2019-09-17T03:15:42.480940759+08:00\n", + want: wantParse("2019-09-17 03:15:42.480940759 +0800 CST"), + }, + { + in: "2019-09-03T11:25:26.81415Z\n", + want: wantParse("2019-09-03 11:25:26.81415 +0000 UTC"), + }, + { + in: "2019-09-03T11:25:26.8141523Z\n", + want: wantParse("2019-09-03 11:25:26.8141523 +0000 UTC"), + }, + { + in: "2019-11-19T18:54:48.084-08:00", + want: wantParse("2019-11-19 18:54:48.084 -0800 -08"), + }, } for _, c := range cases { tm, err := ParseTimeStr(c.in) if err != nil { t.Fatalf("%s fail: %v", c.in, err) } - if tm.String() != c.want { - t.Fatalf("%s != %s", tm.String(), c.want) + if !tm.Equal(c.want) { + t.Fatalf("\n%s:\n got: %s\nwant: %s", c.in, tm, c.want) } } }