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
15 changes: 7 additions & 8 deletions client/src/main/scala/skuber/api/Configuration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,11 @@ object Configuration {
def valueAt[T](parent: YamlMap, key: String, fallback: Option[T] = None) : T =
parent.asScala.get(key).orElse(fallback).get.asInstanceOf[T]

def instantValueAt[T](parent: YamlMap, key: String) : Instant =
parent.asScala.get(key) match {
case Some(d: Date) => d.toInstant
case Some(s: String) => parseInstant(s)
case Some(unsupported) => sys.error(s"Unsupported date type: $unsupported")
case None => sys.error(s"No value found at $key")
def optionalInstantValueAt[T](parent: YamlMap, key: String) : Option[Instant] =
parent.asScala.get(key).flatMap {
case d: Date => Some(d.toInstant)
case s: String => Try(parseInstant(s)).toOption
case _ => None
}

def optionalValueAt[T](parent: YamlMap, key: String) : Option[T] =
Expand Down Expand Up @@ -175,8 +174,8 @@ object Configuration {
case "gcp" =>
Some(
GcpAuth(
accessToken = valueAt(config, "access-token"),
expiry = instantValueAt(config, "expiry"),
accessToken = optionalValueAt(config, "access-token"),
expiry = optionalInstantValueAt(config, "expiry"),
cmdPath = valueAt(config, "cmd-path"),
cmdArgs = valueAt(config, "cmd-args")
)
Expand Down
38 changes: 27 additions & 11 deletions client/src/main/scala/skuber/api/client/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,24 @@ package object client {
final case class GcpAuth private(private val config: GcpConfiguration) extends AuthProviderAuth {
override val name = "gcp"

@volatile private var refresh: GcpRefresh = new GcpRefresh(config.accessToken, config.expiry)
@volatile private var refresh: Option[GcpRefresh] = config.cachedAccessToken.map(token => GcpRefresh(token.accessToken, token.expiry))

def refreshGcpToken(): GcpRefresh = {
private def refreshGcpToken(): GcpRefresh = {
val output = config.cmd.execute()
Json.parse(output).as[GcpRefresh]
val parsed = Json.parse(output).as[GcpRefresh]
refresh = Some(parsed)
parsed
}

def accessToken: String = this.synchronized {
if (refresh.expired)
refresh = refreshGcpToken()

refresh.accessToken
refresh match {
case Some(expired) if expired.expired =>
refreshGcpToken().accessToken
case None =>
refreshGcpToken().accessToken
case Some(token) =>
token.accessToken
}
}

override def toString =
Expand All @@ -120,13 +126,19 @@ package object client {
}

private[client] object GcpRefresh {
// todo - the path to read this from is part of the configuration, use that instead of
// hard coding.
implicit val gcpRefreshReads: Reads[GcpRefresh] = (
(JsPath \ "credential" \ "access_token").read[String] and
(JsPath \ "credential" \ "token_expiry").read[Instant]
) (GcpRefresh.apply _)
}

final case class GcpConfiguration(accessToken: String, expiry: Instant, cmd: GcpCommand)
final case class GcpConfiguration(cachedAccessToken: Option[GcpCachedAccessToken], cmd: GcpCommand)

final case class GcpCachedAccessToken(accessToken: String, expiry: Instant) {
def expired: Boolean = Instant.now.isAfter(expiry.minusSeconds(20))
}

final case class GcpCommand(cmd: String, args: String) {

Expand All @@ -136,14 +148,18 @@ package object client {
}

object GcpAuth {
def apply(accessToken: String, expiry: Instant, cmdPath: String, cmdArgs: String): GcpAuth =
def apply(accessToken: Option[String], expiry: Option[Instant], cmdPath: String, cmdArgs: String): GcpAuth = {
val cachedAccessToken = for {
token <- accessToken
exp <- expiry
} yield GcpCachedAccessToken(token, exp)
new GcpAuth(
GcpConfiguration(
accessToken = accessToken,
expiry = expiry,
cachedAccessToken = cachedAccessToken,
GcpCommand(cmdPath, cmdArgs)
)
)
}
}

// for use with the Watch command
Expand Down
20 changes: 17 additions & 3 deletions client/src/test/scala/skuber/api/ConfigurationSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package skuber.api
import skuber._
import org.specs2.mutable.Specification
import java.nio.file.Paths
import java.time.Instant
import java.time.format.DateTimeFormatter
import java.time.{Instant, ZoneId}

import akka.stream.ActorMaterializer
import akka.actor.ActorSystem
Expand Down Expand Up @@ -88,6 +89,16 @@ users:
expiry-key: '{.credential.token_expiry}'
token-key: '{.credential.access_token}'
name: gcp
- name: other-date-gke-user
user:
auth-provider:
config:
cmd-args: config config-helper --format=json
cmd-path: /home/user/google-cloud-sdk/bin/gcloud
expiry: "2018-03-04 14:08:18"
expiry-key: '{.credential.token_expiry}'
token-key: '{.credential.access_token}'
name: gcp
"""

implicit val system=ActorSystem("test")
Expand All @@ -109,9 +120,12 @@ users:
val blueUser = TokenAuth("blue-token")
val greenUser = CertAuth(clientCertificate = Left("path/to/my/client/cert"), clientKey = Left("path/to/my/client/key"), user = None)
val jwtUser= OidcAuth(idToken = "jwt-token")
val gcpUser = GcpAuth(accessToken = "myAccessToken", expiry = Instant.parse("2018-03-04T14:08:18Z"),
val gcpUser = GcpAuth(accessToken = Some("myAccessToken"), expiry = Some(Instant.parse("2018-03-04T14:08:18Z")),
cmdPath = "/home/user/google-cloud-sdk/bin/gcloud", cmdArgs = "config config-helper --format=json")
val noAccessTokenGcpUser = GcpAuth(accessToken = None, expiry = None,
cmdPath = "/home/user/google-cloud-sdk/bin/gcloud", cmdArgs = "config config-helper --format=json")
val users=Map("blue-user"->blueUser,"green-user"->greenUser,"jwt-user"->jwtUser, "gke-user"->gcpUser, "string-date-gke-user"->gcpUser)
val users=Map("blue-user"->blueUser,"green-user"->greenUser,"jwt-user"->jwtUser, "gke-user"->gcpUser,
"string-date-gke-user"->gcpUser, "other-date-gke-user" -> noAccessTokenGcpUser)

val federalContext=K8SContext(horseCluster,greenUser,Namespace.forName("chisel-ns"))
val queenAnneContext=K8SContext(pigCluster,blueUser, Namespace.forName("saw-ns"))
Expand Down
2 changes: 1 addition & 1 deletion client/src/test/scala/skuber/model/AuthSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class AuthSpec extends Specification {
}

"GcpAuth toString masks accessToken" >> {
GcpAuth(accessToken = "MyAccessToken", expiry = Instant.now, cmdPath = "gcp", cmdArgs = "").toString mustEqual
GcpAuth(accessToken = Some("MyAccessToken"), expiry = Some(Instant.now), cmdPath = "gcp", cmdArgs = "").toString mustEqual
"GcpAuth(accessToken=<redacted>)"
}

Expand Down