2121package eu .openanalytics .containerproxy .backend .docker ;
2222
2323import com .spotify .docker .client .DockerClient ;
24+ import com .spotify .docker .client .exceptions .DockerException ;
25+ import com .spotify .docker .client .messages .RegistryAuth ;
2426import com .spotify .docker .client .messages .mount .Mount ;
2527import com .spotify .docker .client .messages .swarm .DnsConfig ;
2628import com .spotify .docker .client .messages .swarm .EndpointSpec ;
2729import com .spotify .docker .client .messages .swarm .NetworkAttachmentConfig ;
2830import com .spotify .docker .client .messages .swarm .PortConfig ;
31+ import com .spotify .docker .client .messages .swarm .ResourceRequirements ;
32+ import com .spotify .docker .client .messages .swarm .Resources ;
33+ import com .spotify .docker .client .messages .swarm .Secret ;
34+ import com .spotify .docker .client .messages .swarm .SecretBind ;
35+ import com .spotify .docker .client .messages .swarm .SecretFile ;
2936import com .spotify .docker .client .messages .swarm .Service ;
3037import com .spotify .docker .client .messages .swarm .ServiceSpec ;
3138import com .spotify .docker .client .messages .swarm .Task ;
3946import eu .openanalytics .containerproxy .model .runtime .runtimevalues .RuntimeValueKey ;
4047import eu .openanalytics .containerproxy .model .runtime .runtimevalues .UserIdKey ;
4148import eu .openanalytics .containerproxy .model .spec .ContainerSpec ;
49+ import eu .openanalytics .containerproxy .model .spec .DockerSwarmSecret ;
4250import eu .openanalytics .containerproxy .util .Retrying ;
4351
4452import java .net .URI ;
@@ -84,6 +92,11 @@ protected Container startContainer(ContainerSpec spec, Proxy proxy) throws Excep
8492 }
8593 }
8694
95+ List <SecretBind > secretBinds = new ArrayList <>();
96+ for (DockerSwarmSecret secret : spec .getDockerSwarmSecrets ()) {
97+ secretBinds .add (convertSecret (secret ));
98+ }
99+
87100 com .spotify .docker .client .messages .swarm .ContainerSpec containerSpec =
88101 com .spotify .docker .client .messages .swarm .ContainerSpec .builder ()
89102 .image (spec .getImage ())
@@ -92,6 +105,7 @@ protected Container startContainer(ContainerSpec spec, Proxy proxy) throws Excep
92105 .env (convertEnv (buildEnv (spec , proxy )))
93106 .dnsConfig (DnsConfig .builder ().nameServers (spec .getDns ()).build ())
94107 .mounts (mounts )
108+ .secrets (secretBinds )
95109 .build ();
96110
97111 NetworkAttachmentConfig [] networks = Arrays
@@ -104,12 +118,35 @@ protected Container startContainer(ContainerSpec spec, Proxy proxy) throws Excep
104118 networks [networks .length - 1 ] = NetworkAttachmentConfig .builder ().target (spec .getNetwork ()).build ();
105119 }
106120
121+ Resources .Builder reservationsBuilder = Resources .builder ();
122+ // reservations are used by the Docker swarm scheduler
123+ if (spec .getCpuRequest () != null ) {
124+ // note: 1 CPU = 1 * 10e8 nanoCpu -> equivalent to --cpus option
125+ reservationsBuilder .nanoCpus ((long ) (Double .parseDouble (spec .getCpuRequest ()) * 10e8 ));
126+ }
127+ if (spec .getMemoryRequest () != null ) {
128+ reservationsBuilder .memoryBytes (memoryToBytes (spec .getMemoryRequest ()));
129+ }
130+
131+ Resources .Builder limitsBuilder = Resources .builder ();
132+ if (spec .getCpuLimit () != null ) {
133+ // note: 1 CPU = 1 * 10e8 nanoCpu -> equivalent to --cpus option
134+ limitsBuilder .nanoCpus ((long ) (Double .parseDouble (spec .getCpuLimit ()) * 10e8 ));
135+ }
136+ if (spec .getMemoryLimit () != null ) {
137+ limitsBuilder .memoryBytes (memoryToBytes (spec .getMemoryLimit ()));
138+ }
139+
107140 String serviceName = "sp-service-" + UUID .randomUUID ().toString ();
108141 ServiceSpec .Builder serviceSpecBuilder = ServiceSpec .builder ()
109142 .networks (networks )
110143 .name (serviceName )
111144 .taskTemplate (TaskSpec .builder ()
112145 .containerSpec (containerSpec )
146+ .resources (ResourceRequirements .builder ()
147+ .reservations (reservationsBuilder .build ())
148+ .limits (limitsBuilder .build ())
149+ .build ())
113150 .build ());
114151
115152 List <PortConfig > portsToPublish = new ArrayList <>();
@@ -124,7 +161,21 @@ protected Container startContainer(ContainerSpec spec, Proxy proxy) throws Excep
124161 serviceSpecBuilder .endpointSpec (EndpointSpec .builder ().ports (portsToPublish ).build ());
125162 }
126163
127- String serviceId = dockerClient .createService (serviceSpecBuilder .build ()).id ();
164+ String serviceId ;
165+ if (spec .getDockerRegistryDomain () != null
166+ && spec .getDockerRegistryUsername () != null
167+ && spec .getDockerRegistryPassword () != null ) {
168+
169+ RegistryAuth registryAuth = RegistryAuth .builder ()
170+ .serverAddress (spec .getDockerRegistryDomain ())
171+ .username (spec .getDockerRegistryUsername ())
172+ .password (spec .getDockerRegistryPassword ())
173+ .build ();
174+ serviceId = dockerClient .createService (serviceSpecBuilder .build (), registryAuth ).id ();
175+ } else {
176+ serviceId = dockerClient .createService (serviceSpecBuilder .build ()).id ();
177+ }
178+
128179 container .getParameters ().put (PARAM_SERVICE_ID , serviceId );
129180
130181 // Give the service some time to start up and launch a container.
@@ -133,7 +184,9 @@ protected Container startContainer(ContainerSpec spec, Proxy proxy) throws Excep
133184 Task serviceTask = dockerClient
134185 .listTasks (Task .Criteria .builder ().serviceName (serviceName ).build ())
135186 .stream ().findAny ().orElseThrow (() -> new IllegalStateException ("Swarm service has no tasks" ));
136- container .setId (serviceTask .status ().containerStatus ().containerId ());
187+ if (serviceTask .status ().containerStatus () != null ) {
188+ container .setId (serviceTask .status ().containerStatus ().containerId ());
189+ }
137190 } catch (Exception e ) {
138191 throw new RuntimeException ("Failed to inspect swarm service tasks" , e );
139192 }
@@ -181,7 +234,33 @@ protected URI calculateTarget(Container container, int containerPort, int servic
181234 return new URI (String .format ("%s://%s:%s%s" , targetProtocol , targetHostName , targetPort , targetPath ));
182235 }
183236
184- @ Override
237+ private SecretBind convertSecret (DockerSwarmSecret secret ) throws DockerException , InterruptedException {
238+ if (secret .getName () == null ) {
239+ throw new IllegalArgumentException ("No name for a Docker swarm secret provided" );
240+ }
241+ return SecretBind .builder ()
242+ .secretName (secret .getName ())
243+ .secretId (getSecretId (secret .getName ()))
244+ .file (
245+ SecretFile .builder ()
246+ .name (Optional .ofNullable (secret .getTarget ()).orElse (secret .getName ()))
247+ .gid (Optional .ofNullable (secret .getGid ()).orElse ("0" ))
248+ .uid (Optional .ofNullable (secret .getUid ()).orElse ("0" ))
249+ .mode (Long .parseLong (Optional .ofNullable (secret .getMode ()).orElse ("444" ), 8 ))
250+ .build ()
251+ )
252+ .build ();
253+
254+ }
255+
256+ private String getSecretId (String secretName ) throws DockerException , InterruptedException {
257+ return dockerClient .listSecrets ().stream ()
258+ .filter (it -> it .secretSpec ().name ().equals (secretName ))
259+ .findFirst ()
260+ .map (Secret ::id ).orElseThrow (() -> new IllegalArgumentException ("Secret not found!" ));
261+ }
262+
263+ @ Override
185264 protected void doStopProxy (Proxy proxy ) throws Exception {
186265 for (Container container : proxy .getContainers ()) {
187266 String serviceId = (String ) container .getParameters ().get (PARAM_SERVICE_ID );
0 commit comments