feat(image-cri-shim): sync inline registries to registry.d (#6075)

feat: sync inline registries to registry.d
This commit is contained in:
cuisongliu
2025-10-13 15:27:44 +08:00
committed by GitHub
parent da87d765fd
commit ad7010d297
2 changed files with 128 additions and 0 deletions
@@ -17,6 +17,7 @@ limitations under the License.
package types
import (
"bytes"
"crypto/sha256"
"errors"
"fmt"
@@ -63,6 +64,7 @@ type Config struct {
ReloadInterval metav1.Duration `json:"reloadInterval"`
Auth string `json:"auth"`
RegistryDir string `json:"registry.d"`
Registries []Registry `json:"registries" yaml:"registries,omitempty"`
}
type ShimAuthConfig struct {
@@ -78,6 +80,9 @@ func registryMatchDomain(reg Registry) string {
func (c *Config) PreProcess() (*ShimAuthConfig, error) {
c.RegistryDir = NormalizeRegistryDir(c.RegistryDir)
if err := syncRegistriesToDir(c.RegistryDir, c.Registries); err != nil {
return nil, err
}
registries, err := loadRegistriesFromDir(c.RegistryDir)
if err != nil {
return nil, err
@@ -208,6 +213,77 @@ func NormalizeRegistryDir(dir string) string {
return dir
}
func syncRegistriesToDir(dir string, registries []Registry) error {
if len(registries) == 0 {
return nil
}
dir = NormalizeRegistryDir(dir)
if err := os.MkdirAll(dir, 0o755); err != nil {
return fmt.Errorf("ensure registry.d %s: %w", dir, err)
}
for _, reg := range registries {
address := strings.TrimSpace(reg.Address)
if address == "" {
continue
}
host := registryFileName(address)
if host == "" {
logger.Warn("skip registry entry %q: unable to determine filename", address)
continue
}
data, err := yaml.Marshal(reg)
if err != nil {
return fmt.Errorf("marshal registry %s: %w", host, err)
}
path := filepath.Join(dir, fmt.Sprintf("%s.yaml", host))
if existing, err := os.ReadFile(path); err == nil {
if bytes.Equal(bytes.TrimSpace(existing), bytes.TrimSpace(data)) {
continue
}
} else if !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("read registry config %s: %w", path, err)
}
if err := os.WriteFile(path, data, 0o644); err != nil {
return fmt.Errorf("write registry config %s: %w", path, err)
}
logger.Debug("synced registry config to %s", path)
}
return nil
}
func registryFileName(address string) string {
if address == "" {
return ""
}
raw := strings.TrimSpace(address)
if raw == "" {
return ""
}
if u, err := url.Parse(raw); err == nil {
host := u.Host
if host != "" {
return host
}
// Some registry addresses might be provided without scheme, causing Parse to treat them as paths.
}
trimmed := strings.TrimPrefix(raw, "http://")
trimmed = strings.TrimPrefix(trimmed, "https://")
if idx := strings.Index(trimmed, "/"); idx != -1 {
trimmed = trimmed[:idx]
}
return strings.TrimSpace(trimmed)
}
func loadRegistriesFromDir(dir string) ([]Registry, error) {
files, err := listRegistryConfigFiles(dir)
if err != nil {
@@ -77,3 +77,55 @@ func TestRegistriesLoadedFromDir(t *testing.T) {
t.Fatalf("expected credentials for 192.168.64.1:5000, got %#v", auth.CRIConfigs["192.168.64.1:5000"])
}
}
func TestRegistriesFieldSyncedToDir(t *testing.T) {
dir := t.TempDir()
registries := []Registry{
{Address: "https://hub.192.168.64.4.nip.io", Auth: "admin:syncpass"},
{Address: "http://192.168.64.1:5000"},
}
cfg := &Config{
ImageShimSocket: "/var/run/image-cri-shim.sock",
RuntimeSocket: "/run/containerd/containerd.sock",
Address: "https://registry.internal",
Force: true,
Auth: "sync:user",
RegistryDir: dir,
Registries: registries,
}
auth, err := cfg.PreProcess()
if err != nil {
t.Fatalf("preprocess with inline registries failed: %v", err)
}
expectedFiles := map[string]Registry{
"hub.192.168.64.4.nip.io.yaml": registries[0],
"192.168.64.1:5000.yaml": registries[1],
}
for name, want := range expectedFiles {
path := filepath.Join(dir, name)
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("expected registry file %s to be written: %v", path, err)
}
var got Registry
if err := yaml.Unmarshal(data, &got); err != nil {
t.Fatalf("failed to parse registry file %s: %v", path, err)
}
if got != want {
t.Fatalf("registry file %s content mismatch: got %#v, want %#v", path, got, want)
}
}
firstDomain := registryMatchDomain(registries[0])
if authCfg, ok := auth.CRIConfigs[firstDomain]; !ok || authCfg.Username != "admin" || authCfg.Password != "syncpass" {
t.Fatalf("expected credentials for %s, got %#v", firstDomain, auth.CRIConfigs[firstDomain])
}
secondDomain := registryMatchDomain(registries[1])
if !auth.SkipLoginRegistries[secondDomain] {
t.Fatalf("expected %s to skip login, got %#v", secondDomain, auth.SkipLoginRegistries)
}
}