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

Revision Tag Support #413

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Revision Tag Support #413

wants to merge 1 commit into from

Conversation

dgn
Copy link
Collaborator

@dgn dgn commented Oct 11, 2024

Fixes #380

Copy link

codecov bot commented Oct 18, 2024

Codecov Report

Attention: Patch coverage is 86.65208% with 61 lines in your changes missing coverage. Please review.

Project coverage is 78.77%. Comparing base (0fa4f24) to head (a9519cd).
Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
...rs/istiorevisiontag/istiorevisiontag_controller.go 86.21% 36 Missing and 15 partials ⚠️
cmd/main.go 0.00% 6 Missing ⚠️
...trollers/istiorevision/istiorevision_controller.go 94.23% 2 Missing and 1 partial ⚠️
pkg/validation/namespace.go 87.50% 1 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

@dgn dgn force-pushed the revision-tags branch 3 times, most recently from 1f1ceca to 57a01bd Compare October 18, 2024 13:50
@dgn dgn force-pushed the revision-tags branch 2 times, most recently from 526451f to fb2e3ec Compare October 21, 2024 08:33
@dgn dgn changed the title [DRAFT] Revision Tag Support Revision Tag Support Oct 21, 2024
@istio-ecosystem istio-ecosystem deleted a comment from istio-testing Oct 21, 2024
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 {
Copy link
Contributor

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?

Copy link
Collaborator Author

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.

@luksa
Copy link
Contributor

luksa commented Oct 21, 2024

You also need to add IstioRevisionTag to the PROJECT file. I believe some make target(s) only update certain files if the CRD is listed in the PROJECT file.

Comment on lines 36 to 41
// Kind is the kind of the target resource.
//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
Kind string `json:"kind"`
Copy link
Contributor

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?

Comment on lines +66 to +69
// IstioRevision stores the name of the referenced IstioRevision
IstioRevision string `json:"istioRevision"`
Copy link
Contributor

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.

Copy link
Collaborator Author

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

chart/samples/istio-sample-gw-api.yaml Outdated Show resolved Hide resolved
chart/samples/istio-sample-revisionbased.yaml Outdated Show resolved Hide resolved
@@ -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
Copy link
Contributor

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?

Copy link
Contributor

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>.

Copy link
Collaborator Author

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

Copy link
Collaborator Author

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

api/v1alpha1/istiorevisiontags_types.go Show resolved Hide resolved
Comment on lines +370 to +381
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)
}
Copy link
Contributor

@nrfox nrfox Oct 21, 2024

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?

Copy link
Contributor

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.

Copy link
Collaborator Author

@dgn dgn Oct 23, 2024

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.

@dgn
Copy link
Collaborator Author

dgn commented Oct 24, 2024

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

  • there's a pod or namespace that explicitly references the IstioRevisionTag in an istio.io/rev label
  • the name of the IstioRevisionTag name is 'default' and there's a pod or namespace with the istio-injection: enabled label

Note that a pod's istio.io/rev annotation will not be considered as that will always have the name of the referenced IstioRevision rather than the name of the IstioRevisionTag! The labels however are added by users and reflect usage intent, ie the user will use the name of the IstioRevisionTag.

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)

  • there's a pod that explicitly references the IstioRevision in an istio.io/rev annotation or label
  • there's a namespace that explicitly references the IstioRevision in an istio.io/rev label
  • the name of the IstioRevision name is 'default' and there's a pod or namespace with the istio-injection: enabled label
  • there's an IstioRevisionTag referencing this IstioRevision

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.

Copy link
Contributor

@nrfox nrfox left a 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?

Copy link
Contributor

@nrfox nrfox left a 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.

docs/api-reference/sailoperator.io.md Outdated Show resolved Hide resolved
tests/integration/api/istiorevisiontag_test.go Outdated Show resolved Hide resolved
tests/integration/api/istiorevisiontag_test.go Outdated Show resolved Hide resolved
api/v1alpha1/istiorevisiontags_types.go Outdated Show resolved Hide resolved
Comment on lines 54 to 58
IstioInjectionLabel = "istio-injection"
IstioInjectionEnabledValue = "enabled"
IstioRevLabel = "istio.io/rev"
IstioSidecarInjectLabel = "sidecar.istio.io/inject"
Copy link
Contributor

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.

Copy link
Collaborator Author

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
Copy link
Contributor

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?

Copy link
Collaborator Author

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)

@sridhargaddam
Copy link
Contributor

@dgn, does this PR implement the following from the SEP?

When the very first `IstioRevision` is created in a cluster from a `RevisionBased` Istio resource, the Sail Operator will create a `IstioRevisionTag` with the name `default`, referencing that `IstioRevision`. This way, the standard namespace injection label `istio-injection=enabled` will work out of the box for RevisionBased deployments

updateStrategy:
type: RevisionBased
---
apiVersion: sailoperator.io/v1alpha1
Copy link
Contributor

@sridhargaddam sridhargaddam Oct 29, 2024

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"}

Copy link
Collaborator Author

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

should be fixed now!

@dgn dgn force-pushed the revision-tags branch 2 times, most recently from bd5db41 to 869dd43 Compare October 29, 2024 15:02
@dgn dgn force-pushed the revision-tags branch 2 times, most recently from 3a2ec18 to a9b3523 Compare November 4, 2024 15:44
@dgn dgn force-pushed the revision-tags branch 2 times, most recently from 0b6b7b6 to 85879f2 Compare November 6, 2024 09:45
@dgn
Copy link
Collaborator Author

dgn commented Nov 6, 2024

@dgn, does this PR implement the following from the SEP?

When the very first `IstioRevision` is created in a cluster from a `RevisionBased` Istio resource, the Sail Operator will create a `IstioRevisionTag` with the name `default`, referencing that `IstioRevision`. This way, the standard namespace injection label `istio-injection=enabled` will work out of the box for RevisionBased deployments

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.

@dgn dgn force-pushed the revision-tags branch 3 times, most recently from b4432ca to 47bb472 Compare November 6, 2024 15:47
Copy link
Contributor

@nrfox nrfox left a 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 {
Copy link
Contributor

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?

@fjglira
Copy link
Contributor

fjglira commented Nov 8, 2024

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"`
Copy link
Contributor

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.

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

Successfully merging this pull request may close these issues.

Revision Tag support
7 participants