mirror of https://github.com/docker/compose.git
				
				
				
			Display summary of reclaimed ACI resources (CPU/mem) in `docker prune`
Signed-off-by: Guillaume Tardif <guillaume.tardif@docker.com>
This commit is contained in:
		
							parent
							
								
									7f4bfd16dc
								
							
						
					
					
						commit
						50a2ae1100
					
				| 
						 | 
				
			
			@ -380,7 +380,7 @@ func (s serviceConfigAciHelper) getResourceRequestsLimits() (*containerinstance.
 | 
			
		|||
		return s.Deploy != nil && s.Deploy.Resources.Reservations != nil && s.Deploy.Resources.Reservations.NanoCPUs != ""
 | 
			
		||||
	}
 | 
			
		||||
	if hasMemoryRequest() {
 | 
			
		||||
		memRequest = bytesToGb(s.Deploy.Resources.Reservations.MemoryBytes)
 | 
			
		||||
		memRequest = BytesToGB(float64(s.Deploy.Resources.Reservations.MemoryBytes))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hasCPURequest() {
 | 
			
		||||
| 
						 | 
				
			
			@ -393,7 +393,7 @@ func (s serviceConfigAciHelper) getResourceRequestsLimits() (*containerinstance.
 | 
			
		|||
	cpuLimit := cpuRequest
 | 
			
		||||
	if s.Deploy != nil && s.Deploy.Resources.Limits != nil {
 | 
			
		||||
		if s.Deploy.Resources.Limits.MemoryBytes != 0 {
 | 
			
		||||
			memLimit = bytesToGb(s.Deploy.Resources.Limits.MemoryBytes)
 | 
			
		||||
			memLimit = BytesToGB(float64(s.Deploy.Resources.Limits.MemoryBytes))
 | 
			
		||||
			if !hasMemoryRequest() {
 | 
			
		||||
				memRequest = memLimit
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -438,8 +438,9 @@ func getEnvVariables(composeEnv types.MappingWithEquals) *[]containerinstance.En
 | 
			
		|||
	return &result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func bytesToGb(b types.UnitBytes) float64 {
 | 
			
		||||
	f := float64(b) / 1024 / 1024 / 1024 // from bytes to gigabytes
 | 
			
		||||
// BytesToGB convert bytes To GB
 | 
			
		||||
func BytesToGB(b float64) float64 {
 | 
			
		||||
	f := b / 1024 / 1024 / 1024 // from bytes to gigabytes
 | 
			
		||||
	return math.Round(f*100) / 100
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -472,6 +473,47 @@ func fqdn(group containerinstance.ContainerGroup, region string) string {
 | 
			
		|||
 | 
			
		||||
// ContainerGroupToContainer composes a Container from an ACI container definition
 | 
			
		||||
func ContainerGroupToContainer(containerID string, cg containerinstance.ContainerGroup, cc containerinstance.Container, region string) containers.Container {
 | 
			
		||||
	command := ""
 | 
			
		||||
	if cc.Command != nil {
 | 
			
		||||
		command = strings.Join(*cc.Command, " ")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	status := GetStatus(cc, cg)
 | 
			
		||||
	platform := string(cg.OsType)
 | 
			
		||||
 | 
			
		||||
	var envVars map[string]string = nil
 | 
			
		||||
	if cc.EnvironmentVariables != nil && len(*cc.EnvironmentVariables) != 0 {
 | 
			
		||||
		envVars = map[string]string{}
 | 
			
		||||
		for _, envVar := range *cc.EnvironmentVariables {
 | 
			
		||||
			envVars[*envVar.Name] = *envVar.Value
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hostConfig := ToHostConfig(cc, cg)
 | 
			
		||||
	config := &containers.RuntimeConfig{
 | 
			
		||||
		FQDN: fqdn(cg, region),
 | 
			
		||||
		Env:  envVars,
 | 
			
		||||
	}
 | 
			
		||||
	c := containers.Container{
 | 
			
		||||
		ID:          containerID,
 | 
			
		||||
		Status:      status,
 | 
			
		||||
		Image:       to.String(cc.Image),
 | 
			
		||||
		Command:     command,
 | 
			
		||||
		CPUTime:     0,
 | 
			
		||||
		MemoryUsage: 0,
 | 
			
		||||
		PidsCurrent: 0,
 | 
			
		||||
		PidsLimit:   0,
 | 
			
		||||
		Ports:       ToPorts(cg.IPAddress, *cc.Ports),
 | 
			
		||||
		Platform:    platform,
 | 
			
		||||
		Config:      config,
 | 
			
		||||
		HostConfig:  hostConfig,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToHostConfig convert an ACI container to host config value
 | 
			
		||||
func ToHostConfig(cc containerinstance.Container, cg containerinstance.ContainerGroup) *containers.HostConfig {
 | 
			
		||||
	memLimits := uint64(0)
 | 
			
		||||
	memRequest := uint64(0)
 | 
			
		||||
	cpuLimit := 0.
 | 
			
		||||
| 
						 | 
				
			
			@ -494,27 +536,6 @@ func ContainerGroupToContainer(containerID string, cg containerinstance.Containe
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	command := ""
 | 
			
		||||
	if cc.Command != nil {
 | 
			
		||||
		command = strings.Join(*cc.Command, " ")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	status := GetStatus(cc, cg)
 | 
			
		||||
	platform := string(cg.OsType)
 | 
			
		||||
 | 
			
		||||
	var envVars map[string]string = nil
 | 
			
		||||
	if cc.EnvironmentVariables != nil && len(*cc.EnvironmentVariables) != 0 {
 | 
			
		||||
		envVars = map[string]string{}
 | 
			
		||||
		for _, envVar := range *cc.EnvironmentVariables {
 | 
			
		||||
			envVars[*envVar.Name] = *envVar.Value
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	config := &containers.RuntimeConfig{
 | 
			
		||||
		FQDN: fqdn(cg, region),
 | 
			
		||||
		Env:  envVars,
 | 
			
		||||
	}
 | 
			
		||||
	hostConfig := &containers.HostConfig{
 | 
			
		||||
		CPULimit:          cpuLimit,
 | 
			
		||||
		CPUReservation:    cpuReservation,
 | 
			
		||||
| 
						 | 
				
			
			@ -522,22 +543,7 @@ func ContainerGroupToContainer(containerID string, cg containerinstance.Containe
 | 
			
		|||
		MemoryReservation: memRequest,
 | 
			
		||||
		RestartPolicy:     toContainerRestartPolicy(cg.RestartPolicy),
 | 
			
		||||
	}
 | 
			
		||||
	c := containers.Container{
 | 
			
		||||
		ID:          containerID,
 | 
			
		||||
		Status:      status,
 | 
			
		||||
		Image:       to.String(cc.Image),
 | 
			
		||||
		Command:     command,
 | 
			
		||||
		CPUTime:     0,
 | 
			
		||||
		MemoryUsage: 0,
 | 
			
		||||
		PidsCurrent: 0,
 | 
			
		||||
		PidsLimit:   0,
 | 
			
		||||
		Ports:       ToPorts(cg.IPAddress, *cc.Ports),
 | 
			
		||||
		Platform:    platform,
 | 
			
		||||
		Config:      config,
 | 
			
		||||
		HostConfig:  hostConfig,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c
 | 
			
		||||
	return hostConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetStatus returns status for the specified container
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,7 @@ package aci
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/go-multierror"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -30,18 +31,28 @@ type aciResourceService struct {
 | 
			
		|||
	aciContext store.AciContext
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cs *aciResourceService) Prune(ctx context.Context, request resources.PruneRequest) ([]string, error) {
 | 
			
		||||
func (cs *aciResourceService) Prune(ctx context.Context, request resources.PruneRequest) (resources.PruneResult, error) {
 | 
			
		||||
	res, err := getACIContainerGroups(ctx, cs.aciContext.SubscriptionID, cs.aciContext.ResourceGroup)
 | 
			
		||||
	result := resources.PruneResult{}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
		return result, err
 | 
			
		||||
	}
 | 
			
		||||
	multierr := &multierror.Error{}
 | 
			
		||||
	deleted := []string{}
 | 
			
		||||
	cpus := 0.
 | 
			
		||||
	mem := 0.
 | 
			
		||||
 | 
			
		||||
	for _, containerGroup := range res {
 | 
			
		||||
		if !request.Force && convert.GetGroupStatus(containerGroup) == "Node "+convert.StatusRunning {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, container := range *containerGroup.Containers {
 | 
			
		||||
			hostConfig := convert.ToHostConfig(container, containerGroup)
 | 
			
		||||
			cpus += hostConfig.CPUReservation
 | 
			
		||||
			mem += convert.BytesToGB(float64(hostConfig.MemoryReservation))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !request.DryRun {
 | 
			
		||||
			_, err := deleteACIContainerGroup(ctx, cs.aciContext, *containerGroup.Name)
 | 
			
		||||
			multierr = multierror.Append(multierr, err)
 | 
			
		||||
| 
						 | 
				
			
			@ -50,5 +61,7 @@ func (cs *aciResourceService) Prune(ctx context.Context, request resources.Prune
 | 
			
		|||
			deleted = append(deleted, *containerGroup.Name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return deleted, multierr.ErrorOrNil()
 | 
			
		||||
	result.DeletedIDs = deleted
 | 
			
		||||
	result.Summary = fmt.Sprintf("Total CPUs reclaimed: %.2f, total memory reclaimed: %.2f GB", cpus, mem)
 | 
			
		||||
	return result, multierr.ErrorOrNil()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,6 @@ type resourceService struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Prune prune resources
 | 
			
		||||
func (c *resourceService) Prune(ctx context.Context, request resources.PruneRequest) ([]string, error) {
 | 
			
		||||
	return nil, errdefs.ErrNotImplemented
 | 
			
		||||
func (c *resourceService) Prune(ctx context.Context, request resources.PruneRequest) (resources.PruneResult, error) {
 | 
			
		||||
	return resources.PruneResult{}, errdefs.ErrNotImplemented
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,8 +26,14 @@ type PruneRequest struct {
 | 
			
		|||
	DryRun bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PruneResult info on what has been pruned
 | 
			
		||||
type PruneResult struct {
 | 
			
		||||
	DeletedIDs []string
 | 
			
		||||
	Summary    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Service interacts with the underlying container backend
 | 
			
		||||
type Service interface {
 | 
			
		||||
	// Prune prune resources
 | 
			
		||||
	Prune(ctx context.Context, request PruneRequest) ([]string, error)
 | 
			
		||||
	Prune(ctx context.Context, request PruneRequest) (PruneResult, error)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,14 +56,17 @@ func runPrune(ctx context.Context, opts pruneOpts) error {
 | 
			
		|||
		return errors.Wrap(err, "cannot connect to backend")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ids, err := c.ResourceService().Prune(ctx, resources.PruneRequest{Force: opts.force, DryRun: opts.dryRun})
 | 
			
		||||
	result, err := c.ResourceService().Prune(ctx, resources.PruneRequest{Force: opts.force, DryRun: opts.dryRun})
 | 
			
		||||
	if opts.dryRun {
 | 
			
		||||
		fmt.Println("resources that would be deleted:")
 | 
			
		||||
		fmt.Println("Resources that would be deleted:")
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Println("deleted resources:")
 | 
			
		||||
		fmt.Println("Deleted resources:")
 | 
			
		||||
	}
 | 
			
		||||
	for _, id := range ids {
 | 
			
		||||
	for _, id := range result.DeletedIDs {
 | 
			
		||||
		fmt.Println(id)
 | 
			
		||||
	}
 | 
			
		||||
	if result.Summary != "" {
 | 
			
		||||
		fmt.Println(result.Summary)
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -490,21 +490,20 @@ func TestContainerRunAttached(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	t.Run("prune dry run", func(t *testing.T) {
 | 
			
		||||
		res := c.RunDockerCmd("prune", "--dry-run")
 | 
			
		||||
		fmt.Println("prune output:")
 | 
			
		||||
		assert.Equal(t, "resources that would be deleted:\n", res.Stdout())
 | 
			
		||||
		assert.Equal(t, "Resources that would be deleted:\nTotal CPUs reclaimed: 0.00, total memory reclaimed: 0.00 GB\n", res.Stdout())
 | 
			
		||||
		res = c.RunDockerCmd("prune", "--dry-run", "--force")
 | 
			
		||||
		assert.Equal(t, "resources that would be deleted:\n"+container+"\n", res.Stdout())
 | 
			
		||||
		assert.Equal(t, "Resources that would be deleted:\n"+container+"\nTotal CPUs reclaimed: 0.10, total memory reclaimed: 0.10 GB\n", res.Stdout())
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("prune", func(t *testing.T) {
 | 
			
		||||
		res := c.RunDockerCmd("prune")
 | 
			
		||||
		assert.Equal(t, "deleted resources:\n", res.Stdout())
 | 
			
		||||
		assert.Equal(t, "Deleted resources:\nTotal CPUs reclaimed: 0.00, total memory reclaimed: 0.00 GB\n", res.Stdout())
 | 
			
		||||
		res = c.RunDockerCmd("ps")
 | 
			
		||||
		l := lines(res.Stdout())
 | 
			
		||||
		assert.Equal(t, 2, len(l))
 | 
			
		||||
 | 
			
		||||
		res = c.RunDockerCmd("prune", "--force")
 | 
			
		||||
		assert.Equal(t, "deleted resources:\n"+container+"\n", res.Stdout())
 | 
			
		||||
		assert.Equal(t, "Deleted resources:\n"+container+"\nTotal CPUs reclaimed: 0.10, total memory reclaimed: 0.10 GB\n", res.Stdout())
 | 
			
		||||
 | 
			
		||||
		res = c.RunDockerCmd("ps", "--all")
 | 
			
		||||
		l = lines(res.Stdout())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue