* Copyright (c) 2025 Bocloud Technologies Co., Ltd.
* installer is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain n copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
******************************************************************/
package build
import (
"encoding/json"
"fmt"
"os"
"strings"
reg "gopkg.openfuyao.cn/bkeadm/pkg/registry"
"gopkg.openfuyao.cn/bkeadm/pkg/server"
"gopkg.openfuyao.cn/bkeadm/utils"
"gopkg.openfuyao.cn/bkeadm/utils/log"
)
func isInsecureInDocker(source string) bool {
data, err := os.ReadFile("/etc/docker/daemon.json")
if err != nil {
return false
}
var cfg struct {
InsecureRegistries []string `json:"insecure-registries"`
}
if err = json.Unmarshal(data, &cfg); err != nil {
log.Warnf("Failed to parse /etc/docker/daemon.json: %v", err)
return false
}
for _, entry := range cfg.InsecureRegistries {
if strings.HasPrefix(source, entry) {
return true
}
}
return false
}
func syncRepo(cfg *BuildConfig, stopChan chan struct{}, defaultSkipTLSVerify bool) error {
var err error
_ = server.RemoveImageRegistry(utils.LocalImageRegistryName)
if err = server.StartImageRegistry(utils.LocalImageRegistryName, cfg.Registry.ImageAddress, "5000", tmpRegistry); err != nil {
log.Errorf("The mirror warehouse fails to be started, %v", err)
return err
}
for _, cr := range cfg.Repos {
if !cr.NeedDownload {
continue
}
if err = processRepoImages(cr, stopChan, defaultSkipTLSVerify); err != nil {
return err
}
}
return packImageAndCleanup()
}
func processRepoImages(cr Repo, stopChan chan struct{}, defaultSkipTLSVerify bool) error {
for _, subImage := range cr.SubImages {
if err := processSingleSubImage(subImage, cr.Architecture, stopChan, defaultSkipTLSVerify); err != nil {
return err
}
}
return nil
}
func processSingleSubImage(subImage SubImage, architecture []string, stopChan chan struct{}, defaultSkipTLSVerify bool) error {
for _, image := range subImage.Images {
select {
case <-stopChan:
log.Error("sync repo be externally terminated. ")
return nil
default:
}
if err := processImageTags(image, subImage, architecture, defaultSkipTLSVerify); err != nil {
return err
}
}
return nil
}
func processImageTags(image Image, subImage SubImage, architecture []string, defaultSkipTLSVerify bool) error {
for _, tag := range image.Tag {
source, err := imageTrack(subImage.SourceRepo, subImage.ImageTrack, image.Name, tag, architecture)
if err != nil {
return err
}
targetTag := tag
if strings.Contains(tag, cut) {
targetTag = strings.Split(tag, cut)[0]
}
target := fmt.Sprintf("127.0.0.1:5000/%s/%s:%s", subImage.TargetRepo, image.Name, targetTag)
if subImage.TargetRepo == "/" {
target = strings.ReplaceAll(target, "///", "/")
} else {
target = strings.ReplaceAll(target, "//", "/")
}
skipTLSVerify := false
if subImage.SkipTLSVerify != nil {
skipTLSVerify = *subImage.SkipTLSVerify
} else if defaultSkipTLSVerify {
skipTLSVerify = true
} else {
skipTLSVerify = isInsecureInDocker(source)
}
if err = syncRepoImage(source, target, architecture, !skipTLSVerify); err != nil {
return err
}
}
log.Infof("Image tag sync completed: image=%s, tags=%s", image.Name, strings.Join(image.Tag, ","))
return nil
}
multi-architecture image alpine:3.15
or
alpine:3.15-amd64
alpine:3.15-arm64
alpine:3.15-*-202112111112
*/
func syncRepoImage(source, target string, arch []string, srcTLSVerify bool) error {
if len(arch) == 1 {
return syncSingleArchImage(source, target, arch[0], srcTLSVerify)
}
return syncMultiArchImage(source, target, arch, srcTLSVerify)
}
func syncSingleArchImage(source, target, arch string, srcTLSVerify bool) error {
imageAddress := source
if strings.Contains(imageAddress, cut) {
imageAddress = strings.ReplaceAll(imageAddress, cut, fmt.Sprintf("-%s-", arch))
}
op := reg.Options{
MultiArch: false,
SrcTLSVerify: srcTLSVerify,
DestTLSVerify: false,
Arch: arch,
Source: imageAddress,
Target: target,
}
log.Debugf("Sync image %s to %s", imageAddress, target)
if err := reg.CopyRegistry(op); err != nil {
log.Warnf("Sync image %s to %s error %v", imageAddress, target, err)
if strings.Contains(imageAddress, fmt.Sprintf("-%s-", arch)) {
return err
}
return retrySyncWithArchSuffix(imageAddress, target, arch, op)
}
return nil
}
func retrySyncWithArchSuffix(imageAddress, target, arch string, op reg.Options) error {
imageAddress = imageAddress + "-" + arch
log.Debugf("Sync image %s to %s", imageAddress, target)
op.Source = imageAddress
if err := reg.CopyRegistry(op); err != nil {
log.Warnf("Sync image %s to %s error %v", imageAddress, target, err)
return err
}
return nil
}
func syncMultiArchImage(source, target string, arch []string, srcTLSVerify bool) error {
return syncArchImagesAndCreateManifest(source, target, arch, srcTLSVerify)
}
func tryDirectMultiArchSync(source, target string, srcTLSVerify bool) error {
if strings.Contains(source, cut) || !reg.IsMultiArchManifests(srcTLSVerify, source) {
return fmt.Errorf("not a direct multi-arch image")
}
op := reg.Options{
MultiArch: true,
SrcTLSVerify: srcTLSVerify,
DestTLSVerify: false,
Source: source,
Target: target,
}
err := reg.CopyRegistry(op)
if err != nil {
log.Warnf("Sync image %s to %s error %v", source, target, err)
}
return err
}
func syncArchImagesAndCreateManifest(source, target string, arch []string, srcTLSVerify bool) error {
var img []reg.ImageArch
op := reg.Options{
MultiArch: false,
SrcTLSVerify: srcTLSVerify,
DestTLSVerify: false,
}
for _, ar := range arch {
archImg, err := syncSingleArchVariant(source, target, ar, op)
if err != nil {
return err
}
img = append(img, archImg)
}
log.Infof("Multi-arch image sync completed: image=%s, archs=%s", target, strings.Join(arch, ","))
if err := reg.CreateMultiArchImage(img, target); err != nil {
log.Errorf(
"The creation of multiple schema images manifests fails %s: %v", target, err)
return err
}
return nil
}
func syncSingleArchVariant(source, target, arch string, op reg.Options) (reg.ImageArch, error) {
op.Arch = arch
op.Target = target + "-" + arch
if strings.Contains(source, cut) {
op.Source = strings.ReplaceAll(source, cut, fmt.Sprintf("-%s-", arch))
}
if op.Source == "" {
op.Source = source
}
log.Debug(op.Source + "-->" + op.Target)
if err := reg.CopyRegistry(op); err != nil {
log.Errorf("Sync image %s to %s error %v", source, target, err)
return reg.ImageArch{}, err
}
return reg.ImageArch{
Name: op.Target,
OS: "linux",
Architecture: arch,
}, nil
}