feat(plugins): List deck plugins at /plugins/installed
MODIFY
gate-plugins/src/main/kotlin/com/netflix/spinnaker/gate/plugins/deck/DeckPluginService.kt
gate-plugins/src/main/kotlin/com/netflix/spinnaker/gate/plugins/deck/DeckPluginService.kt
@@ -23,7 +23,7 @@ import org.slf4j.LoggerFactory
* Provides Deck with means of discovering what plugins it needs to download and a standard interface for
* retrieving plugin assets.
*/
-class DeckPluginService(
+open class DeckPluginService(
private val pluginCache: DeckPluginCache,
private val registry: Registry
) {

MODIFY
gate-plugins/src/main/kotlin/com/netflix/spinnaker/gate/plugins/web/installed/PluginsInstalledController.kt
gate-plugins/src/main/kotlin/com/netflix/spinnaker/gate/plugins/web/installed/PluginsInstalledController.kt
package com.netflix.spinnaker.gate.plugins.web.installed
+import com.netflix.spinnaker.gate.plugins.deck.DeckPluginService
import com.netflix.spinnaker.gate.services.internal.ClouddriverService
import com.netflix.spinnaker.gate.services.internal.EchoService
import com.netflix.spinnaker.gate.services.internal.ExtendedFiatService
@@ -11,6 +12,7 @@ import com.netflix.spinnaker.gate.services.internal.RoscoService
import com.netflix.spinnaker.gate.services.internal.SwabbieService
import com.netflix.spinnaker.kork.plugins.SpinnakerPluginDescriptor
import com.netflix.spinnaker.kork.plugins.SpinnakerPluginManager
+import com.netflix.spinnaker.kork.plugins.update.SpinnakerUpdateManager
import io.swagger.annotations.ApiOperation
import java.util.stream.Collectors
import org.pf4j.PluginWrapper
@@ -33,7 +35,9 @@ class PluginsInstalledController(
private val orcaServiceSelector: OrcaServiceSelector,
private val roscoService: ObjectProvider<RoscoService>,
private val swabbieService: ObjectProvider<SwabbieService>,
- private val spinnakerPluginManager: SpinnakerPluginManager
+ private val spinnakerPluginManager: SpinnakerPluginManager,
+ private val spinnakerUpdateManager: SpinnakerUpdateManager,
+ private val deckPluginService: DeckPluginService
) {
@ApiOperation(value = "Get all installed Spinnaker plugins")
@@ -41,6 +45,7 @@ class PluginsInstalledController(
fun getInstalledPlugins(@RequestParam(value = "service", required = false) service: String?): Map<String, List<SpinnakerPluginDescriptor>> {
return when (service) {
clouddriver -> mutableMapOf(Pair(service, callService { clouddriverService.installedPlugins }))
+ deck -> mutableMapOf(Pair(service, deckPlugins()))
echo -> if (echoService.ifAvailable != null) mutableMapOf(Pair(service, callService { echoService.ifAvailable!!.installedPlugins })) else emptyMap()
fiat -> mutableMapOf(Pair(service, callService { fiatService.installedPlugins }))
front50 -> mutableMapOf(Pair(service, callService { front50Service.installedPlugins }))
@@ -58,6 +63,7 @@ class PluginsInstalledController(
val swabbiePair = if (swabbieService.ifAvailable != null) Pair(swabbie, callService { swabbieService.ifAvailable!!.installedPlugins }) else Pair(swabbie, emptyList())
mutableMapOf(
Pair(clouddriver, callService { clouddriverService.installedPlugins }),
+ Pair(deck, deckPlugins()),
echoPair,
Pair(fiat, callService { fiatService.installedPlugins }),
Pair(front50, callService { front50Service.installedPlugins }),
@@ -78,6 +84,38 @@ class PluginsInstalledController(
.collect(Collectors.toList())
}
+ /**
+ * Map deck plugins to PF4J plugin descriptors. Deck plugins are not PF4J runtime plugins,
+ * but the properties from the plugin info can be mapped correctly.
+ */
+ fun deckPlugins(): List<SpinnakerPluginDescriptor> {
+ val deckPlugins = deckPluginService.getPluginsManifests()
+ val plugins = spinnakerUpdateManager.plugins
+ val descriptors: MutableList<SpinnakerPluginDescriptor> = mutableListOf()
+
+ for (deckPlugin in deckPlugins) {
+ val plugin = plugins.stream()
+ .filter { it.id == deckPlugin.id }
+ .findFirst()
+ .get()
+
+ val release = plugin.getReleases().stream()
+ .filter { it.version == deckPlugin.version }
+ .findFirst()
+ .get()
+
+ descriptors.add(SpinnakerPluginDescriptor(
+ pluginId = plugin.id,
+ pluginDescription = plugin.description,
+ version = release.version,
+ requires = release.requires,
+ provider = plugin.provider
+ ))
+ }
+
+ return descriptors
+ }
+
fun callService(call: () -> List<SpinnakerPluginDescriptor>): List<SpinnakerPluginDescriptor> {
return try {
call()
@@ -92,6 +130,7 @@ class PluginsInstalledController(
companion object {
const val clouddriver: String = "clouddriver"
+ const val deck: String = "deck"
const val echo: String = "echo"
const val fiat: String = "fiat"
const val front50: String = "front50"

MODIFY
gate-plugins/src/test/groovy/com/netflix/spinnaker/gate/plugins/web/info/PluginInfoControllerSpec.groovy
gate-plugins/src/test/groovy/com/netflix/spinnaker/gate/plugins/web/info/PluginInfoControllerSpec.groovy
@@ -21,6 +21,7 @@ import com.netflix.spinnaker.config.ErrorConfiguration
import com.netflix.spinnaker.config.PluginsAutoConfiguration
import com.netflix.spinnaker.config.okhttp3.OkHttpClientProvider
import com.netflix.spinnaker.gate.config.ServiceConfiguration
+import com.netflix.spinnaker.gate.plugins.deck.DeckPluginService
import com.netflix.spinnaker.gate.plugins.web.PluginWebConfiguration
import com.netflix.spinnaker.gate.plugins.web.SpinnakerExtensionsConfigProperties
import com.netflix.spinnaker.gate.services.TaskService
@@ -104,6 +105,9 @@ class PluginInfoControllerSpec extends Specification {
@MockBean
private SwabbieService swabbieService
+ @MockBean
+ private DeckPluginService deckPluginService
+
private Map requestContent = ['name': 'test plugin', provider: 'Test Co']
def 'should load configuration bean with expected values'() {

feat(plugins): List deck plugins at /plugins/installed (#1379)