Skip to content

Commit

Permalink
feat(plugins): let's log out the plugins we have on startup
Browse files Browse the repository at this point in the history
  • Loading branch information
robfletcher committed Sep 7, 2018
1 parent 50bce37 commit 2ded073
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
/*
* Copyright 2018 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spinnaker.keel.redis

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.readValue
import com.netflix.spinnaker.keel.api.TypeMetadata
import com.netflix.spinnaker.keel.registry.AssetType
import com.netflix.spinnaker.keel.registry.PluginAddress
import com.netflix.spinnaker.keel.registry.PluginRepository
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
import redis.clients.jedis.JedisPool
import javax.annotation.PostConstruct

@Component
class RedisPluginRepository(private val redisPool: JedisPool) : PluginRepository {

private val log by lazy { LoggerFactory.getLogger(javaClass) }

private val objectMapper = ObjectMapper()
.apply { registerModule(KotlinModule()) }

Expand All @@ -28,21 +47,36 @@ class RedisPluginRepository(private val redisPool: JedisPool) : PluginRepository
}
}

override fun assetPluginFor(type: TypeMetadata): PluginAddress? =
override fun assetPluginFor(type: AssetType): PluginAddress? =
redisPool.resource.use { redis ->
redis.hget("keel.plugins.asset", type.serialized)
?.let { objectMapper.readValue(it) }
}

override fun addAssetPluginFor(type: TypeMetadata, address: PluginAddress) {
override fun addAssetPluginFor(type: AssetType, address: PluginAddress) {
redisPool.resource.use { redis ->
redis.hset("keel.plugins.asset", type.serialized, address.serialized)
}
}

private val TypeMetadata.serialized: String
get() = objectMapper.writeValueAsString(mapOf("kind" to kind, "apiVersion" to apiVersion))
@PostConstruct
fun logKnownPlugins() {
redisPool.resource.use { redis ->
redis
.smembers("keel.plugins.veto")
.map { objectMapper.readValue<PluginAddress>(it) }
.forEach { log.info("Veto plugin at {}", it) }
redis
.hgetAll("keel.plugins.asset")
.map { (type, address) ->
with(objectMapper) {
readValue<PluginAddress>(type) to readValue<PluginAddress>(address)
}
}
.forEach { log.info("Asset plugin for {} at {}", it.first, it.second) }
}
}

private val PluginAddress.serialized: String
private val Any.serialized: String
get() = objectMapper.writeValueAsString(this)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
/*
* Copyright 2018 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spinnaker.keel.registry

import com.netflix.spinnaker.keel.api.TypeMetadata
import org.jetbrains.spek.api.Spek
import org.jetbrains.spek.api.dsl.given
import org.jetbrains.spek.api.dsl.it
Expand All @@ -18,14 +32,14 @@ abstract class PluginRepositoryTests<T : PluginRepository>(

val subject = factory()

val securityGroup = TypeMetadata.newBuilder().apply {
val securityGroup = AssetType(
kind = "aws:SecurityGroup",
apiVersion = "1.0"
kind = "aws:SecurityGroup"
}.build()
val loadBalancer = TypeMetadata.newBuilder().apply {
)
val loadBalancer = AssetType(
kind = "aws:LoadBalancer",
apiVersion = "1.0"
kind = "aws:LoadBalancer"
}.build()
)

given("no plugins are stored") {
it("returns null from assetPluginsFor") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/*
* Copyright 2018 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spinnaker.keel.grpc

import com.netflix.discovery.EurekaClient
Expand All @@ -10,6 +25,7 @@ import com.netflix.spinnaker.keel.api.engine.RegisterVetoPluginRequest
import com.netflix.spinnaker.keel.api.engine.RegisterVetoPluginResponse
import com.netflix.spinnaker.keel.api.plugin.AssetPluginGrpc
import com.netflix.spinnaker.keel.platform.NoSuchVip
import com.netflix.spinnaker.keel.registry.AssetType
import com.netflix.spinnaker.keel.registry.PluginAddress
import com.netflix.spinnaker.keel.registry.PluginRepository
import io.grpc.ManagedChannel
Expand All @@ -29,7 +45,7 @@ class GrpcPluginRegistry(

fun pluginFor(type: TypeMetadata): AssetPluginGrpc.AssetPluginBlockingStub? =
pluginRepository
.assetPluginFor(type)
.assetPluginFor(AssetType(type.kind, type.apiVersion))
?.let { (vip, port) ->
stubFor(vip, port, AssetPluginGrpc::newBlockingStub)
}
Expand All @@ -47,7 +63,10 @@ class GrpcPluginRegistry(
request
.typesList
.forEach { type ->
pluginRepository.addAssetPluginFor(type, PluginAddress(request.vip, request.port))
pluginRepository.addAssetPluginFor(
AssetType(type.kind, type.apiVersion),
PluginAddress(request.vip, request.port)
)
log.info("Registered asset plugin supporting {} at vip: {} port: {}", type, request.vip, request.port)
}
responseObserver.apply {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright 2018 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spinnaker.keel.registry

data class AssetType(val kind: String, val apiVersion: String)
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
/*
* Copyright 2018 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spinnaker.keel.registry

import com.netflix.spinnaker.keel.api.TypeMetadata
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.stereotype.Component

@Component
@ConditionalOnMissingBean(PluginRepository::class)
class InMemoryPluginRepository : PluginRepository {

private val assetPlugins: MutableMap<TypeMetadata, PluginAddress> = mutableMapOf()
private val assetPlugins: MutableMap<AssetType, PluginAddress> = mutableMapOf()
private val vetoPlugins: MutableSet<PluginAddress> = mutableSetOf()

override fun vetoPlugins(): Iterable<PluginAddress> = vetoPlugins
Expand All @@ -17,10 +31,10 @@ class InMemoryPluginRepository : PluginRepository {
vetoPlugins.add(address)
}

override fun assetPluginFor(type: TypeMetadata): PluginAddress? =
override fun assetPluginFor(type: AssetType): PluginAddress? =
assetPlugins[type]

override fun addAssetPluginFor(type: TypeMetadata, address: PluginAddress) {
override fun addAssetPluginFor(type: AssetType, address: PluginAddress) {
assetPlugins[type] = address
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/*
* Copyright 2018 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spinnaker.keel.registry

data class PluginAddress(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
/*
* Copyright 2018 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spinnaker.keel.registry

import com.netflix.spinnaker.keel.api.TypeMetadata

interface PluginRepository {
fun vetoPlugins(): Iterable<PluginAddress>

fun addVetoPlugin(address: PluginAddress)

fun assetPluginFor(type: TypeMetadata): PluginAddress? // TODO: don't expose gRPC type here
fun assetPluginFor(type: AssetType): PluginAddress?

fun addAssetPluginFor(type: TypeMetadata, address: PluginAddress)
fun addAssetPluginFor(type: AssetType, address: PluginAddress)
}

0 comments on commit 2ded073

Please sign in to comment.