Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions k8s/src/main/scala/io/buoyant/k8s/IngressCache.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,11 @@ case class IngressSpec(
) {
def getMatchingPath(hostHeader: Option[String], requestPath: String): Option[IngressPath] = {
val matchingPath = rules.find(_.matches(hostHeader, requestPath))
(matchingPath, fallbackBackend) match {
case (Some(path), _) =>
log.info("k8s found rule matching %s %s: %s", hostHeader.getOrElse(""), requestPath, path)
Some(path)
case (None, Some(default)) =>
log.info("k8s using default service %s for request %s %s", default, hostHeader.getOrElse(""), requestPath)
Some(default)
case _ =>
log.info("k8s no suitable rule found in %s for request %s %s", name.getOrElse(""), hostHeader.getOrElse(""), requestPath)
None
matchingPath match {
case Some(path) => log.info("k8s found rule matching %s %s: %s", hostHeader.getOrElse(""), requestPath, path)
case None => log.info("no ingress rule found for request %s %s", hostHeader.getOrElse(""), requestPath)
}
matchingPath
}
}

Expand Down Expand Up @@ -59,13 +53,17 @@ object IngressCache {
type IngressState = Activity.State[Seq[IngressSpec]]
val annotationKey = "kubernetes.io/ingress.class"

private[k8s] def getMatchingPath(hostHeader: Option[String], requestPath: String, ingresses: Seq[IngressSpec]): Option[IngressPath] =
private[k8s] def iterateForMatch(ingresses: Seq[IngressSpec], fn: (IngressSpec) => Option[IngressPath]) =
ingresses
.toIterator // stop after we find a match
.flatMap(_.getMatchingPath(hostHeader, requestPath))
.flatMap(fn(_))
.take(1)
.toSeq.headOption
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I might be missing something, but is this basically the same as Iterator#find()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you, not sure why I'm tiptoeing around toIterator like a sacred cow

Copy link
Contributor Author

@esbie esbie Aug 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh figured it out, we need to return the path that matches, not the whole matching spec. We could do it in 2 phases, but we'd like to be as efficient as possible on the request path.


private[k8s] def getMatchingPath(hostHeader: Option[String], requestPath: String, ingresses: Seq[IngressSpec]): Option[IngressPath] =
iterateForMatch(ingresses, _.getMatchingPath(hostHeader, requestPath))
.orElse(iterateForMatch(ingresses, _.fallbackBackend))

}

/**
Expand Down Expand Up @@ -143,8 +141,16 @@ class IngressCache(namespace: Option[String], apiClient: Service[Request, Respon
}
}

def matchPath(hostHeader: Option[String], requestPath: String): Future[Option[IngressPath]] =
def matchPath(hostHeader: Option[String], requestPath: String): Future[Option[IngressPath]] = {
val hostHeaderSansPort = hostHeader.map {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Peut-être "without" au lieu de "sans"

Copy link
Contributor

@hawkw hawkw Aug 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:) 🇫🇷

_.split(":") match {
case Array(h: String, _) => h
case Array(h: String) => h
case _ => throw new IllegalArgumentException("unable to parse host for request")
}
}
ingresses.map { cache: Seq[IngressSpec] =>
getMatchingPath(hostHeader, requestPath, cache)
getMatchingPath(hostHeaderSansPort, requestPath, cache)
}.toFuture
}
}
37 changes: 37 additions & 0 deletions k8s/src/test/scala/io/buoyant/k8s/IngressCacheTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,12 @@ class IngressCacheTest extends FunSuite with Awaits {
assert(await(cache.matchPath(host, "/non-existing-path")).isEmpty)
}

test("ignores port in a request's hostname") {
val service = mkIngressApiServiceReturning(ingressResourceListWithOneIngress)
val cache = new IngressCache(None, service, annotationClass)
assert(await(cache.matchPath(Some("myhost:80"), "/some-path")).get.svc == "echo")
}

test("on multiple path matches, return first match") {
val paths = Seq(
IngressPath(host, Some("/path"), ns.get, "primary-svc", "80"),
Expand All @@ -346,6 +352,37 @@ class IngressCacheTest extends FunSuite with Awaits {
assert(matchingPath.get.svc == "svc1")
}

test("don't return default backend before checking all resources for matches") {
val resource1 = IngressSpec(
Some("polar-bear1"),
ns,
Some(IngressPath(None, None, ns.get, "fallback", "80")),
Seq(IngressPath(Some("other host"), None, ns.get, "svc1", "80"))
)
val resource2 = IngressSpec(Some("polar-bear2"), ns, None, Seq(IngressPath(host, None, ns.get, "svc2", "80")))
val matchingPath = IngressCache.getMatchingPath(host, "/path", Seq(resource1, resource2))
assert(matchingPath.get.svc == "svc2")
}

test("on no host/path matches, return first default backend") {
val resource1 = IngressSpec(Some("polar-bear1"), ns, None, Seq(IngressPath(host, None, ns.get, "svc1", "80")))
val resource2 = IngressSpec(
Some("polar-bear2"),
ns,
Some(IngressPath(None, None, ns.get, "fallback", "80")),
Seq(IngressPath(host, None, ns.get, "svc2", "80"))
)
val matchingPath = IngressCache.getMatchingPath(Some("unknown host"), "/path", Seq(resource1, resource2))
assert(matchingPath.get.svc == "fallback")
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider the case where no default backend exists?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I will add a test

test("no matches") {
val resource1 = IngressSpec(Some("polar-bear1"), ns, None, Seq(IngressPath(host, None, ns.get, "svc1", "80")))
val resource2 = IngressSpec(Some("polar-bear2"), ns, None, Seq(IngressPath(host, None, ns.get, "svc2", "80")))
val matchingPath = IngressCache.getMatchingPath(Some("unknown host"), "/path", Seq(resource1, resource2))
assert(matchingPath == None)
}

test("match on path regex") {
val path = IngressPath(host, Some("/prefix/.*"), ns.get, "svc1", "80")
assert(path.matches(host, "/prefix/and-other-stuff"))
Expand Down