From 067baa3de8b4beda4936eac2dc9bafdc1601546b Mon Sep 17 00:00:00 2001 From: weiyucheng123 Date: Fri, 12 Dec 2025 11:12:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=86=E4=BB=A3=E7=A0=81pkg=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E4=B8=AD=E6=96=87=E4=BB=A3=E7=A0=81=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E8=8B=B1=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/builder/builder.go | 20 ++-- pkg/imagemanager/imagemanager.go | 90 ++++++++-------- pkg/project/baseimage_build.go | 96 ++++++++--------- pkg/project/bootstrap.go | 122 +++++++++++----------- pkg/project/config_rootfs.go | 150 +++++++++++++-------------- pkg/scanner/dockerfile/auditor.go | 2 +- pkg/scanner/dockerfile/directives.go | 16 +-- pkg/scanner/dockerfile/parser.go | 2 +- pkg/scanner/dockerfile/policy.go | 8 +- pkg/scanner/dockerfile/policyrule.go | 116 ++++++++++----------- pkg/templates/dockerfile.go | 8 +- pkg/templates/unmaskservice.go | 2 +- pkg/types/image.go | 12 +-- pkg/utils/common.go | 4 +- pkg/utils/copier.go | 12 +-- pkg/utils/parse.go | 122 +++++++++++----------- 16 files changed, 391 insertions(+), 391 deletions(-) diff --git a/pkg/builder/builder.go b/pkg/builder/builder.go index 4e5fa60..b504659 100644 --- a/pkg/builder/builder.go +++ b/pkg/builder/builder.go @@ -622,34 +622,34 @@ func (b *Builder) Run(args []string, ops options.RUNOption) error { } func (b *Builder) SetLabel(containerID string, labels map[string]string) error { - // 找到容器的配置文件路径 + // Find the container's configuration file path configDir, err := b.Store.ContainerDirectory(containerID) if err != nil { return err } configPath := filepath.Join(configDir, "ktib.json") - // 读取配置文件 + // Read the configuration file data, err := os.ReadFile(configPath) if err != nil { return err } - // 解析当前配置 + // Parse the current configuration var config map[string]interface{} if err := json.Unmarshal(data, &config); err != nil { return err } - // 更新标签 + // Update labels if config["Labels"] == nil { config["Labels"] = make(map[string]string) } - // 获取现有标签 + // Get existing labels existingLabels := make(map[string]string) if rawLabels, ok := config["Labels"].(map[string]interface{}); ok { - // JSON反序列化产生的类型 + // Type produced by JSON unmarshalling for k, v := range rawLabels { existingLabels[k] = fmt.Sprintf("%v", v) } @@ -657,15 +657,15 @@ func (b *Builder) SetLabel(containerID string, labels map[string]string) error { existingLabels = stringLabels } - // 合并新标签 + // Merge new labels for key, value := range labels { existingLabels[key] = value } - // 更新配置 + // Update configuration config["Labels"] = existingLabels - // 更新配置文件 + // Update the configuration file newData, err := json.Marshal(config) if err != nil { return err @@ -674,6 +674,6 @@ func (b *Builder) SetLabel(containerID string, labels map[string]string) error { return err } - fmt.Printf("成功为容器 %s 设置标签: %v\n", containerID, labels) + fmt.Printf("Successfully set labels for container %s: %v\n", containerID, labels) return nil } diff --git a/pkg/imagemanager/imagemanager.go b/pkg/imagemanager/imagemanager.go index 6310a19..f247e73 100644 --- a/pkg/imagemanager/imagemanager.go +++ b/pkg/imagemanager/imagemanager.go @@ -46,15 +46,15 @@ type Image struct { OriImage storage.Image Size int64 - // 解析后的名称信息 + // Parsed name information ParsedNames []ParsedImageName } -// 解析后的镜像名称结构 +// Parsed image name structure type ParsedImageName struct { - Repository string // 仓库名 - Tag string // 标签 - // Digest string // 摘要 + Repository string // Repository name + Tag string // Tag + // Digest string // Digest } func NewImageManager(store storage.Store) (*ImageManager, error) { @@ -82,9 +82,9 @@ func (im *ImageManager) ListImage(ops options.ImagesOption, store storage.Store, } var ktibImages []*Image - // 遍历获取到的镜像数据 + // Iterate over the fetched image data for _, img := range images { - // 从存储中获取镜像的实际数据 + // Get the actual image data from the store storageImg := img.StorageImage() size, err := store.ImageSize(img.ID()) @@ -92,9 +92,9 @@ func (im *ImageManager) ListImage(ops options.ImagesOption, store storage.Store, return nil, err } - // 创建一个 Image 实例并填充数据 + // Create an Image instance and populate the data ktibImage := &Image{ - OriImage: *storageImg, // storage.Image 直接赋值 + OriImage: *storageImg, // storage.Image direct assignment Size: size, } @@ -119,11 +119,11 @@ func (im *ImageManager) KtibLogin(ctx context.Context, lops *options.LoginOption sctx := &types.SystemContext{ AuthFilePath: loginOps.AuthFile, DockerCertPath: loginOps.CertDir, - // 修复:TLS验证标志语义反转问题:当 lops.TLSVerify 为 true(需要验证)时,跳过验证应为 false + // Fix: TLS verification flag semantic reversal issue: when lops.TLSVerify is true (verification required), skip verification should be false DockerDaemonInsecureSkipTLSVerify: !lops.TLSVerify, } - // 设置 insecure 参数 + // Set insecure parameter if lops.Insecure { sctx.DockerInsecureSkipTLSVerify = types.OptionalBoolTrue } @@ -161,19 +161,19 @@ func (im *ImageManager) Pull(imageName string) error { rd := 2 * time.Second pullOptions.RetryDelay = &rd - // 添加对 SystemContext 的设置 + // Add settings for SystemContext if pullOptions.SystemContext == nil { pullOptions.SystemContext = &types.SystemContext{} } SetRegistriesConfPath(pullOptions.SystemContext) - // 获取已登录的认证信息 + // Get logged-in authentication information credentials, err := auth_config.GetAllCredentials(pullOptions.SystemContext) if err != nil || len(credentials) == 0 { - // 没有认证信息时,使用默认的TLS验证设置 + // Use default TLS verification setting when no authentication information is present pullOptions.InsecureSkipTLSVerify = types.OptionalBoolFalse } else { - // 有认证信息时,检查镜像仓库是否匹配 + // If authentication information is present, check if the image registry matches imageRegistry := extractRegistryFromImageName(imageName) matchFound := false if imageRegistry != "" { @@ -183,10 +183,10 @@ func (im *ImageManager) Pull(imageName string) error { } if matchFound { - // 镜像来源于已登录的镜像仓库 + // Image is from a logged-in registry pullOptions.InsecureSkipTLSVerify = types.OptionalBoolTrue } else { - // 镜像不来源于已登录的镜像仓库 + // Image is not from a logged-in registry pullOptions.InsecureSkipTLSVerify = types.OptionalBoolFalse } } @@ -204,7 +204,7 @@ func (im *ImageManager) Pull(imageName string) error { } func extractRegistryFromImageName(imageName string) string { - // 解析镜像名称 + // Parse image name ref, err := reference.ParseNormalizedNamed(imageName) if err != nil { return "" @@ -225,23 +225,23 @@ func (im *ImageManager) Push(ctx context.Context, source, destination string, op rd := 2 * time.Second pushOptions.RetryDelay = &rd - // 添加对 SystemContext 的设置 + // Add settings for SystemContext if pushOptions.SystemContext == nil { pushOptions.SystemContext = &types.SystemContext{} } SetRegistriesConfPath(pushOptions.SystemContext) - // 如果用户明确设置了insecure参数,优先使用用户设置 + // If the user explicitly set the insecure parameter, prioritize the user setting if op.Insecure { pushOptions.InsecureSkipTLSVerify = types.OptionalBoolTrue } else { - // 获取已登录的认证信息 + // Get logged-in authentication information credentials, err := auth_config.GetAllCredentials(pushOptions.SystemContext) if err != nil || len(credentials) == 0 { - // 没有认证信息时,使用默认的TLS验证设置 + // Use default TLS verification setting when no authentication information is present pushOptions.InsecureSkipTLSVerify = types.OptionalBoolFalse } else { - // 有认证信息时,检查目标仓库是否匹配 + // If authentication information is present, check if the destination registry matches destinationRegistry := extractRegistryFromImageName(destination) matchFound := false if destinationRegistry != "" { @@ -251,10 +251,10 @@ func (im *ImageManager) Push(ctx context.Context, source, destination string, op } if matchFound { - // 目标仓库是已登录的镜像仓库 + // Destination repository is a logged-in registry pushOptions.InsecureSkipTLSVerify = types.OptionalBoolTrue } else { - // 目标仓库不是已登录的镜像仓库 + // Destination repository is not a logged-in registry pushOptions.InsecureSkipTLSVerify = types.OptionalBoolFalse } } @@ -393,7 +393,7 @@ func SetRegistriesConfPath(systemContext *types.SystemContext) { func (im *ImageManager) SaveImage(ctx context.Context, op options.SaveOption, tags []string, name string) error { saveOptions := &libimage.SaveOptions{} - // 理论上saveOptions的如下标签应该基于saveOptions赋值,但是目前save不支持这些flags,所以先置为默认值 + // In theory, the following tags for saveOptions should be assigned based on saveOptions, but currently save does not support these flags, so they are set to default values for now saveOptions.RemoveSignatures = true saveOptions.DirForceCompress = false saveOptions.OciAcceptUncompressedLayers = false @@ -411,7 +411,7 @@ func (im *ImageManager) SaveImage(ctx context.Context, op options.SaveOption, ta func (im *ImageManager) LoadImage(background context.Context, op options.LoadOption) (*options.ImageLoadReport, error) { loadOptions := &libimage.LoadOptions{} - // 理论上应该从load的options里面赋值,但是目前不支持这些参数,暂时写成默认的 + // In theory, it should be assigned from the load options, but currently these parameters are not supported, so they are temporarily written as defaults loadOptions.SignaturePolicyPath = "" loadOptions.Writer = os.Stderr @@ -454,7 +454,7 @@ func (im *ImageManager) ManifestCreate(ctx context.Context, name string, images sysCtx := &types.SystemContext{} SetRegistriesConfPath(sysCtx) - // 添加凭据匹配逻辑 + // Add credential matching logic allCreds, err := auth_config.GetAllCredentials(sysCtx) if err == nil { for _, image := range images { @@ -508,7 +508,7 @@ func (im *ImageManager) ManifestPush(background context.Context, name string, de } pushOptions := &libimage.ManifestListPushOptions{} compressionLevel := 0 - // todo:以下参数暂不支持,赋为默认值,后续按实际情况补充参数 + // todo:The following parameters are currently unsupported and set to default values. They will be supplemented later as needed pushOptions.AuthFilePath = auth.GetDefaultAuthFile() pushOptions.CertDirPath = "" pushOptions.ImageListSelection = cp.CopyAllImages @@ -521,7 +521,7 @@ func (im *ImageManager) ManifestPush(background context.Context, name string, de pushOptions.AddCompression = []string{} pushOptions.ForceCompressionFormat = false - // 已支持参数赋值 + // Supported parameter assignment pushOptions.Password = op.Password pushOptions.Username = op.Username pushOptions.SignBy = op.SignBy @@ -543,23 +543,23 @@ func (im *ImageManager) ManifestPush(background context.Context, name string, de } } - // 添加对 SystemContext 的设置 + // Add settings for SystemContext if pushOptions.SystemContext == nil { pushOptions.SystemContext = &types.SystemContext{} } SetRegistriesConfPath(pushOptions.SystemContext) - // 如果用户明确设置了insecure参数,优先使用用户设置 + // If the user explicitly set the insecure parameter, prioritize the user setting if op.Insecure { pushOptions.InsecureSkipTLSVerify = types.OptionalBoolTrue } else { - // 获取已登录的认证信息 + // Get logged-in authentication information credentials, err := auth_config.GetAllCredentials(pushOptions.SystemContext) if err != nil || len(credentials) == 0 { - // 没有认证信息时,使用默认的TLS验证设置 + // Use default TLS verification setting when no authentication information is present pushOptions.InsecureSkipTLSVerify = types.OptionalBoolFalse } else { - // 有认证信息时,检查目标仓库是否匹配 + // If authentication information is present, check if the destination registry matches destinationRegistry := extractRegistryFromImageName(destination) matchFound := false if destinationRegistry != "" { @@ -569,10 +569,10 @@ func (im *ImageManager) ManifestPush(background context.Context, name string, de } if matchFound { - // 目标仓库是已登录的镜像仓库 + // Destination repository is a logged-in registry pushOptions.InsecureSkipTLSVerify = types.OptionalBoolTrue } else { - // 目标仓库不是已登录的镜像仓库 + // Destination repository is not a logged-in registry pushOptions.InsecureSkipTLSVerify = types.OptionalBoolFalse } } @@ -601,15 +601,15 @@ func (im *ImageManager) ManifestAdd(background context.Context, manifestName str return "", err } - // 添加对 SystemContext 的设置 + // Add settings for SystemContext sysCtx := &types.SystemContext{} SetRegistriesConfPath(sysCtx) - // 获取已登录的认证信息 + // Get logged-in authentication information credentials, err := auth_config.GetAllCredentials(sysCtx) insecureSkipTLS := types.OptionalBoolFalse if err == nil && len(credentials) > 0 { - // 有认证信息时,检查所有镜像的仓库是否匹配 + // If authentication information is present, check if the registry of all images matches matchFound := false for _, image := range images { imageRegistry := extractRegistryFromImageName(image) @@ -622,12 +622,12 @@ func (im *ImageManager) ManifestAdd(background context.Context, manifestName str } if matchFound { - // 至少有一个镜像来源于已登录的镜像仓库 + // At least one image is from a logged-in registry insecureSkipTLS = types.OptionalBoolTrue } } - // 如果用户明确设置了insecure参数,优先使用用户设置 + // If the user explicitly set the insecure parameter, prioritize the user setting if opts.Insecure { insecureSkipTLS = types.OptionalBoolTrue } @@ -708,19 +708,19 @@ func Join(base map[string]string, override map[string]string) map[string]string } func (im *ImageManager) Inspect(ctx context.Context, name string) (*libimage.ImageData, error) { - // 查找镜像,注意这里需要接收三个返回值:image, resolvedName, err + // Look up the image, note that three return values are required here: image, resolvedName, err image, _, err := im.Manager.LookupImage(name, nil) if err != nil { return nil, err } - // 设置检查选项,包括计算镜像大小和父镜像 + // Set inspect options, including calculating image size and parent image inspectOptions := &libimage.InspectOptions{ WithSize: true, WithParent: true, } - // 调用 libimage 的 Inspect 方法获取镜像数据 + // Call libimage's Inspect method to get image data imageData, err := image.Inspect(ctx, inspectOptions) if err != nil { return nil, err diff --git a/pkg/project/baseimage_build.go b/pkg/project/baseimage_build.go index 68f22ff..7f071b1 100644 --- a/pkg/project/baseimage_build.go +++ b/pkg/project/baseimage_build.go @@ -24,43 +24,43 @@ import ( "github.com/containers/buildah/imagebuildah" ) -// BuildImage 方法用于构建容器镜像 +// BuildImage method is used to build a container image func (b *Bootstrap) BuildImage(imageName, tag string) error { - // 检查 rootfs 目录是否存在 + // Check if the rootfs directory exists rootfsDir := filepath.Join(b.DestinationDir, "rootfs") if _, err := os.Stat(rootfsDir); os.IsNotExist(err) { - return fmt.Errorf("rootfs 目录不存在,请先运行 'ktib project build-rootfs' 命令") + return fmt.Errorf("rootfs directory does not exist, please run 'ktib project build-rootfs' first") } - // 创建临时目录用于构建 + // Create a temporary directory for the build buildDir, err := os.MkdirTemp("", "ktib-build-") if err != nil { - return fmt.Errorf("创建临时构建目录失败: %v", err) + return fmt.Errorf("failed to create temporary build directory: %v", err) } defer os.RemoveAll(buildDir) - // 创建 rootfs.tar 文件 + // Create the rootfs.tar file rootfsTarPath := filepath.Join(buildDir, "rootfs.tar") if err := createTarFromDirectory(rootfsDir, rootfsTarPath); err != nil { - return fmt.Errorf("创建 rootfs.tar 文件失败: %v", err) + return fmt.Errorf("failed to create rootfs.tar file: %v", err) } - // 创建 Dockerfile + // Create the Dockerfile dockerfilePath := filepath.Join(buildDir, "Dockerfile") projectDockerfilePath := filepath.Join(b.DestinationDir, "dockerfile", "Dockerfile") if _, err := os.Stat(projectDockerfilePath); err == nil { srcFile, err := os.Open(projectDockerfilePath) if err != nil { - return fmt.Errorf("打开项目 Dockerfile 失败: %v", err) + return fmt.Errorf("failed to open project Dockerfile: %v", err) } defer srcFile.Close() dstFile, err := os.Create(dockerfilePath) if err != nil { - return fmt.Errorf("创建构建 Dockerfile 失败: %v", err) + return fmt.Errorf("failed to create build Dockerfile: %v", err) } defer dstFile.Close() if _, err = io.Copy(dstFile, srcFile); err != nil { - return fmt.Errorf("复制项目 Dockerfile 失败: %v", err) + return fmt.Errorf("failed to copy project Dockerfile: %v", err) } } else { cmd := "/bin/bash" @@ -69,16 +69,16 @@ func (b *Bootstrap) BuildImage(imageName, tag string) error { } dockerfileContent := "FROM scratch\nADD rootfs.tar /\nCMD [\"" + cmd + "\"]\n" if err := os.WriteFile(dockerfilePath, []byte(dockerfileContent), 0644); err != nil { - return fmt.Errorf("创建 Dockerfile 失败: %v", err) + return fmt.Errorf("failed to create Dockerfile: %v", err) } } - // 复制 files 目录中的文件到构建目录(如果需要) + // Copy files from the files directory to the build directory (if necessary) filesDir := filepath.Join(b.DestinationDir, "files") if _, err := os.Stat(filesDir); err == nil { entries, err := os.ReadDir(filesDir) if err != nil { - return fmt.Errorf("读取 files 目录失败: %v", err) + return fmt.Errorf("failed to read files directory: %v", err) } for _, entry := range entries { @@ -86,36 +86,36 @@ func (b *Bootstrap) BuildImage(imageName, tag string) error { dstPath := filepath.Join(buildDir, entry.Name()) if entry.IsDir() { - // 如果是目录,创建对应的目录 + // If it's a directory, create the corresponding directory if err := os.MkdirAll(dstPath, 0755); err != nil { - return fmt.Errorf("创建目录 %s 失败: %v", dstPath, err) + return fmt.Errorf("failed to create directory %s: %v", dstPath, err) } } else { - // 如果是文件,复制文件内容 + // If it's a file, copy the file content srcFile, err := os.Open(srcPath) if err != nil { - return fmt.Errorf("打开源文件 %s 失败: %v", srcPath, err) + return fmt.Errorf("failed to open source file %s: %v", srcPath, err) } defer srcFile.Close() dstFile, err := os.Create(dstPath) if err != nil { - return fmt.Errorf("创建目标文件 %s 失败: %v", dstPath, err) + return fmt.Errorf("failed to create destination file %s: %v", dstPath, err) } defer dstFile.Close() if _, err = io.Copy(dstFile, srcFile); err != nil { - return fmt.Errorf("复制文件内容失败: %v", err) + return fmt.Errorf("failed to copy file content: %v", err) } } } } - // 使用 ktib 内部构建接口构建镜像 + // Use ktib's internal build interface to build the image imageTag := fmt.Sprintf("%s:%s", imageName, tag) - // 直接使用底层的 buildah 接口构建镜像,避免 cobra.Command 初始化问题 - // 创建构建选项 + // Directly use the underlying buildah interface to build the image, avoiding cobra.Command initialization issues + // Create build options buildOptions := &options.BuildOptions{ Tags: []string{imageTag}, Format: utils.DefaultFormat(), @@ -123,22 +123,22 @@ func (b *Bootstrap) BuildImage(imageName, tag string) error { ForceRm: true, } - // 设置构建参数 + // Set build arguments args := []string{buildDir} - // 解析 Dockerfile 路径和上下文目录 + // Resolve Dockerfile path and context directory dockerfiles, contextDir, err := utils.ResolveDockerfiles(buildOptions, args) if err != nil { - return fmt.Errorf("解析 Dockerfile 失败: %v", err) + return fmt.Errorf("failed to resolve Dockerfile: %v", err) } - // 获取存储 + // Get store store, err := utils.GetStore(nil) if err != nil { - return fmt.Errorf("获取存储失败: %v", err) + return fmt.Errorf("failed to get store: %v", err) } - // 手动创建 buildah 构建选项 + // Manually create buildah build options buildahOptions := &imagebuildah.BuildOptions{ ContextDirectory: contextDir, PullPolicy: imagebuildah.PullIfMissing, @@ -152,22 +152,22 @@ func (b *Bootstrap) BuildImage(imageName, tag string) error { RemoveIntermediateCtrs: true, } - // 构建镜像 + // Build image ctx := context.Background() _, _, err = imagebuildah.BuildDockerfiles(ctx, store, *buildahOptions, dockerfiles...) if err != nil { - return fmt.Errorf("构建镜像失败: %v", err) + return fmt.Errorf("failed to build image: %v", err) } - fmt.Printf("成功构建镜像: %s\n", imageTag) + fmt.Printf("Successfully built image: %s\n", imageTag) return nil } -// createTarFromDirectory 创建一个 tar 文件,包含指定目录中的所有内容 +// createTarFromDirectory creates a tar file containing all content in the specified directory func createTarFromDirectory(sourceDir, tarFilePath string) error { tarFile, err := os.Create(tarFilePath) if err != nil { - return fmt.Errorf("创建 tar 文件失败: %v", err) + return fmt.Errorf("failed to create tar file: %v", err) } defer tarFile.Close() @@ -179,50 +179,50 @@ func createTarFromDirectory(sourceDir, tarFilePath string) error { return err } - // 获取相对路径 + // Get relative path relPath, err := filepath.Rel(sourceDir, path) if err != nil { - return fmt.Errorf("获取相对路径失败: %v", err) + return fmt.Errorf("failed to get relative path: %v", err) } - // 跳过根目录 + // Skip root directory if relPath == "." { return nil } - // 创建 tar 头信息 + // Create tar header header, err := tar.FileInfoHeader(info, "") if err != nil { - return fmt.Errorf("创建文件头信息失败: %v", err) + return fmt.Errorf("failed to create file header: %v", err) } - // 修改头信息中的名称为相对路径 + // Modify the name in the header to the relative path header.Name = relPath - // 处理软链接 + // Handle symbolic links if info.Mode()&os.ModeSymlink != 0 { linkTarget, err := os.Readlink(path) if err != nil { - return fmt.Errorf("读取软链接目标失败: %v", err) + return fmt.Errorf("failed to read symlink target: %v", err) } header.Linkname = linkTarget } - // 写入头信息 + // Write header if err := tarWriter.WriteHeader(header); err != nil { - return fmt.Errorf("写入文件头信息失败: %v", err) + return fmt.Errorf("failed to write file header: %v", err) } - // 如果是常规文件,写入文件内容 + // If it is a regular file, write the file content if info.Mode().IsRegular() { file, err := os.Open(path) if err != nil { - return fmt.Errorf("打开文件失败: %v", err) + return fmt.Errorf("failed to open file: %v", err) } defer file.Close() if _, err := io.Copy(tarWriter, file); err != nil { - return fmt.Errorf("写入文件内容失败: %v", err) + return fmt.Errorf("failed to write file content: %v", err) } } @@ -230,7 +230,7 @@ func createTarFromDirectory(sourceDir, tarFilePath string) error { }) } -// createDefaultDockerfile 创建默认的 Dockerfile +// createDefaultDockerfile creates the default Dockerfile func createDefaultDockerfile(projectDir string) error { dockerfileDir := filepath.Join(projectDir, "dockerfile") if err := os.MkdirAll(dockerfileDir, 0755); err != nil { diff --git a/pkg/project/bootstrap.go b/pkg/project/bootstrap.go index 6eeb1f9..957a756 100644 --- a/pkg/project/bootstrap.go +++ b/pkg/project/bootstrap.go @@ -24,14 +24,14 @@ import ( var yumConfig = "/etc/yum.conf" -// Bootstrap 定义项目引导结构 +// Bootstrap defines the project bootstrap structure type Bootstrap struct { - DestinationDir string // 目标目录 - ImageName string // 镜像名称 - BuildType string // 构建类型 + DestinationDir string // Destination directory + ImageName string // Image name + BuildType string // Build type } -// Config 定义配置文件结构 +// Config defines the configuration file structure type Config struct { Packages struct { InstallPkgs []string `yaml:"install_pkgs"` @@ -41,22 +41,22 @@ type Config struct { HOSTNAME string `yaml:"hostname"` } `yaml:"network"` Locale string `yaml:"locale"` - Timezone string `yaml:"timezone"` // 时区配置 + Timezone string `yaml:"timezone"` // Timezone configuration } -// NewBootstrap 创建新的Bootstrap实例 +// NewBootstrap creates a new Bootstrap instance func NewBootstrap(dir string) *Bootstrap { return &Bootstrap{DestinationDir: dir, BuildType: "platform"} } -// InitProjectStructure 初始化项目目录结构 +// InitProjectStructure initializes the project directory structure func (b *Bootstrap) InitProjectStructure() error { - // 创建目录结构 + // Create directory structure dirs := []string{ - filepath.Join(b.DestinationDir, "dockerfile"), // 存放 Dockerfile 的目录 - filepath.Join(b.DestinationDir, "rootfs"), // 用于初始化 rootfs 的目录 - filepath.Join(b.DestinationDir, "files"), // 存放制作rootfs需要的文件 - filepath.Join(b.DestinationDir, "tests"), // 存放测试脚本的目录 + filepath.Join(b.DestinationDir, "dockerfile"), // Directory for storing the Dockerfile + filepath.Join(b.DestinationDir, "rootfs"), // Directory for initializing the rootfs + filepath.Join(b.DestinationDir, "files"), // Directory for storing files needed to create rootfs + filepath.Join(b.DestinationDir, "tests"), // Directory for storing test scripts } for _, dir := range dirs { @@ -64,21 +64,21 @@ func (b *Bootstrap) InitProjectStructure() error { if !info.IsDir() { bak := dir + ".bak" if err := os.Rename(dir, bak); err != nil { - return fmt.Errorf("存在同名文件 %s,重命名失败: %v", dir, err) + return fmt.Errorf("file with the same name exists %s, failed to rename: %v", dir, err) } } else { - // 已存在目录,继续 + // Directory already exists, continue continue } } else if !os.IsNotExist(err) { - return fmt.Errorf("检查目录 %s 失败: %v", dir, err) + return fmt.Errorf("failed to check directory %s: %v", dir, err) } if err := os.MkdirAll(dir, 0755); err != nil { - return fmt.Errorf("创建目录 %s 失败: %v", dir, err) + return fmt.Errorf("failed to create directory %s: %v", dir, err) } } - // 添加必要的文件 + // Add necessary files b.AddDockerfile() b.AddChangeInfo() b.AddRemoveMinimalList() @@ -86,7 +86,7 @@ func (b *Bootstrap) InitProjectStructure() error { return nil } -// InitWorkDir 初始化工作目录 +// InitWorkDir initializes the working directory func (b *Bootstrap) InitWorkDir(types, config string) { baseDir := filepath.Join(b.DestinationDir, "init") @@ -97,77 +97,77 @@ func (b *Bootstrap) InitWorkDir(types, config string) { } } -// BuildRootfs 构建rootfs +// BuildRootfs builds the rootfs func (b *Bootstrap) BuildRootfs(configFile string) error { target, err := filepath.Abs(filepath.Join(b.DestinationDir, "rootfs")) if err != nil { - return fmt.Errorf("获取绝对路径失败: %v", err) + return fmt.Errorf("failed to get absolute path: %v", err) } - // 检查dnf并创建dev目录 + // Check dnf and create dev directory if err := CheckDnfAndCreateDev(target); err != nil { - return fmt.Errorf("检查dnf并创建dev目录失败: %v", err) + return fmt.Errorf("failed to check dnf and create dev directory: %v", err) } - // 创建字符设备和FIFO设备 + // Create character and FIFO devices devices := DefaultDevices() for _, dev := range devices { switch dev.Type { - case "c": // 字符设备 + case "c": // Character device if err := CreateCharDevice(target, dev.Name, dev.Type, dev.Major, dev.Minor, dev.Mode); err != nil { - return fmt.Errorf("创建字符设备 %s 失败: %v", dev.Name, err) + return fmt.Errorf("failed to create character device %s: %v", dev.Name, err) } - case "fifo": // FIFO设备 + case "fifo": // FIFO device if err := CreateFifoDevice(target, dev.Name); err != nil { - return fmt.Errorf("创建FIFO设备 %s 失败: %v", dev.Name, err) + return fmt.Errorf("failed to create FIFO device %s: %v", dev.Name, err) } default: - return fmt.Errorf("未知设备类型: %s", dev.Type) + return fmt.Errorf("unknown device type: %s", dev.Type) } } - // 检查yum/vars目录是否存在 + // Check if yum/vars directory exists if err := CheckVarsFile(target); err != nil { - return fmt.Errorf("检查yum/vars目录失败: %v", err) + return fmt.Errorf("failed to check yum/vars directory: %v", err) } - // 读取配置文件 + // Read configuration file data, err := os.ReadFile(configFile) if err != nil { - return fmt.Errorf("读取配置文件 %s 失败: %v", configFile, err) + return fmt.Errorf("failed to read configuration file %s: %v", configFile, err) } - // 解析YAML配置 + // Parse YAML configuration var config Config if err := yaml.Unmarshal(data, &config); err != nil { - return fmt.Errorf("解析YAML配置失败: %v", err) + return fmt.Errorf("failed to parse YAML configuration: %v", err) } - // 安装软件包 + // Install packages packages := config.Packages.InstallPkgs if len(packages) == 0 { - fmt.Println("警告: 未指定要安装的软件包") + fmt.Println("Warning: No packages specified for installation") } else { if err := InstallPackages(yumConfig, target, packages...); err != nil { - return fmt.Errorf("安装软件包失败: %v", err) + return fmt.Errorf("failed to install packages: %v", err) } } - // 配置rootfs + // Configure rootfs if err := ConfigureRootfs(target, config); err != nil { - return fmt.Errorf("配置系统失败: %v", err) + return fmt.Errorf("failed to configure system: %v", err) } - fmt.Println("rootfs 构建完成,请运行 'ktib project clean-rootfs' 命令清理不必要的文件和软件包") + fmt.Println("rootfs build complete, please run 'ktib project clean-rootfs' to clean unnecessary files and packages") return nil } func (b *Bootstrap) AddDockerfile() { - // 在 dockerfile 目录中创建 Dockerfile + // Create Dockerfile in the dockerfile directory dockerfilePath := filepath.Join(b.DestinationDir, "dockerfile") os.MkdirAll(dockerfilePath, 0755) - // 根据构建类型选择不同的 Dockerfile 模板 + // Select different Dockerfile templates based on the build type if b.BuildType == "platform" || b.BuildType == "minimal" || b.BuildType == "micro" { b.initialize(templates.BaseImageDockerfile, "dockerfile/Dockerfile", 0755) } else if b.BuildType == "init" { @@ -186,7 +186,7 @@ func (b *Bootstrap) AddUnmaskService() { } func (b *Bootstrap) AddChangeInfo() { - // 在项目根目录创建 README 文件 + // Create README file in the project root directory b.initialize(templates.README, "README.md", 0644) } @@ -211,45 +211,45 @@ func (b *Bootstrap) initialize(t string, file string, perm os.FileMode) { } } -// CleanRootfs 方法用于清理 rootfs 中不必要的文件和软件包 +// CleanRootfs method is used to clean unnecessary files and packages in the rootfs func (b *Bootstrap) CleanRootfs() error { target, _ := filepath.Abs(filepath.Join(b.DestinationDir, "rootfs")) - // 检查rootfs目录是否存在 + // Check if the rootfs directory exists if _, err := os.Stat(target); os.IsNotExist(err) { - return fmt.Errorf("rootfs 目录不存在,请先运行 'ktib project build-rootfs' 命令") + return fmt.Errorf("rootfs directory does not exist, please run 'ktib project build-rootfs' first") } - // 1. 移除不必要的包 + // 1. Remove unnecessary packages removeMinimalListPath := filepath.Join(b.DestinationDir, "files", "removeminimallist") - fmt.Printf("正在移除不必要的软件包,镜像类型: %s\n", b.BuildType) + fmt.Printf("Removing unnecessary packages, image type: %s\n", b.BuildType) if err := RemoveUnnecessaryPackages(target, b.BuildType, removeMinimalListPath); err != nil { - fmt.Printf("警告: 移除不必要的软件包失败: %v\n", err) + fmt.Printf("Warning: Failed to remove unnecessary packages: %v\n", err) } - // 2. 移除不必要的文件 + // 2. Remove unnecessary files if err := RemoveUnnecessaryFiles(target); err != nil { - fmt.Printf("移除不必要的文件失败: %v\n", err) + fmt.Printf("Failed to remove unnecessary files: %v\n", err) } - // 3. 配置pip并删除pycache + // 3. Configure pip and remove pycache if err := ConfigurePipAndRemovePycache(target, b.BuildType); err != nil { - fmt.Printf("警告: 配置pip或删除pycache失败: %v\n", err) + fmt.Printf("Warning: Failed to configure pip or remove pycache: %v\n", err) } - // 2. 解除服务屏蔽 + // 4. Unmask services unmaskServicePath := filepath.Join(b.DestinationDir, "files", "unmaskService") - fmt.Println("正在解除服务屏蔽") + fmt.Println("Unmasking services") if err := UnmaskServices(target, unmaskServicePath); err != nil { - fmt.Printf("警告: 解除服务屏蔽失败: %v\n", err) + fmt.Printf("Warning: Failed to unmask services: %v\n", err) } - // 3. 完整清理文件系统 - fmt.Println("正在清理文件系统") + // 5. Complete filesystem cleanup + fmt.Println("Cleaning up filesystem") if err := CleanupRootfsPath(target); err != nil { - fmt.Printf("警告: 清理文件系统失败: %v\n", err) + fmt.Printf("Warning: Failed to clean up filesystem: %v\n", err) } - fmt.Println("rootfs 清理完成") + fmt.Println("rootfs cleanup complete") return nil } diff --git a/pkg/project/config_rootfs.go b/pkg/project/config_rootfs.go index d33a425..b3ab1d1 100644 --- a/pkg/project/config_rootfs.go +++ b/pkg/project/config_rootfs.go @@ -52,7 +52,7 @@ var unnecessaryFiles = []string{ } func ConfigureRootfs(target string, config Config) error { - // 配置网络 + // Configure network network := config.Network.NETWORKING hostname := config.Network.HOSTNAME networkConfig := fmt.Sprintf("NETWORKING=%s\nHOSTNAME=%s\n", network, hostname) @@ -62,10 +62,10 @@ func ConfigureRootfs(target string, config Config) error { return fmt.Errorf("error writing network configuration: %v", err) } - // 设置 DNF infra 变量 + // Set DNF infra variable infraConfig := "container" infraFilePath := filepath.Join(target, "/etc/dnf/vars/infra") - // 确保目录存在 + // Ensure directory exists if err := os.MkdirAll(filepath.Dir(infraFilePath), 0755); err != nil { return fmt.Errorf("error creating directory for infra configuration: %v", err) } @@ -74,10 +74,10 @@ func ConfigureRootfs(target string, config Config) error { return fmt.Errorf("error writing infra configuration: %v", err) } - // 配置语言环境 + // Configure locale environment if config.Locale != "" { localeFilePath := filepath.Join(target, "/etc/rpm/macros.image-language-conf") - // 确保目录存在 + // Ensure directory exists if err := os.MkdirAll(filepath.Dir(localeFilePath), 0755); err != nil { return fmt.Errorf("error creating directory for locale configuration: %v", err) } @@ -86,19 +86,19 @@ func ConfigureRootfs(target string, config Config) error { return fmt.Errorf("error writing language configuration: %v", err) } - // 设置系统语言环境 + // Set system locale environment localePath := filepath.Join(target, "/etc/locale.conf") - // 从 config.Locale 中提取语言代码 - // 假设格式为 "%_install_langs en_US.UTF-8" + // Extract locale code from config.Locale + // Assuming format is "%_install_langs en_US.UTF-8" localeParts := strings.Split(config.Locale, " ") localeValue := "" if len(localeParts) > 1 { localeValue = fmt.Sprintf("LANG=%s\n", localeParts[len(localeParts)-1]) } else { - localeValue = "LANG=en_US.UTF-8\n" // 默认值 + localeValue = "LANG=en_US.UTF-8\n" // Default value } - // 确保目录存在 + // Ensure directory exists if err := os.MkdirAll(filepath.Dir(localePath), 0755); err != nil { return fmt.Errorf("error creating directory for locale.conf: %v", err) } @@ -107,24 +107,24 @@ func ConfigureRootfs(target string, config Config) error { } } - // 配置时区 + // Configure timezone if config.Timezone != "" { - // 创建 /etc/localtime 软链接指向正确的时区文件 + // Create /etc/localtime symlink pointing to the correct timezone file timezonePath := filepath.Join("/usr/share/zoneinfo", config.Timezone) localtimePath := filepath.Join(target, "/etc/localtime") - // 确保目标目录存在 + // Ensure target directory exists if err := os.MkdirAll(filepath.Dir(localtimePath), 0755); err != nil { return fmt.Errorf("error creating directory for localtime: %v", err) } - // 创建软链接 + // Create symlink cmd := exec.Command("ln", "-sf", timezonePath, localtimePath) if err := cmd.Run(); err != nil { - return fmt.Errorf("error setting timezone: %v", err) // 添加 return + return fmt.Errorf("error setting timezone: %v", err) // Add return } - // 写入时区信息到 /etc/timezone + // Write timezone information to /etc/timezone timezoneFPath := filepath.Join(target, "/etc/timezone") if err := os.WriteFile(timezoneFPath, []byte(config.Timezone), 0644); err != nil { return fmt.Errorf("error writing timezone file: %v", err) @@ -139,7 +139,7 @@ func ConfigureRootfs(target string, config Config) error { return fmt.Errorf("error writing machine-id file: %v", err) } - // 复制bash配置文件并设置bash历史 + // Copy bash configuration file and set bash history if err := addCommandToScriptAndRun(target, config); err != nil { return fmt.Errorf("error add command to script and run: %v", err) } @@ -147,16 +147,16 @@ func ConfigureRootfs(target string, config Config) error { } func addCommandToScriptAndRun(target string, config Config) error { - // 复制bash配置文件 + // Copy bash configuration files bashCmd := exec.Command("sh", "-c", fmt.Sprintf("cp /etc/skel/.bash* %s/root/", target)) if err := bashCmd.Run(); err != nil { - return fmt.Errorf("复制bash配置文件失败: %v", err) + return fmt.Errorf("failed to copy bash configuration files: %v", err) } - // 创建空的bash历史文件 + // Create empty bash history file historyPath := filepath.Join(target, "root", ".bash_history") if err := os.WriteFile(historyPath, []byte(""), 0644); err != nil { - return fmt.Errorf("创建bash历史文件失败: %v", err) + return fmt.Errorf("failed to create bash history file: %v", err) } return nil @@ -190,78 +190,78 @@ func removeAllFiles(target string, files []string) error { return nil } -// 添加以下函数来完善文件清理 +// Add the following function to complete file cleanup func CleanupRootfsPath(target string) error { - // 1. 清理RPM数据库历史记录 + // 1. Clean up RPM database history rpmHistoryFiles, err := filepath.Glob(filepath.Join(target, "var/lib/dnf/history.*")) if err == nil && len(rpmHistoryFiles) > 0 { - fmt.Println("清理RPM数据库历史记录...") + fmt.Println("Cleaning up RPM database history...") for _, file := range rpmHistoryFiles { os.Remove(file) } } - // 2. 清理临时文件和日志文件 - fmt.Println("清理临时文件和日志文件...") + // 2. Clean up temporary files and log files + fmt.Println("Cleaning up temporary files and log files...") logDir := filepath.Join(target, "var/log") if _, err := os.Stat(logDir); !os.IsNotExist(err) { - fmt.Printf("清空目录: %s\n", logDir) + fmt.Printf("Emptying directory: %s\n", logDir) os.RemoveAll(logDir) os.MkdirAll(logDir, 0755) } tmpDir := filepath.Join(target, "tmp") if _, err := os.Stat(tmpDir); !os.IsNotExist(err) { - fmt.Printf("清空目录: %s\n", tmpDir) + fmt.Printf("Emptying directory: %s\n", tmpDir) os.RemoveAll(tmpDir) os.MkdirAll(tmpDir, 0755) } - // 3. 删除nologin文件 + // 3. Delete nologin file nologinFile := filepath.Join(target, "run/nologin") if _, err := os.Stat(nologinFile); !os.IsNotExist(err) { - fmt.Printf("删除文件: %s\n", nologinFile) + fmt.Printf("Deleting file: %s\n", nologinFile) os.Remove(nologinFile) } - // 4. 清理bash历史 + // 4. Clean up bash history bashHistoryPath := filepath.Join(target, "root/.bash_history") if _, err := os.Stat(bashHistoryPath); !os.IsNotExist(err) { - fmt.Printf("清空文件: %s\n", bashHistoryPath) + fmt.Printf("Emptying file: %s\n", bashHistoryPath) os.WriteFile(bashHistoryPath, []byte(""), 0644) } return nil } -// 添加以下函数来移除不必要的包 -// 修改函数,接受文件路径参数 +// Add the following function to remove unnecessary packages +// Modify function to accept file path parameter func RemoveUnnecessaryPackages(target string, imageType string, removeMinimalListPath string) error { var packagesToRemove []string var err error var data []byte - // 检查是否有root权限 + // Check for root privilege if os.Geteuid() != 0 { - return fmt.Errorf("需要root权限执行chroot命令") + return fmt.Errorf("root privileges are required to execute chroot command") } - // 根据镜像类型选择要移除的包列表 + // Select the list of packages to remove based on the image type if imageType == "minimal" { - // 读取 removeminimallist 文件 + // Read removeminimallist file data, err = os.ReadFile(removeMinimalListPath) if err != nil { - return fmt.Errorf("无法读取 removeminimallist 文件: %v", err) + return fmt.Errorf("unable to read removeminimallist file: %v", err) } } else { - // micro 类型不需要移除包 + // micro type does not require package removal return nil } packagesToRemove = strings.Split(string(data), "\n") - // 检查是否有包需要移除 + // Check if there are packages to remove hasPackagesToRemove := false for _, pkg := range packagesToRemove { pkg = strings.TrimSpace(pkg) @@ -272,98 +272,98 @@ func RemoveUnnecessaryPackages(target string, imageType string, removeMinimalLis } if !hasPackagesToRemove { - fmt.Println("没有需要移除的软件包") + fmt.Println("No packages need to be removed") return nil } - // 创建移除包的脚本 + // Create the package removal script scriptContent := "#!/bin/bash\n" - scriptContent += "set -e\n" // 遇到错误立即退出 - scriptContent += "echo '开始移除不必要的软件包...'\n" + scriptContent += "set -e\n" // Exit immediately if a command exits with a non-zero status + scriptContent += "echo 'Starting to remove unnecessary packages...'\n" for _, pkg := range packagesToRemove { pkg = strings.TrimSpace(pkg) if pkg != "" && !strings.HasPrefix(pkg, "#") { - // 先检查包是否已安装 + // First check if the package is installed scriptContent += fmt.Sprintf("if rpm -q %s &>/dev/null; then\n", pkg) - scriptContent += fmt.Sprintf(" echo '移除软件包: %s'\n", pkg) - scriptContent += fmt.Sprintf(" rpm -e --nodeps %s || echo '警告: 无法移除 %s'\n", pkg, pkg) + scriptContent += fmt.Sprintf(" echo 'Removing package: %s'\n", pkg) + scriptContent += fmt.Sprintf(" rpm -e --nodeps %s || echo 'Warning: failed to remove %s'\n", pkg, pkg) scriptContent += "fi\n" } } - scriptContent += "echo '软件包移除完成'\n" + scriptContent += "echo 'Package removal complete'\n" - // 使用绝对路径 + // Use absolute path scriptPath := filepath.Join(target, "remove_packages.sh") if err := os.WriteFile(scriptPath, []byte(scriptContent), 0755); err != nil { - return fmt.Errorf("无法创建移除软件包脚本: %v", err) + return fmt.Errorf("unable to create package removal script: %v", err) } - fmt.Println("执行软件包移除脚本...") + fmt.Println("Executing package removal script...") - // 执行脚本 + // Execute the script cmd := exec.Command("chroot", target, "/remove_packages.sh") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Run() - // 清理脚本 + // Clean up script os.Remove(scriptPath) if err != nil { - return fmt.Errorf("执行移除软件包脚本失败: %v", err) + return fmt.Errorf("failed to execute package removal script: %v", err) } return nil } -// 修改函数,接受文件路径参数 +// Modify function to accept file path parameter func UnmaskServices(target string, unmaskServicePath string) error { - // 检查是否有root权限 + // Check for root privilege if os.Geteuid() != 0 { - return fmt.Errorf("需要root权限执行chroot命令") + return fmt.Errorf("root privileges are required to execute chroot command") } - // 读取 unmaskService 文件 + // Read unmaskService file data, err := os.ReadFile(unmaskServicePath) if err != nil { - return fmt.Errorf("无法读取 unmaskService 文件: %v", err) + return fmt.Errorf("unable to read unmaskService file: %v", err) } - // 检查文件内容是否为空 + // Check if file content is empty if len(strings.TrimSpace(string(data))) == 0 { - fmt.Println("unmaskService文件为空,跳过解除服务屏蔽") + fmt.Println("unmaskService file is empty, skipping service unmasking") return nil } - // 创建解除屏蔽服务的脚本 + // Create the script for unmasking services scriptPath := filepath.Join(target, "unmask_services.sh") - // 添加脚本头和错误处理 + // Add script header and error handling scriptContent := "#!/bin/bash\n" - scriptContent += "set -e\n" // 遇到错误立即退出 - scriptContent += "echo '开始解除服务屏蔽...'\n" + scriptContent += "set -e\n" // Exit immediately if a command exits with a non-zero status + scriptContent += "echo 'Starting to unmask services...'\n" scriptContent += string(data) - scriptContent += "\necho '服务屏蔽解除完成'\n" + scriptContent += "\necho 'Service unmasking complete'\n" if err := os.WriteFile(scriptPath, []byte(scriptContent), 0755); err != nil { - return fmt.Errorf("无法创建解除服务屏蔽脚本: %v", err) + return fmt.Errorf("unable to create service unmasking script: %v", err) } - fmt.Println("执行解除服务屏蔽脚本...") + fmt.Println("Executing service unmasking script...") - // 执行脚本 + // Execute the script cmd := exec.Command("chroot", target, "/unmask_services.sh") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Run() - // 清理脚本 + // Clean up script os.Remove(scriptPath) if err != nil { - return fmt.Errorf("执行解除服务屏蔽脚本失败: %v", err) + return fmt.Errorf("failed to execute service unmasking script: %v", err) } return nil @@ -374,12 +374,12 @@ func ConfigurePipAndRemovePycache(target string, imageType string) error { return nil } if os.Geteuid() != 0 { - return fmt.Errorf("需要root权限执行chroot命令") + return fmt.Errorf("root privileges are required to execute chroot command") } scriptPath := filepath.Join(target, "configure_python.sh") scriptContent := templates.PythonConfigureScript if err := os.WriteFile(scriptPath, []byte(scriptContent), 0755); err != nil { - return fmt.Errorf("无法创建Python配置脚本: %v", err) + return fmt.Errorf("unable to create Python configuration script: %v", err) } cmd := exec.Command("chroot", target, "/configure_python.sh") cmd.Stdout = os.Stdout @@ -387,7 +387,7 @@ func ConfigurePipAndRemovePycache(target string, imageType string) error { err := cmd.Run() os.Remove(scriptPath) if err != nil { - return fmt.Errorf("执行Python配置脚本失败: %v", err) + return fmt.Errorf("failed to execute Python configuration script: %v", err) } return nil } diff --git a/pkg/scanner/dockerfile/auditor.go b/pkg/scanner/dockerfile/auditor.go index 78ce294..633937f 100644 --- a/pkg/scanner/dockerfile/auditor.go +++ b/pkg/scanner/dockerfile/auditor.go @@ -42,7 +42,7 @@ func (auditor *DockerfileAuditor) Audit(path string) (PolicyResult, error) { return policyResult, nil } -// ParseOnly 仅对指定的文件路径执行分析操作。 +// ParseOnly executes the analysis operation only on the specified file path. func (auditor *DockerfileAuditor) ParseOnly(path string) (ParseResult, error) { dockerfile, err := NewDockerfile(path) if err != nil { diff --git a/pkg/scanner/dockerfile/directives.go b/pkg/scanner/dockerfile/directives.go index d20b29a..7c6d252 100644 --- a/pkg/scanner/dockerfile/directives.go +++ b/pkg/scanner/dockerfile/directives.go @@ -111,7 +111,7 @@ func NewFromDirective(rawContent string) *FromDirective { if ref == "" { return directive } - // 处理显式 scheme 的镜像引用 + // Process image reference with explicit scheme if strings.HasPrefix(ref, "http://") || strings.HasPrefix(ref, "https://") { if u, err := url.Parse(ref); err == nil { directive.Registry = u.Host @@ -131,7 +131,7 @@ func NewFromDirective(rawContent string) *FromDirective { directive.ImageName = nameTag directive.ImageTag = "latest" } - // 提取 registry(无 scheme 情况) + // Extract registry (no scheme case) if slash := strings.Index(directive.ImageName, "/"); slash != -1 { first := strings.Split(directive.ImageName, "/")[0] if strings.Contains(first, ".") || strings.Contains(first, ":") { @@ -339,7 +339,7 @@ func (d *AddDirective) GetType() DockerfileDirectiveType { } func NewAddDirective(rawContent string) *AddDirective { var chown, source, destination string - // 正则匹配一下是否存在chown + // Regex match to check for the presence of chown re := regexp.MustCompile(`(?:--chown=(\S+)\s+)?(\S+)\s+(\S+)`) matches := re.FindStringSubmatch(rawContent) if len(matches) == 4 { @@ -347,7 +347,7 @@ func NewAddDirective(rawContent string) *AddDirective { source = matches[2] destination = matches[3] } else { - // 没chown前面是源后面是目标 + // If no chown, the first is the source and the second is the destination parts := strings.Split(rawContent, " ") if len(parts) >= 2 { source = parts[0] @@ -432,17 +432,17 @@ func (d *EnvDirective) GetType() DockerfileDirectiveType { func NewEnvDirective(rawContent string) *EnvDirective { vars := make(map[string]string) - // 按空格拆分rawContent以获得单个键值对 + // Split rawContent by space to get individual key-value pairs parts := strings.Fields(rawContent) for _, part := range parts { - // 检查零件是否包含“=”,以确定它是键值对还是只是一个键 + // Check if the part contains "=" to determine if it is a key-value pair or just a key if strings.Contains(part, "=") { - // 按“=”分割零件以获得键和值 + // Split the part by "=" to get the key and value kv := strings.SplitN(part, "=", 2) vars[kv[0]] = kv[1] } else { - // 如果没有“=”,如果没有“=”,假设该部分是键,其余部分是值 + // If there is no "=", assume the first part is the key and the rest is the value key := parts[0] value := strings.Join(parts[1:], " ") vars[key] = value diff --git a/pkg/scanner/dockerfile/parser.go b/pkg/scanner/dockerfile/parser.go index abc8378..2989704 100644 --- a/pkg/scanner/dockerfile/parser.go +++ b/pkg/scanner/dockerfile/parser.go @@ -69,7 +69,7 @@ func (v *DockerfileVisitor) VisitDockerfile(visitedChildren *parser.Node) interf if parsedLine.Next != nil { lineContent = parsedLine.Next.Dump() } - // 拼接命令类型和内容 + // Concatenate command type and content fullLine := parsedLine.Value if lineContent != "" { fullLine = parsedLine.Value + " " + lineContent diff --git a/pkg/scanner/dockerfile/policy.go b/pkg/scanner/dockerfile/policy.go index 887b0d7..ff80da7 100644 --- a/pkg/scanner/dockerfile/policy.go +++ b/pkg/scanner/dockerfile/policy.go @@ -50,10 +50,10 @@ func (p *Policy) EvaluateDockerfile(dockerfileObject Dockerfile) PolicyResult { for _, rule := range p.PolicyRules { testRuleResults := rule.Test(dockerfileObject.GetDirectives()) if testRuleResults != nil && len(*testRuleResults) > 0 { - // 转换规则类型为字符串 + // Convert rule type to string for i := range *testRuleResults { (*testRuleResults)[i].Type = rule.GetType() - // 检查是否有失败项 + // Check for failures if (*testRuleResults)[i].Status == "fail" { hasFailures = true } @@ -62,14 +62,14 @@ func (p *Policy) EvaluateDockerfile(dockerfileObject Dockerfile) PolicyResult { } } - // 根据是否有失败项决定整体结果 + // Determine the overall result based on whether there are failures auditOutcome := "pass" if hasFailures { auditOutcome = "fail" } return PolicyResult{ - Tests: testResults, // 现在包含合规和不合规项 + Tests: testResults, // Now includes compliant and non-compliant items Filename: dockerfileObject.GetFilename(), AuditOutcome: auditOutcome, Maintainers: dockerfileObject.GetMaintainers(), diff --git a/pkg/scanner/dockerfile/policyrule.go b/pkg/scanner/dockerfile/policyrule.go index 9debd2b..bf9eac5 100644 --- a/pkg/scanner/dockerfile/policyrule.go +++ b/pkg/scanner/dockerfile/policyrule.go @@ -44,15 +44,15 @@ type Rule struct { Line int `json:"Line,omitempty"` Directive string `json:"Directive,omitempty"` Level string `json:"Level,omitempty"` - Status string `json:"Status"` // 新增:"pass" 或 "fail" + Status string `json:"Status"` // New addition: "pass" or "fail" } -// 添加MarshalJSON方法以自定义JSON序列化 +// Add MarshalJSON method for custom JSON serialization func (r Rule) MarshalJSON() ([]byte, error) { type Alias Rule buf := new(bytes.Buffer) encoder := json.NewEncoder(buf) - encoder.SetEscapeHTML(false) // 禁用HTML转义,保持特殊字符原样 + encoder.SetEscapeHTML(false) // Disable HTML escaping to keep special characters as they are err := encoder.Encode(&struct { Type string `json:"Type"` Alias @@ -63,7 +63,7 @@ func (r Rule) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - // 移除encoder添加的换行符 + // Remove the newline character added by the encoder bytes := buf.Bytes() return bytes[:len(bytes)-1], nil } @@ -86,11 +86,11 @@ type PolicyRule interface { } func (r *PolicyTestResult) GetResult() *[]Rule { - // 无论是否有结果都返回,包括合规项 + // Return regardless of whether there are results, including compliant items if len(r.Results) > 0 { return &r.Results } - return &[]Rule{} // 返回空切片而不是nil + return &[]Rule{} // Return empty slice instead of nil } func (r *PolicyTestResult) AddResult(details, mitigations string, ruleType PolicyRuleType, content string, directiveType ...string) { @@ -109,7 +109,7 @@ func (r *PolicyTestResult) AddResult(details, mitigations string, ruleType Polic func (r *PolicyTestResult) AddPassResult(details string, ruleType PolicyRuleType, content string) { result := Rule{ Details: details, - Mitigations: "", // 合规项不需要mitigations + Mitigations: "", // Compliant items do not need mitigations Type: ruleType, Status: "pass", } @@ -164,7 +164,7 @@ func NewEnforceRegistryPolicy(allowedRegistries []string, enabled bool) *Enforce GenericPolicyRule: GenericPolicyRule{ Type: ENFORCE_REGISTRY, TestResult: PolicyTestResult{}, - Description: "仅允许使用已批准仓库中的镜像构建镜像。(使用 FROM 命令)", + Description: "Only allow building images from images in approved repositories. (Using the FROM command)", }, AllowedRegistries: allowedRegistries, Enabled: enabled, @@ -205,24 +205,24 @@ func (r *EnforceRegistryPolicy) Test(dockerfileDirectives map[string][]DfDirecti } } if !found { - // 不合规项 + // Non-compliant item r.TestResult.AddResult( func() string { if registry == "" { - return "Registry 默认注册表 不是允许拉取镜像的注册表。" + return "Registry default registry is not an allowed registry for pulling images." } - return "Registry " + registry + " 不是允许拉取镜像的注册表。" + return "Registry " + registry + " is not an allowed registry for pulling images." }(), - "应该更改 FROM 语句,使用来源于允许的镜像仓库注册表的镜像:"+ + "The FROM statement should be changed to use an image from an allowed image repository registry:"+ strings.Join(r.AllowedRegistries, ", "), r.Type, fromDirective.Content, ) } else { - // 新增:合规项 + // New addition: Compliant item if registry != "" { r.TestResult.AddPassResult( - "Registry "+registry+" 是允许的镜像仓库注册表。", + "Registry "+registry+" is an allowed image repository registry.", r.Type, fromDirective.Content, ) @@ -235,7 +235,7 @@ func (r *EnforceRegistryPolicy) Test(dockerfileDirectives map[string][]DfDirecti } func (r *EnforceRegistryPolicy) Details() string { - return "允许使用的镜像仓库: " + strings.Join(r.AllowedRegistries, ", ") + "。" + return "Allowed registries: " + strings.Join(r.AllowedRegistries, ", ") + "." } type ForbidTags struct { @@ -248,7 +248,7 @@ func NewForbidTags(tags []string) *ForbidTags { GenericPolicyRule: GenericPolicyRule{ Type: FORBID_TAGS, TestResult: PolicyTestResult{}, - Description: "限制使用某些标签作为构建的基础镜像(使用 FROM 命令)", + Description: "Restricts the use of certain tags as base images for building (using the FROM command)", }, ForbiddenTags: tags, } @@ -278,13 +278,13 @@ func (r *ForbidTags) Test(directives map[string][]DfDirective) *[]Rule { } tag := fromDirective.ImageTag if contains(r.ForbiddenTags, tag) { - r.TestResult.AddResult(fmt.Sprintf("标签 %s 不允许使用。", tag), - fmt.Sprintf("FROM 语句应该更改为使用具有固定标签的镜像,或者不使用以下任何标签: %s", + r.TestResult.AddResult(fmt.Sprintf("Tag %s is not allowed.", tag), + fmt.Sprintf("The FROM statement should be changed to use an image with a fixed tag, or not use any of the following tags: %s", strings.Join(r.ForbiddenTags, ", ")), r.Type, fromDirective.Content) } else { r.TestResult.AddPassResult( - fmt.Sprintf("允许使用 %s 标签。", tag), + fmt.Sprintf("Tag %s is allowed.", tag), r.Type, fromDirective.Content) } @@ -294,7 +294,7 @@ func (r *ForbidTags) Test(directives map[string][]DfDirective) *[]Rule { } func (rule *ForbidTags) Details() string { - return fmt.Sprintf("以下标签被禁止使用: %s。", strings.Join(rule.ForbiddenTags, ", ")) + return fmt.Sprintf("The following tags are forbidden: %s。", strings.Join(rule.ForbiddenTags, ", ")) } type ForbidInsecureRegistries struct { @@ -307,7 +307,7 @@ func NewForbidInsecureRegistries(enabled bool) *ForbidInsecureRegistries { GenericPolicyRule: GenericPolicyRule{ Type: FORBID_INSECURE_REGISTRIES, TestResult: PolicyTestResult{}, - Description: "禁止使用不安全的镜像仓库。", + Description: "Forbid the use of insecure registries.", }, Enabled: enabled, } @@ -322,13 +322,13 @@ func (rule *ForbidInsecureRegistries) Test(dockerfileStatements map[string][]DfD raw := fromDirective.Content if strings.HasPrefix(raw, "FROM http://") { reg := fromDirective.Registry - testResult.AddResult(fmt.Sprintf("镜像仓库 %s 被视为不安全", reg), - "FROM 语句应该更改为使用 HTTPS 协议的镜像仓库中的镜像。", + testResult.AddResult(fmt.Sprintf("Registry %s is considered insecure", reg), + "The FROM statement should be changed to use an image from a registry using the HTTPS protocol.", rule.Type, fromDirective.Content) } else { if fromDirective.Registry != "" { testResult.AddPassResult( - fmt.Sprintf("镜像仓库 %s 被视为安全", fromDirective.Registry), + fmt.Sprintf("Registry %s is considered secure", fromDirective.Registry), rule.Type, fromDirective.Content) } @@ -350,7 +350,7 @@ type ForbidRoot struct { func NewForbidRoot(enabled bool) *ForbidRoot { return &ForbidRoot{ GenericPolicyRule: GenericPolicyRule{ - Description: "禁止容器以特权用户(root)身份运行。", + Description: "Forbids the container from running as a privileged user (root).", Type: FORBID_ROOT, }, Enabled: enabled, @@ -361,8 +361,8 @@ func (rule *ForbidRoot) Test(dockerfileStatements map[string][]DfDirective) *[]R testResult := NewPolicyTestResult() userStatements := dockerfileStatements["user"] if len(userStatements) == 0 { - testResult.AddResult("未找到 USER 指令。默认情况下,如果不降低权限,容器将以 root 用户身份运行。", - "创建一个用户并在镜像的入口点之前添加 USER 指令,以非特权用户身份运行应用程序。", + testResult.AddResult("USER instruction not found. By default, the container will run as the root user if privileges are not dropped.", + "Create a user and add a USER instruction before the image's entrypoint to run the application as a non-privileged user.", rule.Type, "") } else { lastUserStatement := userStatements[len(userStatements)-1] @@ -370,12 +370,12 @@ func (rule *ForbidRoot) Test(dockerfileStatements map[string][]DfDirective) *[]R if userDirective, ok := lastUserStatementInterface.(*UserDirective); ok { lastUser := userDirective.User if lastUser == "0" || lastUser == "root" { - testResult.AddResult("最后一个 USER 指令将权限提升为 root。", - "在镜像的入口点之前添加另一个 USER 指令,以非特权用户身份运行应用程序。", + testResult.AddResult("The last USER instruction elevates privileges to root.", + "Add another USER instruction before the image's entrypoint to run the application as a non-privileged user.", rule.Type, userDirective.Content) } else { testResult.AddPassResult( - "最后一个 USER 指令指定用户非特权用户。", + "The last USER instruction specifies a non-privileged user.", rule.Type, userDirective.Content) } @@ -392,7 +392,7 @@ type ForbidPrivilegedPorts struct { func NewForbidPrivilegedPorts(enabled bool) *ForbidPrivilegedPorts { return &ForbidPrivilegedPorts{ GenericPolicyRule: GenericPolicyRule{ - Description: "禁止镜像暴露需要管理员权限的特权端口。", + Description: "Forbids the image from exposing privileged ports that require administrator privileges.", Type: FORBID_PRIVILEGED_PORTS, }, Enabled: enabled, @@ -409,36 +409,36 @@ func (rule *ForbidPrivilegedPorts) Test(dockerfileDirective map[string][]DfDirec for _, port := range ports { portNum, err := strconv.Atoi(port) if err == nil { - // 端口号解析成功 + // Port number parsed successfully if portNum <= 1024 { - // 特权端口 - 不合规 + // Privileged port - Non-compliant testResult.AddResult( - fmt.Sprintf("容器暴露了特权端口: %s。特权端口要求使用它的应用程序以 root 身份运行。", port), - "更改应用程序的配置,使其绑定到大于 1024 的端口,并更改 Dockerfile 以反映此修改。", + fmt.Sprintf("Container exposes privileged port: %s. Privileged ports require the application using it to run as root.", port), + "Change the application's configuration to bind to a port greater than 1024, and change the Dockerfile to reflect this modification.", rule.Type, exposeDirective.Content) } else { - // 非特权端口 - 合规 + // Non-privileged port - Compliant testResult.AddPassResult( - fmt.Sprintf("容器没有暴露特权端口,仅暴露了非特权端口: %s,符合安全要求。", port), + fmt.Sprintf("Container does not expose privileged ports, only exposes non-privileged port: %s, which meets security requirements.", port), rule.Type, exposeDirective.Content) } } else { - // 端口号解析失败,可能是环境变量,尝试从环境变量获取 + // Port number parsing failed, may be an environment variable, try to get from environment variable portNumber := rule.getPortFromEnv(port, dockerfileDirective) if portNumber != nil { if *portNumber <= 1024 { - // 从环境变量解析出的特权端口 - 不合规 + // Privileged port parsed from environment variable - Non-compliant testResult.AddResult( - fmt.Sprintf("容器暴露了特权端口: %d。特权端口要求使用它的应用程序以 root 身份运行。", *portNumber), - "更改应用程序的配置,使其绑定到大于 1024 的端口,并更改 Dockerfile 以反映此修改。", + fmt.Sprintf("Container exposes privileged port: %d. Privileged ports require the application using it to run as root.", *portNumber), + "Change the application's configuration to bind to a port greater than 1024, and change the Dockerfile to reflect this modification.", rule.Type, exposeDirective.Content) } else { - // 从环境变量解析出的非特权端口 - 合规 + // Non-privileged port parsed from environment variable - Compliant testResult.AddPassResult( - fmt.Sprintf("容器没有暴露特权端口,仅暴露了非特权端口: %d,符合安全要求。", *portNumber), + fmt.Sprintf("Container does not expose privileged ports, only exposes non-privileged port: %d, which meets security requirements.", *portNumber), rule.Type, exposeDirective.Content) } @@ -476,7 +476,7 @@ func NewForbidPackages(forbiddenPackages []string) *ForbidPackages { return &ForbidPackages{ ForbiddenPackages: forbiddenPackages, GenericPolicyRule: GenericPolicyRule{ - Description: "禁止安装/使用危险的软件包。", + Description: "Forbids the installation/use of dangerous packages.", Type: FORBID_PACKAGES, }, } @@ -495,8 +495,8 @@ func (rule *ForbidPackages) Test(mapDirectives map[string][]DfDirective) *[]Rule packageRegex := regexp.MustCompile(fmt.Sprintf(`(^|[^a-zA-Z0-9])%s([^a-zA-Z0-9]|$)`, pkg)) match := packageRegex.MatchString(statement.Get()["raw_content"].(string)) if match { - testResult.AddResult(fmt.Sprintf("禁止的软件包 \"%s\" 被安装或使用。", pkg), - fmt.Sprintf("应该审查 RUN/CMD/ENTRYPOINT 指令并移除软件包 \"%s\",除非绝对必要。", pkg), + testResult.AddResult(fmt.Sprintf("Forbidden package \"%s\" is installed or used.", pkg), + fmt.Sprintf("The RUN/CMD/ENTRYPOINT instruction should be reviewed and package \"%s\" removed unless absolutely necessary.", pkg), rule.Type, statement.Get()["raw_content"].(string)) } } @@ -504,11 +504,11 @@ func (rule *ForbidPackages) Test(mapDirectives map[string][]DfDirective) *[]Rule } else { for _, pkg := range rule.ForbiddenPackages { if contains(installedPackages, pkg) { - testResult.AddResult(fmt.Sprintf("禁止的软件包 \"%s\" 已安装。", pkg), - fmt.Sprintf("应该审查 RUN 指令并移除软件包 \"%s\",除非绝对必要。", pkg), + testResult.AddResult(fmt.Sprintf("Forbidden package \"%s\" is installed.", pkg), + fmt.Sprintf("The RUN instruction should be reviewed and package \"%s\" removed unless absolutely necessary.", pkg), rule.Type, "") } else { - testResult.AddPassResult(fmt.Sprintf("禁止的软件包 \"%s\" 未安装。", pkg), + testResult.AddPassResult(fmt.Sprintf("Forbidden package \"%s\" is not installed.", pkg), rule.Type, "") } } @@ -587,7 +587,7 @@ func contains(slice []string, str string) bool { } func (rule *ForbidPackages) Details() string { - return fmt.Sprintf("以下软件包被禁止使用: %s。", strings.Join(rule.ForbiddenPackages, ", ")) + return fmt.Sprintf("The following packages are forbidden: %s。", strings.Join(rule.ForbiddenPackages, ", ")) } type ForbidSecrets struct { @@ -599,7 +599,7 @@ type ForbidSecrets struct { func NewForbidSecrets(secretsPatterns, allowedPatterns []string) *ForbidSecrets { return &ForbidSecrets{ GenericPolicyRule: GenericPolicyRule{ - Description: "禁止在镜像中包含敏感信息。", + Description: "Forbid the inclusion of sensitive information in the image.", Type: FORBID_SECRETS, }, secretsPatterns: secretsPatterns, @@ -619,14 +619,14 @@ func (fs *ForbidSecrets) Test(dockerfileStatements map[string][]DfDirective) *[] isForbidden, pattern := fs.isForbiddenPattern(sources.(string)) if isForbidden && !fs.isWhitelistedPattern(sources.(string)) { fs.TestResult.AddResult( - fmt.Sprintf("符合模式 \"%s\" 的禁止文件被添加到镜像中。", pattern), - "应该更改或移除 ADD 指令。应该使用更安全和无状态的方式(如 Vault、Kubernetes secrets)来提供敏感信息。", + fmt.Sprintf("Forbidden file matching pattern \"%s\" is added to the image.", pattern), + "The ADD instruction should be changed or removed. Safer and stateless methods (like Vault, Kubernetes secrets) should be used to provide sensitive information.", fs.Type, statement.Get()["raw_content"].(string), ) } else { fs.TestResult.AddPassResult( - "没有识别到敏感信息。", + "No sensitive information identified.", fs.Type, statement.Get()["raw_content"].(string), ) @@ -637,14 +637,14 @@ func (fs *ForbidSecrets) Test(dockerfileStatements map[string][]DfDirective) *[] isForbidden, pattern := fs.isForbiddenPattern(sources.(string)) if isForbidden && !fs.isWhitelistedPattern(sources.(string)) { fs.TestResult.AddResult( - fmt.Sprintf("符合模式 \"%s\" 的禁止文件被添加到镜像中。", pattern), - "应该更改或移除 COPY 指令。应该使用更安全和无状态的方式(如 Vault、Kubernetes secrets)来提供敏感信息。", + fmt.Sprintf("Forbidden file matching pattern \"%s\" is added to the image.", pattern), + "The COPY instruction should be changed or removed. Safer and stateless methods (like Vault, Kubernetes secrets) should be used to provide sensitive information.", fs.Type, statement.Get()["raw_content"].(string), ) } else { fs.TestResult.AddPassResult( - "没有识别到敏感信息。", + "No sensitive information identified.", fs.Type, statement.Get()["raw_content"].(string), ) @@ -677,7 +677,7 @@ func (fs *ForbidSecrets) isWhitelistedPattern(source string) bool { } func (fs *ForbidSecrets) Details() string { - return fmt.Sprintf("以下模式被禁止: %s。\n以下模式被允许: %s", + return fmt.Sprintf("The following patterns are forbidden: %s.\nThe following patterns are allowed: %s", joinStrings(fs.secretsPatterns), joinStrings(fs.allowedPatterns)) } diff --git a/pkg/templates/dockerfile.go b/pkg/templates/dockerfile.go index 84d33a5..589819c 100644 --- a/pkg/templates/dockerfile.go +++ b/pkg/templates/dockerfile.go @@ -43,15 +43,15 @@ USER 1001 # CMD ["/usr/bin/xxx"] ` -// BaseImageDockerfile 是用于构建基础镜像的 Dockerfile 模板 -const BaseImageDockerfile = `# {{.ImageName}} 基础镜像 +// BaseImageDockerfile is the Dockerfile template used to build base images +const BaseImageDockerfile = `# {{.ImageName}} Base Image FROM scratch ADD rootfs.tar / CMD ["/bin/bash"] ` -// InitImageDockerfile 是用于构建 init 镜像的 Dockerfile 模板 -const InitImageDockerfile = `# {{.ImageName}} init 镜像 +// InitImageDockerfile is the Dockerfile template used to build init images +const InitImageDockerfile = `# {{.ImageName}} Init Image FROM scratch ADD rootfs.tar / CMD ["/sbin/init"] diff --git a/pkg/templates/unmaskservice.go b/pkg/templates/unmaskservice.go index a708be8..3f7c1cb 100644 --- a/pkg/templates/unmaskservice.go +++ b/pkg/templates/unmaskservice.go @@ -36,7 +36,7 @@ if command -v systemctl >/dev/null 2>&1; then systemctl disable systemd-networkd.socket systemd-networkd || true systemctl disable systemd-hostnamed.service || true else - echo "systemctl 未找到,跳过 systemd 服务禁用步骤" + echo "systemctl not found, skipping systemd service disabling step" fi # rm systemd drop file diff --git a/pkg/types/image.go b/pkg/types/image.go index 98cb656..a9dbeab 100644 --- a/pkg/types/image.go +++ b/pkg/types/image.go @@ -44,12 +44,12 @@ type JsonBuilder struct { } func MergeAnnotations(preferred map[string]string, aux []string) (map[string]string, error) { - // 如果 aux 列表不为空,处理附加注释 + // If the aux list is not empty, process auxiliary annotations if len(aux) != 0 { - // 创建一个新的映射来存储附加注释 + // Create a new map to store auxiliary annotations auxAnnotations := make(map[string]string) for _, annotationSpec := range aux { - // 分割注释字符串为键值对 + // Split the annotation string into key-value pairs key, val, hasVal := strings.Cut(annotationSpec, "=") if !hasVal { return nil, fmt.Errorf("no value given for annotation %q", key) @@ -57,13 +57,13 @@ func MergeAnnotations(preferred map[string]string, aux []string) (map[string]str auxAnnotations[key] = val } - // 如果 preferred 为空,初始化为一个新的映射 + // If preferred is nil, initialize it as a new map if preferred == nil { preferred = make(map[string]string) } - // 合并 auxAnnotations 和 preferred 映射 - // 克隆 preferred 映射,以免修改原始映射 + // Merge auxAnnotations and preferred map + // Clone the preferred map to avoid modifying the original map merged := make(map[string]string, len(preferred)+len(auxAnnotations)) for k, v := range preferred { merged[k] = v diff --git a/pkg/utils/common.go b/pkg/utils/common.go index 46839fd..b8f37cc 100644 --- a/pkg/utils/common.go +++ b/pkg/utils/common.go @@ -34,12 +34,12 @@ func check() { } func GetStore(c *cobra.Command) (storage.Store, error) { - // 下面为获取option默认方法,注意需考虑options其他属性是否是必须的,在下面进行展开 + // The following is the default method for obtaining options. Note that other properties of options need to be considered whether they are mandatory, and will be expanded below. options, err := storage.DefaultStoreOptions(unshare.GetRootlessUID() > 0, unshare.GetRootlessUID()) if err != nil { return nil, fmt.Errorf("failed to get default store options: %w", err) } - // TODO 判断参数common-builders 例如:storage-dirver storage-opt root 后续支持 + // TODO Determine parameters common-builders e.g. storage-dirver storage-opt root, support later // umask check force on 022 check() diff --git a/pkg/utils/copier.go b/pkg/utils/copier.go index 8a42c22..16117fd 100644 --- a/pkg/utils/copier.go +++ b/pkg/utils/copier.go @@ -23,12 +23,12 @@ import ( ) func ArchiveUncompress(source, destination string) error { - // 检查源文件是否存在 + // Check if source file exists if _, err := os.Stat(source); os.IsNotExist(err) { return fmt.Errorf("source file '%s' does not exist", source) } - // 检查是否是压缩文件 + // Check if it is a compressed file var reader io.Reader file, err := os.Open(source) if err != nil { @@ -78,23 +78,23 @@ func ArchiveUncompress(source, destination string) error { tarReader := tar.NewReader(gzipReader) reader = tarReader } else { - // 如果不是压缩文件,则直接使用原始文件 + // If it is not a compressed file, use the original file directly reader = file } - // 创建目标目录(如果不存在) + // Create the destination directory (if it does not exist) if err := os.MkdirAll(filepath.Dir(destination), 0755); err != nil { return err } - // 创建目标文件 + // Create the destination file destFile, err := os.Create(destination) if err != nil { return err } defer destFile.Close() - // 复制文件内容 + // Copy file content writer := bufio.NewWriter(destFile) _, err = io.Copy(writer, reader) if err != nil { diff --git a/pkg/utils/parse.go b/pkg/utils/parse.go index 29512bf..ea89cc6 100644 --- a/pkg/utils/parse.go +++ b/pkg/utils/parse.go @@ -74,27 +74,27 @@ func humanSize(s int64) string { } } -// 解析镜像名称的辅助函数 +// Helper function to parse image name func parseImageName(fullName string) (repository, tag string) { if fullName == "" { return unknownState, unknownState } - // 尝试使用标准库解析 + // Try to parse using the standard library parsed, err := reference.ParseNormalizedNamed(fullName) if err != nil { - // 解析失败,尝试手动解析 + // Parsing failed, try manual parsing return manualParseImageName(fullName) } - // 获取仓库名 + // Get repository name repository = reference.FamiliarName(parsed) - // 获取标签 + // Get tag if tagged, ok := parsed.(reference.Tagged); ok { tag = tagged.Tag() } else { - // 检查是否是摘要引用 + // Check if it is a digest reference if digested, ok := parsed.(reference.Digested); ok { digestStr := digested.Digest().String() if len(digestStr) > 12 { @@ -111,32 +111,32 @@ func parseImageName(fullName string) (repository, tag string) { } func manualParseImageName(fullName string) (repository, tag string) { - // 处理特殊字符 + // Handle special characters fullName = strings.TrimSpace(fullName) if fullName == "" { return unknownState, unknownState } - // 处理常见的镜像名称格式 - // 格式1: registry/repository:tag - // 格式2: repository:tag - // 格式3: registry:port/repository:tag + // Handle common image name formats + // Format 1: registry/repository:tag + // Format 2: repository:tag + // Format 3: registry:port/repository:tag - // 查找最后一个 ":",用于分割标签 + // Find the last ":" to split the tag lastColon := strings.LastIndex(fullName, ":") if lastColon <= 0 { - // 没有 ":",只有仓库名 + // No ":", only repository name return fullName, unknownState } - // 检查是否可能是端口号(比如 localhost:5000/image) - // 检查 ":" 前面是否有数字(简单判断) + // Check if it might be a port number (e.g., localhost:5000/image) + // Check if there is a digit before ":" (simple check) if lastColon > 0 { - // 检查 ":" 前是否是数字(端口号) + // Check if the character before ":" is a digit (port number) charBeforeColon := fullName[lastColon-1] if charBeforeColon >= '0' && charBeforeColon <= '9' { - // 可能是端口号,尝试找前一个 ":" + // Might be a port number, try to find the previous ":" prevColon := strings.LastIndex(fullName[:lastColon], ":") if prevColon > 0 { repository = fullName[:prevColon] @@ -146,11 +146,11 @@ func manualParseImageName(fullName string) (repository, tag string) { } } - // 正常的分割 + // Normal splitting repository = fullName[:lastColon] tag = fullName[lastColon+1:] - // 如果标签为空 + // If the tag is empty if tag == "" { tag = unknownState } @@ -172,12 +172,12 @@ func sortImages(imgs []*imagemanager.Image, ops options.ImagesOption) ([]imageRe imgID := img.OriImage.ID if !ops.NoTrunc { - // NoTrunc=false(默认):截断到10位 + // NoTrunc=false (default): truncate to 10 characters if len(imgID) > 10 { imgID = imgID[:10] } } else { - // NoTrunc=true:截断到12位 + // NoTrunc=true: truncate to 12 characters if len(imgID) > 12 { imgID = imgID[:12] } @@ -198,7 +198,7 @@ func sortImages(imgs []*imagemanager.Image, ops options.ImagesOption) ([]imageRe }) } } else { - // 没有名称的情况 + // Case with no names imgReport = append(imgReport, imageReport{ Repository: unknownState, Tag: unknownState, @@ -211,7 +211,7 @@ func sortImages(imgs []*imagemanager.Image, ops options.ImagesOption) ([]imageRe } } - // 按 Repository 排序 + // Sort by Repository sort.Slice(imgReport, func(i, j int) bool { return imgReport[i].Repository < imgReport[j].Repository }) @@ -239,18 +239,18 @@ func sortContainers(containers []container.Container) ([]containerReport, error) } func FormatImages(images []*imagemanager.Image, ops options.ImagesOption) error { - // 定义输出格式 + // Define output format defaultImageTableFormat := "table {{.Repository}} {{.Tag}} {{.ID}} {{.Size}} {{.Created}}" defaultImageTableFormatWithDigest := "table {{.Repository}} {{.Tag}} {{.ID}} {{.Digest}} {{.Size}} {{.Created}}" defaultQuietFormat := "table {{.ID}}" // defaultImageTableFormatWithDigest = "table {{.Repository}}\t{{.Tag}}\t{{.Digest}}\t{{.ID}}\t{{.CreatedSince}}\t{{.Size}}" - // 构造所需的image结构=>sortImage + // Construct the required image structure => sortImage imagesReport, err := sortImages(images, ops) if err != nil { return err } - // 定义表头映射 + // Define table header mapping headers := report.Headers(imageReport{}, map[string]string{ "Repository": "REPOSITORY", "Tag": "TAG", @@ -315,7 +315,7 @@ func JsonFormatImages(images []*imagemanager.Image, ops options.ImagesOption) er } func FormatBuilders(containers []container.Container, ops options.BuildersOption) error { - // TODO 参考docker输出 + // TODO Refer to docker output defaultBuilderTableFormat := "table {{.ID}} {{.Names}} {{.LayerID}} {{.ImageID}} {{.Created}}" containerReports, err := sortContainers(containers) if err != nil { @@ -415,7 +415,7 @@ func ParseBuildOptions(cmd *cobra.Command, flags *options.BuildOptions, contextD var uselayers bool uselayers = true - // 添加build-args处理 + // Add build-args handling var buildArgs map[string]string if len(flags.BuildArg) > 0 { buildArgs = make(map[string]string) @@ -443,33 +443,33 @@ func ParseBuildOptions(cmd *cobra.Command, flags *options.BuildOptions, contextD Output: output, OutputFormat: format, Args: buildArgs, - // 添加 SystemContext 设置 + // Add SystemContext setting SystemContext: &types.SystemContext{}, } - // 设置认证文件路径,使用已登录的认证信息 + // Set authentication file path, using logged-in authentication information opts.SystemContext.AuthFilePath = auth.GetDefaultAuthFile() - // 设置 TLS 验证 + // Set TLS verification if flags.Insecure { opts.SystemContext.DockerInsecureSkipTLSVerify = types.OptionalBoolTrue } - // 设置 registries.conf 路径 + // Set registries.conf path imagemanager.SetRegistriesConfPath(opts.SystemContext) - // 获取已登录的认证信息 + // Get logged-in authentication information credentials, err := auth_config.GetAllCredentials(opts.SystemContext) if err != nil || len(credentials) == 0 { - // 没有认证信息时,使用默认的TLS验证设置 + // If there is no authentication information, use the default TLS verification setting opts.SystemContext.DockerInsecureSkipTLSVerify = types.OptionalBoolFalse } else { - // 有认证信息时,检查Dockerfile中的镜像仓库是否匹配 + // If there is authentication information, check if the repositories in the Dockerfile match the logged-in ones matchFound := false for _, dockerfilePath := range dockerfilePaths { repositories, err := ParseDockerfileFromImage(dockerfilePath) if err != nil { - continue // 忽略解析错误,继续处理其他Dockerfile + continue // Ignore parsing errors, continue processing other Dockerfiles } for _, repo := range repositories { @@ -484,10 +484,10 @@ func ParseBuildOptions(cmd *cobra.Command, flags *options.BuildOptions, contextD } if matchFound { - // Dockerfile中使用的镜像来源于已登录的镜像仓库 + // The image used in the Dockerfile comes from a logged-in registry opts.SystemContext.DockerInsecureSkipTLSVerify = types.OptionalBoolTrue } else { - // Dockerfile中使用的镜像不来源于已登录的镜像仓库 + // The image used in the Dockerfile does not come from a logged-in registry opts.SystemContext.DockerInsecureSkipTLSVerify = types.OptionalBoolFalse } } @@ -497,7 +497,7 @@ func ParseBuildOptions(cmd *cobra.Command, flags *options.BuildOptions, contextD func ResolveDockerfiles(op *options.BuildOptions, args []string) ([]string, string, error) { var dockerfiles []string - // 收集 Dockerfile 路径 + // Collect Dockerfile paths for _, f := range op.File { if f == "-" { if len(args) == 0 { @@ -512,7 +512,7 @@ func ResolveDockerfiles(op *options.BuildOptions, args []string) ([]string, stri var contextDir string if len(args) > 0 { - // 排除 `-`,确保上下文目录是有效的 + // Exclude `-` to ensure the context directory is valid if args[0] != "-" { absDir, err := filepath.Abs(args[0]) if err != nil { @@ -520,7 +520,7 @@ func ResolveDockerfiles(op *options.BuildOptions, args []string) ([]string, stri } contextDir = absDir } else { - // 如果 args 只有 `-`,可以选择使用当前工作目录 + // If args only contains `-`, choose to use the current working directory var err error contextDir, err = os.Getwd() if err != nil { @@ -566,12 +566,12 @@ func ResolveDockerfiles(op *options.BuildOptions, args []string) ([]string, stri return dockerfiles, contextDir, nil } -// ParseDockerfileFromImage 解析dockerfile,获取FROM的镜像的仓库地址 -// 比如: cr.kylinos.cn/test/myapp:01,获取到cr.kylinos.cn +// ParseDockerfileFromImage parses the Dockerfile to get the repository addresses of the FROM images +// For example: cr.kylinos.cn/test/myapp:01, gets cr.kylinos.cn func ParseDockerfileFromImage(dockerfilePath string) ([]string, error) { var repositories []string - // 读取Dockerfile内容 + // Read Dockerfile content file, err := os.Open(dockerfilePath) if err != nil { return nil, fmt.Errorf("failed to open dockerfile %s: %w", dockerfilePath, err) @@ -583,26 +583,26 @@ func ParseDockerfileFromImage(dockerfilePath string) ([]string, error) { return nil, fmt.Errorf("failed to read dockerfile %s: %w", dockerfilePath, err) } - // 按行分割内容 + // Split content by line lines := strings.Split(string(content), "\n") for _, line := range lines { - // 去除前后空格 + // Trim leading and trailing spaces line = strings.TrimSpace(line) - // 跳过空行和注释 + // Skip empty lines and comments if line == "" || strings.HasPrefix(line, "#") { continue } - // 检查是否是FROM指令(不区分大小写) + // Check if it is a FROM instruction (case-insensitive) if strings.HasPrefix(strings.ToUpper(line), "FROM ") { - // 提取FROM后面的镜像名称 + // Extract the image name after FROM parts := strings.Fields(line) if len(parts) >= 2 { imageName := parts[1] - // 解析镜像名称,去除tag和digest + // Parse the image name to get the repository repository := parseImageRepository(imageName) if repository != "" { repositories = append(repositories, repository) @@ -614,36 +614,36 @@ func ParseDockerfileFromImage(dockerfilePath string) ([]string, error) { return repositories, nil } -// parseImageRepository 从完整的镜像名称中提取仓库地址 -// 例如: cr.kylinos.cn/test/myapp:01 -> cr.kylinos.cn -// 例如: ubuntu:20.04 -> "" (没有明确的仓库地址) -// 例如: registry.io:5000/user/app@sha256:abc123 -> registry.io:5000 +// parseImageRepository extracts the repository address from the full image name +// Example: cr.kylinos.cn/test/myapp:01 -> cr.kylinos.cn +// Example: ubuntu:20.04 -> "" (no explicit repository address) +// Example: registry.io:5000/user/app@sha256:abc123 -> registry.io:5000 func parseImageRepository(imageName string) string { if imageName == "" { return "" } - // 去除digest部分 (以@开头的部分) + // Remove the digest part (the part starting with @) if idx := strings.Index(imageName, "@"); idx != -1 { imageName = imageName[:idx] } - // 去除tag部分 (最后一个:后面的部分) + // Remove the tag part (the part after the last :) if idx := strings.LastIndex(imageName, ":"); idx != -1 { - // 检查:后面是否包含/,如果包含则说明这个:是仓库地址的一部分(端口号) + // Check if the part after : contains /, if it does, it means this : is part of the repository address (port number) tagPart := imageName[idx+1:] if !strings.Contains(tagPart, "/") { - // 这是一个tag,去除它 + // This is a tag, remove it imageName = imageName[:idx] } } - // 提取仓库地址部分 - // 如果镜像名称包含/,则第一个/之前的部分是仓库地址 + // Extract the repository address part + // If the image name contains /, the part before the first / is the repository address if idx := strings.Index(imageName, "/"); idx != -1 { return imageName[:idx] } - // 如果没有/,没有明确的仓库地址 + // If there is no /, there is no explicit repository address return "" } -- Gitee