nexus镜像仓库清理

背景:

随着项目的时间越长nexus占的磁盘空间越多,因此我们需要一个清理工具;nexus自带的Cleanup Policy也可以清理但是无法满足我们的版本保留要求;网络上介绍的nexus-cli、由于我们的服务器是arm64版本所以有需要我们去源码编译、这边小小的尝试过发现有些包下不到哦、或者包名有所更改、所以还是选择调用nexus自带的api进行删除、各位好友有好方法给我留言哦。

1、代码功能:输出私钥文件和加密字符串、分别放到下面脚本及 配置文件里

//功能加密文明密码,输出加密字符串到文件encrypted_password.txt、私钥到到文件private_key.pem

package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"encoding/base64"
	"fmt"
	"log"
	"os"
)

func main() {
	// 假设这是您的明文密码
	password := "admin@123456"

	// 生成 RSA 密钥对
	privateKey, publicKey := generateRSAKeyPair()

	// 加密密码
	encryptedPassword := encryptRSA(publicKey, []byte(password))
	fmt.Println(encryptedPassword)

	// 将加密后的密码写入文件
	if err := writeEncryptedPassword("encrypted_password.txt", encryptedPassword); err != nil {
		log.Fatalf("Error writing encrypted password to file: %v", err)
	}

	// 将私钥写入文件
	if err := writePrivateKey("private_key.pem", privateKey); err != nil {
		log.Fatalf("Error writing private key to file: %v", err)
	}
}

// generateRSAKeyPair 生成 RSA 密钥对
func generateRSAKeyPair() (*rsa.PrivateKey, *rsa.PublicKey) {
	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		log.Fatalf("Error generating RSA private key: %v", err)
	}

	return privateKey, &privateKey.PublicKey
}

// encryptRSA 使用 RSA 公钥加密数据
func encryptRSA(pubKey *rsa.PublicKey, data []byte) []byte {
	encryptedData, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, data)
	if err != nil {
		log.Fatalf("Error encrypting data: %v", err)
	}

	return encryptedData
}

// writeEncryptedPassword 将加密后的密码写入文件
func writeEncryptedPassword(filename string, encryptedPassword []byte) error {
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	// 使用 base64 编码后再写入文件
	encodedPassword := base64.StdEncoding.EncodeToString(encryptedPassword)
	_, err = file.WriteString(encodedPassword)
	if err != nil {
		return err
	}

	return nil
}

// writePrivateKey 将私钥写入文件
func writePrivateKey(filename string, privateKey *rsa.PrivateKey) error {
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	privateKeyPEM := &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
	}

	err = pem.Encode(file, privateKeyPEM)
	if err != nil {
		return err
	}

	return nil
}

2、代码主要功能:字符串密码解密、调用api、保留指定数量版本、清理过多镜像

package main

import (
	"encoding/base64"
	"crypto/x509"
	"encoding/pem"
	"encoding/json"
	"fmt"
	"flag"
	"crypto/rsa"
	"io/ioutil"
	"net/http"
	"sort"

	"github.com/fatih/color"
	"gopkg.in/yaml.v2"
)
//设置保留数量
var num int = 2
var help bool
var fileContent = `
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAp40hbR13CSowc0tc/dLPdVSto/uxME2narLRgdfkUEzG6AAh
AxSQXikbjQ0iVEKV48RDQmlKNiYPq++IVHMOcenlj52kpQCxEP6MEtsoAxw3EYxb
TDo9mXKrbqs4jxXHlgSEavp35V1iY0acG191sDFaW0HFeAVw7y21JLaZGxGQ7GmB
1q4NVswpXRo1D4a0L+A0x6YczIRl3QiTXtgIlEKJxybx5n4NswUcWgMMWifmVaYM
HNGam5/5O1q6TdcGCH4jM3I+Sf9Gb9GyCvcVfr4M2WNm+vtX8iWeTyXEM18Dvd6t
0/bdtC/kT32OjTKBJEail368f664/hD+rWBePwIDAQABAoIBAF2yTZQAWzXT0v5W
ka0bu4oIjYXbVW4Gru27C/AJdBL3rG/Gyq83S7hsUozhYGYwSjPx6A72ZFq9lBv5
OorHQLexLf/PD2h6Z4pyXpCY39pFaAI3NEL/er0Z5AZiV6Gkc1VQN2C/qlaoj0Gn
Fds02dJq9vV7ZVtF567JJ7hdKGd61l/0ehgZdwGmJrxZR4JZEdDZuhy01wcNH1En
4Fwv2iIAzil+gt64mrOUB7XP1iqij2REIiedpwU9lnvU1jljBlti+FxdLCia40O9
gOwnVAp1+RS4NG98VH8/maYlHL23W2LySfYjSrHGH4R0NUeqnDQiAlXwvJaF6CI2
R5od16ECgYEA2Xd/4DTez/t9z3wV4pGd0bjOZJ8TSofkmEunPzm8zwCR8CsOs/5n
90iClP2jV2dbizdApdIp9HTDA0LkPlVSK3l0okp/8+wUYOAHpaZ5fKszHqa//zrA
F9y/3dU2EIV0w0O0WgiotjJE2A9K4NNzjtx5K5CjRhPuz9zBvc7AdG8CgYEAxT1q
l2w1U9AwIzIJtXQa/sUHBXIHsTxjPSd9jV38Nydw3baT6z/RHrE1c8S8VJzKpr+f
2wsyIgHA6s/yT2rvPWkB615pnV+UaDcUO8Eltwu0FyIU1/TBMsXUHIr1/UT7oVpE
KolkLgYypqPoCWvDmXVr3VwuSD88khY/yFLTuzECgYB+mNqQozayzS1IhGwZIbxG
QtekLBcG5GVnY2NRo0MPHr3WmrpMfmX3xvZ91copL7pTKS8xWU+jR/XfCCnnP7Q+
Ks6DS+uBDIYwfzr0bB19PgzcYyaSZpk9Sv1HHsACji6THe74GuJcbpc1UHG+wFod
aKwBeIoUWbZEu7bt0FapcQKBgFtH8x6k64EgZkbDj460bxHdKUSx4zF5GwxgTFCy
YFk+HZPePVKwNB2aMXykXexiq77EDD0ZT1hBq4CAQEnU2Wd4Q3UkOoikhVA9vVVy
K/wspGnYUgEnuom+2E1lWjaeSD4CQm3VK9HI+IP6cxZ5EwrgNs05LgotieCp/1a8
TlThAoGBAIzdBPDOf7pmL/Dpb7BVSZQg2o1tyaZhxA701zgSQYTYFijgirhassQB
zZzQF8a75z/64iuuT90y3b3zG4TBSyBb1mGM6PZOHvr0jgBuxSGOAfZH6ZW7VI7G
NZTERW7XVJRRZK+JlPVszXM5FYY2g4pkJMOtMChPu8r/dq8RcAId
-----END RSA PRIVATE KEY-----
`
func init() {
	flag.BoolVar(&help, "help", false, "Show help information")
	flag.BoolVar(&help, "h", false, "Show help information (shorthand)")
}

// Item 结构体用于解析 JSON 中的每个条目
type Item struct {
	ID      string `json:"id"`
	Name    string `json:"name"`
	Version string `json:"version"`
	Assets  []struct {
		BlobCreated string `json:"blobCreated"`
	} `json:"assets"`
}

// NexusConfig 结构体用于解析 Nexus 配置
type NexusConfig struct {
	URL      string `yaml:"url"`
	Username string `yaml:"username"`
	Password string `yaml:"password"`
}

// Config 结构体用于解析整个配置文件
type Config struct {
	Nexus []NexusConfig `yaml:"nexus"`
}

func main() {
	flag.Parse()

	if help {
		showHelp()
		return
	}

	runClean()
	// 读取配置文件
	config := readConfig("config/config.yaml")

	// 解密密码
	for i, nexus := range config.Nexus {
		encryptedPassword, err := readEncryptedPassword(nexus.Password)
		if err != nil {
			fmt.Printf("Error reading encrypted password: %v\n", err)
			return
		}
		privateKey, err := readPrivateKey()
		if err != nil {
			fmt.Printf("Error reading private key: %v\n", err)
			return
		}
		decryptedPassword, err := decryptRSA(privateKey, encryptedPassword)
		if err != nil {
			fmt.Printf("Error decrypting password: %v\n", err)
			return
		}
		config.Nexus[i].Password = string(decryptedPassword)
	}

	// 创建HTTP客户端
	client := &http.Client{}

	// 遍历每个Nexus配置
	for _, nexus := range config.Nexus {
		// 创建GET请求
		req, err := http.NewRequest("GET", nexus.URL, nil)
		if err != nil {
			fmt.Println("Error creating request:", err)
			return
		}

		// 添加Basic Auth头部
		req.SetBasicAuth(nexus.Username, nexus.Password)

		// 发送请求
		resp, err := client.Do(req)
		if err != nil {
			fmt.Println("Error sending request:", err)
			return
		}
		defer resp.Body.Close()

		// 读取响应体
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Println("Error reading response:", err)
			return
		}

		// 如果响应体为空,则跳过解析
		if len(body) == 0 {
			fmt.Println("Response body is empty")
			continue
		}

		// 解析 JSON 数据
		var data struct {
			Items []Item `json:"items"`
		}
		if err := json.Unmarshal(body, &data); err != nil {
			fmt.Println("Error:", err)
			return
		}

		// 创建 map 以按照 name 进行分类
		nameMap := make(map[string][]Item)

		// 将数据按照 name 分类存储在 map 中
		for _, item := range data.Items {
			nameMap[item.Name] = append(nameMap[item.Name], item)
		}

		// 创建 map 以存储要删除的旧 ID
		oldIDs := make(map[string]bool)

		// 遍历分类后的数据,按照 blobCreated 排序,并保留除最新的两个外的其他值
		for name, items := range nameMap {
			// 按照 blobCreated 排序
			sort.Slice(items, func(i, j int) bool {
				return items[i].Assets[0].BlobCreated < items[j].Assets[0].BlobCreated
			})

			// 输出除最新的两个外的其他值,并将旧 ID 存储在 map 中
			if len(items) > num {
				fmt.Printf("Name: %s\n", name)
				for _, item := range items[:len(items)-num] {
					fmt.Printf("Old ID: %s\n", item.ID)
					oldIDs[item.ID] = true
				}
			}
		}

		// 删除旧 ID
		for id := range oldIDs {
			deleteURL := fmt.Sprintf("http://192.168.145.101:8081/service/rest/v1/components/%s", id)
			reqDelete, err := http.NewRequest("DELETE", deleteURL, nil)
			if err != nil {
				fmt.Println("Error creating delete request:", err)
				continue
			}
			reqDelete.SetBasicAuth(nexus.Username, nexus.Password)

			respDelete, err := client.Do(reqDelete)
			if err != nil {
				fmt.Println("Error sending delete request:", err)
				continue
			}
			defer respDelete.Body.Close()

			bodyDelete, err := ioutil.ReadAll(respDelete.Body)
			fmt.Println("-----------:", string(bodyDelete))
			if err != nil {
				fmt.Println("Error reading delete response:", err)
				continue
			}

			if respDelete.StatusCode < 400 {
				color.Green("Deleted ID: %s, Status: Deleted Successfully, Response: %s\n", id, respDelete.Status)
			} else {
				color.Red("Deleted ID: %s, Status: Delete Failed, Response: %s\n", id, respDelete.Status)
			}

		}
	}
}

// readConfig 函数用于从配置文件中读取配置信息
func readConfig(filename string) Config {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Println("Error reading config file:", err)
		return Config{}
	}

	var config Config
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		fmt.Println("Error unmarshalling config data:", err)
		return Config{}
	}

	return config
}


func decryptRSA(privKey *rsa.PrivateKey, data []byte) ([]byte, error) {
	// 使用 RSA 私钥解密数据
	return rsa.DecryptPKCS1v15(nil, privKey, data)
}

func readPrivateKey() (*rsa.PrivateKey, error) {
	// 解码 PEM 格式的 RSA 私钥
	block, _ := pem.Decode([]byte(fileContent))
	if block == nil {
		return nil, fmt.Errorf("failed to parse PEM block containing the key")
	}

	// 解析 RSA 私钥
	key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	return key, nil
}

func readEncryptedPassword(zll string) ([]byte, error) {
	// 解码 Base64 编码的加密密码
	decodedPassword, err := base64.StdEncoding.DecodeString(zll)
	if err != nil {
		return nil, err
	}

	return decodedPassword, nil
}
func showHelp() {
	fmt.Println("Usage: clean [options]")
	fmt.Println("Options:")
	fmt.Println("  -h, --help    Show help information")
	fmt.Println()
	fmt.Println("Nexus Configuration Example:")
	fmt.Println(nexusExample)
}

func runClean() {
	// Your existing code goes here
}

// This is the example Nexus configuration information
var nexusExample = `nexus:
  - url: "http://xxxx:8081/service/rest/v1/components?repository=docker"
    username: "admin"
    password: "encryption password
  - url: "http://xxx:8081/service/rest/v1/components?repository=docker2"
    username: "admin"
    password: "encryption password
`

3、接合nexus的task任务Compact blob store释放磁盘空间

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值