-
Notifications
You must be signed in to change notification settings - Fork 28
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
Revision Tag Support #413
base: main
Are you sure you want to change the base?
Revision Tag Support #413
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #413 +/- ##
==========================================
+ Coverage 76.01% 78.77% +2.75%
==========================================
Files 36 38 +2
Lines 1864 2309 +445
==========================================
+ Hits 1417 1819 +402
- Misses 372 401 +29
- Partials 75 89 +14 ☔ View full report in Codecov by Sentry. |
1f1ceca
to
57a01bd
Compare
526451f
to
fb2e3ec
Compare
return false, fmt.Errorf("failed to list IstioRevisionTags: %w", err) | ||
} | ||
for _, tag := range revisionTagList.Items { | ||
if tag.Status.IstioRevision == rev.Name && tag.Status.GetCondition(v1alpha1.IstioRevisionTagConditionInUse).Status == metav1.ConditionTrue { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it correct to check whether the tag is in use? As a user, if I create a tag and point it to a revision, I don't want that revision to be deleted by the operator. It doesn't matter if there are no pods/namespaces referencing that tag.
Also, is it correct to check status.istioRevision
instead of spec.targetRef
? What if the spec was just updated to point to the revision, but the controller hasn't yet updated the status?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it correct to check whether the tag is in use? As a user, if I create a tag and point it to a revision, I don't want that revision to be deleted by the operator. It doesn't matter if there are no pods/namespaces referencing that tag.
I thought about that, too. Yeah, maybe referencing a revision in a tag should be treated just like a reference from a pod or namespace and prevent deletion. I can implement that.
Also, is it correct to check status.istioRevision instead of spec.targetRef? What if the spec was just updated to point to the revision, but the controller hasn't yet updated the status?
Well, the status shows what is actually referenced ie what the webhooks will inject. So I would consider that to take precedent over what is in the spec. Apart from that, it's not obvious from the spec which IstioRevision is actually referenced, eg if the targetRef points to an Istio resource. I think adding this logic here would mean the IstioRevision controller knowing too many technical details about IstioRevisionTag. But I'm happy to be convinced otherwise.
You also need to add |
// Kind is the kind of the target resource. | ||
// | ||
// +kubebuilder:validation:MinLength=1 | ||
// +kubebuilder:validation:MaxLength=253 | ||
Kind string `json:"kind"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: add enum
validation for Istio
and IstioRevision
?
// IstioRevision stores the name of the referenced IstioRevision | ||
IstioRevision string `json:"istioRevision"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you also need Istio
here in cases where an Istio
is referenced instead of an IstioRevision
? In that case maybe this should be ,omitempty
so you don't have a blank istioRevision
when you reference and Istio
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, actually the reasoning behind this field is exactly the fact that we could be referencing an Istio resource. The IstioRevision controller needs some easy way to know which IstioRevision is being referenced (even if technically it is referencing an Istio, it will always resolve to an IstioRevision), so instead of duplicating the resolution logic behind that, I'm just doing it at reconcile time in the IstioRevisionTag controller and write the result into this field
@@ -409,6 +417,18 @@ func (r *Reconciler) isRevisionReferencedByWorkloads(ctx context.Context, rev *v | |||
log := logf.FromContext(ctx) | |||
nsList := corev1.NamespaceList{} | |||
nsMap := map[string]corev1.Namespace{} | |||
// if we're referenced by an in-use revisionTag, we're done. we can outsource the checking to the IstioRevisionTagController |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the revision tag controller also checking pods? What if you have created a tag but are still using the rev label and not the tag name to label your namespace?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only thing the tag adds, if it exists, is an additional condition for the namespace. Now it could be either istio.io/rev: <rev>
or istio.io/rev: <tag>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually... I was previously checking the pods but that doesn't make sense, as their annotation will always have the actual revision name, not the tag. so we don't need to check pods in the revisiontag controller
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay I removed the annotation checking, but I'm now checking the label to identify injection intent
podList := corev1.PodList{} | ||
if err := r.Client.List(ctx, &podList); err != nil { // TODO: can we optimize this by specifying a label selector | ||
return false, fmt.Errorf("failed to list pods: %w", err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this controller should be checking for pods at all or if it can defer to the istiorevision
controller for since that controller is already checking for the pod annotation that is independent of tag? Basically if this controller's istiorev ref is in use then this tag should be considered in use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Main question I have is this ^^. Rest is basically nits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm. yes that is an optimization, at least for the pods. for the namespaces we'd still need to check.
actually no it doesn't work. I just made a change that means that now, any tag referencing a revision will mean that revision is considered InUse. We'd have to read from the status whether it was specifically a pod/ns/tag that triggered the status.
da8d69e
to
28be30b
Compare
Okay I refactored (and simplified) the InUse detection quite a bit and I'm going to outline it here (will also update the design doc accordingly): An IstioRevisionTag is considered InUse when
Note that a pod's Even if the referenced IstioRevision of an IstioRevisionTag is considered InUse, that does not suffice to make the IstioRevisionTag considered InUse by the operator! It is considered an unused alias for an InUse IstioRevision. An IstioRevision is considered InUse when (new parts in bold)
Note specifically the fourth item here: being referenced by an IstioRevisionTag will now always lead to being considered InUse! It does not matter if the IstioRevisionTag is itself considered InUse! I think this is the cleanest approach with the least amount of circular dependencies. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I first create the sample this is what I get:
kubectl get istios
NAME REVISIONS READY IN USE ACTIVE REVISION STATUS VERSION AGE
default 1 1 1 default-v1-23-2 Healthy v1.23.2 113s
kubectl get istiorevisions.sailoperator.io
NAME TYPE READY STATUS IN USE VERSION AGE
default-v1-23-2 Local True Healthy True v1.23.2 2m5s
kubectl get istiorevisiontags.sailoperator.io
NAME STATUS IN USE REVISION AGE
default NotReferencedByAnything False default-v1-23-2 114s
Notice that the Istio
and IstioRevision
are both IN USE = true
but the tag is not? That rightfully changes when I deploy a sample workload that uses the tag but the istio/rev both seem to have a different definition of IN USE
than the tag does.
edit: Re-reading the above comment outlining when each resource is considered IN USE
, this is actually the expected behavior and I think makes sense.
edit2: I wonder if it would help to have short human readable message for the istiorevtag printer columns like "Istio Tag is not referenced by a pod or a namespace." to make it more clear why the tag is considered not IN USE
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just nits but otherwise LGTM. Everything works well and is nice to use.
IstioInjectionLabel = "istio-injection" | ||
IstioInjectionEnabledValue = "enabled" | ||
IstioRevLabel = "istio.io/rev" | ||
IstioSidecarInjectLabel = "sidecar.istio.io/inject" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably move these to some common constants package as we have similar constants in istiorevistion_controller.go file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch. done!
if revision != "" { | ||
return revision | ||
} | ||
// TODO: if .Values.sidecarInjectorWebhook.enableNamespacesByDefault is true, then all namespaces except system namespaces should use the "default" revision |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this TODO here as we are checking for the enableNamespacesByDefault in Line 396?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah- this function is also called when the namespace watch triggers, so enableNamespacesByDefault is not fully supported yet (just like in our other controllers I'm afraid)
@dgn, does this PR implement the following from the SEP?
|
updateStrategy: | ||
type: RevisionBased | ||
--- | ||
apiVersion: sailoperator.io/v1alpha1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dgn JFYI, I am seeing the following reconcile error (which is probably expected for the first time until IstioRevision is created - Can we mask it some how?) in the Sail Operator logs when applying this yaml.
sail-operator 2024-10-29T12:40:01Z ERROR ctrlr.revtag Reconciler error {"IstioRevisionTag": "default", "reconcileID": "1ff96be2-388e-41e6-a437-fed29156cc84", "error": "failed to retrieve IstioRevision for IstioRevisionTag \"default\": referenced Istio has no active revision"}
sail-operator sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler
sail-operator sigs.k8s.io/[email protected]/pkg/internal/controller/controller.go:316
sail-operator sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem
sail-operator sigs.k8s.io/[email protected]/pkg/internal/controller/controller.go:263
sail-operator sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2.2
sail-operator sigs.k8s.io/[email protected]/pkg/internal/controller/controller.go:224
sail-operator 2024-10-29T12:40:01Z INFO ctrlr.istio Reconciling {"Istio": "default", "reconcileID": "0580456b-b418-4041-a6f8-cd15d7d8d6a3"}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that is a problem. I'll check if I can make it look less like an error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be fixed now!
bd5db41
to
869dd43
Compare
3a2ec18
to
a9b3523
Compare
0b6b7b6
to
85879f2
Compare
As mentioned in Slack, that part will be left out (I moved it to Alternatives Considered) and might be picked up again as part of #439. |
b4432ca
to
47bb472
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 comment but non-blocking. LGTM
return enqueuelogger.WrapIfNecessary(v1alpha1.IstioRevisionTagKind, logger, handler) | ||
} | ||
|
||
func UnpackReconcilerError(err error) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is basically what Unwrap does?
Do you think we should cover the use of the revision tag also in the e2e test? By adding some test cases related to revision based update |
I also added a bunch of integration tests. Documentation is still to come. There are more scenarios that might need testing, we'll need to figure that out as we go. Signed-off-by: Daniel Grimm <[email protected]>
// IstioRevisionTagSpec defines the desired state of IstioRevisionTag | ||
type IstioRevisionTagSpec struct { | ||
// +kubebuilder:validation:Required | ||
TargetRef IstioRevisionTagTargetReference `json:"targetRef"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If an IstioRevisionTag
can reference an Istio
, doesn't that make the name "revision tag" confusing? Should it be called IstioTag
?
Also, why exactly do we need the option to reference Istio
? How do the two reference options fit into the fully-automatic updates? The plan was for the operator to automatically change the istio.io/rev
label from the old to the new revision, so it should probably also change the targetRef
in IstioRevisionTag
. If we decide against the latter, then we should also not implement the former, but then the automatic update wouldn't actually do anything.
We should discuss this and go over all possible scenarios.
Fixes #380