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