Compare commits

...

3 Commits

Author SHA1 Message Date
Jian Qiu
472b0ae4af fix: task params nil params panic (#25699)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2026-09-18 11:57:35 +08:00
Zexi Li
d108efd764 fix(baremetal): ignore RetrieveStorages leftover diagnostics when disks match (#25695)
Only fail buildRaid when no disk is selected, matching CalculateLayout behavior.
2026-09-18 11:17:09 +08:00
Zexi Li
1d1aea797c fix(host): mount kunlunxin xpu-smi and ml lib for containers (#25693)
Ensure container runtime mounts xpu-smi and libxpunvidia-ml.so.1
(including symlink targets) so XPU tooling works inside pods.
2026-09-18 10:58:01 +08:00
12 changed files with 396 additions and 71 deletions

2
go.mod
View File

@@ -122,7 +122,7 @@ require (
yunion.io/x/jsonutils v1.0.1-0.20260917025845-3108cd9a32ea
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.4-0.20260916163305-b7743fa5e758
yunion.io/x/pkg v1.10.4-0.20260918012554-27cd9d2e093b
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1
yunion.io/x/sqlchemy v1.1.3-0.20260917035232-d682485b3a12
yunion.io/x/structarg v0.0.0-20260917033311-96c7653334ac

3
go.sum
View File

@@ -1989,8 +1989,9 @@ 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-20190628082551-f4033ba2ea30/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v0.0.0-20200814072949-4f1b541857d6/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v1.10.4-0.20260916163305-b7743fa5e758 h1:3p/GCOPvDfvlSfovOh9GKxsSa7PPqp//DUjGRX8Wyus=
yunion.io/x/pkg v1.10.4-0.20260916163305-b7743fa5e758/go.mod h1:Crp6aUYB+Yq16DFjZPHhHq7ha5HL2DfyPvzCo2LJ+LU=
yunion.io/x/pkg v1.10.4-0.20260918012554-27cd9d2e093b h1:XUJLpD48685DE8hwxEC+ms1F4T6/g2KKAfGuVn8rZeo=
yunion.io/x/pkg v1.10.4-0.20260918012554-27cd9d2e093b/go.mod h1:S8ITTpENaL3qSXi8Y4L6wVbVlP8XZ5BvABO10DFqF7M=
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.20260917035232-d682485b3a12 h1:xU8Z/Zg+zc199Y58eB3fA8mvOHsA28UCDfd7OuK3QKA=

View File

@@ -123,10 +123,12 @@ func buildRaid(driver raid.IRaidDriver, adapter raid.IRaidAdapter, confs []*api.
for _, conf := range confs {
selected, left, err = baremetal.RetrieveStorages(conf, left)
if err != nil {
return errors.Wrap(err, "baremetal.RetrieveStorages")
}
// RetrieveStorages also returns match diagnostics for leftover disks in rest;
// only treat it as failure when no disk was selected (same as CalculateLayout).
if len(selected) == 0 {
if err != nil {
return errors.Wrapf(err, "no enough disks for config %#v", conf)
}
return errors.Wrapf(httperrors.ErrInputParameter, "no enough disks for config %#v", conf)
}
var err error

View File

@@ -951,7 +951,22 @@ func (task *STask) ClearPendingUsage(index int) error {
}
func (task *STask) GetParams() *jsonutils.JSONDict {
return task.Params
result := jsonutils.NewDict()
if task.Params == nil {
return result
}
copied := task.Params.DeepCopy()
copyParams, ok := copied.(*jsonutils.JSONDict)
if !ok || copyParams == nil {
return result
}
paramsJsonMap, _ := copyParams.GetMap()
for k, v := range paramsJsonMap {
if !strings.HasPrefix(k, "__") {
result.Set(k, v)
}
}
return result
}
func (task *STask) GetUserCred() mcclient.TokenCredential {

View File

@@ -0,0 +1,144 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package taskman
import (
"testing"
"yunion.io/x/jsonutils"
)
func TestSTaskGetParams(t *testing.T) {
newParams := func(pairs map[string]jsonutils.JSONObject) *jsonutils.JSONDict {
params := jsonutils.NewDict()
for k, v := range pairs {
params.Set(k, v)
}
return params
}
nested := jsonutils.NewDict()
nested.Set("ip", jsonutils.NewString("10.0.0.1"))
stages := jsonutils.NewArray(
jsonutils.NewDict(),
jsonutils.NewString("on_init"),
)
cases := []struct {
name string
params *jsonutils.JSONDict
want *jsonutils.JSONDict
}{
{
name: "nil_params",
params: nil,
want: jsonutils.NewDict(),
},
{
name: "empty_params",
params: jsonutils.NewDict(),
want: jsonutils.NewDict(),
},
{
name: "keep_public_keys",
params: newParams(map[string]jsonutils.JSONObject{
"parent_task_id": jsonutils.NewString("task-1"),
"auto_start": jsonutils.JSONTrue,
}),
want: newParams(map[string]jsonutils.JSONObject{
"parent_task_id": jsonutils.NewString("task-1"),
"auto_start": jsonutils.JSONTrue,
}),
},
{
name: "drop_internal_keys",
params: newParams(map[string]jsonutils.JSONObject{
"__stages": stages,
"__pending_usage__": jsonutils.NewDict(),
"__request_context": jsonutils.NewDict(),
"__parent_task_notifyurl": jsonutils.NewString("http://notify"),
}),
want: jsonutils.NewDict(),
},
{
name: "mixed_keys",
params: newParams(map[string]jsonutils.JSONObject{
"desc": nested,
"__stages": stages,
"_private": jsonutils.NewString("keep"),
"__": jsonutils.NewString("drop"),
"guest_id": jsonutils.NewString("g-1"),
}),
want: newParams(map[string]jsonutils.JSONObject{
"desc": nested,
"_private": jsonutils.NewString("keep"),
"guest_id": jsonutils.NewString("g-1"),
}),
},
}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
task := &STask{}
task.Params = c.params
got := task.GetParams()
if got == nil {
t.Fatal("GetParams() returned nil, want empty dict")
}
if !got.Equals(c.want) {
t.Fatalf("GetParams() = %s, want %s", got, c.want)
}
})
}
}
func TestSTaskGetParamsDoesNotMutateOriginal(t *testing.T) {
orig := jsonutils.NewDict()
orig.Set("guest_id", jsonutils.NewString("g-1"))
orig.Set("__stages", jsonutils.NewArray(jsonutils.NewString("on_init")))
nested := jsonutils.NewDict()
nested.Set("ip", jsonutils.NewString("10.0.0.1"))
orig.Set("desc", nested)
task := &STask{STaskBase: STaskBase{Params: orig}}
got := task.GetParams()
got.Set("guest_id", jsonutils.NewString("g-2"))
got.Set("extra", jsonutils.JSONTrue)
desc, err := got.Get("desc")
if err != nil {
t.Fatalf("get desc: %v", err)
}
descDict, ok := desc.(*jsonutils.JSONDict)
if !ok {
t.Fatalf("desc type %T, want *jsonutils.JSONDict", desc)
}
descDict.Set("ip", jsonutils.NewString("10.0.0.2"))
if got.Contains("__stages") {
t.Fatal("GetParams() leaked internal key __stages")
}
guestId, _ := orig.GetString("guest_id")
if guestId != "g-1" {
t.Fatalf("original guest_id = %s, want g-1", guestId)
}
if !orig.Contains("__stages") {
t.Fatal("original __stages was removed")
}
origIp, _ := orig.GetString("desc", "ip")
if origIp != "10.0.0.1" {
t.Fatalf("original nested ip = %s, want 10.0.0.1", origIp)
}
}

View File

@@ -356,6 +356,7 @@ func (self *SBaremetalGuestDriver) RequestStartOnHost(ctx context.Context, guest
if params.Length() > 0 {
config.Add(params, "params")
}
log.Debugf("RequestStartOnHost config: %s", config.String())
headers := task.GetTaskRequestHeader()
url := fmt.Sprintf("/baremetals/%s/servers/%s/start", host.Id, guest.Id)
_, err := host.BaremetalSyncRequest(ctx, "POST", url, headers, config)

View File

@@ -78,7 +78,13 @@ func (m *kunlunxinXPUManager) NewContainerDevices(input *hostapi.ContainerCreate
func (m *kunlunxinXPUManager) GetContainerExtraConfigures(devs []*hostapi.ContainerDevice) ([]*runtimeapi.KeyValue, []*runtimeapi.Mount) {
indices := collectKunlunxinXpuVisibleIndices(devs)
return buildKunlunxinXpuExtraConfigures(indices, kunlunxinXreHome(), hygonPathExists)
return buildKunlunxinXpuExtraConfigures(
indices,
kunlunxinXreHome(),
kunlunxinXpuSmiPath(),
hygonPathExists,
procutils.RemoteReadlink,
)
}
type kunlunxinXPU struct {

View File

@@ -22,12 +22,15 @@ import (
"strings"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
"yunion.io/x/log"
)
const (
defaultKunlunxinXreHome = "/usr/local/xpu"
defaultKunlunxinXpuSmiPath = "/usr/local/bin/xpu-smi"
kunlunxinXpuDevicePrefix = "/dev/xpu"
kunlunxinXpuMlLibName = "libxpunvidia-ml.so.1"
)
var kunlunxinXpuCommonDevicePaths = []string{
@@ -186,23 +189,99 @@ func buildKunlunxinXpuRuntimeEnvs(indices []string, xreHome string, pathExists f
}
}
func buildKunlunxinXpuRuntimeMounts(xreHome string, pathExists func(string) bool) []*runtimeapi.Mount {
func kunlunxinXpuMlLibPathCandidates(xreHome string) []string {
xreHome = normalizeKunlunxinXreHome(xreHome)
return []string{
path.Join("/lib/x86_64-linux-gnu", kunlunxinXpuMlLibName),
path.Join("/usr/lib/x86_64-linux-gnu", kunlunxinXpuMlLibName),
path.Join(xreHome, "so", kunlunxinXpuMlLibName),
path.Join(xreHome, "lib64", kunlunxinXpuMlLibName),
path.Join(xreHome, "lib", kunlunxinXpuMlLibName),
}
}
func resolveKunlunxinXpuMlLibPath(xreHome string, pathExists func(string) bool) string {
if pathExists == nil {
return ""
}
for _, p := range kunlunxinXpuMlLibPathCandidates(xreHome) {
if pathExists(p) {
return p
}
}
return ""
}
// collectSymlinkMountPaths returns start plus the final path from readlink
// (RemoteReadlink uses `readlink -f`, which resolves to the canonical target in one call).
func collectSymlinkMountPaths(start string, readlink func(string) (string, error)) []string {
if start == "" {
return nil
}
start = path.Clean(start)
out := []string{start}
if readlink == nil {
return out
}
final, err := readlink(start)
if err != nil || final == "" {
return out
}
final = path.Clean(final)
if final == start {
return out
}
return append(out, final)
}
func appendReadonlyMountIfExists(mounts []*runtimeapi.Mount, hostPath string, pathExists func(string) bool, seen map[string]bool) []*runtimeapi.Mount {
if hostPath == "" || pathExists == nil || !pathExists(hostPath) || seen[hostPath] {
return mounts
}
seen[hostPath] = true
return append(mounts, &runtimeapi.Mount{
ContainerPath: hostPath,
HostPath: hostPath,
Readonly: true,
})
}
func buildKunlunxinXpuRuntimeMounts(
xreHome, smiPath string,
pathExists func(string) bool,
readlink func(string) (string, error),
) []*runtimeapi.Mount {
xreHome = normalizeKunlunxinXreHome(xreHome)
if pathExists == nil || !pathExists(xreHome) {
return nil
}
return []*runtimeapi.Mount{
{
ContainerPath: xreHome,
HostPath: xreHome,
Readonly: true,
},
seen := map[string]bool{}
mounts := appendReadonlyMountIfExists(nil, xreHome, pathExists, seen)
mounts = appendReadonlyMountIfExists(mounts, smiPath, pathExists, seen)
mlPath := resolveKunlunxinXpuMlLibPath(xreHome, pathExists)
if mlPath == "" {
return mounts
}
for _, p := range collectSymlinkMountPaths(mlPath, readlink) {
if pathExists(p) {
mounts = appendReadonlyMountIfExists(mounts, p, pathExists, seen)
continue
}
log.Warningf("kunlunxin xpu ml lib symlink target %s not found, skip mount", p)
}
return mounts
}
func buildKunlunxinXpuExtraConfigures(indices []string, xreHome string, pathExists func(string) bool) ([]*runtimeapi.KeyValue, []*runtimeapi.Mount) {
func buildKunlunxinXpuExtraConfigures(
indices []string,
xreHome, smiPath string,
pathExists func(string) bool,
readlink func(string) (string, error),
) ([]*runtimeapi.KeyValue, []*runtimeapi.Mount) {
if len(indices) == 0 {
return nil, nil
}
return buildKunlunxinXpuRuntimeEnvs(indices, xreHome, pathExists), buildKunlunxinXpuRuntimeMounts(xreHome, pathExists)
return buildKunlunxinXpuRuntimeEnvs(indices, xreHome, pathExists),
buildKunlunxinXpuRuntimeMounts(xreHome, smiPath, pathExists, readlink)
}

View File

@@ -15,6 +15,7 @@
package container_device
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
@@ -78,7 +79,7 @@ func TestBuildKunlunxinXpuExtraConfigures(t *testing.T) {
exists := func(p string) bool {
return p == "/usr/local/xpu" || p == "/usr/local/xpu/lib"
}
envs, mounts := buildKunlunxinXpuExtraConfigures([]string{"0", "3"}, "/usr/local/xpu", exists)
envs, mounts := buildKunlunxinXpuExtraConfigures([]string{"0", "3"}, "/usr/local/xpu", "/usr/local/bin/xpu-smi", exists, nil)
require.Len(t, envs, 2)
assert.Equal(t, "XPU_VISIBLE_DEVICES", envs[0].Key)
assert.Equal(t, "0,3", envs[0].Value)
@@ -88,11 +89,76 @@ func TestBuildKunlunxinXpuExtraConfigures(t *testing.T) {
assert.Equal(t, "/usr/local/xpu", mounts[0].HostPath)
assert.True(t, mounts[0].Readonly)
envs, mounts = buildKunlunxinXpuExtraConfigures(nil, "/usr/local/xpu", exists)
envs, mounts = buildKunlunxinXpuExtraConfigures(nil, "/usr/local/xpu", "/usr/local/bin/xpu-smi", exists, nil)
assert.Nil(t, envs)
assert.Nil(t, mounts)
}
func TestBuildKunlunxinXpuRuntimeMountsWithSmiAndMlLib(t *testing.T) {
smi := "/usr/local/bin/xpu-smi"
ml := "/lib/x86_64-linux-gnu/libxpunvidia-ml.so.1"
exists := func(p string) bool {
switch p {
case "/usr/local/xpu", "/usr/local/xpu/lib", smi, ml:
return true
default:
return false
}
}
mounts := buildKunlunxinXpuRuntimeMounts("/usr/local/xpu", smi, exists, nil)
require.Len(t, mounts, 3)
assert.Equal(t, "/usr/local/xpu", mounts[0].HostPath)
assert.Equal(t, smi, mounts[1].HostPath)
assert.Equal(t, ml, mounts[2].HostPath)
for _, m := range mounts {
assert.Equal(t, m.HostPath, m.ContainerPath)
assert.True(t, m.Readonly)
}
}
func TestCollectSymlinkMountPathsReadlinkF(t *testing.T) {
link := "/lib/x86_64-linux-gnu/libxpunvidia-ml.so.1"
final := "/lib/x86_64-linux-gnu/libxpunvidia-ml.so.1.0.0"
readlink := func(p string) (string, error) {
if p == link {
return final, nil // RemoteReadlink: readlink -f
}
return "", os.ErrInvalid
}
paths := collectSymlinkMountPaths(link, readlink)
assert.Equal(t, []string{link, final}, paths)
assert.Equal(t, []string{link}, collectSymlinkMountPaths(link, nil))
same := func(p string) (string, error) { return p, nil }
assert.Equal(t, []string{link}, collectSymlinkMountPaths(link, same))
}
func TestBuildKunlunxinXpuRuntimeMountsFollowsSymlink(t *testing.T) {
smi := "/usr/local/bin/xpu-smi"
link := "/lib/x86_64-linux-gnu/libxpunvidia-ml.so.1"
real := "/lib/x86_64-linux-gnu/libxpunvidia-ml.so.1.0.0"
exists := func(p string) bool {
switch p {
case "/usr/local/xpu", smi, link, real:
return true
default:
return false
}
}
readlink := func(p string) (string, error) {
if p == link {
return real, nil
}
return "", os.ErrInvalid
}
mounts := buildKunlunxinXpuRuntimeMounts("/usr/local/xpu", smi, exists, readlink)
require.Len(t, mounts, 4)
assert.Equal(t, "/usr/local/xpu", mounts[0].HostPath)
assert.Equal(t, smi, mounts[1].HostPath)
assert.Equal(t, link, mounts[2].HostPath)
assert.Equal(t, real, mounts[3].HostPath)
}
func TestParseKunlunxinXpuNodeIndex(t *testing.T) {
idx, ok := parseKunlunxinXpuNodeIndex("xpu3")
assert.True(t, ok)

2
vendor/modules.txt vendored
View File

@@ -2788,7 +2788,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.4-0.20260916163305-b7743fa5e758
# yunion.io/x/pkg v1.10.4-0.20260918012554-27cd9d2e093b
## explicit; go 1.18
yunion.io/x/pkg/appctx
yunion.io/x/pkg/errors

View File

@@ -57,7 +57,7 @@ func expandAmbiguousPrefix(fields SStructFieldValueSet) SStructFieldValueSet {
if prefixed[idx] {
continue
}
amPrefix, ok := fields[idx].Info.Tags[TAG_AMBIGUOUS_PREFIX]
amPrefix, ok := fields[idx].Info.Tag(TAG_AMBIGUOUS_PREFIX)
if !ok {
continue
}
@@ -65,15 +65,25 @@ func expandAmbiguousPrefix(fields SStructFieldValueSet) SStructFieldValueSet {
if takenByOther(fields, expanded, indexes) {
continue
}
fields[idx].Info.Name = expanded
if depBy, ok := fields[idx].Info.Tags[TAG_DEPRECATED_BY]; ok {
fields[idx].Info.Tags[TAG_DEPRECATED_BY] = fmt.Sprintf("%s%s", amPrefix, depBy)
info := fields[idx].Info
info.Name = expanded
_, newDepBy := info.tags[TAG_DEPRECATED_BY]
_, oldDepBy := info.tags[TAG_OLD_DEPRECATED_BY]
if newDepBy || oldDepBy {
// the tags may still be shared with other callers
info.copyTags()
}
if depBy, ok := fields[idx].Info.Tags[TAG_OLD_DEPRECATED_BY]; ok {
fields[idx].Info.Tags[TAG_OLD_DEPRECATED_BY] = fmt.Sprintf("%s%s", amPrefix, depBy)
if newDepBy {
info.tags[TAG_DEPRECATED_BY] = fmt.Sprintf("%s%s", amPrefix, info.tags[TAG_DEPRECATED_BY])
}
for i := range fields[idx].Info.Aliases {
fields[idx].Info.Aliases[i] = fmt.Sprintf("%s%s", amPrefix, fields[idx].Info.Aliases[i])
if oldDepBy {
info.tags[TAG_OLD_DEPRECATED_BY] = fmt.Sprintf("%s%s", amPrefix, info.tags[TAG_OLD_DEPRECATED_BY])
}
if len(info.aliases) > 0 {
info.copyAliases()
for i := range info.aliases {
info.aliases[i] = fmt.Sprintf("%s%s", amPrefix, info.aliases[i])
}
}
prefixed[idx] = true
changed = true

View File

@@ -30,9 +30,10 @@ import (
// package. Do not construct a literal or modify the exported fields in an
// unmanaged way
//
// The tags of a field are meant to be read through Tag and TagMap rather than
// off the Tags map, which is not handed out to stay writable by whoever read
// it; a caller may modify what TagMap returns.
// The tags of a field are read through Tag and TagMap. The map behind them
// is shared with the other callers of the fetch functions, which is what
// makes reading a field several times cheap, so a caller may only modify what
// TagMap hands out, which is a copy of its own.
type SStructFieldInfo struct {
// True if the field has json tag `json:"-"`
Ignore bool
@@ -72,40 +73,34 @@ type SStructFieldInfo struct {
// value as a json string
ForceString bool
// Tags holds the tags of the field keyed by tag name, a tag without a
// value being mapped to the empty string. Read it through Tag or
// TagMap rather than off here.
Tags map[string]string
// tags holds the tags of the field keyed by tag name, a tag without a
// value being mapped to the empty string. The map is shared with the
// other callers of the fetch functions, so it has to be copied with
// copyTags before being written to; read it through Tag or TagMap.
tags map[string]string
// Aliases are the other names the field is looked up by, taken from
// the "alias" tag
Aliases []string
// aliases are the other names the field is looked up by, taken from
// the "alias" tag. Like tags it is shared, and has to be copied with
// copyAliases before being written to.
aliases []string
}
func (s *SStructFieldInfo) updateTags(k, v string) {
s.Tags[k] = v
}
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,
kebabFieldName: s.kebabFieldName,
}
tags := make(map[string]string, len(s.Tags))
for k, v := range s.Tags {
// copyTags takes a private copy of the tags, so that they can be written to
// without touching the ones this info was read out of.
func (s *SStructFieldInfo) copyTags() {
tags := make(map[string]string, len(s.tags)+1)
for k, v := range s.tags {
tags[k] = v
}
scopy.Tags = tags
aliases := make([]string, len(s.Aliases))
copy(aliases, s.Aliases)
scopy.Aliases = aliases
return &scopy
s.tags = tags
}
// copyAliases takes a private copy of the aliases, so that they can be
// written to without touching the ones this info was read out of.
func (s *SStructFieldInfo) copyAliases() {
aliases := make([]string, len(s.aliases))
copy(aliases, s.aliases)
s.aliases = aliases
}
func ParseStructFieldJsonInfo(sf reflect.StructField) SStructFieldInfo {
@@ -120,8 +115,8 @@ func ParseFieldJsonInfo(name string, tag reflect.StructTag) SStructFieldInfo {
info.OmitZero = false
info.OmitFalse = false
info.Tags = utils.TagMap(tag)
if val, ok := info.Tags["json"]; ok {
info.tags = utils.TagMap(tag)
if val, ok := info.tags["json"]; ok {
keys := strings.Split(val, ",")
if len(keys) > 0 {
if keys[0] == "-" {
@@ -155,14 +150,14 @@ func ParseFieldJsonInfo(name string, tag reflect.StructTag) SStructFieldInfo {
}
}
}
if val, ok := info.Tags["name"]; ok {
if val, ok := info.tags["name"]; ok {
info.Name = val
}
if !info.Ignore && len(info.Name) == 0 {
info.Name = info.kebabFieldName
}
if val, ok := info.Tags["alias"]; !info.Ignore && ok {
info.Aliases = strings.Split(val, ",")
if val, ok := info.tags["alias"]; !info.Ignore && ok {
info.aliases = strings.Split(val, ",")
}
return info
}
@@ -179,15 +174,15 @@ func (info *SStructFieldInfo) MarshalName() string {
// Tag returns the value of the tag named name and whether the field has it.
// A tag without a value is reported as present with an empty value.
func (info *SStructFieldInfo) Tag(name string) (string, bool) {
val, ok := info.Tags[name]
val, ok := info.tags[name]
return val, ok
}
// TagMap returns a copy of the tags of the field, which the caller owns and
// is free to modify.
func (info *SStructFieldInfo) TagMap() map[string]string {
tags := make(map[string]string, len(info.Tags))
for k, v := range info.Tags {
tags := make(map[string]string, len(info.tags))
for k, v := range info.tags {
tags[k] = v
}
return tags
@@ -375,10 +370,10 @@ func fetchStructFieldValueSet3(dataValue reflect.Value, allocatePtr bool, tags m
continue
}
}
fieldInfo := fieldInfos[sf.Name].deepCopy()
fieldInfo := fieldInfos[sf.Name]
if !fieldInfo.Ignore || includeIgnore {
structFieldVaule := SStructFieldValue{
Info: fieldInfo,
Info: &fieldInfo,
Value: fv,
}
if parent != nil {
@@ -390,6 +385,7 @@ func fetchStructFieldValueSet3(dataValue reflect.Value, allocatePtr bool, tags m
if len(tags) > 0 {
for i := range fields {
fieldName := fields[i].Info.MarshalName()
owned := false
for k, v := range tags {
target := ""
pos := strings.Index(k, "->")
@@ -400,7 +396,12 @@ func fetchStructFieldValueSet3(dataValue reflect.Value, allocatePtr bool, tags m
if len(target) > 0 && target != fieldName {
continue
}
fields[i].Info.updateTags(k, v)
if !owned {
// the tags may still be shared with other callers
fields[i].Info.copyTags()
owned = true
}
fields[i].Info.tags[k] = v
}
}
}
@@ -445,7 +446,7 @@ func (fields SStructFieldValueSet) GetStructFieldIndexes2(name string, strictMod
ret = append(ret, i)
} else if info.FieldName == capName {
ret = append(ret, i)
} else if len(info.Aliases) > 0 && utils.IsInArray(name, info.Aliases) {
} else if len(info.aliases) > 0 && utils.IsInArray(name, info.aliases) {
ret = append(ret, i)
}
}