feat: implement image cleanup
This commit is contained in:
@@ -210,7 +210,7 @@ func DockerCleanup(appName string, forceCleanup bool) error {
|
||||
}
|
||||
|
||||
// delete dangling images
|
||||
imageIDs, _ := listDanglingImages(appName)
|
||||
imageIDs, _ := ListDanglingImages(appName)
|
||||
if len(imageIDs) > 0 {
|
||||
RemoveImages(imageIDs)
|
||||
}
|
||||
@@ -301,6 +301,34 @@ func IsImageHerokuishBased(image string, appName string) bool {
|
||||
return output != ""
|
||||
}
|
||||
|
||||
func ListDanglingImages(appName string) ([]string, error) {
|
||||
command := []string{
|
||||
DockerBin(),
|
||||
"image",
|
||||
"list",
|
||||
"--quiet",
|
||||
"--filter",
|
||||
"dangling=true",
|
||||
}
|
||||
|
||||
if appName != "" {
|
||||
command = append(command, []string{"--filter", fmt.Sprintf("label=com.dokku.app-name=%v", appName)}...)
|
||||
}
|
||||
|
||||
var stderr bytes.Buffer
|
||||
listCmd := NewShellCmd(strings.Join(command, " "))
|
||||
listCmd.ShowOutput = false
|
||||
listCmd.Command.Stderr = &stderr
|
||||
b, err := listCmd.Output()
|
||||
|
||||
if err != nil {
|
||||
return []string{}, errors.New(strings.TrimSpace(stderr.String()))
|
||||
}
|
||||
|
||||
output := strings.Split(strings.TrimSpace(string(b[:])), "\n")
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// RemoveImages removes images by ID
|
||||
func RemoveImages(imageIDs []string) {
|
||||
command := []string{
|
||||
@@ -356,34 +384,6 @@ func listContainers(status string, appName string) ([]string, error) {
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func listDanglingImages(appName string) ([]string, error) {
|
||||
command := []string{
|
||||
DockerBin(),
|
||||
"image",
|
||||
"list",
|
||||
"--quiet",
|
||||
"--filter",
|
||||
"dangling=true",
|
||||
}
|
||||
|
||||
if appName != "" {
|
||||
command = append(command, []string{"--filter", fmt.Sprintf("label=com.dokku.app-name=%v", appName)}...)
|
||||
}
|
||||
|
||||
var stderr bytes.Buffer
|
||||
listCmd := NewShellCmd(strings.Join(command, " "))
|
||||
listCmd.ShowOutput = false
|
||||
listCmd.Command.Stderr = &stderr
|
||||
b, err := listCmd.Output()
|
||||
|
||||
if err != nil {
|
||||
return []string{}, errors.New(strings.TrimSpace(stderr.String()))
|
||||
}
|
||||
|
||||
output := strings.Split(strings.TrimSpace(string(b[:])), "\n")
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func pruneUnusedImages(appName string) {
|
||||
command := []string{
|
||||
DockerBin(),
|
||||
|
||||
@@ -29,7 +29,7 @@ func isPushEnabled(appName string) bool {
|
||||
return reportComputedPushOnRelease(appName) == "true"
|
||||
}
|
||||
|
||||
func incrementTagVersion(appName string) (string, error) {
|
||||
func incrementTagVersion(appName string) (int, error) {
|
||||
tag := common.PropertyGet("registry", appName, "tag-version")
|
||||
if tag == "" {
|
||||
tag = "0"
|
||||
@@ -37,19 +37,19 @@ func incrementTagVersion(appName string) (string, error) {
|
||||
|
||||
version, err := strconv.Atoi(tag)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Unable to convert existing tag version (%s) to integer: %v", tag, err)
|
||||
return 0, fmt.Errorf("Unable to convert existing tag version (%s) to integer: %v", tag, err)
|
||||
}
|
||||
|
||||
version++
|
||||
common.LogVerboseQuiet(fmt.Sprintf("Bumping tag to %d", version))
|
||||
if err = common.PropertyWrite("registry", appName, "tag-version", strconv.Itoa(version)); err != nil {
|
||||
return "", err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return strconv.Itoa(version), nil
|
||||
return version, nil
|
||||
}
|
||||
|
||||
func pushToRegistry(appName string, tag string) error {
|
||||
func pushToRegistry(appName string, tag int) error {
|
||||
common.LogVerboseQuiet("Retrieving image info for app")
|
||||
|
||||
registryServer := getRegistryServerForApp(appName)
|
||||
@@ -58,27 +58,29 @@ func pushToRegistry(appName string, tag string) error {
|
||||
image := common.GetAppImageName(appName, imageTag, "")
|
||||
imageID, _ := common.DockerInspect(image, "{{ .Id }}")
|
||||
|
||||
common.LogVerboseQuiet(fmt.Sprintf("Tagging $IMAGE_REPO:%s in registry format", tag))
|
||||
if !dockerTag(imageID, fmt.Sprintf("%s%s:%s", registryServer, imageRepo, tag)) {
|
||||
common.LogVerboseQuiet(fmt.Sprintf("Tagging $IMAGE_REPO:%d in registry format", tag))
|
||||
if !dockerTag(imageID, fmt.Sprintf("%s%s:%d", registryServer, imageRepo, tag)) {
|
||||
// TODO: better error
|
||||
return errors.New("Unable to tag image")
|
||||
}
|
||||
|
||||
if !dockerTag(imageID, fmt.Sprintf("%s:%s", imageRepo, tag)) {
|
||||
if !dockerTag(imageID, fmt.Sprintf("%s:%d", imageRepo, tag)) {
|
||||
// TODO: better error
|
||||
return errors.New("Unable to tag image")
|
||||
}
|
||||
|
||||
// fn-registry-create-repository "$APP" "$DOKKU_REGISTRY_SERVER" "$IMAGE_REPO"
|
||||
// For the future, we should also add the ability to create the remote repository
|
||||
// This is only really important for registries that do not support creation on push
|
||||
// Examples include AWS and Quay.io
|
||||
|
||||
common.LogVerboseQuiet("Pushing $IMAGE_REPO:$TAG")
|
||||
if !dockerPush(fmt.Sprintf("%s%s:%s", registryServer, imageRepo, tag)) {
|
||||
if !dockerPush(fmt.Sprintf("%s%s:%d", registryServer, imageRepo, tag)) {
|
||||
// TODO: better error
|
||||
return errors.New("Unable to push image")
|
||||
}
|
||||
|
||||
common.LogVerboseQuiet("Cleaning up")
|
||||
// fn-registry-image-cleanup "$APP" "$DOKKU_REGISTRY_SERVER" "$IMAGE_REPO" "$IMAGE_TAG" "$TAG"
|
||||
imageCleanup(appName, registryServer, imageRepo, imageTag, tag)
|
||||
|
||||
common.LogVerboseQuiet("Image $IMAGE_REPO:$TAG pushed")
|
||||
return nil
|
||||
@@ -105,3 +107,23 @@ func dockerPush(imageTag string) bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func imageCleanup(appName string, registryServer string, imageRepo string, imageTag string, tag int) {
|
||||
// # keep last two images in place
|
||||
oldTag := tag - 1
|
||||
tenImagesAgoTag := tag - 12
|
||||
|
||||
imagesToRemove := []string{}
|
||||
for oldTag > 0 {
|
||||
imagesToRemove = append(imagesToRemove, fmt.Sprintf("%s%s:%d", registryServer, imageRepo, oldTag))
|
||||
imagesToRemove = append(imagesToRemove, fmt.Sprintf("%s:%d", imageRepo, oldTag))
|
||||
oldTag = oldTag - 1
|
||||
if tenImagesAgoTag == oldTag {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
imageIDs, _ := common.ListDanglingImages(appName)
|
||||
imagesToRemove = append(imagesToRemove, imageIDs...)
|
||||
common.RemoveImages(imagesToRemove)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user