Ignore missing containers when compose down -p

Signed-off-by: koooge <koooooge@gmail.com>
This commit is contained in:
koooge 2024-04-06 01:28:30 +02:00 committed by Nicolas De loof
parent bfee07e1eb
commit 8ceaf49296
4 changed files with 59 additions and 9 deletions

View File

@ -77,7 +77,7 @@ func downDirectionTraversal(visitorFn func(context.Context, string) error) *grap
// InDependencyOrder applies the function to the services of the project taking in account the dependency order // InDependencyOrder applies the function to the services of the project taking in account the dependency order
func InDependencyOrder(ctx context.Context, project *types.Project, fn func(context.Context, string) error, options ...func(*graphTraversal)) error { func InDependencyOrder(ctx context.Context, project *types.Project, fn func(context.Context, string) error, options ...func(*graphTraversal)) error {
graph, err := NewGraph(project, ServiceStopped) graph, err := NewGraph(project, ServiceStopped, false)
if err != nil { if err != nil {
return err return err
} }
@ -89,8 +89,8 @@ func InDependencyOrder(ctx context.Context, project *types.Project, fn func(cont
} }
// InReverseDependencyOrder applies the function to the services of the project in reverse order of dependencies // InReverseDependencyOrder applies the function to the services of the project in reverse order of dependencies
func InReverseDependencyOrder(ctx context.Context, project *types.Project, fn func(context.Context, string) error, options ...func(*graphTraversal)) error { func InReverseDependencyOrder(ctx context.Context, project *types.Project, ignoreMissing bool, fn func(context.Context, string) error, options ...func(*graphTraversal)) error {
graph, err := NewGraph(project, ServiceStarted) graph, err := NewGraph(project, ServiceStarted, ignoreMissing)
if err != nil { if err != nil {
return err return err
} }
@ -257,7 +257,7 @@ func (v *Vertex) GetChildren() []*Vertex {
} }
// NewGraph returns the dependency graph of the services // NewGraph returns the dependency graph of the services
func NewGraph(project *types.Project, initialStatus ServiceStatus) (*Graph, error) { func NewGraph(project *types.Project, initialStatus ServiceStatus, ignoreMissing bool) (*Graph, error) {
graph := &Graph{ graph := &Graph{
lock: sync.RWMutex{}, lock: sync.RWMutex{},
Vertices: map[string]*Vertex{}, Vertices: map[string]*Vertex{},
@ -271,7 +271,7 @@ func NewGraph(project *types.Project, initialStatus ServiceStatus) (*Graph, erro
for _, name := range s.GetDependencies() { for _, name := range s.GetDependencies() {
err := graph.AddEdge(s.Name, name) err := graph.AddEdge(s.Name, name)
if err != nil { if err != nil {
if !s.DependsOn[name].Required { if ignoreMissing || !s.DependsOn[name].Required {
delete(s.DependsOn, name) delete(s.DependsOn, name)
project.Services[index] = s project.Services[index] = s
continue continue

View File

@ -115,7 +115,7 @@ func TestInDependencyReverseDownCommandOrder(t *testing.T) {
t.Cleanup(cancel) t.Cleanup(cancel)
var order []string var order []string
err := InReverseDependencyOrder(ctx, createTestProject(), func(ctx context.Context, service string) error { err := InReverseDependencyOrder(ctx, createTestProject(), false, func(ctx context.Context, service string) error {
order = append(order, service) order = append(order, service)
return nil return nil
}) })
@ -270,7 +270,57 @@ func TestBuildGraph(t *testing.T) {
Services: tC.services, Services: tC.services,
} }
graph, err := NewGraph(&project, ServiceStopped) graph, err := NewGraph(&project, ServiceStopped, false)
assert.NilError(t, err, fmt.Sprintf("failed to build graph for: %s", tC.desc))
for k, vertex := range graph.Vertices {
expected, ok := tC.expectedVertices[k]
assert.Equal(t, true, ok)
assert.Equal(t, true, isVertexEqual(*expected, *vertex))
}
})
}
}
func TestBuildGraphIgnoreMissing(t *testing.T) {
testCases := []struct {
desc string
services types.Services
expectedVertices map[string]*Vertex
}{
{
desc: "service depends on init container which is already removed",
services: types.Services{
"test": {
Name: "test",
DependsOn: types.DependsOnConfig{
"test-removed-init-container": types.ServiceDependency{
Condition: "service_completed_successfully",
Restart: false,
Extensions: types.Extensions(nil),
Required: true,
},
},
},
},
expectedVertices: map[string]*Vertex{
"test": {
Key: "test",
Service: "test",
Status: ServiceStopped,
Children: map[string]*Vertex{},
Parents: map[string]*Vertex{},
},
},
},
}
for _, tC := range testCases {
t.Run(tC.desc, func(t *testing.T) {
project := types.Project{
Services: tC.services,
}
graph, err := NewGraph(&project, ServiceStopped, true)
assert.NilError(t, err, fmt.Sprintf("failed to build graph for: %s", tC.desc)) assert.NilError(t, err, fmt.Sprintf("failed to build graph for: %s", tC.desc))
for k, vertex := range graph.Vertices { for k, vertex := range graph.Vertices {

View File

@ -74,7 +74,7 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
resourceToRemove = true resourceToRemove = true
} }
err = InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error { err = InReverseDependencyOrder(ctx, project, true, func(c context.Context, service string) error {
serviceContainers := containers.filter(isService(service)) serviceContainers := containers.filter(isService(service))
err := s.removeContainers(ctx, serviceContainers, options.Timeout, options.Volumes) err := s.removeContainers(ctx, serviceContainers, options.Timeout, options.Volumes)
return err return err

View File

@ -50,7 +50,7 @@ func (s *composeService) stop(ctx context.Context, projectName string, options a
} }
w := progress.ContextWriter(ctx) w := progress.ContextWriter(ctx)
return InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error { return InReverseDependencyOrder(ctx, project, false, func(c context.Context, service string) error {
if !utils.StringContains(options.Services, service) { if !utils.StringContains(options.Services, service) {
return nil return nil
} }