Repository: kubernetes-sigs/node-feature-discovery
Pull request #1049 (2023-02-03 14:21:47)

topology-updater:compute pod set fingerprint
Add an option to compute the fingerprint of the current pod set on each node.
Report this new fingerprint using an attribute in new released `v1alpha2` NRT object.

needs: #1053 
address: #1048 
see: [this issue](https://github.com/k8stopologyawareschedwg/noderesourcetopology-api/issues/30) for more info about new NRT api version

k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/node-feature-discovery/pull/1049#" title="Author self-approved">jlojosnegros</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign marquiz for approval by writing `/assign @marquiz` in a comment. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fnode-feature-discovery).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/node-feature-discovery/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["marquiz"]} -->
linux-foundation-easycla[bot]:  <a href="https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/20677499/63999998/1049/#/?version=2"><img src="https://s3.amazonaws.com/cla-project-logo-prod/cla-not-signed.svg" alt="CLA Not Signed" align="left" height="28" width="328"></a><br/><br /><ul><li><a href='https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/20677499/63999998/1049/#/?version=2' target='_blank'>:x:</a> - login: @jlojosnegros / name: Jose Luis Ojosnegros . The commit (dd36774b5a08ab4121db94a6a2bd2732363f2683) is not authorized under a signed CLA. <a href='https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/20677499/63999998/1049/#/?version=2' target='_blank'>Please click here to be authorized</a>. For further assistance with EasyCLA, <a href='https://jira.linuxfoundation.org/servicedesk/customer/portal/4' target='_blank'>please submit a support request ticket</a>.</li></ul>
netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *kubernetes-sigs-nfd* processing.


|  Name | Link |
|---------------------------------|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | dd36774b5a08ab4121db94a6a2bd2732363f2683 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/kubernetes-sigs-nfd/deploys/63dd187dea09270008b1c93a |
k8s-ci-robot: Welcome @jlojosnegros! <br><br>It looks like this is your first PR to <a href='https://github.com/kubernetes-sigs/node-feature-discovery'>kubernetes-sigs/node-feature-discovery</a> 🎉. Please refer to our [pull request process documentation](https://git.k8s.io/community/contributors/guide/pull-requests.md) to help your PR have a smooth ride to approval. <br><br>You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. [Here is the bot commands documentation](https://go.k8s.io/bot-commands). <br><br>You can also check if kubernetes-sigs/node-feature-discovery has [its own contribution guidelines](https://github.com/kubernetes-sigs/node-feature-discovery/tree/master/CONTRIBUTING.md). <br><br>You may want to refer to our [testing guide](https://git.k8s.io/community/contributors/devel/sig-testing/testing.md) if you run into trouble with your tests not passing. <br><br>If you are having difficulty getting your pull request seen, please follow the [recommended escalation practices](https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#why-is-my-pull-request-not-getting-reviewed). Also, for tips and tricks in the contribution process you may want to read the [Kubernetes contributor cheat sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md). We want to make sure your contribution gets all the attention it needs! <br><br>Thank you, and welcome to Kubernetes. :smiley:
k8s-ci-robot: Hi @jlojosnegros. Thanks for your PR.

I'm waiting for a [kubernetes-sigs](https://github.com/orgs/kubernetes-sigs/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://git.k8s.io/community/community-membership.md#member) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fnode-feature-discovery).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

ffromani: /cc
ffromani: /ok-to-test
k8s-ci-robot: @jlojosnegros: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-node-feature-discovery-verify-master | dd36774b5a08ab4121db94a6a2bd2732363f2683 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1049/pull-node-feature-discovery-verify-master/1621517280833703936) | true | `/test pull-node-feature-discovery-verify-master`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=node-feature-discovery&pr=1049). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Ajlojosnegros). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/node-feature-discovery/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @jlojosnegros: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-node-feature-discovery-verify-master | fcb05bb8ac4c0992ae6df452c997a6e50e634b3e | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1049/pull-node-feature-discovery-verify-master/1623285336329162752) | true | `/test pull-node-feature-discovery-verify-master`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=node-feature-discovery&pr=1049). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Ajlojosnegros). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/node-feature-discovery/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
ffromani: > I think we should create a seperate PR that upgrades NRT api to `v1alpha2` and not bundle it in this one(you are missing changes to kustomize and helm deployment to install new CRDs).

good point! I'm fine with both approaches (and yes your suggestion is cleaner indeed)
PiotrProkop: > > I think we should create a seperate PR that upgrades NRT api to `v1alpha2` and not bundle it in this one(you are missing changes to kustomize and helm deployment to install new CRDs).
> 
> good point! I'm fine with both approaches (and yes your suggestion is cleaner indeed)

I just feel like we can easily miss important things we need to do with an upgrade like advertising Policies and Scope with Attributes 😄 
ffromani: > > > I think we should create a seperate PR that upgrades NRT api to `v1alpha2` and not bundle it in this one(you are missing changes to kustomize and helm deployment to install new CRDs).
> > 
> > 
> > good point! I'm fine with both approaches (and yes your suggestion is cleaner indeed)
> 
> I just feel like we can easily miss important things we need to do with an upgrade like advertising Policies and Scope with Attributes smile

Yes, thinking about it you're right. Let's split the update to its own PR
jlojosnegros: Let's split this PR and create a new one only for NRT API update to `v1alpha2`
/hold
jlojosnegros: New PR created to update NRT API version: #1053 
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/node-feature-discovery/pull/1049#" title="Author self-approved">jlojosnegros</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign marquiz for approval. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fnode-feature-discovery).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/node-feature-discovery/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["marquiz"]} -->
k8s-ci-robot: LGTM label has been added.  <details>Git tree hash: 08bfec7dd0872a1a496f13befa64bed1110c2388</details>
k8s-ci-robot: New changes are detected. LGTM label has been removed.
jlojosnegros: just a rebase....
ffromani: /lgtm
k8s-ci-robot: LGTM label has been added.  <details>Git tree hash: 6413b8b69676abb9cc81744cd7ddf21ab2d52957</details>
ArangoGutierrez: > /lgtm

/Approve
/lgtm
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/node-feature-discovery/pull/1049#issuecomment-1427747271" title="LGTM">ArangoGutierrez</a>*, *<a href="https://github.com/kubernetes-sigs/node-feature-discovery/pull/1049#" title="Author self-approved">jlojosnegros</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign marquiz for approval. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fnode-feature-discovery).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/node-feature-discovery/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["marquiz"]} -->
jlojosnegros: /assign @marquiz 
k8s-ci-robot: New changes are detected. LGTM label has been removed.
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/node-feature-discovery/pull/1049#issuecomment-1427747271" title="LGTM">ArangoGutierrez</a>*, *<a href="https://github.com/kubernetes-sigs/node-feature-discovery/pull/1049#" title="Author self-approved">jlojosnegros</a>*
**Once this PR has been reviewed and has the lgtm label**, please ask for approval from marquiz. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fnode-feature-discovery).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/node-feature-discovery/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["marquiz"]} -->
jlojosnegros: rebase after #1054 was merged.
jlojosnegros: Address comments.
k8s-ci-robot: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
k8s-ci-robot: @jlojosnegros: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-node-feature-discovery-build-master | d7106ddd95541576e8ca969b2bd87783dc71a2a4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1049/pull-node-feature-discovery-build-master/1627961191861063680) | true | `/test pull-node-feature-discovery-build-master`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=node-feature-discovery&pr=1049). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Ajlojosnegros). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/node-feature-discovery/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @jlojosnegros: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-node-feature-discovery-build-master | d7106ddd95541576e8ca969b2bd87783dc71a2a4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1049/pull-node-feature-discovery-build-master/1627961191861063680) | true | `/test pull-node-feature-discovery-build-master`
pull-node-feature-discovery-build-image-cross-generic | d7106ddd95541576e8ca969b2bd87783dc71a2a4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1049/pull-node-feature-discovery-build-image-cross-generic/1627961191747817472) | true | `/test pull-node-feature-discovery-build-image-cross-generic`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=node-feature-discovery&pr=1049). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Ajlojosnegros). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/node-feature-discovery/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @jlojosnegros: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-node-feature-discovery-build-master | d7106ddd95541576e8ca969b2bd87783dc71a2a4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1049/pull-node-feature-discovery-build-master/1627961191861063680) | true | `/test pull-node-feature-discovery-build-master`
pull-node-feature-discovery-build-image-cross-generic | d7106ddd95541576e8ca969b2bd87783dc71a2a4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1049/pull-node-feature-discovery-build-image-cross-generic/1627961191747817472) | true | `/test pull-node-feature-discovery-build-image-cross-generic`
pull-node-feature-discovery-verify-master | d7106ddd95541576e8ca969b2bd87783dc71a2a4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1049/pull-node-feature-discovery-verify-master/1627961191810732032) | true | `/test pull-node-feature-discovery-verify-master`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=node-feature-discovery&pr=1049). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Ajlojosnegros). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/node-feature-discovery/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @jlojosnegros: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-node-feature-discovery-build-master | d7106ddd95541576e8ca969b2bd87783dc71a2a4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1049/pull-node-feature-discovery-build-master/1627961191861063680) | true | `/test pull-node-feature-discovery-build-master`
pull-node-feature-discovery-build-image-cross-generic | d7106ddd95541576e8ca969b2bd87783dc71a2a4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1049/pull-node-feature-discovery-build-image-cross-generic/1627961191747817472) | true | `/test pull-node-feature-discovery-build-image-cross-generic`
pull-node-feature-discovery-verify-master | d7106ddd95541576e8ca969b2bd87783dc71a2a4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1049/pull-node-feature-discovery-verify-master/1627961191810732032) | true | `/test pull-node-feature-discovery-verify-master`
pull-node-feature-discovery-build-image-generic | d7106ddd95541576e8ca969b2bd87783dc71a2a4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1049/pull-node-feature-discovery-build-image-generic/1627961191697485824) | true | `/test pull-node-feature-discovery-build-image-generic`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=node-feature-discovery&pr=1049). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Ajlojosnegros). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/node-feature-discovery/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @jlojosnegros: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-node-feature-discovery-build-image-cross-generic | f720f60a05a26282c2d6667371dcf72dee031950 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1049/pull-node-feature-discovery-build-image-cross-generic/1627991002142740480) | true | `/test pull-node-feature-discovery-build-image-cross-generic`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=node-feature-discovery&pr=1049). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Ajlojosnegros). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/node-feature-discovery/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
jlojosnegros: /test pull-node-feature-discovery-build-image-cross-generic

Patch:
diff --git a/cmd/nfd-topology-updater/main.go b/cmd/nfd-topology-updater/main.go
index ba472ebfc..a345beaa8 100644
--- a/cmd/nfd-topology-updater/main.go
+++ b/cmd/nfd-topology-updater/main.go
@@ -139,6 +139,7 @@ func initFlags(flagset *flag.FlagSet) (*topology.Args, *resourcemonitor.Args) {
 		"Pod Resource Socket path to use.")
 	flagset.StringVar(&args.ConfigFile, "config", "/etc/kubernetes/node-feature-discovery/nfd-topology-updater.conf",
 		"Config file to use.")
+	flagset.BoolVar(&resourcemonitorArgs.PodSetFingerprint, "pods-fingerprint", false, "Compute and report the pod set fingerprint")
 
 	klog.InitFlags(flagset)
 
diff --git a/deployment/helm/node-feature-discovery/templates/topologyupdater.yaml b/deployment/helm/node-feature-discovery/templates/topologyupdater.yaml
index 8f4849ac5..dc8ea898d 100644
--- a/deployment/helm/node-feature-discovery/templates/topologyupdater.yaml
+++ b/deployment/helm/node-feature-discovery/templates/topologyupdater.yaml
@@ -55,6 +55,9 @@ spec:
           - "-key-file=/etc/kubernetes/node-feature-discovery/certs/tls.key"
           - "-cert-file=/etc/kubernetes/node-feature-discovery/certs/tls.crt"
           {{- end }}
+          {{- if .Values.topologyUpdater.podSetFingerprint }}
+          - "-pods-fingerprint"
+          {{- end }}
         volumeMounts:
         - name: kubelet-config
           mountPath: /host-var/lib/kubelet/config.yaml
diff --git a/deployment/helm/node-feature-discovery/values.yaml b/deployment/helm/node-feature-discovery/values.yaml
index 5f2835c24..a44467394 100644
--- a/deployment/helm/node-feature-discovery/values.yaml
+++ b/deployment/helm/node-feature-discovery/values.yaml
@@ -424,6 +424,7 @@ topologyUpdater:
   tolerations: []
   annotations: {}
   affinity: {}
+  podSetFingerprint: true
 
 topologyGC:
   enable: true
diff --git a/docs/deployment/helm.md b/docs/deployment/helm.md
index 7ee29debc..d28a94304 100644
--- a/docs/deployment/helm.md
+++ b/docs/deployment/helm.md
@@ -173,6 +173,7 @@ We have introduced the following Chart parameters.
 | `topologyUpdater.annotations`                 | dict   | {}      | Topology updater pod [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)                                                                                    |
 | `topologyUpdater.affinity`                    | dict   | {}      | Topology updater pod [affinity](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/)                                                                      |
 | `topologyUpdater.config`                      | dict   |         | [configuration](../reference/topology-updater-configuration-reference)                                                                                                                                |
+| `topologyUpdater.podSetFingerprint`           | bool   | false   | Enables compute and report of pod fingerprint in NRT objects.                                                                                                                                         |
 
 ### Topology garbage collector parameters
 
diff --git a/docs/reference/topology-updater-commandline-reference.md b/docs/reference/topology-updater-commandline-reference.md
index 101824a67..aa67ac665 100644
--- a/docs/reference/topology-updater-commandline-reference.md
+++ b/docs/reference/topology-updater-commandline-reference.md
@@ -147,3 +147,16 @@ Example:
 ```bash
 nfd-topology-updater -podresources-socket=/var/lib/kubelet/pod-resources/kubelet.sock
 ```
+
+### -pods-fingerprint
+
+Enbles the compute and report the pod set fingerprint in the NRT.
+A pod fingerprint is a compact representation of the "node state" regarding resources.
+
+Default: `false`
+
+Example:
+
+```bash
+nfd-topology-updater -pods-fingerprint
+```
diff --git a/go.mod b/go.mod
index c70026e33..7f9f2dae5 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,7 @@ require (
 	github.com/google/go-cmp v0.5.9
 	github.com/jaypipes/ghw v0.8.1-0.20210827132705-c7224150a17e
 	github.com/k8stopologyawareschedwg/noderesourcetopology-api v0.1.0
+	github.com/k8stopologyawareschedwg/podfingerprint v0.1.2
 	github.com/klauspost/cpuid/v2 v2.2.4
 	github.com/onsi/ginkgo/v2 v2.4.0
 	github.com/onsi/gomega v1.23.0
@@ -49,6 +50,7 @@ require (
 	github.com/Microsoft/go-winio v0.4.17 // indirect
 	github.com/Microsoft/hcsshim v0.8.22 // indirect
 	github.com/NYTimes/gziphandler v1.1.1 // indirect
+	github.com/OneOfOne/xxhash v1.2.8 // indirect
 	github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
 	github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
 	github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e // indirect
diff --git a/go.sum b/go.sum
index af51371e4..5e21b3f39 100644
--- a/go.sum
+++ b/go.sum
@@ -86,6 +86,8 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0
 github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
 github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
+github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
 github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
@@ -422,6 +424,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
 github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
 github.com/k8stopologyawareschedwg/noderesourcetopology-api v0.1.0 h1:2uCRJbv+A+fmaUaO0wLZ8oYd6cLE1dRzBQcFNxggH3s=
 github.com/k8stopologyawareschedwg/noderesourcetopology-api v0.1.0/go.mod h1:AkACMQGiTgCt0lQw3m7TTU8PLH9lYKNK5e9DqFf5VuM=
+github.com/k8stopologyawareschedwg/podfingerprint v0.1.2 h1:Db5KLJjPg2mKaCoeEliMlea+JMyDMWdbNPXnWbPNDyM=
+github.com/k8stopologyawareschedwg/podfingerprint v0.1.2/go.mod h1:C23pM15t06dXg/OihGlqBvnYzLr+MXDXJ7zMfbNAyXI=
 github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI=
 github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
diff --git a/pkg/nfd-topology-updater/nfd-topology-updater.go b/pkg/nfd-topology-updater/nfd-topology-updater.go
index 7488680a8..f72445117 100644
--- a/pkg/nfd-topology-updater/nfd-topology-updater.go
+++ b/pkg/nfd-topology-updater/nfd-topology-updater.go
@@ -130,7 +130,7 @@ func (w *nfdTopologyUpdater) Run() error {
 
 	var resScan resourcemonitor.ResourcesScanner
 
-	resScan, err = resourcemonitor.NewPodResourcesScanner(w.resourcemonitorArgs.Namespace, podResClient, w.apihelper)
+	resScan, err = resourcemonitor.NewPodResourcesScanner(w.resourcemonitorArgs.Namespace, podResClient, w.apihelper, w.resourcemonitorArgs.PodSetFingerprint)
 	if err != nil {
 		return fmt.Errorf("failed to initialize ResourceMonitor instance: %w", err)
 	}
@@ -154,16 +154,16 @@ func (w *nfdTopologyUpdater) Run() error {
 		select {
 		case <-crTrigger.C:
 			klog.Infof("Scanning")
-			podResources, err := resScan.Scan()
-			utils.KlogDump(1, "podResources are", "  ", podResources)
+			scanResponse, err := resScan.Scan()
+			utils.KlogDump(1, "podResources are", "  ", scanResponse.PodResources)
 			if err != nil {
 				klog.Warningf("Scan failed: %v", err)
 				continue
 			}
-			zones = resAggr.Aggregate(podResources)
+			zones = resAggr.Aggregate(scanResponse.PodResources)
 			utils.KlogDump(1, "After aggregating resources identified zones are", "  ", zones)
 			if !w.args.NoPublish {
-				if err = w.updateNodeResourceTopology(zones); err != nil {
+				if err = w.updateNodeResourceTopology(zones, scanResponse); err != nil {
 					return err
 				}
 			}
@@ -188,7 +188,7 @@ func (w *nfdTopologyUpdater) Stop() {
 	}
 }
 
-func (w *nfdTopologyUpdater) updateNodeResourceTopology(zoneInfo v1alpha2.ZoneList) error {
+func (w *nfdTopologyUpdater) updateNodeResourceTopology(zoneInfo v1alpha2.ZoneList, scanResponse resourcemonitor.ScanResponse) error {
 	cli, err := w.apihelper.GetTopologyClient()
 	if err != nil {
 		return err
@@ -205,6 +205,8 @@ func (w *nfdTopologyUpdater) updateNodeResourceTopology(zoneInfo v1alpha2.ZoneLi
 			Attributes:       createTopologyAttributes(w.nodeInfo.tmPolicy, w.nodeInfo.tmScope),
 		}
 
+		updateAttributes(&nrtNew.Attributes, scanResponse.Attributes)
+
 		_, err := cli.TopologyV1alpha2().NodeResourceTopologies().Create(context.TODO(), &nrtNew, metav1.CreateOptions{})
 		if err != nil {
 			return fmt.Errorf("failed to create NodeResourceTopology: %w", err)
@@ -216,6 +218,7 @@ func (w *nfdTopologyUpdater) updateNodeResourceTopology(zoneInfo v1alpha2.ZoneLi
 
 	nrtMutated := nrt.DeepCopy()
 	nrtMutated.Zones = zoneInfo
+	updateAttributes(&nrtMutated.Attributes, scanResponse.Attributes)
 
 	nrtUpdated, err := cli.TopologyV1alpha2().NodeResourceTopologies().Update(context.TODO(), nrtMutated, metav1.UpdateOptions{})
 	if err != nil {
@@ -261,3 +264,22 @@ func createTopologyAttributes(policy string, scope string) v1alpha2.AttributeLis
 		},
 	}
 }
+
+func updateAttribute(attrList *v1alpha2.AttributeList, attrInfo v1alpha2.AttributeInfo) {
+	if attrList == nil {
+		return
+	}
+
+	for idx := range *attrList {
+		if (*attrList)[idx].Name == attrInfo.Name {
+			(*attrList)[idx].Value = attrInfo.Value
+			return
+		}
+	}
+	*attrList = append(*attrList, attrInfo)
+}
+func updateAttributes(lhs *v1alpha2.AttributeList, rhs v1alpha2.AttributeList) {
+	for _, attr := range rhs {
+		updateAttribute(lhs, attr)
+	}
+}
diff --git a/pkg/resourcemonitor/podresourcesscanner.go b/pkg/resourcemonitor/podresourcesscanner.go
index a42801efc..00775d9ba 100644
--- a/pkg/resourcemonitor/podresourcesscanner.go
+++ b/pkg/resourcemonitor/podresourcesscanner.go
@@ -27,20 +27,25 @@ import (
 	podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1"
 
 	"sigs.k8s.io/node-feature-discovery/pkg/apihelper"
+
+	"github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/apis/topology/v1alpha2"
+	"github.com/k8stopologyawareschedwg/podfingerprint"
 )
 
 type PodResourcesScanner struct {
 	namespace         string
 	podResourceClient podresourcesapi.PodResourcesListerClient
 	apihelper         apihelper.APIHelpers
+	podFingerprint    bool
 }
 
 // NewPodResourcesScanner creates a new ResourcesScanner instance
-func NewPodResourcesScanner(namespace string, podResourceClient podresourcesapi.PodResourcesListerClient, kubeApihelper apihelper.APIHelpers) (ResourcesScanner, error) {
+func NewPodResourcesScanner(namespace string, podResourceClient podresourcesapi.PodResourcesListerClient, kubeApihelper apihelper.APIHelpers, podFingerprint bool) (ResourcesScanner, error) {
 	resourcemonitorInstance := &PodResourcesScanner{
 		namespace:         namespace,
 		podResourceClient: podResourceClient,
 		apihelper:         kubeApihelper,
+		podFingerprint:    podFingerprint,
 	}
 	if resourcemonitorInstance.namespace != "*" {
 		klog.Infof("watching namespace %q", resourcemonitorInstance.namespace)
@@ -113,24 +118,43 @@ func hasIntegralCPUs(pod *corev1.Pod, container *corev1.Container) bool {
 }
 
 // Scan gathers all the PodResources from the system, using the podresources API client.
-func (resMon *PodResourcesScanner) Scan() ([]PodResources, error) {
+func (resMon *PodResourcesScanner) Scan() (ScanResponse, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), defaultPodResourcesTimeout)
 	defer cancel()
 
 	// Pod Resource API client
 	resp, err := resMon.podResourceClient.List(ctx, &podresourcesapi.ListPodResourcesRequest{})
 	if err != nil {
-		return nil, fmt.Errorf("can't receive response: %v.Get(_) = _, %w", resMon.podResourceClient, err)
+		return ScanResponse{}, fmt.Errorf("can't receive response: %v.Get(_) = _, %w", resMon.podResourceClient, err)
+	}
+
+	respPodResources := resp.GetPodResources()
+	retVal := ScanResponse{
+		Attributes: v1alpha2.AttributeList{},
 	}
 
+	if resMon.podFingerprint && len(respPodResources) > 0 {
+		var status podfingerprint.Status
+		podFingerprintSign, err := computePodFingerprint(respPodResources, &status)
+		if err != nil {
+			klog.Errorf("podFingerprint: Unable to compute fingerprint %v", err)
+		} else {
+			klog.Info("podFingerprint: " + status.Repr())
+
+			retVal.Attributes = append(retVal.Attributes, v1alpha2.AttributeInfo{
+				Name:  podfingerprint.Attribute,
+				Value: podFingerprintSign,
+			})
+		}
+	}
 	var podResData []PodResources
 
-	for _, podResource := range resp.GetPodResources() {
+	for _, podResource := range respPodResources {
 		klog.Infof("podresource iter: %s", podResource.GetName())
 		hasDevice := hasDevice(podResource)
 		isWatchable, isIntegralGuaranteed, err := resMon.isWatchable(podResource.GetNamespace(), podResource.GetName(), hasDevice)
 		if err != nil {
-			return nil, fmt.Errorf("checking if pod in a namespace is watchable, namespace:%v, pod name %v: %v", podResource.GetNamespace(), podResource.GetName(), err)
+			return ScanResponse{}, fmt.Errorf("checking if pod in a namespace is watchable, namespace:%v, pod name %v: %v", podResource.GetNamespace(), podResource.GetName(), err)
 		}
 		if !isWatchable {
 			continue
@@ -198,7 +222,9 @@ func (resMon *PodResourcesScanner) Scan() ([]PodResources, error) {
 
 	}
 
-	return podResData, nil
+	retVal.PodResources = podResData
+
+	return retVal, nil
 }
 
 func hasDevice(podResource *podresourcesapi.PodResources) bool {
@@ -225,3 +251,14 @@ func getNumaNodeIds(topologyInfo *podresourcesapi.TopologyInfo) []int {
 
 	return topology
 }
+
+func computePodFingerprint(podResources []*podresourcesapi.PodResources, status *podfingerprint.Status) (string, error) {
+	fingerprint := podfingerprint.NewTracingFingerprint(len(podResources), status)
+	for _, podResource := range podResources {
+		err := fingerprint.Add(podResource.Namespace, podResource.Name)
+		if err != nil {
+			return "", err
+		}
+	}
+	return fingerprint.Sign(), nil
+}
diff --git a/pkg/resourcemonitor/types.go b/pkg/resourcemonitor/types.go
index e079bb612..96589da13 100644
--- a/pkg/resourcemonitor/types.go
+++ b/pkg/resourcemonitor/types.go
@@ -31,6 +31,7 @@ type Args struct {
 	Namespace             string
 	KubeletConfigURI      string
 	APIAuthTokenFile      string
+	PodSetFingerprint     bool
 }
 
 // ResourceInfo stores information of resources and their corresponding IDs obtained from PodResource API
@@ -53,9 +54,14 @@ type PodResources struct {
 	Containers []ContainerResources
 }
 
+type ScanResponse struct {
+	PodResources []PodResources
+	Attributes   topologyv1alpha2.AttributeList
+}
+
 // ResourcesScanner gathers all the PodResources from the system, using the podresources API client
 type ResourcesScanner interface {
-	Scan() ([]PodResources, error)
+	Scan() (ScanResponse, error)
 }
 
 // ResourcesAggregator aggregates resource information based on the received data from underlying hardware and podresource API


Test patch:
diff --git a/pkg/nfd-topology-updater/nfd-topology-updater_test.go b/pkg/nfd-topology-updater/nfd-topology-updater_test.go
new file mode 100644
index 000000000..f81990427
--- /dev/null
+++ b/pkg/nfd-topology-updater/nfd-topology-updater_test.go
@@ -0,0 +1,112 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+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 nfdtopologyupdater
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/apis/topology/v1alpha2"
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+func TestTopologyUpdater(t *testing.T) {
+
+	Convey("Given a list of Attributes", t, func() {
+
+		attr_two := v1alpha2.AttributeInfo{
+			Name:  "attr_two_name",
+			Value: "attr_two_value",
+		}
+
+		attrList := v1alpha2.AttributeList{
+			v1alpha2.AttributeInfo{
+				Name:  "attr_one_name",
+				Value: "attr_one_value",
+			},
+			attr_two,
+			v1alpha2.AttributeInfo{
+				Name:  "attr_three_name",
+				Value: "attr_three_value",
+			},
+		}
+		attrListLen := len(attrList)
+		attrNames := getListOfNames(attrList)
+
+		Convey("When an existing attribute is updated", func() {
+
+			updatedAttribute := v1alpha2.AttributeInfo{
+				Name:  attr_two.Name,
+				Value: attr_two.Value + "_new",
+			}
+			updateAttribute(&attrList, updatedAttribute)
+
+			Convey("Then list should have the same number of elements", func() {
+				So(attrList, ShouldHaveLength, attrListLen)
+			})
+			Convey("Then the order of the elemens should be the same", func() {
+				So(attrNames, ShouldResemble, getListOfNames(attrList))
+			})
+			Convey("Then Attribute value in the list should be updated", func() {
+				attr, err := findAttributeByName(attrList, attr_two.Name)
+				So(err, ShouldBeNil)
+				So(attr.Value, ShouldEqual, updatedAttribute.Value)
+			})
+		})
+
+		Convey("When a non existing attribute is updated", func() {
+			completelyNewAttribute := v1alpha2.AttributeInfo{
+				Name:  "NonExistingAttribute_Name",
+				Value: "NonExistingAttribute_Value",
+			}
+			_, err := findAttributeByName(attrList, completelyNewAttribute.Name)
+			So(err, ShouldNotBeNil)
+
+			updateAttribute(&attrList, completelyNewAttribute)
+
+			Convey("Then list should have the one more element", func() {
+				So(attrList, ShouldHaveLength, attrListLen+1)
+			})
+
+			Convey("Then new Attribute should be added at the end of the list", func() {
+				So(attrList[len(attrList)-1], ShouldResemble, completelyNewAttribute)
+			})
+
+			Convey("Then the order of the elemens should be the same", func() {
+				So(attrNames, ShouldResemble, getListOfNames(attrList[:len(attrList)-1]))
+			})
+		})
+	})
+}
+
+func getListOfNames(attrList v1alpha2.AttributeList) []string {
+	ret := make([]string, len(attrList))
+
+	for idx, attr := range attrList {
+		ret[idx] = attr.Name
+	}
+	return ret
+}
+
+func findAttributeByName(attrList v1alpha2.AttributeList, name string) (v1alpha2.AttributeInfo, error) {
+	for _, attr := range attrList {
+		if attr.Name == name {
+			return attr, nil
+		}
+	}
+	return v1alpha2.AttributeInfo{}, fmt.Errorf("Attribute Not Found name:=%s", name)
+}
diff --git a/pkg/resourcemonitor/podresourcesscanner_test.go b/pkg/resourcemonitor/podresourcesscanner_test.go
index c8b927364..1a0a3d091 100644
--- a/pkg/resourcemonitor/podresourcesscanner_test.go
+++ b/pkg/resourcemonitor/podresourcesscanner_test.go
@@ -22,6 +22,7 @@ import (
 	"sort"
 	"testing"
 
+	"github.com/k8stopologyawareschedwg/podfingerprint"
 	. "github.com/smartystreets/goconvey/convey"
 	"github.com/stretchr/testify/mock"
 
@@ -40,11 +41,24 @@ func TestPodScanner(t *testing.T) {
 	var resScan ResourcesScanner
 	var err error
 
+	// PodFingerprint only depends on Name/Namespace of the pods running on a Node
+	// so we can precalculate the expected value
+	expectedFingerprintCompute := func(pods []*corev1.Pod) (string, error) {
+		pf := podfingerprint.NewFingerprint(len(pods))
+		for _, pr := range pods {
+			if err := pf.Add(pr.Namespace, pr.Name); err != nil {
+				return "", err
+			}
+		}
+		return pf.Sign(), nil
+	}
+
 	Convey("When I scan for pod resources using fake client and no namespace", t, func() {
 		mockPodResClient := new(podres.MockPodResourcesListerClient)
 		mockAPIHelper := new(apihelper.MockAPIHelpers)
 		mockClient := &k8sclient.Clientset{}
-		resScan, err = NewPodResourcesScanner("*", mockPodResClient, mockAPIHelper)
+		computePodFingerprint := true
+		resScan, err = NewPodResourcesScanner("*", mockPodResClient, mockAPIHelper, computePodFingerprint)
 
 		Convey("Creating a Resources Scanner using a mock client", func() {
 			So(err, ShouldBeNil)
@@ -58,7 +72,10 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldNotBeNil)
 			})
 			Convey("Return PodResources should be nil", func() {
-				So(res, ShouldBeNil)
+				So(res.PodResources, ShouldBeNil)
+			})
+			Convey("Return Attributes should be empty", func() {
+				So(res.Attributes, ShouldBeEmpty)
 			})
 		})
 
@@ -70,7 +87,10 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldBeNil)
 			})
 			Convey("Return PodResources should be zero", func() {
-				So(len(res), ShouldEqual, 0)
+				So(len(res.PodResources), ShouldEqual, 0)
+			})
+			Convey("Return Attributes should be empty", func() {
+				So(res.Attributes, ShouldBeEmpty)
 			})
 		})
 
@@ -160,7 +180,7 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldBeNil)
 			})
 			Convey("Return PodResources should have values", func() {
-				So(len(res), ShouldBeGreaterThan, 0)
+				So(len(res.PodResources), ShouldBeGreaterThan, 0)
 
 				expected := []PodResources{
 					{
@@ -194,14 +214,22 @@ func TestPodScanner(t *testing.T) {
 						},
 					},
 				}
-				for _, podresource := range res {
+				for _, podresource := range res.PodResources {
 					for _, container := range podresource.Containers {
-						sort.Slice(res, func(i, j int) bool {
+						sort.Slice(res.PodResources, func(i, j int) bool {
 							return container.Resources[i].Name < container.Resources[j].Name
 						})
 					}
 				}
-				So(reflect.DeepEqual(res, expected), ShouldBeTrue)
+				So(reflect.DeepEqual(res.PodResources, expected), ShouldBeTrue)
+			})
+			Convey("Return Attributes should have pod fingerprint attribute with proper value", func() {
+				So(len(res.Attributes), ShouldEqual, 1)
+				// can compute expected fringerprint only with the list of pods in the node.
+				expectedFingerprint, err := expectedFingerprintCompute([]*corev1.Pod{pod})
+				So(err, ShouldBeNil)
+				So(res.Attributes[0].Name, ShouldEqual, podfingerprint.Attribute)
+				So(res.Attributes[0].Value, ShouldEqual, expectedFingerprint)
 			})
 		})
 
@@ -266,7 +294,7 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldBeNil)
 			})
 			Convey("Return PodResources should have values", func() {
-				So(len(res), ShouldBeGreaterThan, 0)
+				So(len(res.PodResources), ShouldBeGreaterThan, 0)
 
 				expected := []PodResources{
 					{
@@ -290,7 +318,15 @@ func TestPodScanner(t *testing.T) {
 					},
 				}
 
-				So(reflect.DeepEqual(res, expected), ShouldBeTrue)
+				So(reflect.DeepEqual(res.PodResources, expected), ShouldBeTrue)
+			})
+			Convey("Return Attributes should have pod fingerprint attribute with proper value", func() {
+				So(len(res.Attributes), ShouldEqual, 1)
+				// can compute expected fringerprint only with the list of pods in the node.
+				expectedFingerprint, err := expectedFingerprintCompute([]*corev1.Pod{pod})
+				So(err, ShouldBeNil)
+				So(res.Attributes[0].Name, ShouldEqual, podfingerprint.Attribute)
+				So(res.Attributes[0].Value, ShouldEqual, expectedFingerprint)
 			})
 		})
 
@@ -345,7 +381,7 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldBeNil)
 			})
 			Convey("Return PodResources should have values", func() {
-				So(len(res), ShouldBeGreaterThan, 0)
+				So(len(res.PodResources), ShouldBeGreaterThan, 0)
 
 				expected := []PodResources{
 					{
@@ -365,7 +401,15 @@ func TestPodScanner(t *testing.T) {
 					},
 				}
 
-				So(reflect.DeepEqual(res, expected), ShouldBeTrue)
+				So(reflect.DeepEqual(res.PodResources, expected), ShouldBeTrue)
+			})
+			Convey("Return Attributes should have pod fingerprint attribute with proper value", func() {
+				So(len(res.Attributes), ShouldEqual, 1)
+				// can compute expected fringerprint only with the list of pods in the node.
+				expectedFingerprint, err := expectedFingerprintCompute([]*corev1.Pod{pod})
+				So(err, ShouldBeNil)
+				So(res.Attributes[0].Name, ShouldEqual, podfingerprint.Attribute)
+				So(res.Attributes[0].Value, ShouldEqual, expectedFingerprint)
 			})
 		})
 
@@ -427,7 +471,7 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldBeNil)
 			})
 			Convey("Return PodResources should have values", func() {
-				So(len(res), ShouldBeGreaterThan, 0)
+				So(len(res.PodResources), ShouldBeGreaterThan, 0)
 
 				expected := []PodResources{
 					{
@@ -447,7 +491,7 @@ func TestPodScanner(t *testing.T) {
 					},
 				}
 
-				So(reflect.DeepEqual(res, expected), ShouldBeTrue)
+				So(reflect.DeepEqual(res.PodResources, expected), ShouldBeTrue)
 			})
 		})
 
@@ -505,7 +549,15 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldBeNil)
 			})
 			Convey("Return PodResources should have values", func() {
-				So(len(res), ShouldBeGreaterThan, 0)
+				So(len(res.PodResources), ShouldBeGreaterThan, 0)
+			})
+			Convey("Return Attributes should have pod fingerprint attribute with proper value", func() {
+				So(len(res.Attributes), ShouldEqual, 1)
+				// can compute expected fringerprint only with the list of pods in the node.
+				expectedFingerprint, err := expectedFingerprintCompute([]*corev1.Pod{pod})
+				So(err, ShouldBeNil)
+				So(res.Attributes[0].Name, ShouldEqual, podfingerprint.Attribute)
+				So(res.Attributes[0].Value, ShouldEqual, expectedFingerprint)
 			})
 
 			expected := []PodResources{
@@ -526,7 +578,7 @@ func TestPodScanner(t *testing.T) {
 				},
 			}
 
-			So(reflect.DeepEqual(res, expected), ShouldBeTrue)
+			So(reflect.DeepEqual(res.PodResources, expected), ShouldBeTrue)
 		})
 
 		Convey("When I successfully get valid response for (non-guaranteed) pods with devices with cpus", func() {
@@ -589,7 +641,7 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldBeNil)
 			})
 			Convey("Return PodResources should have values", func() {
-				So(len(res), ShouldBeGreaterThan, 0)
+				So(len(res.PodResources), ShouldBeGreaterThan, 0)
 			})
 
 			expected := []PodResources{
@@ -609,16 +661,26 @@ func TestPodScanner(t *testing.T) {
 					},
 				},
 			}
-			So(reflect.DeepEqual(res, expected), ShouldBeTrue)
-		})
+			So(reflect.DeepEqual(res.PodResources, expected), ShouldBeTrue)
 
+			Convey("Return Attributes should have pod fingerprint attribute with proper value", func() {
+				So(len(res.Attributes), ShouldEqual, 1)
+
+				// can compute expected fringerprint only with the list of pods in the node.
+				expectedFingerprint, err := expectedFingerprintCompute([]*corev1.Pod{pod})
+				So(err, ShouldBeNil)
+				So(res.Attributes[0].Name, ShouldEqual, podfingerprint.Attribute)
+				So(res.Attributes[0].Value, ShouldEqual, expectedFingerprint)
+			})
+		})
 	})
 
 	Convey("When I scan for pod resources using fake client and given namespace", t, func() {
 		mockPodResClient := new(podres.MockPodResourcesListerClient)
 		mockAPIHelper := new(apihelper.MockAPIHelpers)
 		mockClient := &k8sclient.Clientset{}
-		resScan, err = NewPodResourcesScanner("pod-res-test", mockPodResClient, mockAPIHelper)
+		computePodFingerprint := false
+		resScan, err = NewPodResourcesScanner("pod-res-test", mockPodResClient, mockAPIHelper, computePodFingerprint)
 
 		Convey("Creating a Resources Scanner using a mock client", func() {
 			So(err, ShouldBeNil)
@@ -632,7 +694,7 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldNotBeNil)
 			})
 			Convey("Return PodResources should be nil", func() {
-				So(res, ShouldBeNil)
+				So(res.PodResources, ShouldBeNil)
 			})
 		})
 
@@ -644,7 +706,7 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldBeNil)
 			})
 			Convey("Return PodResources should be zero", func() {
-				So(len(res), ShouldEqual, 0)
+				So(len(res.PodResources), ShouldEqual, 0)
 			})
 		})
 
@@ -712,7 +774,7 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldBeNil)
 			})
 			Convey("Return PodResources should be zero", func() {
-				So(len(res), ShouldEqual, 0)
+				So(len(res.PodResources), ShouldEqual, 0)
 			})
 		})
 
@@ -778,7 +840,7 @@ func TestPodScanner(t *testing.T) {
 			})
 
 			Convey("Return PodResources should have values", func() {
-				So(len(res), ShouldBeGreaterThan, 0)
+				So(len(res.PodResources), ShouldBeGreaterThan, 0)
 
 				expected := []PodResources{
 					{
@@ -802,7 +864,7 @@ func TestPodScanner(t *testing.T) {
 					},
 				}
 
-				So(reflect.DeepEqual(res, expected), ShouldBeTrue)
+				So(reflect.DeepEqual(res.PodResources, expected), ShouldBeTrue)
 			})
 		})
 
@@ -858,7 +920,7 @@ func TestPodScanner(t *testing.T) {
 			})
 			Convey("Return PodResources should have values", func() {
 				Convey("Return PodResources should be zero", func() {
-					So(len(res), ShouldEqual, 0)
+					So(len(res.PodResources), ShouldEqual, 0)
 				})
 			})
 		})
@@ -923,7 +985,7 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldBeNil)
 			})
 			Convey("Return PodResources should have values", func() {
-				So(len(res), ShouldEqual, 0)
+				So(len(res.PodResources), ShouldEqual, 0)
 			})
 		})
 
@@ -981,7 +1043,7 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldBeNil)
 			})
 			Convey("Return PodResources should have values", func() {
-				So(len(res), ShouldBeGreaterThan, 0)
+				So(len(res.PodResources), ShouldBeGreaterThan, 0)
 			})
 
 			expected := []PodResources{
@@ -1002,7 +1064,7 @@ func TestPodScanner(t *testing.T) {
 				},
 			}
 
-			So(reflect.DeepEqual(res, expected), ShouldBeTrue)
+			So(reflect.DeepEqual(res.PodResources, expected), ShouldBeTrue)
 		})
 
 		Convey("When I successfully get valid response for (non-guaranteed) pods with devices with cpus", func() {
@@ -1065,7 +1127,7 @@ func TestPodScanner(t *testing.T) {
 				So(err, ShouldBeNil)
 			})
 			Convey("Return PodResources should have values", func() {
-				So(len(res), ShouldBeGreaterThan, 0)
+				So(len(res.PodResources), ShouldBeGreaterThan, 0)
 			})
 
 			expected := []PodResources{
@@ -1085,7 +1147,7 @@ func TestPodScanner(t *testing.T) {
 					},
 				},
 			}
-			So(reflect.DeepEqual(res, expected), ShouldBeTrue)
+			So(reflect.DeepEqual(res.PodResources, expected), ShouldBeTrue)
 		})
 
 	})
diff --git a/test/e2e/node_feature_discovery_test.go b/test/e2e/node_feature_discovery_test.go
index e8a43dd3a..30306f2a4 100644
--- a/test/e2e/node_feature_discovery_test.go
+++ b/test/e2e/node_feature_discovery_test.go
@@ -146,7 +146,7 @@ func cleanupCRDs(cli *nfdclient.Clientset) {
 }
 
 // Actual test suite
-var _ = SIGDescribe("NFD master and worker", func() {
+var _ = SIGDescribe("Node Feature Discovery", func() {
 	f := framework.NewDefaultFramework("node-feature-discovery")
 
 	nfdTestSuite := func(useNodeFeatureApi bool) {
diff --git a/test/e2e/topology_updater_test.go b/test/e2e/topology_updater_test.go
index bfe5ce0e1..94da9dbaa 100644
--- a/test/e2e/topology_updater_test.go
+++ b/test/e2e/topology_updater_test.go
@@ -29,6 +29,7 @@ import (
 
 	"github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/apis/topology/v1alpha2"
 	topologyclientset "github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/generated/clientset/versioned"
+	"github.com/k8stopologyawareschedwg/podfingerprint"
 
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
@@ -46,7 +47,7 @@ import (
 	testpod "sigs.k8s.io/node-feature-discovery/test/e2e/utils/pod"
 )
 
-var _ = SIGDescribe("NFD topology updater", func() {
+var _ = SIGDescribe("Node Feature Discovery topology updater", func() {
 	var (
 		extClient                *extclient.Clientset
 		topologyClient           *topologyclientset.Clientset
@@ -304,8 +305,74 @@ excludeList:
 			}, 1*time.Minute, 10*time.Second).Should(BeFalse())
 		})
 	})
+	When("topology-updater configure to compute pod fingerprint", func() {
+		BeforeEach(func() {
+			cfg, err := testutils.GetConfig()
+			Expect(err).ToNot(HaveOccurred())
+
+			kcfg := cfg.GetKubeletConfig()
+			By(fmt.Sprintf("Using config (%#v)", kcfg))
+
+			podSpecOpts := []testpod.SpecOption{
+				testpod.SpecWithContainerImage(dockerImage()),
+				testpod.SpecWithContainerExtraArgs("-pods-fingerprint"),
+			}
+			topologyUpdaterDaemonSet = testds.NFDTopologyUpdater(kcfg, podSpecOpts...)
+		})
+		It("noderesourcetopology should advertise pod fingerprint in top-level attribute", func() {
+			Eventually(func() bool {
+				// get node topology
+				nodeTopology := testutils.GetNodeTopology(topologyClient, topologyUpdaterNode.Name)
+
+				// look for attribute
+				podFingerprintAttribute, err := findAttribute(nodeTopology.Attributes, podfingerprint.Attribute)
+				if err != nil {
+					framework.Logf("podFingerprint attributte %q not found:  %v", podfingerprint.Attribute, err)
+					return false
+				}
+				// get pods in node
+				pods, err := f.ClientSet.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{FieldSelector: "spec.nodeName=" + topologyUpdaterNode.Name})
+				if err != nil {
+					framework.Logf("podFingerprint error while recovering %q node pods: %v", topologyUpdaterNode.Name, err)
+					return false
+				}
+				if len(pods.Items) == 0 {
+					framework.Logf("podFingerprint No pods in node  %q", topologyUpdaterNode.Name)
+					return false
+				}
+
+				// compute expected value
+				pf := podfingerprint.NewFingerprint(len(pods.Items))
+				for _, pod := range pods.Items {
+					err = pf.Add(pod.Namespace, pod.Name)
+					if err != nil {
+						framework.Logf("error while computing expected podFingerprint %v", err)
+						return false
+					}
+				}
+				expectedPodFingerprint := pf.Sign()
+
+				if podFingerprintAttribute.Value != expectedPodFingerprint {
+					framework.Logf("podFingerprint attributte error expected: %q actual: %q", expectedPodFingerprint, podFingerprintAttribute.Value)
+					return false
+				}
+
+				return true
+
+			}, 1*time.Minute, 10*time.Second).Should(BeTrue())
+		})
+	})
 })
 
+func findAttribute(attributes v1alpha2.AttributeList, attributeName string) (v1alpha2.AttributeInfo, error) {
+	for _, attrInfo := range attributes {
+		if attrInfo.Name == attributeName {
+			return attrInfo, nil
+		}
+	}
+	return v1alpha2.AttributeInfo{}, fmt.Errorf("attribute %q not found", attributeName)
+}
+
 // lessAllocatableResources specialize CompareAllocatableResources for this specific e2e use case.
 func lessAllocatableResources(expected, got map[string]corev1.ResourceList) (string, string, bool) {
 	zoneName, resName, cmp, ok := testutils.CompareAllocatableResources(expected, got)