1919import java .io .File ;
2020import java .util .ArrayList ;
2121import java .util .Collections ;
22+ import java .util .HashMap ;
2223import java .util .List ;
24+ import java .util .Map ;
2325import java .util .Set ;
2426import java .util .function .Consumer ;
2527
3941 */
4042class DockerCli {
4143
44+ private static final Map <File , DockerCommands > dockerCommandsCache = new HashMap <>();
45+
4246 private static final Log logger = LogFactory .getLog (DockerCli .class );
4347
4448 private final ProcessRunner processRunner ;
4549
46- private final List <String > dockerCommand ;
47-
48- private final List <String > dockerComposeCommand ;
50+ private final DockerCommands dockerCommands ;
4951
5052 private final DockerComposeFile composeFile ;
5153
@@ -59,56 +61,12 @@ class DockerCli {
5961 */
6062 DockerCli (File workingDirectory , DockerComposeFile composeFile , Set <String > activeProfiles ) {
6163 this .processRunner = new ProcessRunner (workingDirectory );
62- this .dockerCommand = getDockerCommand ( this . processRunner );
63- this . dockerComposeCommand = getDockerComposeCommand (this .processRunner );
64+ this .dockerCommands = dockerCommandsCache . computeIfAbsent ( workingDirectory ,
65+ ( key ) -> new DockerCommands (this .processRunner ) );
6466 this .composeFile = composeFile ;
6567 this .activeProfiles = (activeProfiles != null ) ? activeProfiles : Collections .emptySet ();
6668 }
6769
68- private List <String > getDockerCommand (ProcessRunner processRunner ) {
69- try {
70- String version = processRunner .run ("docker" , "version" , "--format" , "{{.Client.Version}}" );
71- logger .trace (LogMessage .format ("Using docker %s" , version ));
72- return List .of ("docker" );
73- }
74- catch (ProcessStartException ex ) {
75- throw new DockerProcessStartException ("Unable to start docker process. Is docker correctly installed?" , ex );
76- }
77- catch (ProcessExitException ex ) {
78- if (ex .getStdErr ().contains ("docker daemon is not running" )
79- || ex .getStdErr ().contains ("Cannot connect to the Docker daemon" )) {
80- throw new DockerNotRunningException (ex .getStdErr (), ex );
81- }
82- throw ex ;
83-
84- }
85- }
86-
87- private List <String > getDockerComposeCommand (ProcessRunner processRunner ) {
88- try {
89- DockerCliComposeVersionResponse response = DockerJson .deserialize (
90- processRunner .run ("docker" , "compose" , "version" , "--format" , "json" ),
91- DockerCliComposeVersionResponse .class );
92- logger .trace (LogMessage .format ("Using docker compose %s" , response .version ()));
93- return List .of ("docker" , "compose" );
94- }
95- catch (ProcessExitException ex ) {
96- // Ignore and try docker-compose
97- }
98- try {
99- DockerCliComposeVersionResponse response = DockerJson .deserialize (
100- processRunner .run ("docker-compose" , "version" , "--format" , "json" ),
101- DockerCliComposeVersionResponse .class );
102- logger .trace (LogMessage .format ("Using docker-compose %s" , response .version ()));
103- return List .of ("docker-compose" );
104- }
105- catch (ProcessStartException ex ) {
106- throw new DockerProcessStartException (
107- "Unable to start 'docker-compose' process or use 'docker compose'. Is docker correctly installed?" ,
108- ex );
109- }
110- }
111-
11270 /**
11371 * Run the given {@link DockerCli} command and return the response.
11472 * @param <R> the response type
@@ -132,9 +90,9 @@ private Consumer<String> createOutputConsumer(LogLevel logLevel) {
13290
13391 private List <String > createCommand (Type type ) {
13492 return switch (type ) {
135- case DOCKER -> new ArrayList <>(this .dockerCommand );
93+ case DOCKER -> new ArrayList <>(this .dockerCommands . get ( type ) );
13694 case DOCKER_COMPOSE -> {
137- List <String > result = new ArrayList <>(this .dockerComposeCommand );
95+ List <String > result = new ArrayList <>(this .dockerCommands . get ( type ) );
13896 if (this .composeFile != null ) {
13997 result .add ("--file" );
14098 result .add (this .composeFile .toString ());
@@ -158,4 +116,71 @@ DockerComposeFile getDockerComposeFile() {
158116 return this .composeFile ;
159117 }
160118
119+ /**
120+ * Holds details of the actual CLI commands to invoke.
121+ */
122+ private static class DockerCommands {
123+
124+ private final List <String > dockerCommand ;
125+
126+ private final List <String > dockerComposeCommand ;
127+
128+ DockerCommands (ProcessRunner processRunner ) {
129+ this .dockerCommand = getDockerCommand (processRunner );
130+ this .dockerComposeCommand = getDockerComposeCommand (processRunner );
131+ }
132+
133+ private List <String > getDockerCommand (ProcessRunner processRunner ) {
134+ try {
135+ String version = processRunner .run ("docker" , "version" , "--format" , "{{.Client.Version}}" );
136+ logger .trace (LogMessage .format ("Using docker %s" , version ));
137+ return List .of ("docker" );
138+ }
139+ catch (ProcessStartException ex ) {
140+ throw new DockerProcessStartException ("Unable to start docker process. Is docker correctly installed?" ,
141+ ex );
142+ }
143+ catch (ProcessExitException ex ) {
144+ if (ex .getStdErr ().contains ("docker daemon is not running" )
145+ || ex .getStdErr ().contains ("Cannot connect to the Docker daemon" )) {
146+ throw new DockerNotRunningException (ex .getStdErr (), ex );
147+ }
148+ throw ex ;
149+ }
150+ }
151+
152+ private List <String > getDockerComposeCommand (ProcessRunner processRunner ) {
153+ try {
154+ DockerCliComposeVersionResponse response = DockerJson .deserialize (
155+ processRunner .run ("docker" , "compose" , "version" , "--format" , "json" ),
156+ DockerCliComposeVersionResponse .class );
157+ logger .trace (LogMessage .format ("Using docker compose %s" , response .version ()));
158+ return List .of ("docker" , "compose" );
159+ }
160+ catch (ProcessExitException ex ) {
161+ // Ignore and try docker-compose
162+ }
163+ try {
164+ DockerCliComposeVersionResponse response = DockerJson .deserialize (
165+ processRunner .run ("docker-compose" , "version" , "--format" , "json" ),
166+ DockerCliComposeVersionResponse .class );
167+ logger .trace (LogMessage .format ("Using docker-compose %s" , response .version ()));
168+ return List .of ("docker-compose" );
169+ }
170+ catch (ProcessStartException ex ) {
171+ throw new DockerProcessStartException (
172+ "Unable to start 'docker-compose' process or use 'docker compose'. Is docker correctly installed?" ,
173+ ex );
174+ }
175+ }
176+
177+ List <String > get (Type type ) {
178+ return switch (type ) {
179+ case DOCKER -> this .dockerCommand ;
180+ case DOCKER_COMPOSE -> this .dockerComposeCommand ;
181+ };
182+ }
183+
184+ }
185+
161186}
0 commit comments