Skip to content

Commit

Permalink
fix(titus): handle environment-level locations
Browse files Browse the repository at this point in the history
  • Loading branch information
robfletcher committed Jul 10, 2020
1 parent 69a7d9d commit c8eb54f
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 3 deletions.
@@ -1,25 +1,30 @@
package com.netflix.spinnaker.titus.jackson

import com.fasterxml.jackson.core.TreeNode
import com.fasterxml.jackson.databind.BeanProperty
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonMappingException
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.deser.std.StdNodeBasedDeserializer
import com.fasterxml.jackson.databind.node.ObjectNode
import com.netflix.spinnaker.keel.api.Moniker
import com.netflix.spinnaker.keel.api.RedBlack
import com.netflix.spinnaker.keel.api.SimpleLocations
import com.netflix.spinnaker.keel.api.SubnetAwareLocations
import com.netflix.spinnaker.keel.api.titus.TitusClusterSpec
import com.netflix.spinnaker.keel.api.titus.TitusServerGroupSpec
import com.netflix.spinnaker.keel.api.toSimpleLocations
import com.netflix.spinnaker.keel.docker.ContainerProvider

class TitusClusterSpecDeserializer : StdNodeBasedDeserializer<TitusClusterSpec>(TitusClusterSpec::class.java) {
override fun convert(root: JsonNode, context: DeserializationContext): TitusClusterSpec =
with(context) {
TitusClusterSpec(
moniker = treeToValue<Moniker>(root.get("moniker")) ?: error("moniker is required"),
moniker = treeToValue<Moniker>(root.get("moniker")) ?: root.missingFieldError("moniker"),
deployWith = treeToValue(root.get("deployWith")) ?: RedBlack(),
locations = treeToValue<SimpleLocations>(root.get("locations"))
?: error("locations is required"),
?: findInjectableLocations()
?: root.missingFieldError("locations"),
_defaults = TitusServerGroupSpec(
capacity = treeToValue(root.get("capacity")),
dependencies = treeToValue(root.get("dependencies")),
Expand All @@ -33,10 +38,16 @@ class TitusClusterSpecDeserializer : StdNodeBasedDeserializer<TitusClusterSpec>(
?.filterNotNullValues()
?: emptyMap(),
containerProvider = treeToValue<ContainerProvider>(root.get("container"))
?: error("container is required")
?: root.missingFieldError("container")
)
}

private fun DeserializationContext.findInjectableLocations() =
(findInjectableValue("locations", BeanProperty.Bogus(), null) as SubnetAwareLocations?)?.toSimpleLocations()

private fun JsonNode.missingFieldError(fieldName: String): Nothing =
throw JsonMappingException.wrapWithPath(IllegalStateException("$fieldName is required"), this, fieldName)

private inline fun <reified T> DeserializationContext.treeToValue(node: TreeNode?): T? =
if (node == null) null else parser.codec.treeToValue(node, T::class.java)

Expand Down
@@ -0,0 +1,62 @@
package com.netflix.spinnaker.keel.titus.jackson

import com.fasterxml.jackson.databind.jsontype.NamedType
import com.fasterxml.jackson.module.kotlin.readValue
import com.netflix.spinnaker.keel.api.titus.TitusClusterSpec
import com.netflix.spinnaker.keel.api.toSimpleLocations
import com.netflix.spinnaker.keel.core.api.SubmittedDeliveryConfig
import com.netflix.spinnaker.keel.test.configuredTestYamlMapper
import com.netflix.spinnaker.titus.jackson.registerKeelTitusApiModule
import dev.minutest.junit.JUnit5Minutests
import dev.minutest.rootContext
import strikt.api.expectThat
import strikt.assertions.isA
import strikt.assertions.isEqualTo

class TitusClusterSpecDeserializationTests : JUnit5Minutests {

data class Fixture(val manifest: String) {
val mapper = configuredTestYamlMapper()
.registerKeelTitusApiModule()
.apply {
registerSubtypes(NamedType(TitusClusterSpec::class.java, "titus/cluster@v1"))
}
}

fun tests() = rootContext<Fixture> {
context("a titus cluster where the locations are derived from the environment") {
fixture {
Fixture("""
---
application: fnord
serviceAccount: fzlem@spinnaker.io
environments:
- name: test
locations:
account: test
regions:
- name: us-west-2
- name: us-east-1
resources:
- kind: titus/cluster@v1
spec:
moniker:
app: fnord
container:
organization: fnord
image: fnord
digest: sha:9e860d779528ea32b1692cdbb840c66c5d173b2c63aee0e7a75a957e06790de7
""".trimMargin())
}

test("locations on the cluster are set based on the environment") {
val deliveryConfig = mapper.readValue<SubmittedDeliveryConfig>(manifest)

expectThat(deliveryConfig.environments.first().resources.first().spec)
.isA<TitusClusterSpec>()
.get(TitusClusterSpec::locations)
.isEqualTo(deliveryConfig.environments.first().locations!!.toSimpleLocations())
}
}
}
}

0 comments on commit c8eb54f

Please sign in to comment.