Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(artifact): introduce SKIPPED status, more data for previous #959

Merged
merged 5 commits into from
Apr 6, 2020

Conversation

emjburns
Copy link
Contributor

@emjburns emjburns commented Apr 2, 2020

Recently we were talking about artifact statuses. We have an APPROVED status which means "this artifact is ready to be deployed". But, we sometimes have the situation where an artifact is approved for an environment but never deployed to that environment. This is probably because the artifact was superseded by another version of an artifact (both were ready to deploy, but we deployed the latest one).

We also have a PENDING status for artifacts to represent that something might be going on in the future, but we have no idea what. That is calculated like this: we look at all versions we know about, and versions that we have data for in an environment (like APPROVED/CURRENT etc). The versions that we know about but are not in the environment are marked PENDING. This is weird, because some versions are never going to happen (for example: two artifacts are waiting on manual judgment. You approve the latest one. We ignore that other version for ever and ever).

This PR introduces a SKIPPED status which will get set when an artifact is marked as CURRENT in an environment. When an artifact is marked as deployed, we

  • set the existing CURRENT artifact to PREVIOUS (this pr also updates the promotion_reference column to contain what artifact replaced it)
  • set any versions that are lower than the existing version and have a status of APPROVED to SKIPPED (for example: 1.1.1, 1.2.1, and 1.3.1 are all approved. 1.2.1 is marked as CURRENT. 1.1.1 would be marked as SKIPPED. 1.3.1 would be untouched)

This will make our existing data a bit wonkey (i.e. every place we now have APPROVED but that's out of date and confusing). It should be straightforward going forward.

This SKIPPED status is also used for pending judgments. That works like this:

  • we calculate PENDING in the same way as described above
  • we mark any versions that are < the current version in the environment as SKIPPED

After this PR is merged, we should have a better view of artifacts that will never be deployed.

@asher
Copy link
Contributor

asher commented Apr 2, 2020

promotion_reference has a special meaning currently, see i.e. https://github.com/spinnaker/keel/blob/master/keel-sql/src/main/kotlin/com/netflix/spinnaker/keel/sql/SqlArtifactRepository.kt#L488-L494. Repurposing it this way, without rewriting the existing use will probably break automated artifact version vetoing and rollbacks.


context("there are two approved versions for the environment and the latter was deployed") {
before {
clock.incrementBy(Duration.ofHours(1))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you think it's easier to read, you can now do clock.tickHours(1).

@@ -1,5 +1,32 @@
package com.netflix.spinnaker.keel.core.api

enum class PromotionStatus {
PENDING, APPROVED, DEPLOYING, CURRENT, PREVIOUS, VETOED
/**
* Waiting on constraint evaluation before being approved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for documenting the enum values! ❤️

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1!

@erikmunson
Copy link
Member

😍 fantastic!

Just for my own clarification: does this also modify the behavior of pending in a similar way to approved? Don't know enough about the code to tell fo sure from reading the diff. I know we'd talked about both and couldn't tell if you were thinking about each state separately or also changing that here.

.where(ENVIRONMENT_ARTIFACT_VERSIONS.ENVIRONMENT_UID.eq(environmentUid))
.and(ENVIRONMENT_ARTIFACT_VERSIONS.ARTIFACT_UID.eq(artifact.uid))
.and(ENVIRONMENT_ARTIFACT_VERSIONS.PROMOTION_STATUS.eq(APPROVED.name))
.and(ENVIRONMENT_ARTIFACT_VERSIONS.ARTIFACT_VERSION.`in`(*approvedButOld.toTypedArray()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does the star in *approvedButOld? does? just wondering since I haven't seen this pattern here before

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It takes a list of strings and turns it into whatever form sql needs to actually be able to tell whether a string is in a list of strings :D

@emjburns
Copy link
Contributor Author

emjburns commented Apr 3, 2020

Updated this PR to address my accidental misuse of the PROMOTION_REFERENCE field and deal with PENDING status.

fun isHigher(artifact: DeliveryArtifact, new: String, existing: String): Boolean =
artifact.versioningStrategy.comparator.compare(new, existing) < 0

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General question - we are relaying here on the fact that a newer version will get the higher number. is it always the case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! It is always the case that we are deciding which is the newer version by this comparator, and "higher" version will always be newer. It's one of our core artifact assumptions. It's definitely an assumption, but it's what allows us to do cool artifact things.

@lorin
Copy link
Contributor

lorin commented Apr 3, 2020

The PR description says:

This PR introduces a SKIPPED status which will get set when an artifact is marked as DEPLOYED to an environment.

What does when an artifact is marked as DEPLOYED mean? At first, I thought it was about PromotionStatus, but there's no DEPLOYED enum value.

.and(ENVIRONMENT_ARTIFACT_VERSIONS.ARTIFACT_VERSION.ne(version))
.execute()
// update any past artifacts that were "APPROVED" to be "SKIPPED
// // because the new version takes precedence
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// // because the new version takes precedence
// because the new version takes precedence

/**
* Marks a version of an artifact as skipped for an environment, with information on what version skipped it.
*/
fun markAsSkipped(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is currently only used in tests. Do you anticipate it being used elsewhere? If not, should probably move it to the test classes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I anticipate that it might, which is why I kept it here.

Copy link
Contributor

@lorin lorin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Some of the comments have been obsoleted by recent changes)

.and(ENVIRONMENT_ARTIFACT_VERSIONS.PROMOTION_STATUS.eq(CURRENT.name))
.and(ENVIRONMENT_ARTIFACT_VERSIONS.ARTIFACT_VERSION.ne(version))
.execute()
jooq.transaction { config ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've noticed that sometimes we use @Transactional annotations and sometimes we call jooq.transaction. Is there some guidance on when to do which?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Transactional is something that spring does (we use it on the api layer sometimes), and the jooq one is for when you're actually in the sql repository and can control every part of the transaction

@@ -594,6 +617,32 @@ class SqlArtifactRepository(
}
}

override fun markAsSkipped(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this called in application logic yet, or just in tests? I didn't see any reference in application code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's just in tests (there's a comment on the interface method for that) I wanted to easily call it in the tests with a separate impl in each repository

emjburns and others added 2 commits April 4, 2020 13:48
Copy link
Contributor

@gal-yardeni gal-yardeni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the comments! lgtm :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants