From 13cf36fd81c1f867806c109580c59b516f44ace1 Mon Sep 17 00:00:00 2001 From: Cameron Fieber Date: Mon, 23 May 2016 09:35:29 -0700 Subject: [PATCH] Bring back interfaces to mine service to enable experimentation around alternate ACA sources --- .../spinnaker/gate/config/GateConfig.groovy | 6 ++ .../gate/controllers/CanaryController.groovy | 91 +++++++++++++++++ .../gate/services/CanaryService.groovy | 98 +++++++++++++++++++ .../gate/services/internal/MineService.groovy | 59 +++++++++++ 4 files changed, 254 insertions(+) create mode 100644 gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/CanaryController.groovy create mode 100644 gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CanaryService.groovy create mode 100644 gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/internal/MineService.groovy diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/config/GateConfig.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/config/GateConfig.groovy index ab39adc8d7..8278e86f5d 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/config/GateConfig.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/config/GateConfig.groovy @@ -138,6 +138,12 @@ class GateConfig { createClient "igor", IgorService, okHttpClient } + @Bean + @ConditionalOnProperty('services.mine.enabled') + MineService mineService(OkHttpClient okHttpClient) { + createClient "mine", MineService, okHttpClient + } + private T createClient(String serviceName, Class type, OkHttpClient okHttpClient) { Service service = serviceConfiguration.getService(serviceName) if (service == null) { diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/CanaryController.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/CanaryController.groovy new file mode 100644 index 0000000000..2b23c607f7 --- /dev/null +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/CanaryController.groovy @@ -0,0 +1,91 @@ +/* + * Copyright 2015 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.controllers + +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse +import com.netflix.spinnaker.gate.retrofit.UpstreamBadRequest +import com.netflix.spinnaker.gate.services.CanaryService +import groovy.transform.CompileStatic +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.http.HttpStatus +import org.springframework.web.bind.annotation.* + +@RestController +@CompileStatic +@ConditionalOnProperty('services.mine.enabled') +class CanaryController { + + @Autowired + CanaryService canaryService + + @RequestMapping(value = "/canaries/{id:.+}/generateCanaryResult", method = RequestMethod.POST) + @ResponseStatus(value = HttpStatus.CREATED) + void generateCanaryResult(@PathVariable("id") String id, + @RequestBody GenerateResultCommand generateResultCommand) { + canaryService.generateCanaryResult(id, generateResultCommand.duration, generateResultCommand.durationUnit) + } + + @RequestMapping(value = "/canaryDeployments/{canaryDeploymentId}/canaryAnalysisHistory", method = RequestMethod.GET) + List showCanaryAnalysisHistory(@PathVariable String canaryDeploymentId) { + canaryService.getCanaryAnalysisHistory(canaryDeploymentId) + } + + @RequestMapping(value = "/canaries/{id}", method = RequestMethod.GET) + Map showCanary(@PathVariable String id) { + canaryService.showCanary(id) + } + + @RequestMapping(value = "/canaries/{id:.+}/end", method = RequestMethod.PUT) + Map endCanary(@PathVariable String id, @RequestBody OverrideResultCommand command) { + canaryService.endCanary(id, command.result, command.reason) + } + + @RequestMapping(value = "/canaryConfig/names", method = RequestMethod.GET) + List getCanaryConfigNames() { + canaryService.getCanaryConfigNames(); + } + + @RequestMapping(value = "/canaryConfigs/{application}", method = RequestMethod.GET) + List canaryConfigsForApplication(@PathVariable String application) { + canaryService.canaryConfigsForApplication(application) + } + + static class GenerateResultCommand { + int duration + String durationUnit + } + + static class OverrideResultCommand { + String reason + String result + } + + @ExceptionHandler(UpstreamBadRequest.class) + Map upstreamBadRequestHandler( + HttpServletRequest request, + HttpServletResponse response, + UpstreamBadRequest error) { + response.setStatus(error.getStatus()) + [ + url : request.requestURI, + message : error.message, + upstreamUrl: error.url + ] + } +} diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CanaryService.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CanaryService.groovy new file mode 100644 index 0000000000..7eeeaf30b1 --- /dev/null +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CanaryService.groovy @@ -0,0 +1,98 @@ +/* + * Copyright 2015 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.services + +import com.netflix.spinnaker.gate.services.commands.HystrixFactory +import com.netflix.spinnaker.gate.services.internal.MineService +import groovy.transform.CompileStatic +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.stereotype.Component +import retrofit.RetrofitError +import static com.netflix.spinnaker.gate.retrofit.UpstreamBadRequest.classifyError + +@Component +@CompileStatic +@ConditionalOnProperty('services.mine.enabled') +class CanaryService { + + private static final String HYSTRIX_GROUP = "canaries" + + @Autowired + MineService mineService + + void generateCanaryResult(String canaryId, int duration, String durationUnit) { + HystrixFactory.newVoidCommand(HYSTRIX_GROUP, "generateCanaryResult", { + try { + mineService?.generateCanaryResult(canaryId, duration, durationUnit, "") + } catch (RetrofitError error) { + throw classifyError(error) + } + }).execute() + } + + List getCanaryAnalysisHistory(String canaryDeploymentId) { + HystrixFactory.newListCommand(HYSTRIX_GROUP, "getCanaryAnalysisHistory", { + try { + mineService ? mineService.getCanaryAnalysisHistory(canaryDeploymentId) : [] + } catch (RetrofitError error) { + throw classifyError(error) + } + }).execute() + } + + Map endCanary(String canaryId, String result, String reason) { + HystrixFactory.newMapCommand(HYSTRIX_GROUP, "endCanary", { + try { + mineService ? mineService.endCanary(canaryId, result, reason, "") : [:] + } catch (RetrofitError error) { + throw classifyError(error) + } + }).execute() + } + + Map showCanary(String canaryId) { + HystrixFactory.newMapCommand(HYSTRIX_GROUP, "showCanary", { + try { + mineService ? mineService.showCanary(canaryId) : [:] + } catch (RetrofitError error) { + throw classifyError(error) + } + }).execute() + } + + List getCanaryConfigNames() { + HystrixFactory.newListCommand(HYSTRIX_GROUP, "getCanaryConfigNames", { + try { + mineService ? mineService.getCanaryConfigNames() : [] + } catch (RetrofitError error) { + throw classifyError(error) + } + }).execute() + } + + List canaryConfigsForApplication(String applicationName) { + HystrixFactory.newListCommand(HYSTRIX_GROUP, "canaryConfigsForApplication", { + try { + mineService ? mineService.canaryConfigsForApplication(applicationName) : [] + } catch (RetrofitError error) { + throw classifyError(error) + } + }).execute() + } + +} diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/internal/MineService.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/internal/MineService.groovy new file mode 100644 index 0000000000..556d349492 --- /dev/null +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/internal/MineService.groovy @@ -0,0 +1,59 @@ +/* + * Copyright 2015 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.services.internal +import retrofit.client.Response +import retrofit.http.Body +import retrofit.http.GET +import retrofit.http.POST +import retrofit.http.PUT +import retrofit.http.Path +import retrofit.http.Query + +public interface MineService { + + @GET("/canaries/{id}") + Map showCanary(@Path("id") String canaryId) + + @POST("/canaries/{id}/generateCanaryResult") + Response generateCanaryResult(@Path("id") String id, + @Query("duration") int duration, + @Query("durationUnit") String durationUnit, + @Body String ignored) + + @PUT("/canaries/{id}/overrideCanaryResult/{result}") + Map overrideCanaryResult(@Path("id") String canaryId, + @Path("result") String result, + @Query("reason") String reason, + @Body String ignored) + + @PUT("/canaries/{id}/end") + Map endCanary(@Path("id") String canaryId, + @Query("result") String result, + @Query("reason") String reason, + @Body String ignored) + + @GET("/canaryDeployments/{id}/canaryAnalysisHistory") + List getCanaryAnalysisHistory(@Path("id") String canaryDeploymentId) + + @GET("/canaryConfig/names") + List getCanaryConfigNames() + + + @GET("/canaryConfigs") + List canaryConfigsForApplication(@Query("application") String application) + +}