Skip to content

Commit

Permalink
feat(kayenta): Adopt Kayenta code for ongoing development
Browse files Browse the repository at this point in the history
I've updated the existing Kayenta code to use strongly-typed classes representing stage configuration / context.
Given that we don't want to be adding new Groovy code I've also transcribed everything.

There are some optimizations around shared utility and test code dragged along for the ride here.
  • Loading branch information
robfletcher committed Feb 27, 2018
1 parent a5da0ae commit 7e1f0cb
Show file tree
Hide file tree
Showing 98 changed files with 1,725 additions and 2,071 deletions.
2 changes: 1 addition & 1 deletion orca-applications/orca-applications.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ dependencies {
compile project(":orca-clouddriver")
compile project(":orca-front50")
compile project(":orca-retrofit")
testCompile project(":orca-test")
testCompile project(":orca-test-groovy")
}
3 changes: 2 additions & 1 deletion orca-bakery/orca-bakery.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ dependencies {
compile project(":orca-retrofit")
spinnaker.group('jackson')
compile spinnaker.dependency('jacksonGuava')
testCompile project(":orca-test")
testCompile project(":orca-test-groovy")
testCompile "com.github.tomakehurst:wiremock:2.15.0"
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,30 @@

package com.netflix.spinnaker.orca.bakery.api

import com.github.tomakehurst.wiremock.junit.WireMockRule
import com.netflix.spinnaker.orca.bakery.config.BakeryConfiguration
import com.netflix.spinnaker.orca.test.httpserver.HttpServerRule
import com.netflix.spinnaker.orca.jackson.OrcaObjectMapper
import org.junit.Rule
import retrofit.RetrofitError
import retrofit.client.OkClient
import spock.lang.Specification
import spock.lang.Subject
import static com.github.tomakehurst.wiremock.client.WireMock.*
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig
import static com.google.common.net.HttpHeaders.LOCATION
import static java.net.HttpURLConnection.*
import static retrofit.Endpoints.newFixedEndpoint
import static retrofit.RestAdapter.LogLevel.FULL

class BakeryServiceSpec extends Specification {

@Rule HttpServerRule httpServer = new HttpServerRule()
@Rule
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().dynamicPort())

@Subject BakeryService bakery

final region = "us-west-1"
final bake = BakeRequest.Default.copyWith(user:"rfletcher", packageName:"orca")
final bake = BakeRequest.Default.copyWith(user: "rfletcher", packageName: "orca")
final bakePath = "/api/v1/$region/bake"
final statusPath = "/api/v1/$region/status"
final bakeId = "b-123456789"
Expand All @@ -44,30 +48,39 @@ class BakeryServiceSpec extends Specification {
String bakeURI
String statusURI

def mapper = OrcaObjectMapper.newInstance()

def setup() {
bakeURI = "$httpServer.baseURI$bakePath"
statusURI = "$httpServer.baseURI$statusPath"
bakeURI = wireMockRule.url(bakePath)
statusURI = wireMockRule.url(statusPath)

bakery = new BakeryConfiguration(retrofitClient: new OkClient(), retrofitLogLevel: FULL)
.bakery(newFixedEndpoint(httpServer.baseURI))
.bakery(newFixedEndpoint(wireMockRule.url("/")))
}

def "can lookup a bake status"() {
given:
httpServer.expect("GET", "$statusPath/$statusId").andRespond().withStatus(HTTP_OK).withJsonContent {
state "COMPLETED"
progress 100
status "SUCCESS"
code 0
resource_id bakeId
resource_uri "$bakeURI/$bakeId"
uri "$statusURI/$statusId"
id statusId
attempts: 0
ctime 1382310109766
mtime 1382310294223
messages(["amination success"])
}
stubFor(
get("$statusPath/$statusId")
.willReturn(
aResponse()
.withStatus(HTTP_OK)
.withBody(mapper.writeValueAsString([
state : "COMPLETED",
progress : 100,
status : "SUCCESS",
code : 0,
resource_id : bakeId,
resource_uri: "$bakeURI/$bakeId",
uri : "$statusURI/$statusId",
id : statusId,
attempts : 0,
ctime : 1382310109766,
mtime : 1382310294223,
messages : ["amination success"]
]))
)
)

expect:
with(bakery.lookupStatus(region, statusId).toBlocking().first()) {
Expand All @@ -79,7 +92,13 @@ class BakeryServiceSpec extends Specification {

def "looking up an unknown status id will throw an exception"() {
given:
httpServer.expect("GET", "$statusPath/$statusId").andRespond().withStatus(HTTP_NOT_FOUND)
stubFor(
get("$statusPath/$statusId")
.willReturn(
aResponse()
.withStatus(HTTP_NOT_FOUND)
)
)

when:
bakery.lookupStatus(region, statusId).toBlocking().first()
Expand All @@ -91,18 +110,25 @@ class BakeryServiceSpec extends Specification {

def "should return status of newly created bake"() {
given: "the bakery accepts a new bake"
httpServer.expect("POST", bakePath).andRespond().withStatus(HTTP_ACCEPTED).withJsonContent {
state "PENDING"
progress 0
resource_id bakeId
resource_uri "$bakeURI/$bakeId"
uri "$statusURI/$statusId"
id statusId
attempts 0
ctime 1382310109766
mtime 1382310109766
messages([])
}
stubFor(
post(bakePath)
.willReturn(
aResponse()
.withStatus(HTTP_ACCEPTED)
.withBody(mapper.writeValueAsString([
state : "PENDING",
progress : 0,
resource_id : bakeId,
resource_uri: "$bakeURI/$bakeId",
uri : "$statusURI/$statusId",
id : statusId,
attempts : 0,
ctime : 1382310109766,
mtime : 1382310109766,
messages : []
]))
)
)

expect: "createBake should return the status of the bake"
with(bakery.createBake(region, bake, null).toBlocking().first()) {
Expand All @@ -114,38 +140,61 @@ class BakeryServiceSpec extends Specification {

def "should handle a repeat create bake response"() {
given: "the POST to /bake redirects to the status of an existing bake"
httpServer.expect("POST", bakePath).andRespond().withStatus(HTTP_SEE_OTHER).withHeader(LOCATION, "$statusURI/$statusId")
httpServer.expect("GET", "$statusPath/$statusId").andRespond().withStatus(HTTP_OK).withJsonContent {
state "RUNNING"
progress 1
resource_id bakeId
resource_uri "$bakeURI/$bakeId"
uri "$statusURI/$statusId"
id statusId
attempts 1
ctime 1382310109766
mtime 1382310109766
messages(["on instance i-66f5913d runnning: aminate ..."])
}
stubFor(
post(bakePath)
.willReturn(
aResponse()
.withStatus(HTTP_SEE_OTHER)
.withHeader(LOCATION, "$statusURI/$statusId")
)
)
stubFor(
get("$statusPath/$statusId")
.willReturn(
aResponse()
.withStatus(HTTP_OK)
.withBody(mapper.writeValueAsString([
state : "RUNNING",
progress : 1,
resource_id : bakeId,
resource_uri: "$bakeURI/$bakeId",
uri : "$statusURI/$statusId",
id : statusId,
attempts : 1,
ctime : 1382310109766,
mtime : 1382310109766,
messages : ["on instance i-66f5913d runnning: aminate ..."]
])
)
)
)

expect: "createBake should return the status of the bake"
with(bakery.createBake(region, bake, null).toBlocking().first()) {
id == statusId
state == BakeStatus.State.RUNNING
resourceId == bakeId // TODO: would we actually get a bake id if it was incomplete?
resourceId == bakeId
// TODO: would we actually get a bake id if it was incomplete?
}
}

def "can lookup the details of a bake"() {
given:
httpServer.expect("GET", "$bakePath/$bakeId").andRespond().withStatus(HTTP_OK).withJsonContent {
ami "ami"
base_ami "base_ami"
ami_suffix "ami_suffix"
base_name "base_name"
ami_name "ami_name"
id bakeId
}
stubFor(
get("$bakePath/$bakeId")
.willReturn(
aResponse()
.withStatus(HTTP_OK)
.withBody(mapper.writeValueAsString([
ami : "ami",
base_ami : "base_ami",
ami_suffix: "ami_suffix",
base_name : "base_name",
ami_name : "ami_name",
id : bakeId
]))
)
)

expect:
with(bakery.lookupBake(region, bakeId).toBlocking().first()) {
Expand Down
2 changes: 2 additions & 0 deletions orca-clouddriver/orca-clouddriver.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ dependencies {
compile project(":orca-bakery")
compile 'com.netflix.spinnaker.moniker:moniker:0.2.0'
testCompile project(":orca-test")
testCompile project(":orca-test-groovy")
testCompile "com.github.tomakehurst:wiremock:2.15.0"
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,25 @@

package com.netflix.spinnaker.orca.clouddriver

import com.github.tomakehurst.wiremock.junit.WireMockRule
import com.netflix.spinnaker.orca.clouddriver.config.CloudDriverConfiguration
import com.netflix.spinnaker.orca.clouddriver.config.CloudDriverConfigurationProperties
import com.netflix.spinnaker.orca.jackson.OrcaObjectMapper
import com.netflix.spinnaker.orca.test.httpserver.HttpServerRule
import org.junit.Rule
import retrofit.RequestInterceptor
import retrofit.client.OkClient
import spock.lang.Specification
import spock.lang.Subject
import static com.github.tomakehurst.wiremock.client.WireMock.*
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig
import static java.net.HttpURLConnection.HTTP_ACCEPTED
import static java.net.HttpURLConnection.HTTP_OK
import static retrofit.RestAdapter.LogLevel.FULL

class KatoRestServiceSpec extends Specification {

@Rule
HttpServerRule httpServer = new HttpServerRule()
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().dynamicPort())

@Subject
KatoRestService service
Expand All @@ -47,45 +49,65 @@ class KatoRestServiceSpec extends Specification {
}
}

def mapper = OrcaObjectMapper.newInstance()

final taskId = "e1jbn3"

def setup() {
def cfg = new CloudDriverConfiguration()
def builder = cfg.clouddriverRetrofitBuilder(
OrcaObjectMapper.newInstance(),
new OkClient(),
FULL,
noopInterceptor,
new CloudDriverConfigurationProperties(clouddriver: new CloudDriverConfigurationProperties.CloudDriver(baseUrl: httpServer.baseURI)))
mapper,
new OkClient(),
FULL,
noopInterceptor,
new CloudDriverConfigurationProperties(clouddriver: new CloudDriverConfigurationProperties.CloudDriver(baseUrl: wireMockRule.url("/"))))
service = cfg.katoDeployService(builder)
taskStatusService = cfg.cloudDriverTaskStatusService(builder)
}

def "can interpret the response from an operation request"() {
given: "kato accepts an operations request"
httpServer.expect("POST", "/ops").andRespond().withStatus(HTTP_ACCEPTED).withJsonContent {
id taskId
resourceLink "/task/$taskId"
}
stubFor(
post("/ops?clientRequestId=$requestId")
.willReturn(
aResponse()
.withStatus(HTTP_ACCEPTED)
.withBody(mapper.writeValueAsString([
id : taskId,
resourceLink: "/task/$taskId"
])
)
)
)

and: "we request a deployment"
def operation = [:]

expect: "kato should return the details of the task it created"
with(service.requestOperations(UUID.randomUUID().toString(), [operation])) {
with(service.requestOperations(requestId, [operation])) {
it.id == taskId
}

where:
requestId = UUID.randomUUID().toString()
}

def "can interpret the response from a task lookup"() {
given:
httpServer.expect("GET", "/task/$taskId").andRespond().withStatus(HTTP_OK).withJsonContent {
id taskId
status {
completed true
failed true
}
}
stubFor(
get("/task/$taskId")
.willReturn(
aResponse()
.withStatus(HTTP_OK)
.withBody(mapper.writeValueAsString([
id : taskId,
status: [
completed: true,
failed : true
]
]))
)
)

expect:
with(taskStatusService.lookupTask(taskId)) {
Expand All @@ -94,5 +116,4 @@ class KatoRestServiceSpec extends Specification {
status.failed
}
}

}
2 changes: 2 additions & 0 deletions orca-core/orca-core.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ dependencies {
compile spinnaker.dependency("logstashEncoder")
compile "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:${spinnaker.version('jackson')}"
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${spinnaker.version('jackson')}"
compile "com.fasterxml.jackson.module:jackson-module-kotlin:${spinnaker.version('jackson')}"

compileOnly spinnaker.dependency("lombok")

testCompile project(":orca-test")
testCompile project(":orca-test-groovy")
testCompile spinnaker.dependency("korkJedisTest")
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import com.fasterxml.jackson.datatype.guava.GuavaModule;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.kotlin.KotlinModule;

import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
import static com.fasterxml.jackson.databind.DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS;
Expand All @@ -32,6 +34,7 @@ public static ObjectMapper newInstance() {
instance.registerModule(new Jdk8Module());
instance.registerModule(new GuavaModule());
instance.registerModule(new JavaTimeModule());
instance.registerModule(new KotlinModule());
instance.disable(READ_DATE_TIMESTAMPS_AS_NANOSECONDS);
instance.disable(WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS);
instance.disable(FAIL_ON_UNKNOWN_PROPERTIES);
Expand Down
Loading

0 comments on commit 7e1f0cb

Please sign in to comment.