Initial commit
This commit is contained in:
parent
7f5d2a1c90
commit
0123ffc572
@ -1,3 +1,3 @@
|
||||
REGISTRY_NAME=registry.docker.stuzer.link
|
||||
REGISTRY_USERNAME=stuzer05
|
||||
REGISTRY_PASSWORD=rnxt0FZxqXiXsExNTkMd
|
||||
REGISTRY_NAME=registry.example.com
|
||||
REGISTRY_USERNAME=user
|
||||
REGISTRY_PASSWORD=password
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -0,0 +1,3 @@
|
||||
/.idea
|
||||
/.env
|
||||
/docker-registry-manager
|
12
Makefile
Normal file
12
Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
.PHONY: build fmt lint
|
||||
|
||||
build: fmt lint
|
||||
go build .
|
||||
|
||||
fmt:
|
||||
gofmt -w -r "interface{} -> any" .
|
||||
go fmt ./...
|
||||
|
||||
lint:
|
||||
go vet ./...
|
||||
staticcheck ./...
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module gitea.stuzer.link/stuzer05/docker-registry-manager
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require github.com/joho/godotenv v1.5.1
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
260
main.go
Normal file
260
main.go
Normal file
@ -0,0 +1,260 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/joho/godotenv"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
registryName string
|
||||
registryURL string
|
||||
registryUsername string
|
||||
registryPassword string
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Load .env file
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
fmt.Println("Error loading .env file:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Read values from environment
|
||||
registryName = os.Getenv("REGISTRY_NAME")
|
||||
if registryName == "" {
|
||||
fmt.Println("REGISTRY_NAME not found in .env")
|
||||
return
|
||||
}
|
||||
|
||||
registryURL = "https://" + registryName
|
||||
registryUsername = os.Getenv("REGISTRY_USERNAME")
|
||||
registryPassword = os.Getenv("REGISTRY_PASSWORD")
|
||||
if registryUsername == "" || registryPassword == "" {
|
||||
fmt.Println("REGISTRY_USERNAME or REGISTRY_PASSWORD not found in .env")
|
||||
return
|
||||
}
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Println("Invalid command. Usage: registry [images|tags|rm|push|tag] <args>")
|
||||
return
|
||||
}
|
||||
|
||||
switch os.Args[1] {
|
||||
case "images":
|
||||
registryImages()
|
||||
case "tags":
|
||||
if len(os.Args) < 3 {
|
||||
fmt.Println("Error: Please provide an image name.")
|
||||
return
|
||||
}
|
||||
registryTags(os.Args[2])
|
||||
case "rm":
|
||||
if len(os.Args) < 3 {
|
||||
fmt.Println("Error: Please provide an image name and optionally a tag (e.g., my-image or my-image:latest).")
|
||||
return
|
||||
}
|
||||
registryRm(os.Args[2])
|
||||
case "push":
|
||||
if len(os.Args) < 3 {
|
||||
fmt.Println("Error: Please provide an image name to push.")
|
||||
return
|
||||
}
|
||||
registryPush(os.Args[2])
|
||||
case "tag":
|
||||
if len(os.Args) < 4 {
|
||||
fmt.Println("Error: Please provide both source and target image names.")
|
||||
return
|
||||
}
|
||||
registryTag(os.Args[2], os.Args[3])
|
||||
default:
|
||||
fmt.Println("Invalid command. Usage: registry [images|tags|rm|push|tag] <args>")
|
||||
}
|
||||
}
|
||||
|
||||
func registryImages() {
|
||||
type catalog struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
|
||||
resp, err := http.Get(registryURL + "/v2/_catalog")
|
||||
if err != nil {
|
||||
fmt.Println("Error fetching images:", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
fmt.Println("No images found in the registry.")
|
||||
return
|
||||
}
|
||||
|
||||
var cat catalog
|
||||
if err := json.NewDecoder(resp.Body).Decode(&cat); err != nil {
|
||||
fmt.Println("Error decoding response:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(cat.Repositories) == 0 {
|
||||
fmt.Println("No images found in the registry.")
|
||||
} else {
|
||||
for _, img := range cat.Repositories {
|
||||
fmt.Println(img)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func registryTags(imageName string) {
|
||||
type tagList struct {
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
resp, err := http.Get(fmt.Sprintf("%s/v2/%s/tags/list", registryURL, imageName))
|
||||
if err != nil {
|
||||
fmt.Println("Error fetching tags:", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
fmt.Printf("Error: Image '%s' not found in the registry.\n", imageName)
|
||||
return
|
||||
}
|
||||
|
||||
var tl tagList
|
||||
if err := json.NewDecoder(resp.Body).Decode(&tl); err != nil {
|
||||
fmt.Println("Error decoding response:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(tl.Tags) == 0 {
|
||||
fmt.Printf("No tags found for image: %s\n", imageName)
|
||||
} else {
|
||||
for _, tag := range tl.Tags {
|
||||
fmt.Println(tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func registryRm(imageArg string) {
|
||||
parts := strings.SplitN(imageArg, ":", 2)
|
||||
imageName := parts[0]
|
||||
tag := ""
|
||||
if len(parts) > 1 {
|
||||
tag = parts[1]
|
||||
}
|
||||
|
||||
if tag == "" {
|
||||
deleteImageByName(imageName)
|
||||
} else {
|
||||
deleteImageByTag(imageName, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteImageByName(imageName string) {
|
||||
type tagList struct {
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
resp, err := http.Get(fmt.Sprintf("%s/v2/%s/tags/list", registryURL, imageName))
|
||||
if err != nil {
|
||||
fmt.Println("Error fetching tags:", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
fmt.Printf("Error: Image '%s' not found in the registry.\n", imageName)
|
||||
return
|
||||
}
|
||||
|
||||
var tl tagList
|
||||
if err := json.NewDecoder(resp.Body).Decode(&tl); err != nil {
|
||||
fmt.Println("Error decoding response:", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, tag := range tl.Tags {
|
||||
deleteImageByTag(imageName, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteImageByTag(imageName, tag string) {
|
||||
client := &http.Client{}
|
||||
|
||||
req, err := http.NewRequest("HEAD", fmt.Sprintf("%s/v2/%s/manifests/%s", registryURL, imageName, tag), nil)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating request:", err)
|
||||
return
|
||||
}
|
||||
req.Header.Set("Accept", "application/vnd.docker.distribution.manifest.v2+json")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
fmt.Println("Error fetching manifest:", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
fmt.Printf("Error: Image '%s:%s' not found in the registry.\n", imageName, tag)
|
||||
return
|
||||
}
|
||||
|
||||
digest := resp.Header.Get("Docker-Content-Digest")
|
||||
|
||||
req, err = http.NewRequest("DELETE", fmt.Sprintf("%s/v2/%s/manifests/%s", registryURL, imageName, digest), nil)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating delete request:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Join username and password for Basic Auth
|
||||
registryCredentials := registryUsername + ":" + registryPassword
|
||||
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(registryCredentials)))
|
||||
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
fmt.Println("Error deleting image:", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusAccepted {
|
||||
fmt.Printf("Deleted image: %s:%s\n", imageName, tag)
|
||||
} else {
|
||||
fmt.Printf("Failed to delete image: %s:%s (status code: %d)\n", imageName, tag, resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func registryPush(imageName string) {
|
||||
if !strings.HasPrefix(imageName, registryName) {
|
||||
imageName = registryName + "/" + imageName
|
||||
}
|
||||
|
||||
cmd := exec.Command("docker", "push", imageName)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Println("Error pushing image:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func registryTag(sourceImage, targetImage string) {
|
||||
if !strings.HasPrefix(targetImage, registryName) {
|
||||
targetImage = registryName + "/" + targetImage
|
||||
}
|
||||
|
||||
cmd := exec.Command("docker", "tag", sourceImage, targetImage)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Println("Error tagging image:", err)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user