Skip to content

Commit

Permalink
feat(plugins): Introduce KeelServiceSdk for external plugins (#1416)
Browse files Browse the repository at this point in the history
* fix(resources): Fix discovery of supported resource kinds

* feat(plugins): Introduce KeelServiceSdk for plugins

* fix(pr): Add KeelReadOnlyRepository for plugin usage
  • Loading branch information
luispollo committed Aug 8, 2020
1 parent 00ad0e5 commit 9260cb5
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.netflix.spinnaker.keel.api.persistence

import com.netflix.spinnaker.keel.api.DeliveryConfig
import com.netflix.spinnaker.keel.api.Environment
import com.netflix.spinnaker.keel.api.Resource
import com.netflix.spinnaker.keel.api.ResourceSpec
import com.netflix.spinnaker.keel.api.artifacts.ArtifactType
import com.netflix.spinnaker.keel.api.artifacts.DeliveryArtifact
import com.netflix.spinnaker.keel.api.constraints.ConstraintState

/**
* A read-only repository for interacting with delivery configs, artifacts, and resources.
*/
interface KeelReadOnlyRepository {
fun getDeliveryConfig(name: String): DeliveryConfig

fun environmentFor(resourceId: String): Environment

fun deliveryConfigFor(resourceId: String): DeliveryConfig

fun getDeliveryConfigForApplication(application: String): DeliveryConfig

fun getConstraintState(deliveryConfigName: String, environmentName: String, artifactVersion: String, type: String, artifactReference: String?): ConstraintState?

fun constraintStateFor(application: String): List<ConstraintState>

fun constraintStateFor(deliveryConfigName: String, environmentName: String, limit: Int): List<ConstraintState>

fun constraintStateFor(deliveryConfigName: String, environmentName: String, artifactVersion: String): List<ConstraintState>

fun pendingConstraintVersionsFor(deliveryConfigName: String, environmentName: String): List<String>

fun getQueuedConstraintApprovals(deliveryConfigName: String, environmentName: String, artifactReference: String?): Set<String>

fun getResource(id: String): Resource<ResourceSpec>

fun hasManagedResources(application: String): Boolean

fun getResourceIdsByApplication(application: String): List<String>

fun getResourcesByApplication(application: String): List<Resource<*>>

fun getArtifact(name: String, type: ArtifactType, deliveryConfigName: String): List<DeliveryArtifact>

fun getArtifact(name: String, type: ArtifactType, reference: String, deliveryConfigName: String): DeliveryArtifact

fun getArtifact(deliveryConfigName: String, reference: String): DeliveryArtifact

fun isRegistered(name: String, type: ArtifactType): Boolean

fun artifactVersions(artifact: DeliveryArtifact): List<String>

fun artifactVersions(name: String, type: ArtifactType): List<String>

fun latestVersionApprovedIn(deliveryConfig: DeliveryConfig, artifact: DeliveryArtifact, targetEnvironment: String): String?

fun isApprovedFor(deliveryConfig: DeliveryConfig, artifact: DeliveryArtifact, version: String, targetEnvironment: String): Boolean

fun wasSuccessfullyDeployedTo(deliveryConfig: DeliveryConfig, artifact: DeliveryArtifact, version: String, targetEnvironment: String): Boolean

fun isCurrentlyDeployedTo(deliveryConfig: DeliveryConfig, artifact: DeliveryArtifact, version: String, targetEnvironment: String): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.netflix.spinnaker.keel.api.plugins

import com.netflix.spinnaker.keel.api.actuation.TaskLauncher
import com.netflix.spinnaker.keel.api.persistence.KeelReadOnlyRepository

/**
* A simple SDK that can be consumed by external plugins to access core Keel functionality.
*/
interface KeelServiceSdk {
val repository: KeelReadOnlyRepository
val taskLauncher: TaskLauncher
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.netflix.spinnaker.keel.persistence

import com.netflix.spinnaker.keel.api.DeliveryConfig
import com.netflix.spinnaker.keel.api.Environment
import com.netflix.spinnaker.keel.api.Resource
import com.netflix.spinnaker.keel.api.ResourceSpec
import com.netflix.spinnaker.keel.api.artifacts.ArtifactStatus
import com.netflix.spinnaker.keel.api.artifacts.ArtifactType
import com.netflix.spinnaker.keel.api.artifacts.DeliveryArtifact
import com.netflix.spinnaker.keel.api.constraints.ConstraintState
import com.netflix.spinnaker.keel.api.id
import com.netflix.spinnaker.keel.api.persistence.KeelReadOnlyRepository
import com.netflix.spinnaker.keel.core.api.ApplicationSummary
import com.netflix.spinnaker.keel.core.api.ArtifactSummaryInEnvironment
import com.netflix.spinnaker.keel.core.api.EnvironmentArtifactPin
Expand Down Expand Up @@ -37,7 +37,7 @@ import org.springframework.transaction.annotation.Transactional
/**
* A repository for interacting with delivery configs, artifacts, and resources.
*/
interface KeelRepository {
interface KeelRepository : KeelReadOnlyRepository {
val clock: Clock
val publisher: ApplicationEventPublisher
val log: Logger
Expand Down Expand Up @@ -94,36 +94,16 @@ interface KeelRepository {
// START Delivery config methods
fun storeDeliveryConfig(deliveryConfig: DeliveryConfig)

fun getDeliveryConfig(name: String): DeliveryConfig

fun environmentFor(resourceId: String): Environment

fun deliveryConfigFor(resourceId: String): DeliveryConfig

fun getDeliveryConfigForApplication(application: String): DeliveryConfig

fun deleteResourceFromEnv(deliveryConfigName: String, environmentName: String, resourceId: String)

fun deleteEnvironment(deliveryConfigName: String, environmentName: String)

fun storeConstraintState(state: ConstraintState)

fun getConstraintState(deliveryConfigName: String, environmentName: String, artifactVersion: String, type: String, artifactReference: String?): ConstraintState?

fun getConstraintStateById(uid: UID): ConstraintState?

fun deleteConstraintState(deliveryConfigName: String, environmentName: String, type: String)

fun constraintStateFor(application: String): List<ConstraintState>

fun constraintStateFor(deliveryConfigName: String, environmentName: String, limit: Int): List<ConstraintState>

fun constraintStateFor(deliveryConfigName: String, environmentName: String, artifactVersion: String): List<ConstraintState>

fun pendingConstraintVersionsFor(deliveryConfigName: String, environmentName: String): List<String>

fun getQueuedConstraintApprovals(deliveryConfigName: String, environmentName: String, artifactReference: String?): Set<String>

fun queueAllConstraintsApproved(deliveryConfigName: String, environmentName: String, artifactVersion: String, artifactReference: String?)

fun deleteQueuedConstraintApproval(deliveryConfigName: String, environmentName: String, artifactVersion: String, artifactReference: String?)
Expand All @@ -136,14 +116,6 @@ interface KeelRepository {
// START ResourceRepository methods
fun allResources(callback: (ResourceHeader) -> Unit)

fun getResource(id: String): Resource<ResourceSpec>

fun hasManagedResources(application: String): Boolean

fun getResourceIdsByApplication(application: String): List<String>

fun getResourcesByApplication(application: String): List<Resource<*>>

fun storeResource(resource: Resource<*>)

fun deleteResource(id: String)
Expand All @@ -168,14 +140,6 @@ interface KeelRepository {
// START ArtifactRepository methods
fun register(artifact: DeliveryArtifact)

fun getArtifact(name: String, type: ArtifactType, deliveryConfigName: String): List<DeliveryArtifact>

fun getArtifact(name: String, type: ArtifactType, reference: String, deliveryConfigName: String): DeliveryArtifact

fun getArtifact(deliveryConfigName: String, reference: String): DeliveryArtifact

fun isRegistered(name: String, type: ArtifactType): Boolean

fun getAllArtifacts(type: ArtifactType? = null): List<DeliveryArtifact>

fun storeArtifact(name: String, type: ArtifactType, version: String, status: ArtifactStatus?): Boolean
Expand All @@ -184,22 +148,10 @@ interface KeelRepository {

fun deleteArtifact(artifact: DeliveryArtifact)

fun artifactVersions(artifact: DeliveryArtifact): List<String>

fun artifactVersions(name: String, type: ArtifactType): List<String>

fun latestVersionApprovedIn(deliveryConfig: DeliveryConfig, artifact: DeliveryArtifact, targetEnvironment: String): String?

fun approveVersionFor(deliveryConfig: DeliveryConfig, artifact: DeliveryArtifact, version: String, targetEnvironment: String): Boolean

fun isApprovedFor(deliveryConfig: DeliveryConfig, artifact: DeliveryArtifact, version: String, targetEnvironment: String): Boolean

fun markAsDeployingTo(deliveryConfig: DeliveryConfig, artifact: DeliveryArtifact, version: String, targetEnvironment: String)

fun wasSuccessfullyDeployedTo(deliveryConfig: DeliveryConfig, artifact: DeliveryArtifact, version: String, targetEnvironment: String): Boolean

fun isCurrentlyDeployedTo(deliveryConfig: DeliveryConfig, artifact: DeliveryArtifact, version: String, targetEnvironment: String): Boolean

fun markAsSuccessfullyDeployedTo(deliveryConfig: DeliveryConfig, artifact: DeliveryArtifact, version: String, targetEnvironment: String)

fun getEnvironmentSummaries(deliveryConfig: DeliveryConfig): List<EnvironmentSummary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.netflix.spinnaker.config;

import com.netflix.spinnaker.keel.plugins.KeelServiceSdkFactory;
import com.netflix.spinnaker.kork.plugins.sdk.SdkFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
class KeelServiceSdkConfiguration {
@Bean
public static SdkFactory serviceSdkFactory(ApplicationContext applicationContext) {
return new KeelServiceSdkFactory(applicationContext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.netflix.spinnaker.keel.plugins

import com.netflix.spinnaker.keel.api.actuation.TaskLauncher
import com.netflix.spinnaker.keel.persistence.KeelRepository
import com.netflix.spinnaker.kork.exceptions.SystemException
import com.netflix.spinnaker.kork.plugins.sdk.SdkFactory
import org.pf4j.PluginWrapper
import org.slf4j.LoggerFactory
import org.springframework.context.ApplicationContext

/**
* Creates [KeelServiceSdk] objects that can be consumed by external plugins.
*/
class KeelServiceSdkFactory(
private val applicationContext: ApplicationContext
) : SdkFactory {

private val log by lazy { LoggerFactory.getLogger(javaClass) }

private val keelServiceSdk by lazy {
val repository = getFirstBeanOfType(KeelRepository::class.java)
val taskLauncher = getFirstBeanOfType(TaskLauncher::class.java)
KeelServiceSdkImpl(repository, taskLauncher)
}

override fun create(extensionClass: Class<*>, pluginWrapper: PluginWrapper?): Any =
keelServiceSdk

private inline fun <reified T> getFirstBeanOfType(clazz: Class<T>): T =
applicationContext.getBeansOfType(clazz)
.let {
if (it.isEmpty()) {
throw SystemException("Failed to locate bean of type ${T::class.java.name} in application context")
} else {
val first = it.entries.first()
if (it.size > 1) {
val options = it.keys.joinToString()
log.warn("Found more than one bean of type ${T::class.java.name} ($options), selecting '${first.key}'")
}
first.value
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.netflix.spinnaker.keel.plugins

import com.netflix.spinnaker.keel.api.actuation.TaskLauncher
import com.netflix.spinnaker.keel.api.persistence.KeelReadOnlyRepository
import com.netflix.spinnaker.keel.api.plugins.KeelServiceSdk

class KeelServiceSdkImpl(
override val repository: KeelReadOnlyRepository,
override val taskLauncher: TaskLauncher
) : KeelServiceSdk

0 comments on commit 9260cb5

Please sign in to comment.