2014-08-05 06:41:30 +00:00
package daemon
import (
"fmt"
"strings"
"github.com/docker/docker/engine"
"github.com/docker/docker/graph"
"github.com/docker/docker/image"
2015-02-21 04:48:23 +00:00
"github.com/docker/docker/pkg/common"
2014-08-05 06:41:30 +00:00
"github.com/docker/docker/pkg/parsers"
)
func ( daemon * Daemon ) ImageDelete ( job * engine . Job ) engine . Status {
if n := len ( job . Args ) ; n != 1 {
return job . Errorf ( "Usage: %s IMAGE" , job . Name )
}
imgs := engine . NewTable ( "" , 0 )
if err := daemon . DeleteImage ( job . Eng , job . Args [ 0 ] , imgs , true , job . GetenvBool ( "force" ) , job . GetenvBool ( "noprune" ) ) ; err != nil {
return job . Error ( err )
}
if len ( imgs . Data ) == 0 {
return job . Errorf ( "Conflict, %s wasn't deleted" , job . Args [ 0 ] )
}
if _ , err := imgs . WriteListTo ( job . Stdout ) ; err != nil {
return job . Error ( err )
}
return engine . StatusOK
}
// FIXME: make this private and use the job instead
func ( daemon * Daemon ) DeleteImage ( eng * engine . Engine , name string , imgs * engine . Table , first , force , noprune bool ) error {
var (
repoName , tag string
tags = [ ] string { }
)
// FIXME: please respect DRY and centralize repo+tag parsing in a single central place! -- shykes
repoName , tag = parsers . ParseRepositoryTag ( name )
if tag == "" {
tag = graph . DEFAULTTAG
}
img , err := daemon . Repositories ( ) . LookupImage ( name )
if err != nil {
if r , _ := daemon . Repositories ( ) . Get ( repoName ) ; r != nil {
return fmt . Errorf ( "No such image: %s:%s" , repoName , tag )
}
return fmt . Errorf ( "No such image: %s" , name )
}
if strings . Contains ( img . ID , name ) {
repoName = ""
tag = ""
}
byParents , err := daemon . Graph ( ) . ByParent ( )
if err != nil {
return err
}
2014-09-10 00:32:14 +00:00
repos := daemon . Repositories ( ) . ByID ( ) [ img . ID ]
2014-08-05 06:41:30 +00:00
//If delete by id, see if the id belong only to one repository
if repoName == "" {
2014-09-10 00:32:14 +00:00
for _ , repoAndTag := range repos {
2014-08-05 06:41:30 +00:00
parsedRepo , parsedTag := parsers . ParseRepositoryTag ( repoAndTag )
if repoName == "" || repoName == parsedRepo {
repoName = parsedRepo
if parsedTag != "" {
tags = append ( tags , parsedTag )
}
2015-02-26 04:01:35 +00:00
} else if repoName != parsedRepo && ! force && first {
2014-08-05 06:41:30 +00:00
// the id belongs to multiple repos, like base:latest and user:test,
// in that case return conflict
return fmt . Errorf ( "Conflict, cannot delete image %s because it is tagged in multiple repositories, use -f to force" , name )
}
}
} else {
tags = append ( tags , tag )
}
if ! first && len ( tags ) > 0 {
return nil
}
2014-09-10 00:32:14 +00:00
if len ( repos ) <= 1 {
if err := daemon . canDeleteImage ( img . ID , force ) ; err != nil {
return err
}
}
// Untag the current image
2014-08-05 06:41:30 +00:00
for _ , tag := range tags {
2014-09-10 00:32:14 +00:00
tagDeleted , err := daemon . Repositories ( ) . Delete ( repoName , tag )
2014-08-05 06:41:30 +00:00
if err != nil {
return err
}
if tagDeleted {
out := & engine . Env { }
out . Set ( "Untagged" , repoName + ":" + tag )
imgs . Add ( out )
2014-08-06 09:28:30 +00:00
eng . Job ( "log" , "untag" , img . ID , "" ) . Run ( )
2014-08-05 06:41:30 +00:00
}
}
tags = daemon . Repositories ( ) . ByID ( ) [ img . ID ]
if ( len ( tags ) <= 1 && repoName == "" ) || len ( tags ) == 0 {
if len ( byParents [ img . ID ] ) == 0 {
if err := daemon . Repositories ( ) . DeleteAll ( img . ID ) ; err != nil {
return err
}
if err := daemon . Graph ( ) . Delete ( img . ID ) ; err != nil {
return err
}
out := & engine . Env { }
2014-12-11 12:56:21 +00:00
out . SetJson ( "Deleted" , img . ID )
2014-08-05 06:41:30 +00:00
imgs . Add ( out )
2014-08-06 09:28:30 +00:00
eng . Job ( "log" , "delete" , img . ID , "" ) . Run ( )
2014-08-05 06:41:30 +00:00
if img . Parent != "" && ! noprune {
err := daemon . DeleteImage ( eng , img . Parent , imgs , false , force , noprune )
if first {
return err
}
}
}
}
return nil
}
2014-09-10 00:32:14 +00:00
func ( daemon * Daemon ) canDeleteImage ( imgID string , force bool ) error {
2014-08-05 06:41:30 +00:00
for _ , container := range daemon . List ( ) {
2014-10-28 21:06:23 +00:00
parent , err := daemon . Repositories ( ) . LookupImage ( container . ImageID )
2014-08-05 06:41:30 +00:00
if err != nil {
2014-11-17 21:16:33 +00:00
if daemon . Graph ( ) . IsNotExist ( err ) {
return nil
}
2014-08-05 06:41:30 +00:00
return err
}
if err := parent . WalkHistory ( func ( p * image . Image ) error {
if imgID == p . ID {
2014-08-31 15:20:35 +00:00
if container . IsRunning ( ) {
2014-08-05 06:41:30 +00:00
if force {
2015-02-21 04:48:23 +00:00
return fmt . Errorf ( "Conflict, cannot force delete %s because the running container %s is using it, stop it and retry" , common . TruncateID ( imgID ) , common . TruncateID ( container . ID ) )
2014-08-05 06:41:30 +00:00
}
2015-02-21 04:48:23 +00:00
return fmt . Errorf ( "Conflict, cannot delete %s because the running container %s is using it, stop it and use -f to force" , common . TruncateID ( imgID ) , common . TruncateID ( container . ID ) )
2014-08-05 06:41:30 +00:00
} else if ! force {
2015-02-21 04:48:23 +00:00
return fmt . Errorf ( "Conflict, cannot delete %s because the container %s is using it, use -f to force" , common . TruncateID ( imgID ) , common . TruncateID ( container . ID ) )
2014-08-05 06:41:30 +00:00
}
}
return nil
} ) ; err != nil {
return err
}
}
return nil
}