mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-19 10:46:58 +08:00
fix: support server change ipv6 address (#22016)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
This commit is contained in:
@@ -93,12 +93,12 @@ require (
|
||||
k8s.io/cri-api v0.22.17
|
||||
k8s.io/klog/v2 v2.20.0
|
||||
moul.io/http2curl/v2 v2.3.0
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250108102611-1b422fb27e8b
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250113015514-eb7eb3112a75
|
||||
yunion.io/x/executor v0.0.0-20241205080005-48f5b1212256
|
||||
yunion.io/x/jsonutils v1.0.1-0.20240930100528-1671a2d0d22f
|
||||
yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91
|
||||
yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900
|
||||
yunion.io/x/pkg v1.10.3
|
||||
yunion.io/x/pkg v1.10.4-0.20250123070256-9247ce856f07
|
||||
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1
|
||||
yunion.io/x/sqlchemy v1.1.3-0.20240926163039-d41512b264e1
|
||||
yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c
|
||||
|
||||
@@ -1376,8 +1376,8 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250108102611-1b422fb27e8b h1:CnmPWb1fnMwF1ZlUxSCCIjiwdlfLd6/vFlScvHPIMjQ=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250108102611-1b422fb27e8b/go.mod h1:KQ/jWx7bZlmjCE711KEWuvHW/dzpdr/UTlBjjutkj0Y=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250113015514-eb7eb3112a75 h1:3GfPFiOXGcNlbMZbLg2TTov7VdRx2FS+FzrHQsmj64o=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250113015514-eb7eb3112a75/go.mod h1:KQ/jWx7bZlmjCE711KEWuvHW/dzpdr/UTlBjjutkj0Y=
|
||||
yunion.io/x/executor v0.0.0-20241205080005-48f5b1212256 h1:kLKQ6zbgPDQflRwoHFAjxNChcbhXIFgsUVLkJwiXu/8=
|
||||
yunion.io/x/executor v0.0.0-20241205080005-48f5b1212256/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws=
|
||||
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634=
|
||||
@@ -1391,8 +1391,8 @@ yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900 h1:Hu/4ERvoWaN6aiFs4h4/yvVB
|
||||
yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900/go.mod h1:0vLkNEhlmA64HViPBAnSTUMrx5QP1CLsxXmxDKQ80tc=
|
||||
yunion.io/x/pkg v0.0.0-20190620104149-945c25821dbf/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
|
||||
yunion.io/x/pkg v0.0.0-20190628082551-f4033ba2ea30/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
|
||||
yunion.io/x/pkg v1.10.3 h1:oaJAtMSIwASgF6jB/0W37iOQBLh6ICswfPL3ISnRZC4=
|
||||
yunion.io/x/pkg v1.10.3/go.mod h1:0Bwxqd9MA3ACi119/l02FprY/o9gHahmYC2bsSbnVpM=
|
||||
yunion.io/x/pkg v1.10.4-0.20250123070256-9247ce856f07 h1:XZsL8+YpgVBHMcRwdpZ4cOfSZCc+KsgUCTdV3KeK8FI=
|
||||
yunion.io/x/pkg v1.10.4-0.20250123070256-9247ce856f07/go.mod h1:0Bwxqd9MA3ACi119/l02FprY/o9gHahmYC2bsSbnVpM=
|
||||
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1 h1:1KJ3YYinydPHpDEQRXdr/T8SYcKZ5Er+m489H+PnaQ4=
|
||||
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1/go.mod h1:0iFKpOs1y4lbCxeOmq3Xx/0AcQoewVPwj62eRluioEo=
|
||||
yunion.io/x/sqlchemy v1.1.3-0.20240926163039-d41512b264e1 h1:HWPqY1I5JSmM6Sks6FyK9hnq/MjL7FDghM6M8DXHob0=
|
||||
|
||||
@@ -253,11 +253,27 @@ func ParseNetworkConfigByJSON(desc jsonutils.JSONObject, idx int) (*compute.Netw
|
||||
return conf, err
|
||||
}
|
||||
|
||||
func isQuoteChar(ch byte) (bool, string) {
|
||||
switch ch {
|
||||
case '[':
|
||||
return true, "]"
|
||||
default:
|
||||
return false, ""
|
||||
}
|
||||
}
|
||||
|
||||
func splitConfig(confStr string) ([]string, error) {
|
||||
return utils.FindWords2([]byte(confStr), 0, ":", isQuoteChar)
|
||||
}
|
||||
|
||||
func ParseNetworkConfig(desc string, idx int) (*compute.NetworkConfig, error) {
|
||||
if len(desc) == 0 {
|
||||
return nil, ErrorEmptyDesc
|
||||
}
|
||||
parts := strings.Split(desc, ":")
|
||||
parts, err := splitConfig(desc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "splitConfig")
|
||||
}
|
||||
netConfig := new(compute.NetworkConfig)
|
||||
netConfig.Index = idx
|
||||
for _, p := range parts {
|
||||
@@ -300,23 +316,23 @@ func ParseNetworkConfig(desc string, idx int) (*compute.NetworkConfig, error) {
|
||||
}
|
||||
netConfig.Addresses6[i] = addr6.String()
|
||||
}
|
||||
} else if p == "[require_designated_ip]" {
|
||||
} else if p == "require_designated_ip" {
|
||||
netConfig.RequireDesignatedIP = true
|
||||
} else if p == "[random_exit]" {
|
||||
} else if p == "random_exit" {
|
||||
netConfig.Exit = true
|
||||
} else if p == "[random]" {
|
||||
} else if p == "random" {
|
||||
netConfig.Exit = false
|
||||
} else if p == "[private]" {
|
||||
} else if p == "private" {
|
||||
netConfig.Private = true
|
||||
} else if p == "[reserved]" {
|
||||
} else if p == "reserved" {
|
||||
netConfig.Reserved = true
|
||||
} else if p == "[teaming]" {
|
||||
} else if p == "teaming" {
|
||||
netConfig.RequireTeaming = true
|
||||
} else if p == "[try-teaming]" {
|
||||
} else if p == "try-teaming" {
|
||||
netConfig.TryTeaming = true
|
||||
} else if p == "[defaultgw]" {
|
||||
} else if p == "defaultgw" {
|
||||
netConfig.IsDefault = true
|
||||
} else if p == "[ipv6]" {
|
||||
} else if p == "ipv6" {
|
||||
netConfig.RequireIPv6 = true
|
||||
} else if strings.HasPrefix(p, "standby-port=") {
|
||||
netConfig.StandbyPortCount, _ = strconv.Atoi(p[len("standby-port="):])
|
||||
@@ -332,7 +348,7 @@ func ParseNetworkConfig(desc string, idx int) (*compute.NetworkConfig, error) {
|
||||
return nil, err
|
||||
}
|
||||
netConfig.BwLimit = bw
|
||||
} else if p == "[vip]" {
|
||||
} else if p == "vip" {
|
||||
netConfig.Vip = true
|
||||
} else if strings.HasPrefix(p, "sriov-nic-id=") {
|
||||
netConfig.SriovDevice = &compute.IsolatedDeviceConfig{
|
||||
|
||||
@@ -282,3 +282,68 @@ func TestParseBaremetalRootDiskMatcher(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNetworkConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
args string
|
||||
want *compute.NetworkConfig
|
||||
}{
|
||||
{
|
||||
args: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94",
|
||||
want: &compute.NetworkConfig{Network: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94"},
|
||||
},
|
||||
{
|
||||
args: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94:172.22.121.12",
|
||||
want: &compute.NetworkConfig{
|
||||
Network: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94",
|
||||
Address: "172.22.121.12",
|
||||
},
|
||||
},
|
||||
{
|
||||
args: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94:172.22.121.12:[fd:3ffe:3200:90::2]",
|
||||
want: &compute.NetworkConfig{
|
||||
Network: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94",
|
||||
Address: "172.22.121.12",
|
||||
Address6: "fd:3ffe:3200:90::2",
|
||||
},
|
||||
},
|
||||
{
|
||||
args: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94:172.22.121.12:[fd:3ffe:3200:90::2]:[teaming]",
|
||||
want: &compute.NetworkConfig{
|
||||
Network: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94",
|
||||
Address: "172.22.121.12",
|
||||
Address6: "fd:3ffe:3200:90::2",
|
||||
RequireTeaming: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
args: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94:172.22.121.12:[fd:3ffe:3200:90::2]:wire=77a7d3a7-022d-4b48-8bb6-e37ea3a20c94",
|
||||
want: &compute.NetworkConfig{
|
||||
Network: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94",
|
||||
Address: "172.22.121.12",
|
||||
Address6: "fd:3ffe:3200:90::2",
|
||||
Wire: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94",
|
||||
},
|
||||
},
|
||||
{
|
||||
args: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94:172.22.121.12:[fd:3ffe:3200:90::2]:wire=77a7d3a7-022d-4b48-8bb6-e37ea3a20c94:[try-teaming]",
|
||||
want: &compute.NetworkConfig{
|
||||
Network: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94",
|
||||
Address: "172.22.121.12",
|
||||
Address6: "fd:3ffe:3200:90::2",
|
||||
Wire: "77a7d3a7-022d-4b48-8bb6-e37ea3a20c94",
|
||||
TryTeaming: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.args, func(t *testing.T) {
|
||||
got, err := ParseNetworkConfig(tt.args, 0)
|
||||
if err != nil {
|
||||
t.Errorf("ParseNetworkConfig %s fail %s", tt.args, err)
|
||||
} else if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("ParseNetworkConfig() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2602,7 +2602,11 @@ func (self *SGuest) PerformChangeIpaddr(
|
||||
if input.NetConf != nil {
|
||||
conf = input.NetConf
|
||||
} else if len(input.NetDesc) > 0 {
|
||||
netConf, err := cmdline.ParseNetworkConfigByJSON(jsonutils.NewString(input.NetDesc), -1)
|
||||
netDescJson, err := jsonutils.ParseString(input.NetDesc)
|
||||
if err != nil {
|
||||
netDescJson = jsonutils.NewString(input.NetDesc)
|
||||
}
|
||||
netConf, err := cmdline.ParseNetworkConfigByJSON(netDescJson, -1)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInputParameterError("fail to parse net_desc %s: %s", input.NetDesc, err)
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -1785,7 +1785,7 @@ sigs.k8s.io/structured-merge-diff/v4/value
|
||||
# sigs.k8s.io/yaml v1.2.0
|
||||
## explicit; go 1.12
|
||||
sigs.k8s.io/yaml
|
||||
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250108102611-1b422fb27e8b
|
||||
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250113015514-eb7eb3112a75
|
||||
## explicit; go 1.21
|
||||
yunion.io/x/cloudmux/pkg/apis
|
||||
yunion.io/x/cloudmux/pkg/apis/billing
|
||||
@@ -1882,7 +1882,7 @@ yunion.io/x/log/hooks
|
||||
yunion.io/x/ovsdb/cli_util
|
||||
yunion.io/x/ovsdb/schema/ovn_nb
|
||||
yunion.io/x/ovsdb/types
|
||||
# yunion.io/x/pkg v1.10.3
|
||||
# yunion.io/x/pkg v1.10.4-0.20250123070256-9247ce856f07
|
||||
## explicit; go 1.18
|
||||
yunion.io/x/pkg/appctx
|
||||
yunion.io/x/pkg/errors
|
||||
|
||||
+19
-11
@@ -94,7 +94,8 @@ type DataDisks struct {
|
||||
type SInstance struct {
|
||||
multicloud.SInstanceBase
|
||||
SKsTag
|
||||
host *SHost
|
||||
host *SHost
|
||||
region *SRegion
|
||||
|
||||
InstanceID string `json:"InstanceId"`
|
||||
ProjectID string `json:"ProjectId"`
|
||||
@@ -186,16 +187,16 @@ func (region *SRegion) GetInstance(instanceId string) (*SInstance, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetInstances")
|
||||
}
|
||||
for _, instance := range instances {
|
||||
if instance.GetGlobalId() == instanceId {
|
||||
return &instance, nil
|
||||
for i := range instances {
|
||||
if instances[i].GetGlobalId() == instanceId {
|
||||
return &instances[i], nil
|
||||
}
|
||||
}
|
||||
return nil, errors.Wrapf(err, "instance id:%s", instanceId)
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, instanceId)
|
||||
}
|
||||
|
||||
func (ins *SInstance) Refresh() error {
|
||||
extIns, err := ins.host.zone.region.GetInstance(ins.GetGlobalId())
|
||||
extIns, err := ins.getRegion().GetInstance(ins.GetGlobalId())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetInstance")
|
||||
}
|
||||
@@ -203,13 +204,20 @@ func (ins *SInstance) Refresh() error {
|
||||
}
|
||||
|
||||
func (ins *SInstance) GetTags() (map[string]string, error) {
|
||||
tags, err := ins.host.zone.region.ListTags("kec-instance", ins.InstanceID)
|
||||
tags, err := ins.getRegion().ListTags("kec-instance", ins.InstanceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tags.GetTags(), nil
|
||||
}
|
||||
|
||||
func (ins *SInstance) getRegion() *SRegion {
|
||||
if ins.region != nil {
|
||||
return ins.region
|
||||
}
|
||||
return ins.host.zone.region
|
||||
}
|
||||
|
||||
func (ins *SInstance) AssignSecurityGroup(secgroupId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
@@ -320,7 +328,7 @@ func (ins *SInstance) GetHypervisor() string {
|
||||
}
|
||||
|
||||
func (ins *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
||||
disks, err := ins.host.zone.region.GetDiskByInstanceId(ins.GetId())
|
||||
disks, err := ins.getRegion().GetDiskByInstanceId(ins.GetId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getDisks")
|
||||
}
|
||||
@@ -339,7 +347,7 @@ func (ins *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
|
||||
if len(eipIds) == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
eips, err := ins.host.zone.region.GetEips(eipIds)
|
||||
eips, err := ins.getRegion().GetEips(eipIds)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get eips")
|
||||
}
|
||||
@@ -348,7 +356,7 @@ func (ins *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
|
||||
}
|
||||
for _, eip := range eips {
|
||||
if utils.IsInStringArray(eip.GetId(), eipIds) {
|
||||
eip.region = ins.host.zone.region
|
||||
eip.region = ins.getRegion()
|
||||
return &eip, nil
|
||||
}
|
||||
}
|
||||
@@ -371,7 +379,7 @@ func (ins *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
|
||||
|
||||
func (ins *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
|
||||
// TODO
|
||||
resp, err := ins.host.zone.region.ecsRequest("GetVNCAddress", map[string]string{"InstanceId": ins.InstanceID})
|
||||
resp, err := ins.getRegion().ecsRequest("GetVNCAddress", map[string]string{"InstanceId": ins.InstanceID})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetVNCAddress")
|
||||
}
|
||||
|
||||
+22
@@ -238,6 +238,28 @@ func (region *SRegion) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
|
||||
return iStores, nil
|
||||
}
|
||||
|
||||
func (r *SRegion) GetIVMs() ([]cloudprovider.ICloudVM, error) {
|
||||
vms, err := r.GetInstances("", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudVM{}
|
||||
for i := range vms {
|
||||
vms[i].region = r
|
||||
ret = append(ret, &vms[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *SRegion) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
|
||||
vm, err := r.GetInstance(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vm.region = r
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) ecsRequest(action string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
return region.client.ec2Request(region.Region, action, params)
|
||||
}
|
||||
|
||||
+4
-4
@@ -32,9 +32,9 @@ import (
|
||||
)
|
||||
|
||||
var RegionLocations = map[string]string{
|
||||
"cn-beijing": "华东2(北京)",
|
||||
"cn-beijing": "华北2(北京)",
|
||||
"cn-shanghai": "华东2(上海)",
|
||||
"cn-guangzhou": "华东2(广州)",
|
||||
"cn-guangzhou": "华南1(广州)",
|
||||
"ap-southeast-1": "亚太东南(柔佛)",
|
||||
"cn-hongkong": "中国香港",
|
||||
}
|
||||
@@ -88,9 +88,9 @@ func (region *SRegion) GetId() string {
|
||||
|
||||
func (region *SRegion) GetName() string {
|
||||
if localName, ok := RegionLocations[region.RegionId]; ok {
|
||||
return fmt.Sprintf("%s %s", CLOUD_PROVIDER_VOLCENGINE_CN, localName)
|
||||
return localName
|
||||
}
|
||||
return fmt.Sprintf("%s %s", CLOUD_PROVIDER_VOLCENGINE_CN, region.RegionId)
|
||||
return region.RegionId
|
||||
}
|
||||
|
||||
func (region *SRegion) GetGlobalId() string {
|
||||
|
||||
+29
-17
@@ -39,28 +39,32 @@ func Unquote(str string) string {
|
||||
}
|
||||
|
||||
func findString(str []byte, offset int) (string, int) {
|
||||
return _findWord(str, offset, "\n\r")
|
||||
return _findWord(str, offset, "\n\r", isQuoteCharInternal)
|
||||
}
|
||||
|
||||
func findWord(str []byte, offset int) (string, int) {
|
||||
return _findWord(str, offset, " :,\t\n}]")
|
||||
return _findWord(str, offset, " :,\t\n}]", isQuoteCharInternal)
|
||||
}
|
||||
|
||||
func _findWord(str []byte, offset int, sepChars string) (string, int) {
|
||||
func isQuoteCharInternal(ch byte) (bool, string) {
|
||||
switch ch {
|
||||
case '"':
|
||||
return true, "\""
|
||||
case '\'':
|
||||
return true, "'"
|
||||
default:
|
||||
return false, ""
|
||||
}
|
||||
}
|
||||
|
||||
func _findWord(str []byte, offset int, sepChars string, isQuoteChar func(ch byte) (bool, string)) (string, int) {
|
||||
var buffer bytes.Buffer
|
||||
i := skipEmpty(str, offset)
|
||||
if i >= len(str) {
|
||||
return "", i
|
||||
}
|
||||
var endstr string
|
||||
quote := false
|
||||
if str[i] == '"' {
|
||||
quote = true
|
||||
endstr = "\""
|
||||
i++
|
||||
} else if str[i] == '\'' {
|
||||
quote = true
|
||||
endstr = "'"
|
||||
quote, endstr := isQuoteChar(str[i])
|
||||
if quote {
|
||||
i++
|
||||
} else {
|
||||
// endstr = " :,\t\n\r}]"
|
||||
@@ -98,22 +102,30 @@ func _findWord(str []byte, offset int, sepChars string) (string, int) {
|
||||
}
|
||||
|
||||
func FindWords(str []byte, offset int) []string {
|
||||
words, err := FindWords2(str, offset, " :,\t\n}]", isQuoteCharInternal)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return words
|
||||
}
|
||||
|
||||
func FindWords2(str []byte, offset int, sepChars string, isQuoteChar func(ch byte) (bool, string)) ([]string, error) {
|
||||
words := make([]string, 0)
|
||||
for offset < len(str) {
|
||||
word, i := findWord(str, offset)
|
||||
word, i := _findWord(str, offset, sepChars, isQuoteChar)
|
||||
words = append(words, word)
|
||||
i = skipEmpty(str, i)
|
||||
if i < len(str) {
|
||||
if str[i] == ',' {
|
||||
if strings.IndexByte(sepChars, str[i]) >= 0 {
|
||||
offset = i + 1
|
||||
} else {
|
||||
panic(fmt.Sprintf("Malformed multi value string: %s", string(str[offset:])))
|
||||
return nil, fmt.Errorf("Malformed multi value string: %s", string(str[offset:]))
|
||||
}
|
||||
} else {
|
||||
offset = i
|
||||
}
|
||||
}
|
||||
return words
|
||||
return words, nil
|
||||
}
|
||||
|
||||
func TagMap(tag reflect.StructTag) map[string]string {
|
||||
@@ -155,7 +167,7 @@ func SplitCSV(csv string) []string {
|
||||
str := []byte(csv)
|
||||
for offset < len(str) {
|
||||
var word string
|
||||
word, offset = _findWord(str, offset, ",\r\n")
|
||||
word, offset = _findWord(str, offset, ",\r\n", isQuoteCharInternal)
|
||||
words = append(words, word)
|
||||
if offset < len(str) {
|
||||
offset++
|
||||
|
||||
Reference in New Issue
Block a user