Skip to content

Commit 19a1981

Browse files
dgrove-osssven-lange-last
authored andcommitted
additional container factory dns configuration (#4176)
Allow configuration of the --dns-search and --dns-opt (--dns-option) arguments to the docker run command of the DockerContainerFactory. Motivated by better support of event providers in Kubernetes when using the DockerContainerFactory.
1 parent 1d8e7c5 commit 19a1981

File tree

11 files changed

+71
-4
lines changed

11 files changed

+71
-4
lines changed

common/scala/src/main/scala/org/apache/openwhisk/core/containerpool/ContainerFactory.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import scala.concurrent.Future
2727

2828
case class ContainerArgsConfig(network: String,
2929
dnsServers: Seq[String] = Seq.empty,
30+
dnsSearch: Seq[String] = Seq.empty,
31+
dnsOptions: Seq[String] = Seq.empty,
3032
extraArgs: Map[String, Set[String]] = Map.empty)
3133

3234
case class ContainerPoolConfig(userMemory: ByteSize, concurrentPeekFactor: Double, akkaClient: Boolean) {

core/invoker/src/main/resources/application.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ whisk {
2828
inspect: 1 minute
2929
pause: 10 seconds
3030
unpause: 10 seconds
31+
version: 10 seconds
3132
}
3233
}
3334

@@ -68,7 +69,10 @@ whisk {
6869
# args for 'docker run' to use
6970
container-factory.container-args {
7071
network: bridge
72+
# See https://docs.docker.com/config/containers/container-networking/#dns-services for documentation of dns-*
7173
dns-servers: []
74+
dns-search: []
75+
dns-options: []
7276
extra-args: {} # to pass additional args to 'docker run'; format is `{key1: [v1, v2], key2: [v1, v2]}`
7377
}
7478

core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/docker/DockerClient.scala

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import akka.actor.ActorSystem
2727
import scala.collection.concurrent.TrieMap
2828
import scala.concurrent.blocking
2929
import scala.concurrent.ExecutionContext
30-
import scala.concurrent.Future
30+
import scala.concurrent.{Await, Future}
3131
import scala.util.Failure
3232
import scala.util.Success
3333
import scala.util.Try
@@ -61,6 +61,7 @@ case class DockerClientTimeoutConfig(run: Duration,
6161
ps: Duration,
6262
pause: Duration,
6363
unpause: Duration,
64+
version: Duration,
6465
inspect: Duration)
6566

6667
/**
@@ -98,6 +99,20 @@ class DockerClient(dockerHost: Option[String] = None,
9899
Seq(dockerBin) ++ host
99100
}
100101

102+
// Invoke docker CLI to determine client version.
103+
// If the docker client version cannot be determined, an exception will be thrown and instance initialization will fail.
104+
// Rationale: if we cannot invoke `docker version` successfully, it is unlikely subsequent `docker` invocations will succeed.
105+
protected def getClientVersion(): String = {
106+
val vf = executeProcess(dockerCmd ++ Seq("version", "--format", "{{.Client.Version}}"), config.timeouts.version)
107+
.andThen {
108+
case Success(version) => log.info(this, s"Detected docker client version $version")
109+
case Failure(e) =>
110+
log.error(this, s"Failed to determine docker client version: ${e.getClass} - ${e.getMessage}")
111+
}
112+
Await.result(vf, 2 * config.timeouts.version)
113+
}
114+
val clientVersion: String = getClientVersion()
115+
101116
protected val maxParallelRuns = config.parallelRuns
102117
protected val runSemaphore =
103118
new Semaphore( /* permits= */ if (maxParallelRuns > 0) maxParallelRuns else Int.MaxValue, /* fair= */ true)
@@ -197,6 +212,13 @@ class DockerClient(dockerHost: Option[String] = None,
197212

198213
trait DockerApi {
199214

215+
/**
216+
* The version number of the docker client cli
217+
*
218+
* @return The version of the docker client cli being used by the invoker
219+
*/
220+
def clientVersion: String
221+
200222
/**
201223
* Spawns a container in detached mode.
202224
*

core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/docker/DockerContainer.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ object DockerContainer {
6565
environment: Map[String, String] = Map.empty,
6666
network: String = "bridge",
6767
dnsServers: Seq[String] = Seq.empty,
68+
dnsSearch: Seq[String] = Seq.empty,
69+
dnsOptions: Seq[String] = Seq.empty,
6870
name: Option[String] = None,
6971
useRunc: Boolean = true,
7072
dockerRunParameters: Map[String, Set[String]])(implicit docker: DockerApiWithFileAccess,
@@ -82,6 +84,8 @@ object DockerContainer {
8284
case (key, valueList) => valueList.toList.flatMap(Seq(key, _))
8385
}
8486

87+
// NOTE: --dns-option on modern versions of docker, but is --dns-opt on docker 1.12
88+
val dnsOptString = if (docker.clientVersion.startsWith("1.12")) { "--dns-opt" } else { "--dns-option" }
8589
val args = Seq(
8690
"--cpu-shares",
8791
cpuShares.toString,
@@ -93,6 +97,8 @@ object DockerContainer {
9397
network) ++
9498
environmentArgs ++
9599
dnsServers.flatMap(d => Seq("--dns", d)) ++
100+
dnsSearch.flatMap(d => Seq("--dns-search", d)) ++
101+
dnsOptions.flatMap(d => Seq(dnsOptString, d)) ++
96102
name.map(n => Seq("--name", n)).getOrElse(Seq.empty) ++
97103
params
98104

core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/docker/DockerContainerFactory.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ class DockerContainerFactory(instance: InvokerInstanceId,
6969
environment = Map("__OW_API_HOST" -> config.wskApiHost),
7070
network = containerArgsConfig.network,
7171
dnsServers = containerArgsConfig.dnsServers,
72+
dnsSearch = containerArgsConfig.dnsSearch,
73+
dnsOptions = containerArgsConfig.dnsOptions,
7274
name = Some(name),
7375
useRunc = dockerContainerFactoryConfig.useRunc,
7476
parameters ++ containerArgsConfig.extraArgs.map { case (k, v) => ("--" + k, v) })

tests/src/test/scala/org/apache/openwhisk/core/containerpool/docker/test/DockerClientTests.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class DockerClientTests
6767
/** Returns a DockerClient with a mocked result for 'executeProcess' */
6868
def dockerClient(execResult: => Future[String]) = new DockerClient()(global) {
6969
override val dockerCmd = Seq(dockerCommand)
70+
override def getClientVersion() = "mock-test-client"
7071
override def executeProcess(args: Seq[String], timeout: Duration)(implicit ec: ExecutionContext, as: ActorSystem) =
7172
execResult
7273
}
@@ -189,6 +190,7 @@ class DockerClientTests
189190
var runCmdCount = 0
190191
val dc = new DockerClient()(global) {
191192
override val dockerCmd = Seq(dockerCommand)
193+
override def getClientVersion() = "mock-test-client"
192194
override def executeProcess(args: Seq[String], timeout: Duration)(implicit ec: ExecutionContext,
193195
as: ActorSystem) = {
194196
runCmdCount += 1
@@ -237,6 +239,7 @@ class DockerClientTests
237239
var runCmdCount = 0
238240
val dc = new DockerClient()(global) {
239241
override val dockerCmd = Seq(dockerCommand)
242+
override def getClientVersion() = "mock-test-client"
240243
override def executeProcess(args: Seq[String], timeout: Duration)(implicit ec: ExecutionContext,
241244
as: ActorSystem) = {
242245
runCmdCount += 1

tests/src/test/scala/org/apache/openwhisk/core/containerpool/docker/test/DockerClientWithFileAccessTests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class DockerClientWithFileAccessTestsIp
7373
readResult: Future[JsObject] = Future.successful(dockerConfig)) =
7474
new DockerClientWithFileAccess()(global) {
7575
override val dockerCmd = Seq(dockerCommand)
76+
override def getClientVersion() = "mock-test-client"
7677
override def executeProcess(args: Seq[String], timeout: Duration)(implicit ec: ExecutionContext,
7778
as: ActorSystem) = execResult
7879
override def configFileContents(configFile: File) = readResult
@@ -129,6 +130,7 @@ class DockerClientWithFileAccessTestsOom
129130
def dockerClient(readResult: Future[JsObject]) =
130131
new DockerClientWithFileAccess()(global) {
131132
override val dockerCmd = Seq("docker")
133+
override def getClientVersion() = "mock-test-client"
132134
override def configFileContents(configFile: File) = readResult
133135
}
134136

tests/src/test/scala/org/apache/openwhisk/core/containerpool/docker/test/DockerContainerFactoryTests.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,16 @@ class DockerContainerFactoryTests
102102
.rm(_: ContainerId)(_: TransactionId))
103103
.expects(ContainerId("fakecontainerid"), *)
104104
.returning(Future.successful(Unit))
105+
//setup clientVersion exceptation
106+
(dockerApiStub.clientVersion _)
107+
.expects()
108+
.returning("mock_test_client")
105109

106110
val factory =
107111
new DockerContainerFactory(
108112
InvokerInstanceId(0, userMemory = defaultUserMemory),
109113
Map.empty,
110-
ContainerArgsConfig("net1", Seq("dns1", "dns2"), Map("env" -> Set("e1", "e2"))),
114+
ContainerArgsConfig("net1", Seq("dns1", "dns2"), Seq.empty, Seq.empty, Map("env" -> Set("e1", "e2"))),
111115
DockerContainerFactoryConfig(true))(actorSystem, executionContext, logging, dockerApiStub, mock[RuncApi])
112116

113117
val cf = factory.createContainer(tid, "testContainer", image, false, 10.MB, 32)

tests/src/test/scala/org/apache/openwhisk/core/containerpool/docker/test/DockerContainerTests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,8 @@ class DockerContainerTests
807807
var pulls = mutable.Buffer.empty[String]
808808
var rawContainerLogsInvocations = mutable.Buffer.empty[(ContainerId, Long, Option[FiniteDuration])]
809809

810+
def clientVersion: String = "mock-test-client"
811+
810812
def run(image: String, args: Seq[String] = Seq.empty[String])(
811813
implicit transid: TransactionId): Future[ContainerId] = {
812814
runs += ((image, args))

tests/src/test/scala/org/apache/openwhisk/core/containerpool/mesos/test/MesosContainerFactoryTest.scala

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,12 @@ class MesosContainerFactoryTest
8686
val mesosCpus = poolConfig.cpuShare(actionMemory) / 1024.0
8787

8888
val containerArgsConfig =
89-
new ContainerArgsConfig("net1", Seq("dns1", "dns2"), Map("extra1" -> Set("e1", "e2"), "extra2" -> Set("e3", "e4")))
89+
new ContainerArgsConfig(
90+
"net1",
91+
Seq("dns1", "dns2"),
92+
Seq.empty,
93+
Seq.empty,
94+
Map("extra1" -> Set("e1", "e2"), "extra2" -> Set("e3", "e4")))
9095

9196
override def beforeEach() = {
9297
stream.reset()
@@ -247,7 +252,12 @@ class MesosContainerFactoryTest
247252
system,
248253
logging,
249254
Map("--arg1" -> Set("v1", "v2"), "--arg2" -> Set("v3", "v4"), "other" -> Set("v5", "v6")),
250-
new ContainerArgsConfig("bridge", Seq.empty, Map("extra1" -> Set("e1", "e2"), "extra2" -> Set("e3", "e4"))),
255+
new ContainerArgsConfig(
256+
"bridge",
257+
Seq.empty,
258+
Seq.empty,
259+
Seq.empty,
260+
Map("extra1" -> Set("e1", "e2"), "extra2" -> Set("e3", "e4"))),
251261
mesosConfig,
252262
(system, mesosConfig) => probe.testActor,
253263
testTaskId _)

0 commit comments

Comments
 (0)