fix: save container image panic (#25786)

Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
This commit is contained in:
Zexi Li
2026-10-07 17:45:23 +08:00
committed by GitHub
co-authored by Qiu Jian
parent 4a08c9545d
commit bd61d7c2fc
3 changed files with 168 additions and 13 deletions
+5
View File
@@ -294,10 +294,15 @@ type KubeServerContainerRegistryConfigHarbor struct {
KubeServerContainerRegistryConfigCommon
}
type KubeServerContainerRegistryConfigCustom struct {
KubeServerContainerRegistryConfigCommon
}
type KubeServerContainerRegistryConfig struct {
Type string `json:"type"`
Common *KubeServerContainerRegistryConfigCommon `json:"common"`
Harbor *KubeServerContainerRegistryConfigHarbor `json:"harbor"`
Custom *KubeServerContainerRegistryConfigCustom `json:"custom"`
}
type KubeServerContainerRegistryDetails struct {
+119
View File
@@ -0,0 +1,119 @@
// 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 models
import (
"testing"
"yunion.io/x/onecloud/pkg/apis"
api "yunion.io/x/onecloud/pkg/apis/compute"
hostapi "yunion.io/x/onecloud/pkg/apis/host"
)
func TestContainerCommitRegistryAuthNilCommon(t *testing.T) {
reg := &api.KubeServerContainerRegistryDetails{
Type: "common",
Config: &api.KubeServerContainerRegistryConfig{
Type: "common",
},
}
hostInput := &hostapi.ContainerCommitInput{
Auth: new(apis.ContainerPullImageAuthConfig),
}
if err := applyContainerCommitRegistryAuth(hostInput, reg); err != nil {
t.Fatalf("anonymous common registry: %v", err)
}
if hostInput.Auth.Username != "" || hostInput.Auth.Password != "" {
t.Fatalf("expected empty auth, got %#v", hostInput.Auth)
}
}
func TestContainerCommitRegistryAuth(t *testing.T) {
cases := []struct {
name string
reg *api.KubeServerContainerRegistryDetails
user string
pass string
}{
{
name: "common",
reg: &api.KubeServerContainerRegistryDetails{
Type: "common",
Config: &api.KubeServerContainerRegistryConfig{
Common: &api.KubeServerContainerRegistryConfigCommon{Username: "u", Password: "p"},
},
},
user: "u",
pass: "p",
},
{
name: "harbor",
reg: &api.KubeServerContainerRegistryDetails{
Type: "harbor",
Config: &api.KubeServerContainerRegistryConfig{
Harbor: &api.KubeServerContainerRegistryConfigHarbor{
KubeServerContainerRegistryConfigCommon: api.KubeServerContainerRegistryConfigCommon{Username: "hu", Password: "hp"},
},
},
},
user: "hu",
pass: "hp",
},
{
name: "custom",
reg: &api.KubeServerContainerRegistryDetails{
Type: "custom",
Config: &api.KubeServerContainerRegistryConfig{
Custom: &api.KubeServerContainerRegistryConfigCustom{
KubeServerContainerRegistryConfigCommon: api.KubeServerContainerRegistryConfigCommon{Username: "cu", Password: "cp"},
},
},
},
user: "cu",
pass: "cp",
},
{
name: "harbor without config section",
reg: &api.KubeServerContainerRegistryDetails{
Type: "harbor",
Config: &api.KubeServerContainerRegistryConfig{Type: "harbor"},
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
hostInput := &hostapi.ContainerCommitInput{
Auth: new(apis.ContainerPullImageAuthConfig),
}
if err := applyContainerCommitRegistryAuth(hostInput, tc.reg); err != nil {
t.Fatal(err)
}
if hostInput.Auth.Username != tc.user || hostInput.Auth.Password != tc.pass {
t.Fatalf("auth = %#v, want user=%q pass=%q", hostInput.Auth, tc.user, tc.pass)
}
})
}
}
func TestContainerCommitRegistryAuthInvalidType(t *testing.T) {
reg := &api.KubeServerContainerRegistryDetails{
Type: "unknown",
Config: &api.KubeServerContainerRegistryConfig{},
}
err := applyContainerCommitRegistryAuth(&hostapi.ContainerCommitInput{}, reg)
if err == nil {
t.Fatal("expected error for unknown registry type")
}
}
+44 -13
View File
@@ -1418,6 +1418,48 @@ func (c *SContainer) PerformStatus(ctx context.Context, userCred mcclient.TokenC
return c.SVirtualResourceBase.PerformStatus(ctx, userCred, query, input.PerformStatusInput)
}
// containerCommitRegistryAuth returns the username/password section for a registry.
// A nil result means the registry has no credentials (anonymous pull/push is allowed).
// Callers must not dereference the nested config directly: glance omits common/harbor/custom
// when the registry has no credential, and that nil pointer used to panic in PerformCommit.
func containerCommitRegistryAuth(reg *api.KubeServerContainerRegistryDetails) (*api.KubeServerContainerRegistryConfigCommon, error) {
if reg == nil || reg.Config == nil {
return nil, nil
}
switch reg.Type {
case "common":
return reg.Config.Common, nil
case "harbor":
if reg.Config.Harbor == nil {
return nil, nil
}
return &reg.Config.Harbor.KubeServerContainerRegistryConfigCommon, nil
case "custom":
if reg.Config.Custom == nil {
return nil, nil
}
return &reg.Config.Custom.KubeServerContainerRegistryConfigCommon, nil
default:
return nil, httperrors.NewInputParameterError("invalid registry type %s", reg.Type)
}
}
func applyContainerCommitRegistryAuth(hostInput *hostapi.ContainerCommitInput, reg *api.KubeServerContainerRegistryDetails) error {
cfg, err := containerCommitRegistryAuth(reg)
if err != nil {
return err
}
if cfg == nil {
return nil
}
if hostInput.Auth == nil {
hostInput.Auth = new(apis.ContainerPullImageAuthConfig)
}
hostInput.Auth.Username = cfg.Username
hostInput.Auth.Password = cfg.Password
return nil
}
func (c *SContainer) getContainerHostCommitInput(ctx context.Context, userCred mcclient.TokenCredential, input *api.ContainerCommitInput) (*hostapi.ContainerCommitInput, error) {
var hostInput = &hostapi.ContainerCommitInput{
Auth: new(apis.ContainerPullImageAuthConfig),
@@ -1469,19 +1511,8 @@ func (c *SContainer) getContainerHostCommitInput(ctx context.Context, userCred m
}
}
repoUrl = reg.Url
if reg.Config != nil {
switch reg.Type {
case "common":
cfg := reg.Config.Common
hostInput.Auth.Username = cfg.Username
hostInput.Auth.Password = cfg.Password
case "harbor":
cfg := reg.Config.Harbor
hostInput.Auth.Username = cfg.Username
hostInput.Auth.Password = cfg.Password
default:
return nil, httperrors.NewInputParameterError("invalid registry type %s", reg.Type)
}
if err := applyContainerCommitRegistryAuth(hostInput, reg); err != nil {
return nil, err
}
} else if input.ExternalRegistry != nil {
repoUrl = input.ExternalRegistry.Url