Skip to content

Commit

Permalink
refactor(core): Change valid IntentStatus values (#78)
Browse files Browse the repository at this point in the history
* refactor(status): Change around the IntentStatus values to prep for delete

* chore(intent): Scaffold delete support in intents
  • Loading branch information
robzienert committed Apr 4, 2018
1 parent ab63d64 commit 2123e85
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ interface IntentRepository {
fun getIntent(id: String): Intent<IntentSpec>?

/**
* Deletes an intent. [preserveHistory] should be an internal-only flag, for example only called by Janitor or
* other Spinnaker services; clients via gate should always have `preserveHistory=true` forced.
* Deletes an intent. If [preserveHistory] is true, the Intent will be updated
* to INACTIVE. If false, the record will be physically removed from the
* persistence store.
*
* Does not perform any action against the underlying resource.
*/
fun deleteIntent(id: String, preserveHistory: Boolean = true)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,27 @@ package com.netflix.spinnaker.keel

/**
* ACTIVE: An Intent is currently being enforced and will be regularly checked for any state to converge on.
* INACTIVE: An Intent that is not currently being enforced, but whose resource may still exist.
* ABSENT: An Intent whose underlying resource has been deleted, and its expected state is to remain deleted.
* ISOLATED_ACTIVE: An Intent that is meant to be applied once, but has not been yet.
* ISOLATED_APPLIED: An Intent that is meant to be applied once, and has been.
* DELETED: An Intent that has been soft-deleted.
* ISOLATED_INACTIVE: An Intent that is meant to be applied once, and has been.
* ISOLATED_ABSENT: An Intent whose underlying resource has been deleted, and its absence unenforced.
*/
enum class IntentStatus {
ACTIVE,
INACTIVE,
ABSENT,
ISOLATED_ACTIVE,
ISOLATED_APPLIED,
DELETED
ISOLATED_INACTIVE,
ISOLATED_ABSENT;

fun shouldSchedule() = scheduleValues().contains(this)
fun shouldIsolate() = isolateValues().contains(this)
fun shouldDeleteResource() = absentValues().contains(this)

companion object {
fun scheduleValues() = listOf(ACTIVE, ABSENT, ISOLATED_ACTIVE, ISOLATED_ABSENT)
fun isolateValues() = listOf(ISOLATED_ACTIVE, ISOLATED_INACTIVE, ISOLATED_ABSENT)
fun absentValues() = listOf(ABSENT, ISOLATED_ABSENT)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class MemoryIntentRepository(

override fun deleteIntent(id: String, preserveHistory: Boolean) {
if (preserveHistory) {
getIntent(id)?.status = IntentStatus.DELETED
getIntent(id)?.status = IntentStatus.ABSENT
} else {
intents.remove(id)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class Front50IntentRepository(
getIntent(id).also { intent ->
applicationEventPublisher.publishEvent(BeforeIntentDeleteEvent(intent))
if (preserveHistory) {
intent.status = IntentStatus.DELETED
intent.status = IntentStatus.INACTIVE
upsertIntent(intent)
} else {
front50Service.deleteIntent(id)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2018 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.keel.intent.processor

import com.netflix.spinnaker.keel.ConvergeResult
import com.netflix.spinnaker.keel.Intent
import com.netflix.spinnaker.keel.IntentProcessor
import com.netflix.spinnaker.keel.IntentSpec
import com.netflix.spinnaker.keel.intent.ApplicationIntent

class ApplicationDeleteIntentProcessor : IntentProcessor<ApplicationIntent> {
override fun supports(intent: Intent<IntentSpec>) =
intent is ApplicationIntent && intent.status.shouldDeleteResource()

override fun converge(intent: ApplicationIntent): ConvergeResult {
throw UnsupportedOperationException("not implemented")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,20 @@ import com.netflix.spinnaker.keel.state.FieldMutator
import com.netflix.spinnaker.keel.state.StateInspector
import com.netflix.spinnaker.keel.tracing.Trace
import com.netflix.spinnaker.keel.tracing.TraceRepository
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import retrofit.RetrofitError

@Component
class ApplicationIntentProcessor
class ApplicationUpsertIntentProcessor
@Autowired constructor(
private val traceRepository: TraceRepository,
private val front50Service: Front50Service,
private val objectMapper: ObjectMapper
): IntentProcessor<ApplicationIntent> {

private val log = LoggerFactory.getLogger(javaClass)

override fun supports(intent: Intent<IntentSpec>) = intent is ApplicationIntent
override fun supports(intent: Intent<IntentSpec>) =
intent is ApplicationIntent && !intent.status.shouldDeleteResource()

override fun converge(intent: ApplicationIntent): ConvergeResult {
val changeSummary = ChangeSummary(intent.id())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ import org.junit.jupiter.api.Test
import retrofit.RetrofitError.httpError
import retrofit.client.Response

object ApplicationIntentProcessorTest {
object ApplicationUpsertIntentProcessorTest {

val traceRepository = mock<TraceRepository>()
val front50Service = mock<Front50Service>()
val objectMapper = ObjectMapper()

val subject = ApplicationIntentProcessor(traceRepository, front50Service, objectMapper)
val subject = ApplicationUpsertIntentProcessor(traceRepository, front50Service, objectMapper)

@AfterEach
fun cleanup() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ class ConvergeIntentHandler
intentActivityRepository.addOrchestrations(intent.id(), result.orchestrationIds)
applicationEventPublisher.publishEvent(IntentConvergeSuccessEvent(intent, result.orchestrationIds))

if (intent.status == IntentStatus.ISOLATED_ACTIVE) {
intent.status = IntentStatus.ISOLATED_APPLIED
if (intent.status.shouldIsolate()) {
intent.status = IntentStatus.ISOLATED_INACTIVE
intentRepository.upsertIntent(intent)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ package com.netflix.spinnaker.keel.scheduler.handler
import com.netflix.spectator.api.BasicTag
import com.netflix.spectator.api.Registry
import com.netflix.spinnaker.keel.IntentRepository
import com.netflix.spinnaker.keel.IntentStatus.ACTIVE
import com.netflix.spinnaker.keel.IntentStatus.ISOLATED_ACTIVE
import com.netflix.spinnaker.keel.IntentStatus
import com.netflix.spinnaker.keel.event.BeforeIntentScheduleEvent
import com.netflix.spinnaker.keel.filter.Filter
import com.netflix.spinnaker.keel.scheduler.ScheduleConvergence
Expand Down Expand Up @@ -50,7 +49,7 @@ class ScheduleConvergeHandler
log.info("Scheduling intent convergence work")

try {
intentRepository.getIntents(status = listOf(ACTIVE, ISOLATED_ACTIVE))
intentRepository.getIntents(status = IntentStatus.scheduleValues())
.also { log.info("Attempting to schedule ${it.size} active intents") }
.filter { intent ->
applicationEventPublisher.publishEvent(BeforeIntentScheduleEvent(intent))
Expand Down

0 comments on commit 2123e85

Please sign in to comment.