feat(plugins): Add gate-plugins-test module
ADD
gate-plugins-test/gate-plugins-test.gradle
+apply from: "${project.rootDir}/gradle/kotlin.gradle"
+apply from: "${project.rootDir}/gradle/kotlin-test.gradle"
+
+dependencies {
+ implementation(project(":gate-api"))
+
+ testImplementation(project(":gate-web"))
+ testImplementation(project(":gate-core"))
+
+ testImplementation("com.netflix.spinnaker.kork:kork-plugins")
+ testImplementation("com.netflix.spinnaker.kork:kork-plugins-tck")
+
+ testRuntimeOnly("org.junit.platform:junit-platform-launcher")
+ testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
+}
+
+test {
+ // Without filtering down to a specific test, the test task fails.
+ // It fails because the Gate Main class package does not have an Implementation-Version set.
+ //TODO(jonsie): Determine why and how we can remove this.
+ filter {
+ includeTestsMatching "com.netflix.spinnaker.gate.plugins.test.GatePluginsTest"
+ }
+
+ useJUnitPlatform {
+ includeEngines("junit-jupiter", "junit-vintage")
+ }
+}

ADD
gate-plugins-test/src/main/kotlin/com/netflix/spinnaker/gate/plugins/GateApiExtension.kt
+/*
+ * Copyright 2020 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.gate.plugins
+
+import com.netflix.spinnaker.gate.api.extension.ApiExtension
+import com.netflix.spinnaker.gate.api.extension.HttpRequest
+import com.netflix.spinnaker.gate.api.extension.HttpResponse
+import com.netflix.spinnaker.kork.plugins.api.ExtensionConfiguration
+import com.netflix.spinnaker.kork.plugins.api.PluginSdks
+import javax.annotation.Nonnull
+import org.pf4j.Extension
+
+@Extension
+class GateApiExtension(
+ private val pluginSdks: PluginSdks,
+ private val apiExtensionConfigProperties: ApiExtensionConfigProperties
+) : ApiExtension {
+
+ override fun handles(@Nonnull httpRequest: HttpRequest): Boolean {
+ return supportedGet(httpRequest) || supportedPost(httpRequest)
+ }
+
+ override fun handle(@Nonnull httpRequest: HttpRequest): HttpResponse {
+ if (supportedGet(httpRequest)) {
+ return get()
+ }
+ return echo(httpRequest)
+ }
+
+ private fun supportedGet(@Nonnull httpRequest: HttpRequest): Boolean {
+ return httpRequest.method.equals("GET", ignoreCase = true) &&
+ httpRequest.requestURI.endsWith("")
+ }
+
+ private fun supportedPost(@Nonnull httpRequest: HttpRequest): Boolean {
+ return httpRequest.method.equals("PUT", ignoreCase = true) &&
+ httpRequest.requestURI.endsWith("/echo")
+ }
+
+ private fun get(): HttpResponse {
+ return HttpResponse.of(204, emptyMap(), null)
+ }
+
+ private fun echo(@Nonnull httpRequest: HttpRequest): HttpResponse {
+ val echo = httpRequest.parameters["parameter"]
+ return HttpResponse.of(200, emptyMap(), echo)
+ }
+
+ override fun id(): String {
+ return apiExtensionConfigProperties.id
+ }
+}
+
+@ExtensionConfiguration("api-extension")
+class ApiExtensionConfigProperties {
+ var id: String = "test"
+}

ADD
gate-plugins-test/src/main/kotlin/com/netflix/spinnaker/gate/plugins/GatePlugin.kt
+/*
+ * Copyright 2020 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.gate.plugins
+
+import org.pf4j.Plugin
+import org.pf4j.PluginWrapper
+
+class GatePlugin(pluginWrapper: PluginWrapper) : Plugin(pluginWrapper)

ADD
gate-plugins-test/src/test/kotlin/com/netflix/spinnaker/gate/plugins/test/GatePluginsFixture.kt
+/*
+ * Copyright 2020 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.gate.plugins.test
+
+import com.netflix.spinnaker.gate.Main
+import com.netflix.spinnaker.gate.plugins.GateApiExtension
+import com.netflix.spinnaker.gate.plugins.GatePlugin
+import com.netflix.spinnaker.kork.plugins.SpinnakerPluginManager
+import com.netflix.spinnaker.kork.plugins.internal.PluginJar
+import com.netflix.spinnaker.kork.plugins.tck.PluginsTckFixture
+import java.io.File
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.boot.test.context.TestConfiguration
+import org.springframework.test.context.ContextConfiguration
+import org.springframework.test.context.TestPropertySource
+import org.springframework.test.web.servlet.MockMvc
+
+class GatePluginsFixture : PluginsTckFixture, GateTestService() {
+
+ final override val plugins = File("build/plugins")
+
+ final override val enabledPlugin: PluginJar
+ final override val disabledPlugin: PluginJar
+ final override val versionNotSupportedPlugin: PluginJar
+
+ override val extensionClassNames: MutableList<String> = mutableListOf(
+ GateApiExtension::class.java.name
+ )
+
+ final override fun buildPlugin(pluginId: String, systemVersionRequirement: String): PluginJar {
+ return PluginJar.Builder(plugins.toPath().resolve("$pluginId.jar"), pluginId)
+ .pluginClass(GatePlugin::class.java.name)
+ .pluginVersion("1.0.0")
+ .manifestAttribute("Plugin-Requires", "gate$systemVersionRequirement")
+ .extensions(extensionClassNames)
+ .build()
+ }
+
+ @Autowired
+ override lateinit var spinnakerPluginManager: SpinnakerPluginManager
+
+ @Autowired
+ lateinit var mockMvc: MockMvc
+
+ init {
+ plugins.delete()
+ plugins.mkdir()
+ enabledPlugin = buildPlugin("com.netflix.gate.enabled.plugin", ">=1.0.0")
+ disabledPlugin = buildPlugin("com.netflix.gate.disabled.plugin", ">=1.0.0")
+ versionNotSupportedPlugin = buildPlugin("com.netflix.gate.version.not.supported.plugin", ">=2.0.0")
+ }
+}
+
+@SpringBootTest(classes = [Main::class])
+@ContextConfiguration(classes = [PluginTestConfiguration::class])
+@TestPropertySource(properties = ["spring.config.location=classpath:gate-plugins-test.yml"])
+@AutoConfigureMockMvc
+abstract class GateTestService
+
+@TestConfiguration
+internal open class PluginTestConfiguration

ADD
gate-plugins-test/src/test/kotlin/com/netflix/spinnaker/gate/plugins/test/GatePluginsTest.kt
+/*
+ * Copyright 2020 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.gate.plugins.test
+
+import com.netflix.spinnaker.kork.plugins.tck.PluginsTck
+import com.netflix.spinnaker.kork.plugins.tck.serviceFixture
+import dev.minutest.rootContext
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
+import strikt.api.expect
+import strikt.assertions.contains
+import strikt.assertions.isEqualTo
+
+class GatePluginsTest : PluginsTck<GatePluginsFixture>() {
+
+ fun tests() = rootContext<GatePluginsFixture> {
+ context("a gate integration test environment and a gate plugin") {
+ serviceFixture {
+ GatePluginsFixture()
+ }
+
+ defaultPluginTests()
+
+ test("Extension supports GET") {
+ val response = mockMvc.perform(get("/extensions/test"))
+ .andReturn()
+ .response
+
+ expect {
+ that(response.status).isEqualTo(204)
+ }
+ }
+
+ test("Extension supports PUT and echoes content") {
+ val response = mockMvc.perform(put("/extensions/test/echo?parameter=foo"))
+ .andReturn()
+ .response
+
+ expect {
+ that(response.status).isEqualTo(200)
+ that(response.contentAsString).contains("foo")
+ }
+ }
+
+ test("Unsupported HTTP method results in a 404") {
+ val response = mockMvc.perform(post("/extensions/test"))
+ .andReturn()
+ .response
+
+ expect {
+ that(response.status).isEqualTo(404)
+ }
+ }
+ }
+ }
+}

ADD
gate-plugins-test/src/test/resources/gate-plugins-test.yml
+spring:
+ application:
+ name: gate
+
+services:
+ clouddriver.baseUrl: "http://localhost:7002"
+
+ deck.baseUrl: "http://localhost:9000"
+
+ echo.enabled: false
+
+ fiat.enabled: false
+
+ front50.baseUrl: "http://localhost:8080"
+
+ igor.enabled: false
+
+ kayenta.enabled: false
+
+ rosco.enabled: false
+
+ orca.baseUrl: "http://localhost:8083"
+
+ mine.enabled: false
+
+ swabbie.enabled: false
+
+spinnaker:
+ extensibility:
+ plugins-root-path: build/plugins
+ plugins:
+ com.netflix.gate.enabled.plugin:
+ enabled: true
+ com.netflix.gate.disabled.plugin:
+ enabled: false
+ com.netflix.gate.version.not.supported.plugin:
+ enabled: true

MODIFY
gate-web/gate-web.gradle
gate-web/gate-web.gradle
@@ -75,3 +75,15 @@ configurations.all {
exclude group: 'javax.servlet', module: 'servlet-api'
exclude group: "org.slf4j", module: "slf4j-log4j12"
}
+
+test {
+ //The Implementation-Version is set in the MANIFEST.MF for the JAR produced via testing so that
+ //assertions can be made against the version (see gate-plugins-test, for example).
+ jar {
+ manifest {
+ attributes(
+ 'Implementation-Version': '1.0.0'
+ )
+ }
+ }
+}

MODIFY
gate-web/src/main/groovy/com/netflix/spinnaker/gate/Main.groovy
gate-web/src/main/groovy/com/netflix/spinnaker/gate/Main.groovy
@@ -18,24 +18,25 @@ package com.netflix.spinnaker.gate
import com.netflix.spinnaker.hystrix.spectator.HystrixSpectatorConfig
import org.springframework.boot.actuate.autoconfigure.ldap.LdapHealthContributorAutoConfiguration
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration
+import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.context.properties.EnableConfigurationProperties
-import org.springframework.context.annotation.ComponentScan
-import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.scheduling.annotation.EnableAsync
@EnableAsync
-@Configuration
@EnableConfigurationProperties
@Import([HystrixSpectatorConfig])
-@ComponentScan(["com.netflix.spinnaker.gate", "com.netflix.spinnaker.config"])
-@EnableAutoConfiguration(exclude = [GroovyTemplateAutoConfiguration,
- GsonAutoConfiguration,
- LdapHealthContributorAutoConfiguration])
+@SpringBootApplication(
+ scanBasePackages = [
+ "com.netflix.spinnaker.gate",
+ "com.netflix.spinnaker.config"
+ ],
+ exclude = [GroovyTemplateAutoConfiguration, GsonAutoConfiguration,
+ LdapHealthContributorAutoConfiguration]
+)
class Main {
static final Map<String, String> DEFAULT_PROPS = [

MODIFY
settings.gradle
settings.gradle
@@ -19,6 +19,7 @@ rootProject.name = "gate"
include "gate-api",
"gate-core",
"gate-plugins",
+ "gate-plugins-test",
"gate-basic",
"gate-bom",
"gate-iap",

feat(plugins): Add gate-plugins-test module (#1145)