Skip to content

Commit

Permalink
feat(keel): send keel docker events if enabled (#535)
Browse files Browse the repository at this point in the history
  • Loading branch information
emjburns committed Oct 29, 2019
1 parent 1e3dd38 commit 37fe1ed
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 3 deletions.
1 change: 1 addition & 0 deletions igor-web/igor-web.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ dependencies {
implementation "com.squareup.okhttp:okhttp-apache"
implementation "com.squareup.okhttp3:okhttp-sse"
implementation "com.squareup.retrofit:converter-simplexml"
implementation "com.jakewharton.retrofit:retrofit1-okhttp3-client:1.1.0"

implementation "com.netflix.spinnaker.fiat:fiat-api:$fiatVersion"
implementation "com.netflix.spinnaker.fiat:fiat-core:$fiatVersion"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ class IgorConfigurationProperties {
final ServiceConfiguration clouddriver = new ServiceConfiguration()
@NestedConfigurationProperty
final ServiceConfiguration echo = new ServiceConfiguration()
@NestedConfigurationProperty
final ServiceConfiguration keel = new ServiceConfiguration()
}

@NestedConfigurationProperty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ import com.netflix.spinnaker.igor.docker.model.DockerRegistryAccounts
import com.netflix.spinnaker.igor.docker.service.TaggedImage
import com.netflix.spinnaker.igor.history.EchoService
import com.netflix.spinnaker.igor.history.model.DockerEvent
import com.netflix.spinnaker.igor.keel.KeelService
import com.netflix.spinnaker.igor.polling.CommonPollingMonitor
import com.netflix.spinnaker.igor.polling.DeltaItem
import com.netflix.spinnaker.igor.polling.LockService
import com.netflix.spinnaker.igor.polling.PollContext
import com.netflix.spinnaker.igor.polling.PollingDelta
import com.netflix.spinnaker.kork.artifacts.model.Artifact
import com.netflix.spinnaker.security.AuthenticatedRequest
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
Expand All @@ -48,6 +50,7 @@ class DockerMonitor extends CommonPollingMonitor<ImageDelta, DockerPollingDelta>
private final DockerRegistryCache cache
private final DockerRegistryAccounts dockerRegistryAccounts
private final Optional<EchoService> echoService
private final Optional<KeelService> keelService
private final Optional<DockerRegistryCacheV2KeysMigration> keysMigration
private final DockerRegistryProperties dockerRegistryProperties

Expand All @@ -59,6 +62,7 @@ class DockerMonitor extends CommonPollingMonitor<ImageDelta, DockerPollingDelta>
DockerRegistryCache cache,
DockerRegistryAccounts dockerRegistryAccounts,
Optional<EchoService> echoService,
Optional<KeelService> keelService,
Optional<DockerRegistryCacheV2KeysMigration> keysMigration,
DockerRegistryProperties dockerRegistryProperties) {
super(properties, registry, discoveryClient, lockService)
Expand All @@ -67,6 +71,7 @@ class DockerMonitor extends CommonPollingMonitor<ImageDelta, DockerPollingDelta>
this.echoService = echoService
this.keysMigration = keysMigration
this.dockerRegistryProperties = dockerRegistryProperties
this.keelService = keelService
}

@Override
Expand Down Expand Up @@ -187,6 +192,26 @@ class DockerMonitor extends CommonPollingMonitor<ImageDelta, DockerPollingDelta>
account: image.account,
), artifact: dockerArtifact))
}

if (keelService.isPresent()) {
String imageReference = image.repository + ":" + image.tag
Artifact artifact = Artifact.builder()
.type("DOCKER")
.customKind(false)
.name(image.repository)
.version(image.tag)
.location(image.account)
.reference(imageId)
.metadata([fullname: imageReference, registry: image.account, tag: image.tag],)
.provenance(image.registry)
.build()

Map artifactEvent = [
payload: [artifacts: [artifact], details: [:]],
eventName: "spinnaker_artifacts_docker"
]
AuthenticatedRequest.allowAnonymous { keelService.get().sendArtifactEvent(artifactEvent) }
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2019 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.igor.config;

import com.jakewharton.retrofit.Ok3Client;
import com.netflix.spinnaker.config.OkHttp3ClientConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class IgorRetrofitConfig {
@Bean
public Ok3Client ok3Client(OkHttp3ClientConfiguration okHttpClientConfig) {
return new Ok3Client(okHttpClientConfig.create().build());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2019 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.igor.config;

import com.jakewharton.retrofit.Ok3Client;
import com.netflix.spinnaker.igor.keel.KeelService;
import com.netflix.spinnaker.retrofit.Slf4jRetrofitLogger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import retrofit.Endpoint;
import retrofit.Endpoints;
import retrofit.RestAdapter;
import retrofit.converter.JacksonConverter;

@ConditionalOnProperty("services.keel.base-url")
@Configuration
public class KeelConfig {

@Bean
public RestAdapter.LogLevel retrofitLogLevel(
@Value("${retrofit.log-level:BASIC}") String retrofitLogLevel) {
return RestAdapter.LogLevel.valueOf(retrofitLogLevel);
}

@Bean
public Endpoint keelEndpoint(@Value("${services.keel.base-url}") String keelBaseUrl) {
return Endpoints.newFixedEndpoint(keelBaseUrl);
}

@Bean
public KeelService keelService(
Endpoint keelEndpoint, Ok3Client ok3Client, RestAdapter.LogLevel retrofitLogLevel) {
return new RestAdapter.Builder()
.setEndpoint(keelEndpoint)
.setConverter(new JacksonConverter())
.setClient(ok3Client)
.setLogLevel(retrofitLogLevel)
.setLog(new Slf4jRetrofitLogger(KeelService.class))
.build()
.create(KeelService.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2019 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.igor.keel;

import java.util.Map;
import retrofit.http.Body;
import retrofit.http.POST;

public interface KeelService {
/**
* Events should be sent with this format (inherited from echo events): [ payload: [artifacts:
* List<Artifact>, details: Map], eventName: String ]
*/
@POST("/artifacts/events")
Void sendArtifactEvent(@Body Map event);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.netflix.spinnaker.igor.docker.model.DockerRegistryAccounts
import com.netflix.spinnaker.igor.docker.service.TaggedImage
import com.netflix.spinnaker.igor.history.EchoService
import com.netflix.spinnaker.igor.history.model.DockerEvent
import com.netflix.spinnaker.igor.keel.KeelService
import com.netflix.spinnaker.igor.polling.LockService
import spock.lang.Specification
import spock.lang.Unroll
Expand All @@ -38,6 +39,7 @@ class DockerMonitorSpec extends Specification {
def dockerRegistryCache = Mock(DockerRegistryCache)
def dockerRegistryAccounts = Mock(DockerRegistryAccounts)
def echoService = Mock(EchoService)
def keelService = Mock(KeelService)
Optional<DockerRegistryCacheV2KeysMigration> keysMigration = Optional.empty()
def dockerRegistryProperties = new DockerRegistryProperties(enabled: true, itemUpperThreshold: 5)

Expand All @@ -53,7 +55,7 @@ class DockerMonitorSpec extends Specification {
)

when:
new DockerMonitor(properties, registry, discoveryClient, lockService, dockerRegistryCache, dockerRegistryAccounts, Optional.of(echoService), Optional.empty(), dockerRegistryProperties)
new DockerMonitor(properties, registry, discoveryClient, lockService, dockerRegistryCache, dockerRegistryAccounts, Optional.of(echoService), Optional.of(keelService), Optional.empty(), dockerRegistryProperties)
.postEvent(cachedImages, taggedImage, "imageId")

then:
Expand Down Expand Up @@ -102,7 +104,13 @@ class DockerMonitorSpec extends Specification {
assert event.artifact.metadata.registry == taggedImage.registry
return true
})

1 * keelService.sendArtifactEvent({ Map event ->
def artifacts = event.payload.artifacts
assert artifacts.size() == 1
assert artifacts[0].name == "repository"
assert artifacts[0].type == "DOCKER"
return true
})
}

@Unroll
Expand Down Expand Up @@ -164,7 +172,7 @@ class DockerMonitorSpec extends Specification {
}

private DockerMonitor createSubject() {
return new DockerMonitor(properties, registry, discoveryClient, lockService, dockerRegistryCache, dockerRegistryAccounts, Optional.of(echoService), keysMigration, dockerRegistryProperties)
return new DockerMonitor(properties, registry, discoveryClient, lockService, dockerRegistryCache, dockerRegistryAccounts, Optional.of(echoService), Optional.of(keelService), keysMigration, dockerRegistryProperties)
}

private static String keyFromTaggedImage(TaggedImage taggedImage) {
Expand Down

0 comments on commit 37fe1ed

Please sign in to comment.