Skip to content

Commit ac87a6c

Browse files
committed
fix: generate stable state for cache
1 parent ce1b9b0 commit ac87a6c

File tree

1 file changed

+35
-34
lines changed

1 file changed

+35
-34
lines changed

src/main/groovy/com/avast/gradle/dockercompose/tasks/ComposeUp.groovy

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import com.avast.gradle.dockercompose.DockerExecutor
66
import com.avast.gradle.dockercompose.ServiceHost
77
import com.avast.gradle.dockercompose.ServiceInfo
88
import com.avast.gradle.dockercompose.ServiceInfoCache
9+
import groovy.json.JsonGenerator
10+
import groovy.json.JsonOutput
911
import groovy.json.JsonSlurper
1012
import org.gradle.api.DefaultTask
1113
import org.gradle.api.file.DirectoryProperty
@@ -209,45 +211,44 @@ abstract class ComposeUp extends DefaultTask {
209211
}
210212
}
211213

212-
private static final VOLATILE_STATE_KEYS = ['RunningFor']
213-
private static final UNSTABLE_ARRAY_STATE_KEYS = ['Mounts', 'Ports', 'Networks', 'Labels', 'Publishers']
214-
215214
@Internal
216215
protected def getStateForCache() {
217-
String processesAsString = composeExecutor.get().execute('ps', '--format', 'json')
218-
String processesState = processesAsString
216+
def state = []
217+
219218
try {
220-
// Since Docker Compose 2.21.0, the output is not one JSON array but newline-separated JSONs.
221-
Map<String, Object>[] processes
222-
if (processesAsString.startsWith('[')) {
223-
processes = new JsonSlurper().parseText(processesAsString)
224-
} else {
225-
processes = processesAsString.split('\\R').findAll { it.trim() }.collect { new JsonSlurper().parseText(it) }
226-
}
227-
List<Object> transformed = processes.collect {
228-
// Status field contains something like "Up 8 seconds", so we have to strip the duration.
229-
if (it.containsKey('Status') && it.Status.startsWith('Up ')) it.Status = 'Up'
230-
VOLATILE_STATE_KEYS.each { key -> it.remove(key) }
231-
UNSTABLE_ARRAY_STATE_KEYS.each { key -> it[key] = parseAndSortStateArray(it[key]) }
232-
it
233-
}
234-
processesState = transformed.join('\t')
235-
} catch (Exception e) {
236-
logger.warn("Cannot process JSON returned from 'docker compose ps --format json'", e)
237-
}
238-
processesState + composeExecutor.get().execute('config') + startedServices.get().join(',')
239-
}
219+
def containersIds = composeExecutor.get()
220+
.execute('ps', '--quiet', '--all')
221+
.readLines()
222+
def containersInfos = new JsonSlurper().parseText(
223+
dockerExecutor.execute(['inspect', *containersIds] as String[])
224+
)
225+
def jsonGenerator = new JsonGenerator.Options()
226+
.excludeFieldsByName('Log')
227+
.addConverter(List) { items, key ->
228+
// Prevent sorting of objects in the root list and list
229+
// items in the "Args" key (usually command arguments)
230+
if (key in [null, 'Args']) {
231+
return items
232+
}
240233

241-
protected Object parseAndSortStateArray(Object list) {
242-
if (list instanceof List) {
243-
//Already provided as a List, no pre-parsing needed
244-
return list.sort { (first, second) -> first.toString() <=> second.toString() }
245-
} else if (list instanceof String && list.contains(",")) {
246-
//Not already a list, but a comma separated string
247-
return list.split(',').collect { it.trim() }.sort()
248-
} else {
249-
return list
234+
// Sort the elements of all other lists
235+
return items.sort()
236+
}
237+
.build()
238+
239+
state += JsonOutput.prettyPrint(
240+
jsonGenerator.toJson(containersInfos)
241+
)
242+
} catch (Throwable t) {
243+
logger.warn(
244+
'Cannot generate inspections for containers, for this reason the state will be incomplete', t
245+
)
250246
}
247+
248+
state += composeExecutor.get().execute('config')
249+
state += startedServices.get().join(',')
250+
251+
return state.join('\n')
251252
}
252253

253254
protected Iterable<ServiceInfo> loadServicesInfo(Iterable<String> servicesNames) {

0 commit comments

Comments
 (0)