mirror of
https://github.com/labring/sealos.git
synced 2026-10-29 17:25:58 +08:00
feat(image-cri-shim): sync inline registries to registry.d (#6075)
feat: sync inline registries to registry.d
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user