Skip to content

CI-3567: Design Release CRD#413

Merged
goelozev merged 10 commits into
pre-release-5.1.0from
CI-3567-release-crds
Dec 8, 2025
Merged

CI-3567: Design Release CRD#413
goelozev merged 10 commits into
pre-release-5.1.0from
CI-3567-release-crds

Conversation

@goelozev

@goelozev goelozev commented Dec 1, 2025

Copy link
Copy Markdown
Member

The Release CR is responsible for storing the following configuration:

  • targetName - a unique name which identifies a release in a namespace. We intentionally don't use .metadata.name as we intend to generate it out of targetName and revisions
  • revisions - a list of revisions (git, image, chart), that form that release

A Release will have the form of:

apiVersion: deploy.gocardless.com/v1alpha1
kind: Release
metadata:
  name: connectivity-3a93868
config:
  targetName: connectivity
  revisions:
    anu:
      id: 7ec506e066721c0e1d6430d8ec1d6519964fa59b
      source: github.com/gocardless/anu.git
      metadata:
        author: platform-engineering
        branch: master
        message: "hello"
      type: git
    gcr:
      id: 5a638c1d1a69bf47f63eed430fa1ea26bc53e7c4
      source: gcr.io/gocardless/connectivity
      type: container_image
      metadata:
        tags:
          - v1.2.5

The Release CRD intentionally doesn't have a .spec path, as there is no desired state for a release; release resources are intended to be created after a release has occurred.

@goelozev
goelozev requested a review from a team December 1, 2025 16:49
Comment thread api/deploy/v1alpha1/release_types.go Outdated

// SupersededTime is the time when this release was superseded.
// +kubebuilder:validation:Optional
SupersededTime metav1.Time `json:"supersededTime,omitempty"`

This comment was marked as outdated.

Comment thread api/deploy/v1alpha1/release_types.go Outdated
Comment thread api/deploy/v1alpha1/release_types.go
Comment thread api/deploy/v1alpha1/release_types.go Outdated
@goelozev
goelozev marked this pull request as ready for review December 4, 2025 08:59
@goelozev
goelozev changed the base branch from master to pre-release-5.1.0 December 5, 2025 11:27
@goelozev goelozev changed the title [WIP] CI-3567: Design Release CRD CI-3567: Design Release CRD Dec 5, 2025

@IsaacJames IsaacJames left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looks good! Just one small question

Comment thread api/deploy/v1alpha1/release_types.go Outdated
@IsaacJames
IsaacJames self-requested a review December 8, 2025 13:53
@goelozev
goelozev merged commit 9ed8738 into pre-release-5.1.0 Dec 8, 2025
1 check passed
@goelozev
goelozev deleted the CI-3567-release-crds branch December 8, 2025 14:43

@0x0013 0x0013 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Even though this is already merged, I'm adding a couple of comments for considerations.

I think the history list could be made simpler. Arguably, the most important contents of a history entry, would be:

  • SupersededBy
  • SupersededFrom (see my comment on this. Name could be better - maybe Previous or PreviousRelease.)

both fields would include the time of transition.

Up for discussion in History:

  • Do we need the health status? I think we would only be interested in the last (or current) health status.
  • The phase can be implied easily - the release itself either is active or isn't, as indicated by a Condition. Any past historical activations are, by definition, inactive. This also means that we may not even need to store the current active release state, and only store it when the Release becomes inactive (superseded). In this case, we might want to store SupersededFrom in the main status of the Release.
  • ID of historical entry - arguable, see my comment. We might want to use it to refer to a specific entry in one of the other SupersededBy fields. But if not, then I'm curious of the purpose of the ID.

Of course, all of these proposals are still up for discussion.

Comment on lines +77 to +81
// Phase is the current phase of the release. Active indicates the release
// is currently live in the cluster, Inactive indicates the release is no
// longer the latest release.
// +kubebuilder:validation:Enum:=Active;Inactive
Phase Phase `json:"phase"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As mentioned in our discussion and Rollback PR, "phase" status field is considered deprecated, and should use conditions instead:

Some resources in the v1 API contain fields called phase, and associated message, reason, and other status fields. The pattern of using phase is deprecated. Newer API types should use conditions instead.

I do think we should refactor this to use Condition "Active" instead.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I agree, the Phase should simply become a condition[].type = "Active" with two possible ConditionStatus-es of True and False.

Comment on lines +98 to +105
// HealthStatus indicates whether the release is healthy or not, as determined by an external monitoring system.
// +kubebuilder:validation:Enum:=Healthy;Unhealthy;Unknown
// +kubebuilder:default:=Unknown
HealthStatus HealthStatus `json:"healthStatus,omitempty"`

// HealthStatusLastChecked is the last time the health status was checked by the external system.
// +kubebuilder:validation:Optional
HealthStatusLastChecked metav1.Time `json:"healthStatusLastChecked,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same as previous comment - I think it makes sense to use Condition "Healthy" to mark this.

I don't think we need to store the past health state in the history. In general, we will be interested in the latest health state for this Release, in order to determine whether it is a valid rollback candidate. However, there could be an argument made that historical health status might be useful, in case a deployment becomes unhealthy on subsequent evaluation, after already being healthy. However, this would most likely occur in the case of external impact, so I'm not sure if we want to cover that use case.

@goelozev goelozev Dec 9, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Again, agree that this can be and should be a Condition by itself.

However, there could be an argument made that historical health status might be useful, in case a deployment becomes unhealthy on subsequent evaluation, after already being healthy.

I think this could be useful if we run multiple health evaluations, which we aren't planning to do as of now. I would say that it wouldn't hurt to record the previous health statuses, just in case we decide we will need to reason on them for manual and automated rollbacks.

Comment thread api/deploy/v1alpha1/release_types.go
DeploymentEndTime metav1.Time `json:"deploymentEndTime,omitempty"`

// SupersededBy is the name of the release that superseded this release.
SupersededBy string `json:"supersededBy,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if it's worth conforming to the reference field guidelines (some good details in there), with something like:

supersededBy:
  releaseRef: foo
  time: <time>
  id: <id> # optional

I've added id field in the ref, if we want to target a specific ID from the history list - I was wondering what we would use that ID field for, or if it's needed, and this is the only use case that comes to mind. However, it should be sufficient to find it by time, as those would presumably be updated in both resources with the same value.

I'm also wondering whether we might want to also add something like supersededFrom, in order to make it easier to walk back from the currently active release, to the previous ones.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

supersededBy:
  releaseRef: foo
  time: <time>
  id: <id> # optional

Yes, this definitely makes it a bit cleaner. On your question about the id field - as of now, I don't see much value in it, as you already mentioned, the time field would be sufficient to identify the history entry, but having a simple id to identify them in the history might be a better UX.

On your second point about supersededFrom - isn't that the release itself? Or I might have misunderstood!

@goelozev

Copy link
Copy Markdown
Member Author

@0x0013 I've opened #422 to add conditions. Let's continue the conversation over there!

0x0013 pushed a commit that referenced this pull request Dec 18, 2025
@goelozev goelozev mentioned this pull request Jan 19, 2026
0x0013 pushed a commit that referenced this pull request Jan 29, 2026
0x0013 pushed a commit that referenced this pull request Mar 13, 2026
0x0013 pushed a commit that referenced this pull request Mar 16, 2026
goelozev added a commit that referenced this pull request Apr 15, 2026
goelozev added a commit that referenced this pull request Apr 15, 2026
goelozev added a commit that referenced this pull request Apr 15, 2026
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.

3 participants