[
  {
    "path": ".github/renovate.json",
    "content": "{\n  \"extends\": [\n    \"github>rancher/renovate-config#release\"\n  ],\n  \"baseBranchPatterns\": [\n    \"main\",\n    \"release/v2\"\n  ],\n  \"prHourlyLimit\": 2,\n  \"packageRules\": [\n    {\n      \"enabled\": false,\n      \"matchPackageNames\": [\n        \"/k8s.io/*/\",\n        \"/sigs.k8s.io/*/\",\n        \"/go.opentelemetry.io/*/\",\n        \"/github.com/prometheus/*/\"\n      ]\n    },\n    {\n      \"matchUpdateTypes\": [\n        \"major\",\n        \"minor\"\n      ],\n      \"enabled\": false,\n      \"matchPackageNames\": [\n        \"/github.com/rancher/lasso/*/\"\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "content": "name: Wrangler CI\n\non:\n  push:\n  pull_request:\n    tags:\n      - v*\n    branches:\n      - 'release/*'\n      - 'main'\n\njobs:\n  ci:\n    strategy:\n      matrix:\n        arch:\n        - amd64\n        - arm64\n    runs-on: org-${{ github.repository_owner_id }}-${{ matrix.arch }}-k8s\n    container: registry.suse.com/bci/golang:1.25\n    steps:\n    - name : Checkout repository\n      # https://github.com/actions/checkout/releases/tag/v4.1.1\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n    - name : Install mockgen\n      run: go install -v -x go.uber.org/mock/mockgen@v0.6.0\n    - name : Run CI\n      run: bash scripts/ci\n  golangci:\n    name: golangci-lint\n    runs-on: ubuntu-latest\n    env:\n      SETUP_GO_VERSION: '^1.25'\n      GOLANG_CI_LINT_VERSION: v2.7.1\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          submodules: recursive\n\n      - name: Setup Go\n        uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6\n        with:\n          go-version: ${{ env.SETUP_GO_VERSION }}\n\n      - name: Generate Golang\n        run: |\n          export PATH=$PATH:/home/runner/go/bin/\n\n      - name: golangci-lint\n        uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0\n        with:\n          # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.\n          version: ${{ env.GOLANG_CI_LINT_VERSION }}\n\n"
  },
  {
    "path": ".github/workflows/fossa.yml",
    "content": "name: FOSSA Scanning\n\non:\n  push:\n    branches: [\"main\", \"master\", \"release/**\"]\n  workflow_dispatch:\n\npermissions:\n  contents: read\n  id-token: write\n\njobs:\n  fossa-scanning:\n    runs-on: ubuntu-latest\n    timeout-minutes: 30\n    steps:\n    - name: Checkout\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n\n    # The FOSSA token is shared between all repos in Rancher's GH org. It can be\n    # used directly and there is no need to request specific access to EIO.\n    - name: Read FOSSA token\n      uses: rancher-eio/read-vault-secrets@0da85151ad1f19ed7986c41587e45aac1ace74b6 # v3\n      with:\n        secrets: |\n          secret/data/github/org/rancher/fossa/push token | FOSSA_API_KEY_PUSH_ONLY\n\n    - name: FOSSA scan\n      uses: fossas/fossa-action@c414b9ad82eaad041e47a7cf62a4f02411f427a0 # v1.8.0\n      with:\n        api-key: ${{ env.FOSSA_API_KEY_PUSH_ONLY }}\n        # Only runs the scan and do not provide/returns any results back to the\n        # pipeline.\n        run-tests: false\n"
  },
  {
    "path": ".github/workflows/release.yaml",
    "content": "name: Release\n\non:\n  push:\n    tags:\n      - v*\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n    steps:\n      - name : Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Create release on Github\n        env:\n          GH_TOKEN:  ${{ github.token }}\n        run: |\n          if [[ \"${{ github.ref_name }}\" == *-rc* ]]; then\n            gh --repo \"${{ github.repository }}\" release create ${{ github.ref_name }} --verify-tag --generate-notes --prerelease\n          else\n            gh --repo \"${{ github.repository }}\" release create ${{ github.ref_name }} --verify-tag --generate-notes\n          fi\n"
  },
  {
    "path": ".github/workflows/renovate-vault.yml",
    "content": "name: Renovate\non:\n  workflow_dispatch:\n    inputs:\n      logLevel:\n        description: \"Override default log level\"\n        required: false\n        default: \"info\"\n        type: string\n      overrideSchedule:\n        description: \"Override all schedules\"\n        required: false\n        default: \"false\"\n        type: string\n  # Run twice in the early morning (UTC) for initial and follow up steps (create pull request and merge)\n  schedule:\n    - cron: '30 4,6 * * *'\n\npermissions:\n  contents: read\n  id-token: write\n\njobs:\n  call-workflow:\n    uses: rancher/renovate-config/.github/workflows/renovate-vault.yml@84bf074154364f80af052ebba8e23614212b79df # release\n    with:\n      logLevel: ${{ inputs.logLevel || 'info' }}\n      overrideSchedule: ${{ github.event.inputs.overrideSchedule == 'true' && '{''schedule'':null}' || '' }}\n    secrets: inherit\n"
  },
  {
    "path": ".gitignore",
    "content": "/.dapper\n/bin\n/dist\n/build\n*.swp\n/.trash-cache\n/trash.lock\n/.idea\n/package/rancher\n/package/agent\n/tests/MANIFEST\n/tests/integration/MANIFEST\ntests/integration/MANIFEST\ntests/integration/.idea/\n/tests/.cache\n/tests/.tox\n/tests/integration/.tox/\n/tests/.venv\n/tests/.idea\n/default.etcd\n*.pyc\n__pycache__\n/management-state\n/rancher\n*.pytest_cache\n.kube/\n.vscode/\n.DS_Store\ntests/validation/.idea\nall.yaml\nkustomization.yaml\n"
  },
  {
    "path": ".golangci.json",
    "content": "{\n    \"formatters\": {\n        \"enable\": [\n            \"gofmt\",\n            \"goimports\"\n        ],\n        \"exclusions\": {\n            \"generated\": \"lax\",\n            \"paths\": [\n                \"vendor\",\n                \"tests\",\n                \"pkg/client\",\n                \"pkg/generated\",\n                \"third_party$\",\n                \"builtin$\",\n                \"examples$\"\n            ]\n        },\n        \"settings\": {\n            \"gofmt\": {\n                \"simplify\": false\n            }\n        }\n    },\n    \"linters\": {\n        \"default\": \"none\",\n        \"enable\": [\n            \"govet\",\n            \"ineffassign\",\n            \"misspell\",\n            \"revive\"\n        ],\n        \"exclusions\": {\n            \"generated\": \"lax\",\n            \"paths\": [\n                \"vendor\",\n                \"tests\",\n                \"pkg/client\",\n                \"pkg/generated\",\n                \"third_party$\",\n                \"builtin$\",\n                \"examples$\"\n            ],\n            \"presets\": [\n                \"comments\",\n                \"common-false-positives\",\n                \"legacy\",\n                \"std-error-handling\"\n            ],\n            \"rules\": [\n                {\n                    \"linters\": [\n                        \"govet\"\n                    ],\n                    \"text\": \"^(nilness|structtag)\"\n                },\n                {\n                    \"path\": \"pkg/apis/management.cattle.io/v3/globaldns_types.go\",\n                    \"text\": \".*lobalDns.*\"\n                },\n                {\n                    \"path\": \"pkg/apis/management.cattle.io/v3/zz_generated_register.go\",\n                    \"text\": \".*lobalDns.*\"\n                },\n                {\n                    \"path\": \"pkg/apis/management.cattle.io/v3/zz_generated_list_types.go\",\n                    \"text\": \".*lobalDns.*\"\n                },\n                {\n                    \"linters\": [\n                        \"revive\"\n                    ],\n                    \"text\": \"should have comment\"\n                },\n                {\n                    \"linters\": [\n                        \"revive\"\n                    ],\n                    \"text\": \"should be of the form\"\n                },\n                {\n                    \"linters\": [\n                        \"revive\"\n                    ],\n                    \"text\": \"by other packages, and that stutters\"\n                },\n                {\n                    \"linters\": [\n                        \"revive\"\n                    ],\n                    \"text\": \"unused-parameter\"\n                },\n                {\n                    \"linters\": [\n                        \"revive\"\n                    ],\n                    \"text\": \"redefines-builtin-id\"\n                },\n                {\n                    \"linters\": [\n                        \"revive\"\n                    ],\n                    \"text\": \"superfluous-else\"\n                },\n                {\n                    \"linters\": [\n                        \"revive\"\n                    ],\n                    \"text\": \"empty-block\"\n                },\n                {\n                    \"linters\": [\n                        \"revive\"\n                    ],\n                    \"text\": \"if-return: redundant if\"\n                },\n                {\n                    \"linters\": [\n                        \"revive\"\n                    ],\n                    \"text\": \"var-naming: avoid meaningless package names\"\n                }\n            ]\n        }\n    },\n    \"run\": {\n        \"tests\": false\n    },\n    \"version\": \"2\"\n}\n"
  },
  {
    "path": "CODEOWNERS",
    "content": "*   @rancher/rancher-squad-frameworks\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n"
  },
  {
    "path": "Makefile",
    "content": "all: generate validate build\n\ngenerate:\n\tgo generate\n\nvalidate:\n\tgo fmt ./...\n\tgo vet ./...\n\nbuild:\n\tgo build ./...\n"
  },
  {
    "path": "README.md",
    "content": "# Wrangler\n\nMost people writing controllers are a bit lost as they find that there is nothing in Kubernetes that is like `type Controller interface` where you can just do `NewController`.  Instead a controller is really just a pattern of how you use the generated clientsets, informers, and listers combined with some custom event handlers and a workqueue.\n\nWrangler is a framework for using controllers. Controllers wrap clients, informers, listers into a simple usable controller pattern that promotes some good practices.\n\n<br>\n\n## Some Projects that use Wrangler\n[rancher](https://github.com/rancher/rancher)\n\n[eks-operator](https://github.com/rancher/eks-operator)\n\n[aks-operator](https://github.com/rancher/aks-operator)\n\n[gke-operator](https://github.com/rancher/gke-operator)\n\n## Versioning and Updates\n\nWrangler releases use [semantic versioning](https://semver.org/). New major releases are created for breaking changes, new minor releases are created for features, and patches are added for everything else.\n\nThe most recent Major.Minor.x release and any releases being used by the most recent patch version of a [supported rancher version](https://www.suse.com/lifecycle/#rancher) will be maintained. The most recent major will receive minor releases, along with patch releases on its most up to date minor release. Older Major.Minor.x releases still in use by rancher will receive security patches at minimum. Consequently, there will be 1-3 maintained releases of the form Major.Minor.x at a time. Currently maintained versions:\n\n| Wrangler Version | Rancher Version | Update Level           |\n| ---------------- | --------------- | ---------------------- |\n| 1.0.x            | 2.6.x           | Security Fixes         |\n| 1.1.x            | 2.7.x           | Bug and Security Fixes |\n\nWrangler releases are not from the default branch. Instead they are from branches with the naming pattern `release-MAJOR.MINOR`. The default branch (i.e. master) is where changes initially go. This includes bug fixes and new features. Bug fixes are cherry-picked to release branches to be included in patch releases. When it's time to create a new minor or major release, a new release branch is created from the default branch.\n\n<br>\n\n# Table of Contents\n1. [How it Works](#How-it-works)\n\t1. [Useful Definitions](#useful-definitions)\n2. [How to Use Wrangler](#how-to-use-wrangler)\n\t1. [How to Write and Register a Handler](#how-to-write-and-register-a-handler-to-a-controller)\n\t\t1. [Creating an Instance of a Controller](#creating-an-instance-of-a-controller)\n\t2. [How to Run Handlers](#how-to-run-handlers)\n\t3. [Different Ways of Interacting with Objects](#different-ways-of-interacting-with-objects)\n\t4. [A Look at Structures Used in Wrangler](#a-look-at-structures-used-in-wrangler)\n\n<br>\n\n# How it Works\n\nWrangler provides a code generator that will generate the clientset, informers, listers and\nadditionally generate a controller per resource type.  The interface to the controller can be seen in the [Looking at Structures Used in Wrangler](#a-look-at-structures-used-in-wrangler) section.\n\n<br>\n\nThe controller interface along with other helpful structs, interfaces, and functions are provided by another project [lasso](https://github.com/rancher/lasso). Lasso ties together the aforementioned tools while wrangler leverages them in a user friendly way.\n\nTo use the controller to run custom code for Kubernetes resource types all one needs to do is register OnChange handlers and run the controller.  Also using the controller interface one can access the client and caches through a simple flat API.\n\nA typical, non-wrangler Kubernetes application would most likely use an informer for a resource type to add an event handler. Instead, wrangler uses lasso to register each handler which then aggregates the handlers into one function that accepts an object for the controller's resource type and then runs that object through all the handlers. This function is then registered to the Kubernetes informer for that controller's respective resource type. This is done so that an object can run through the handlers in a serialized way. This allows each handler to receive the updated version of the object and avoid many conflicts that would otherwise occur if the handlers were not chained together in this fashion.\n\n<br>\n\n## Useful Definitions:\n<dl>\n\t<dt>factory</dt>\n\t<dd>Factories manage controllers. Wrangler generates factories for each API group. Wrangler factories use lasso shared factories for caches and controllers underneath.\n\tThe lasso factories do most of the heavy lifting but are more resource type agnostic. Wrangler wraps lasso's factories to provide resource type specific clients and controllers.\n\tWhen accessing a wrangler generated controller, a controller for that resource type is requested from a lasso factory. If the controller exists it will be returned. Otherwise, the lasso factory will create it, persist it, and return it. You can consult the [lasso](https://github.com/rancher/lasso) repository for more details on factories.</dd>\n\t<dt>informers</dt>\n\t<dd>Broadcasts events for a given resource type and can register handlers for those events.</dd>\n\t<dt>listers</dt>\n\t<dd>Sometimes referred to as a cache, uses informers to update a local list of objects for a certain resource type to avoid making requests to the K8s API.</dd>\n\t<dt>event handlers</dt>\n\t<dd>Functions that run when a particular event is applied to the resource type the event handler is assigned to.</dd>\n\t<dt>workqueue</dt>\n\t<dd>A queue of items to be processed. In this context a queue will usually be a queue of objects of a certain resource type waiting to be processed by all handlers assigned to that resource type.</dd>\n</dl>\n<br>\n\n# How to Use Wrangler\n\nGenerate controllers for CRDs by using Run() from the controllergen package. This will look like the\nfollowing:\n\n```golang\ncontrollergen.Run(args.Options{\n\t\tOutputPackage: \"github.com/rancher/rancher/pkg/generated\",\n\t\tBoilerplate:   \"scripts/boilerplate.go.txt\",\n\t\tGroups: map[string]args.Group{\n\t\t\t\"management.cattle.io\": {\n\t\t\t\tPackageName: \"management.cattle.io\",\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\t// All structs with an embedded ObjectMeta field will be picked up\n\t\t\t\t\t\"./pkg/apis/management.cattle.io/v3\",\n\t\t\t\t\t// ProjectCatalog and ClusterCatalog are named\n\t\t\t\t\t// explicitly here because they do not have an\n\t\t\t\t\t// ObjectMeta field in their struct. Instead\n\t\t\t\t\t// they embed type v3.Catalog{} which\n\t\t\t\t\t// is a valid object on its own and is generated\n\t\t\t\t\t// above.\n\t\t\t\t\tv3.ProjectCatalog{},\n\t\t\t\t\tv3.ClusterCatalog{},\n\t\t\t\t},\n\t\t\t\tGenerateTypes: true,\n\t\t\t},\n\t\t\t\"ui.cattle.io\": {\n\t\t\t\tPackageName: \"ui.cattle.io\",\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\t\"./pkg/apis/ui.cattle.io/v1\",\n\t\t\t\t},\n\t\t\t\tGenerateTypes: true,\n\t\t\t},\n\t\t},\n\t})\n```\n\nFor the structs to be used when generating controllers they must have the following comments above the structs (note the newline between the comment and struct so it is not rejected by linters):\n```\n// +genclient\n// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n\n```\n\nFour types are shown below. This file would be located at\n`\"pkg/apis/management.cattle.io/v3\"` relative to the project root\ndirectory. The line passing the \"./pkg/apis/management.cattle.io/v3\"\npath ensure that the Setting and Catalog controllers are generated.\nThe lines naming the ProjectCatalog and ClusterCatalog structs ensure\nthe respective controllers are generated since neither directly have\nan ObjectMeta field.:\n\n``` golang\nimport (\n\t\"github.com/rancher/norman/types\"\n)\n\n// +genclient\n// +genclient:nonNamespaced\n// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n\ntype Setting struct {\n\tmetav1.TypeMeta   `json:\",inline\"`\n\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\n\tValue      string `json:\"value\" norman:\"required\"`\n\tDefault    string `json:\"default\" norman:\"nocreate,noupdate\"`\n\tCustomized bool   `json:\"customized\" norman:\"nocreate,noupdate\"`\n\tSource     string `json:\"source\" norman:\"nocreate,noupdate,options=db|default|env\"`\n}\n\n// +genclient\n// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n\ntype ProjectCatalog struct {\n\ttypes.Namespaced\n\n\tCatalog     `json:\",inline\" mapstructure:\",squash\"`\n\tProjectName string `json:\"projectName,omitempty\" norman:\"type=reference[project]\"`\n}\n\n// +genclient\n// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n\ntype ClusterCatalog struct {\n\ttypes.Namespaced\n\n\tCatalog     `json:\",inline\" mapstructure:\",squash\"`\n\tClusterName string `json:\"clusterName,omitempty\" norman:\"required,type=reference[cluster]\"`\n}\n\n// +genclient\n// +genclient:nonNamespaced\n// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n\ntype Catalog struct {\n\tmetav1.TypeMeta `json:\",inline\"`\n\t// Standard object’s metadata. More info:\n\t// https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#metadata\n\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\t// Specification of the desired behavior of the catalog. More info:\n\t// https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status\n\tSpec   CatalogSpec   `json:\"spec\"`\n\tStatus CatalogStatus `json:\"status\"`\n}\n```\n\n__Note:__ This is real code taken from [rancher](https://github.com/rancher/rancher) and may not run at the time of reading this. This is meant to provide an example of how one might begin to use wrangler.\n\n<br>\n\n### Creating an Instance of a Controller\n\nControllers are categorized by their API group and bundled into a struct called a factory. Functions to create factories are generated by the Run function discussed above. To run one of the functions that creates a factory, import the proper package from the output directory of the generated code.\n\n```golang\nimport (\n\t\"github.com/rancher/rancher/pkg/generated/controllers/management.cattle.io\"\n\t\"k8s.io/client-go/rest\"\n)\n\nfunc createFactory(config *rest.Config) {\n\tmgmt, err := management.NewFactoryFromConfig(restConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n}\n\n// Running the functions Management() and V3(), which are the api group and version of the resource types I have generated in this example, is necessary\n// to instantiate the controller factories for the group and version. User() instantiates the controller for the user resource. This\n// can be done elsewhere, like when creating a struct but it must be done before the controller is run. Otherwise, the cache will\n// not work. In this case we are registering a handler so we would have ended up using these methods by necessity, but if we wanted\t\n// to access a cache for another resource type in our handler then we also need to make sure it is instantiated in a similar fashion.\nusers := mgmt.Management().V3().User(\"\")\n\n```\n## How to Write and Register a Handler to a Controller\n\nRegistering a handler means to assign a handler to a specific Kubernetes resource's controller. These handlers will then run when\nthe appropriate event occurs on an object of that controller's resource type.\n\nThis will be a continuation of our above example:\n\n```golang\nimport (\n\t\"context\"\n\n\t\"github.com/rancher/rancher/pkg/generated/controllers/management.cattle.io\"\n\t\"github.com/rancher/wrangler/v3/pkg/generated/controllers/core\"\n\t\"k8s.io/client-go/rest\"\n)\n\nmgmt, err := management.NewFactoryFromConfig(restConfig)\nif err != nil {\n\treturn nil, err\n}\n\nusers := mgmt.Management().V3().User(\"\")\n// passing a namespace here is optional. If an empty string is passed then the client will look at\n// all configmap objects from all namespaces\nconfigmaps := core.Management().Core().Configmaps(\"examplenamespace\")\n\nsyncHandler := func(id string, obj *v3.User) (*v3.User, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\trecordedNote := obj.Annotations != nil && obj.Annotations[\"wroteanoteaboutuser\"] == \"true\"\n\n\tif recordedNote {\n\t\t// there already is a note, noop\n\t\treturn obj, nil\n\t}\n\n\t// we are getting the \"mainrecord\" configmap from the configmap cache. The cache is maintained\n\t// locally and can try to fulfill requests without using the k8s api. This is much faster and\n\t// efficient, however it does not update immediately so it is possible that if the object\n\t// being requested was recently created that the cache will miss and return a not found error.\n\t// In this scenario you can either count on the handler reenqueueing and retrying or you can\n\t// just use the regular client.\n\trecord, err := configmaps.Cache().Get(\"\", \"mainrecord\")\n\tif err != nil {\n\t\treturn obj, err\n\t}\n\n\trecord.Data[obj.name] = \"recorded\"\n\trecord, err = configmaps.Update(record)\n\tif err != nil {\n\t\treturn obj, err\n\t}\n\n\t// This is done because obj is from the cache that is iterated over to run handlers and perform other tasks. If the subsequent\n\t// update fails then we will end up with an object on our cache that does not match the \"truth\" (how the object is in etcd).\n\tobj = obj.DeepCopy()\n\n\tif obj.Annotations == nil {\n\t\tobj.Anotations = make(map[string]string)\n\t}\n\n\tobj.Annotations[\"wroteanoteaboutuser\"] = \"true\"\n\n\t// Here we are using the k8s client embedded onto the users controller to perform an update. This will go to the K8s API.\n\treturn users.Update(obj)\n}\n\nusers.OnChange(context.Background(), \"user-example-annotate-note-handler\", syncHandler)\n```\n### How to Run Handlers\n\nNow that we have registered an OnChange handler, we can run it like so:\n`mgmt.Start(context.Background(), 50)`\n\n<br>\n\n### Different Ways of Interacting With Objects\nIn the above example, two clients and one cache are being used to interact with objects. A client can\nCreate, Update, UpdateStatus, Delete, Get, Watch and Patch an object, or List and Watch objects of its respective resource type. A Cache can get an object or list the objects for its respective resource type and will try to get the data locally (from its cache) if possible. The client and cache are the most common ways to interact with an object using wrangler.\n\nAnother way to interact with objects is to use the Apply client. The apply client works similarly to applying yaml using kubectl. This has benefits such as not assuming the existence of an object like the Update method on a client does. Instead, you can apply a state and the object will be created if it does not exist already or be updated to match the passed desired state if the object does exist\nalready. Apply also allows the use of multiple Owner References in a way unique from the client- if any owner reference is deleted the object will be deleted.\n\n<br>\n\n## A Look at Structures Used in Wrangler\n```golang\ntype FooController interface {\n\tFooClient\n\n\t// OnChange registers a handler that will run whenever an object of the matching resource type is created or updated. This function accepts a sync function specifically generated for the object type and then wraps the function in a function that is compatible with AddGenericHandler. It then uses AddGenericHandler to register the wrapped function.\n\tOnChange(ctx context.Context, name string, sync FooHandler)\n\t// OnRemove registers a handler that will run whenever an object of the matching resource type is removed. This function accepts a sync function specifically generated for the object type and then wraps the function in a function that is compatible with AddGenericRemoveHandler. It then uses AddGenericRemoveHandler to register the wrapped function.\n\tOnRemove(ctx context.Context, name string, sync FooHandler)\n\t// Enqueue will rerun all handlers registered to the object's type against the object\n\tEnqueue(namespace, name string)\n\n\t// Cache returns a locally maintained cache that can be used for get and list requests\n\tCache() FooCache\n\n\t// Informer returns an informer for the resource type\n\tInformer() cache.SharedIndexInformer\n\t// GroupVersionKind returns the API group, version, and Kind of the resource type the controller is for\n\tGroupVersionKind() schema.GroupVersionKind\n\n\t// AddGenericHandler registers the handler function for the controller\n\tAddGenericHandler(ctx context.Context, name string, handler generic.Handler)\n\t// AddGenericRemoveHandler registers a handler that will happen when an object of the controller's resource type is removed\n\tAddGenericRemoveHandler(ctx context.Context, name string, handler generic.Handler)\n\t// Updater returns a function that accepts a runtime.Object and asserts it as the controller's respective resource type struct. It then passes the object to the resource type's client's update function. This is mainly consumed internally by wrangler to implement other functionality.\n\tUpdater() generic.Updater\n}\n\ntype FooClient interface {\n\t// Create creates a new instance of resource type in kubernetes\n\tCreate(*v1alpha1.Foo) (*v1alpha1.Foo, error)\n\t// Update updates the given object in kubernetes\n\tUpdate(*v1alpha1.Foo) (*v1alpha1.Foo, error)\n\t// Status of type's CRD must be a subresource for this method to be generated. Only updates\n\t// status and does not trigger OnChange handlers.\n\tUpdateStatus(*v1alpha1.Foo) (*v1alpha1.Foo, error)\n\t// Delete deletes the given object in kubernetes\n\tDelete(namespace, name string, options *metav1.DeleteOptions) error\n\t// Get gets the object of the given name and namespace in kubernetes\n\tGet(namespace, name string, options metav1.GetOptions) (*v1alpha1.Foo, error)\n\t// List lists all the objects matching the given namespace for the resource type\n\tList(namespace string, opts metav1.ListOptions) (*v1alpha1.FooList, error)\n\t// Watch returns a channel that will stream objects as they are created, removed, or updated\n\tWatch(namespace string, opts metav1.ListOptions) (watch.Interface, error)\n\t// Patch accepts a diff that can be applied to an existing object for the client's resource type. Depending on PatchType, which specifies the strategy\n\t// to be used when applying the diff, patch can also create a new object. See the following for more information: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/, https://kubernetes.io/docs/reference/using-api/server-side-apply/.\n\tPatch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Foo, err error)\n}\n\ntype FooCache interface {\n\t// Get gets the object of the given name and namespace from the local cache\n\tGet(namespace, name string) (*v1alpha1.Foo, error)\n\t// List lists all the objects matching the given namespace for the resource type from the cache\n\tList(namespace string, selector labels.Selector) ([]*v1alpha1.Foo, error)\n\n\t// AddIndexer is used to register a function will be used to organize objects in the cache. The indexer will return a string which indicates something about the object.\n\tAddIndexer(indexName string, indexer FooIndexer)\n\t// GetByIndex will search for objects that match the given key when the named indexer is applied to it\n\tGetByIndex(indexName, key string) ([]*v1alpha1.Foo, error)\n}\n\ntype FooIndexer func(obj *v1alpha1.Foo) ([]string, error)\n\n```\n\n# Versioning\n\nSee [VERSION.md](VERSION.md).\n"
  },
  {
    "path": "VERSION.md",
    "content": "Each wrangler major version supports a range of Kubernetes minor versions. The range supported by each major release line is include below. Wrangler follows the following rules for changes between major/minor/patch:\n\n<ins>Major Version Increases</ins>:\n- Support for a kubernetes version is explicitly removed (note that this means that wrangler uses a feature that does not work on this version).\n- A breaking change is made, which is not necessary to resolve a defect.\n\n<ins>Minor Version Increases</ins>:\n- Support for a kubernetes version is added.\n- A breaking change in an exported function is made to resolve a defect.\n\n<ins>Patch Version Increases</ins>\n- A bug was fixed.\n- A feature was added, in a backwards-compatible way.\n- A breaking change in an exported function is made to resolve a CVE.\n\n<ins>Dealing with Kubernetes 1.35</ins>\nClients working with versions of Kubernetes before 1.35 might not work with the `main`\nbranch. Use a tag off the `release/v3` branch instead.  At a later point there will be\na Wrangler Major version `v4`.\n\nThe current supported release lines are:\n\n| Wrangler Branch | Wrangler Major version | Supported Kubernetes Versions |\n|--------------------------|------------------------------------|------------------------------------------------|\n| main | v3 | v1.26 - v1.35 |\n| release/v2 | v2 | v1.23 - v1.26 |\n| release/v3 | v3 | v1.23 - v1.34 |\n"
  },
  {
    "path": "codegen.go",
    "content": "//go:generate /bin/rm -rf pkg/generated\n//go:generate go run pkg/codegen/main.go\n\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nvar (\n\tVersion   = \"v0.0.0-dev\"\n\tGitCommit = \"v0.0.0-dev\"\n)\n\nfunc main() {\n\tfmt.Println(\"Run go generate\")\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/rancher/wrangler/v3\n\ngo 1.25.0\n\nrequire (\n\tgithub.com/evanphx/json-patch v5.9.11+incompatible\n\tgithub.com/ghodss/yaml v1.0.0\n\tgithub.com/moby/locker v1.0.1\n\tgithub.com/rancher/lasso v0.2.7\n\tgithub.com/sirupsen/logrus v1.9.4\n\tgithub.com/stretchr/testify v1.11.1\n\tgo.uber.org/mock v0.6.0\n\tgolang.org/x/sync v0.20.0\n\tgolang.org/x/text v0.35.0\n\tgolang.org/x/tools v0.43.0\n\tk8s.io/api v0.35.0\n\tk8s.io/apiextensions-apiserver v0.35.0\n\tk8s.io/apimachinery v0.35.0\n\tk8s.io/client-go v0.35.0\n\tk8s.io/code-generator v0.35.0\n\tk8s.io/gengo v0.0.0-20250130153323-76c5745d3511\n\tk8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b\n\tk8s.io/kube-aggregator v0.35.0\n\tk8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912\n\tsigs.k8s.io/cli-utils v0.37.2\n)\n\nrequire (\n\tcel.dev/expr v0.24.0 // indirect\n\tgithub.com/antlr4-go/antlr/v4 v4.13.0 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/blang/semver/v4 v4.0.0 // indirect\n\tgithub.com/cenkalti/backoff/v4 v4.3.0 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect\n\tgithub.com/emicklei/go-restful/v3 v3.12.2 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.4 // indirect\n\tgithub.com/fxamacker/cbor/v2 v2.9.0 // indirect\n\tgithub.com/go-logr/logr v1.4.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.21.0 // indirect\n\tgithub.com/go-openapi/jsonreference v0.21.0 // indirect\n\tgithub.com/go-openapi/swag v0.23.0 // indirect\n\tgithub.com/google/cel-go v0.26.0 // indirect\n\tgithub.com/google/gnostic-models v0.7.0 // indirect\n\tgithub.com/google/go-cmp v0.7.0 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/mailru/easyjson v0.7.7 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect\n\tgithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect\n\tgithub.com/prometheus/client_golang v1.23.2 // indirect\n\tgithub.com/prometheus/client_model v0.6.2 // indirect\n\tgithub.com/prometheus/common v0.66.1 // indirect\n\tgithub.com/prometheus/procfs v0.16.1 // indirect\n\tgithub.com/spf13/cobra v1.10.0 // indirect\n\tgithub.com/spf13/pflag v1.0.9 // indirect\n\tgithub.com/stoewer/go-strcase v1.3.0 // indirect\n\tgithub.com/x448/float16 v0.8.4 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.1.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect\n\tgo.opentelemetry.io/otel v1.36.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect\n\tgo.opentelemetry.io/otel/metric v1.36.0 // indirect\n\tgo.opentelemetry.io/otel/sdk v1.36.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.36.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v1.5.0 // indirect\n\tgo.yaml.in/yaml/v2 v2.4.3 // indirect\n\tgo.yaml.in/yaml/v3 v3.0.4 // indirect\n\tgolang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect\n\tgolang.org/x/mod v0.34.0 // indirect\n\tgolang.org/x/net v0.52.0 // indirect\n\tgolang.org/x/oauth2 v0.30.0 // indirect\n\tgolang.org/x/sys v0.42.0 // indirect\n\tgolang.org/x/term v0.41.0 // indirect\n\tgolang.org/x/time v0.9.0 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect\n\tgoogle.golang.org/grpc v1.72.2 // indirect\n\tgoogle.golang.org/protobuf v1.36.8 // indirect\n\tgopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect\n\tgopkg.in/inf.v0 v0.9.1 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tk8s.io/apiserver v0.35.0 // indirect\n\tk8s.io/component-base v0.35.0 // indirect\n\tk8s.io/klog/v2 v2.130.1 // indirect\n\tk8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect\n\tsigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect\n\tsigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect\n\tsigs.k8s.io/randfill v1.0.0 // indirect\n\tsigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect\n\tsigs.k8s.io/yaml v1.6.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=\ncel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=\ngithub.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=\ngithub.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=\ngithub.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=\ngithub.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=\ngithub.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=\ngithub.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=\ngithub.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=\ngithub.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=\ngithub.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=\ngithub.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=\ngithub.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=\ngithub.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=\ngithub.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=\ngithub.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=\ngithub.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=\ngithub.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=\ngithub.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=\ngithub.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=\ngithub.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=\ngithub.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=\ngithub.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=\ngithub.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=\ngithub.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=\ngithub.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=\ngithub.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI=\ngithub.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=\ngithub.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=\ngithub.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=\ngithub.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=\ngithub.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=\ngithub.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=\ngithub.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=\ngithub.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=\ngithub.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=\ngithub.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=\ngithub.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=\ngithub.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=\ngithub.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=\ngithub.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=\ngithub.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=\ngithub.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=\ngithub.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=\ngithub.com/rancher/lasso v0.2.7 h1:N56Pm8KJSk7gRVYILSGb35QBfjcDDeGFMfwUCivsbeg=\ngithub.com/rancher/lasso v0.2.7/go.mod h1:L3ol8PdO21KoMhNa3RWjpR3ZBnE70JCAod1nJuOvT1E=\ngithub.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=\ngithub.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=\ngithub.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=\ngithub.com/spf13/cobra v1.10.0 h1:a5/WeUlSDCvV5a45ljW2ZFtV0bTDpkfSAj3uqB6Sc+0=\ngithub.com/spf13/cobra v1.10.0/go.mod h1:9dhySC7dnTtEiqzmqfkLj47BslqLCUPMXjG2lj/NgoE=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.8/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=\ngithub.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=\ngithub.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngo.etcd.io/etcd/api/v3 v3.6.5 h1:pMMc42276sgR1j1raO/Qv3QI9Af/AuyQUW6CBAWuntA=\ngo.etcd.io/etcd/api/v3 v3.6.5/go.mod h1:ob0/oWA/UQQlT1BmaEkWQzI0sJ1M0Et0mMpaABxguOQ=\ngo.etcd.io/etcd/client/pkg/v3 v3.6.5 h1:Duz9fAzIZFhYWgRjp/FgNq2gO1jId9Yae/rLn3RrBP8=\ngo.etcd.io/etcd/client/pkg/v3 v3.6.5/go.mod h1:8Wx3eGRPiy0qOFMZT/hfvdos+DjEaPxdIDiCDUv/FQk=\ngo.etcd.io/etcd/client/v3 v3.6.5 h1:yRwZNFBx/35VKHTcLDeO7XVLbCBFbPi+XV4OC3QJf2U=\ngo.etcd.io/etcd/client/v3 v3.6.5/go.mod h1:ZqwG/7TAFZ0BJ0jXRPoJjKQJtbFo/9NIY8uoFFKcCyo=\ngo.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=\ngo.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=\ngo.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=\ngo.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=\ngo.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=\ngo.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=\ngo.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=\ngo.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=\ngo.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=\ngo.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=\ngo.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=\ngo.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=\ngo.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=\ngo.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngo.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=\ngo.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=\ngo.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=\ngo.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=\ngo.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=\ngo.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=\ngo.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=\ngo.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=\ngolang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=\ngolang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=\ngolang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=\ngolang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=\ngolang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=\ngolang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=\ngolang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=\ngolang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=\ngolang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=\ngolang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=\ngolang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=\ngolang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=\ngolang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=\ngolang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=\ngolang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=\ngolang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=\ngolang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=\ngoogle.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=\ngoogle.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=\ngoogle.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=\ngoogle.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=\ngopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=\ngopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nk8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY=\nk8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA=\nk8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4=\nk8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU=\nk8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8=\nk8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=\nk8s.io/apiserver v0.35.0 h1:CUGo5o+7hW9GcAEF3x3usT3fX4f9r8xmgQeCBDaOgX4=\nk8s.io/apiserver v0.35.0/go.mod h1:QUy1U4+PrzbJaM3XGu2tQ7U9A4udRRo5cyxkFX0GEds=\nk8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE=\nk8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o=\nk8s.io/code-generator v0.35.0 h1:TvrtfKYZTm9oDF2z+veFKSCcgZE3Igv0svY+ehCmjHQ=\nk8s.io/code-generator v0.35.0/go.mod h1:iS1gvVf3c/T71N5DOGYO+Gt3PdJ6B9LYSvIyQ4FHzgc=\nk8s.io/component-base v0.35.0 h1:+yBrOhzri2S1BVqyVSvcM3PtPyx5GUxCK2tinZz1G94=\nk8s.io/component-base v0.35.0/go.mod h1:85SCX4UCa6SCFt6p3IKAPej7jSnF3L8EbfSyMZayJR0=\nk8s.io/gengo v0.0.0-20250130153323-76c5745d3511 h1:4eL6zr5VCj71nu2nOuQ6j6m/kqh5WueXBN8daZkNe90=\nk8s.io/gengo v0.0.0-20250130153323-76c5745d3511/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=\nk8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b h1:gMplByicHV/TJBizHd9aVEsTYoJBnnUAT5MHlTkbjhQ=\nk8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b/go.mod h1:CgujABENc3KuTrcsdpGmrrASjtQsWCT7R99mEV4U/fM=\nk8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=\nk8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=\nk8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=\nk8s.io/kube-aggregator v0.35.0 h1:FBtbuRFA7Ohe2QKirFZcJf8rgimC8oSaNiCi4pdU5xw=\nk8s.io/kube-aggregator v0.35.0/go.mod h1:vKBRpQUfDryb7udwUwF3eCSvv3AJNgHtL4PGl6PqAg8=\nk8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=\nk8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=\nk8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck=\nk8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=\nsigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=\nsigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=\nsigs.k8s.io/cli-utils v0.37.2 h1:GOfKw5RV2HDQZDJlru5KkfLO1tbxqMoyn1IYUxqBpNg=\nsigs.k8s.io/cli-utils v0.37.2/go.mod h1:V+IZZr4UoGj7gMJXklWBg6t5xbdThFBcpj4MrZuCYco=\nsigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=\nsigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=\nsigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=\nsigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=\nsigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=\nsigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=\nsigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=\nsigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=\nsigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=\n"
  },
  {
    "path": "pkg/apply/apply.go",
    "content": "package apply\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply/injectors\"\n\t\"github.com/rancher/wrangler/v3/pkg/objectset\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"k8s.io/client-go/discovery\"\n\t\"k8s.io/client-go/dynamic\"\n\t\"k8s.io/client-go/rest\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\nconst (\n\tdefaultNamespace = \"default\"\n)\n\ntype Patcher func(namespace, name string, pt types.PatchType, data []byte) (runtime.Object, error)\n\n// Reconciler return false if it did not handle this object\ntype Reconciler func(oldObj runtime.Object, newObj runtime.Object) (bool, error)\n\ntype ClientFactory func(gvr schema.GroupVersionResource) (dynamic.NamespaceableResourceInterface, error)\n\ntype InformerFactory interface {\n\tGet(gvk schema.GroupVersionKind, gvr schema.GroupVersionResource) (cache.SharedIndexInformer, error)\n}\n\ntype InformerGetter interface {\n\tInformer() cache.SharedIndexInformer\n\tGroupVersionKind() schema.GroupVersionKind\n}\n\ntype PatchByGVK map[schema.GroupVersionKind]map[objectset.ObjectKey]string\n\nfunc (p PatchByGVK) Add(gvk schema.GroupVersionKind, namespace, name, patch string) {\n\td, ok := p[gvk]\n\tif !ok {\n\t\td = map[objectset.ObjectKey]string{}\n\t\tp[gvk] = d\n\t}\n\td[objectset.ObjectKey{\n\t\tName:      name,\n\t\tNamespace: namespace,\n\t}] = patch\n}\n\ntype Plan struct {\n\tCreate  objectset.ObjectKeyByGVK\n\tDelete  objectset.ObjectKeyByGVK\n\tUpdate  PatchByGVK\n\tObjects []runtime.Object\n}\n\ntype Apply interface {\n\tApply(set *objectset.ObjectSet) error\n\tApplyObjects(objs ...runtime.Object) error\n\tWithContext(ctx context.Context) Apply\n\tWithCacheTypes(igs ...InformerGetter) Apply\n\tWithCacheTypeFactory(factory InformerFactory) Apply\n\tWithSetID(id string) Apply\n\tWithOwner(obj runtime.Object) Apply\n\tWithOwnerKey(key string, gvk schema.GroupVersionKind) Apply\n\tWithInjector(injs ...injectors.ConfigInjector) Apply\n\tWithInjectorName(injs ...string) Apply\n\tWithPatcher(gvk schema.GroupVersionKind, patchers Patcher) Apply\n\tWithReconciler(gvk schema.GroupVersionKind, reconciler Reconciler) Apply\n\tWithStrictCaching() Apply\n\tWithDynamicLookup() Apply\n\tWithRestrictClusterScoped() Apply\n\tWithDefaultNamespace(ns string) Apply\n\tWithListerNamespace(ns string) Apply\n\tWithRateLimiting(ratelimitingQPS float32) Apply\n\tWithNoDelete() Apply\n\tWithNoDeleteGVK(gvks ...schema.GroupVersionKind) Apply\n\tWithGVK(gvks ...schema.GroupVersionKind) Apply\n\tWithSetOwnerReference(controller, block bool) Apply\n\tWithIgnorePreviousApplied() Apply\n\tWithDiffPatch(gvk schema.GroupVersionKind, namespace, name string, patch []byte) Apply\n\n\tFindOwner(obj runtime.Object) (runtime.Object, error)\n\tPurgeOrphan(obj runtime.Object) error\n\tDryRun(objs ...runtime.Object) (Plan, error)\n}\n\nfunc NewForConfig(cfg *rest.Config) (Apply, error) {\n\tdiscovery, err := discovery.NewDiscoveryClientForConfig(cfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn New(discovery, NewClientFactory(cfg)), nil\n}\n\nfunc New(discovery discovery.DiscoveryInterface, cf ClientFactory, igs ...InformerGetter) Apply {\n\ta := &apply{\n\t\tclients: &clients{\n\t\t\tclientFactory: cf,\n\t\t\tdiscovery:     discovery,\n\t\t\tnamespaced:    map[schema.GroupVersionKind]bool{},\n\t\t\tgvkToGVR:      map[schema.GroupVersionKind]schema.GroupVersionResource{},\n\t\t\tclients:       map[schema.GroupVersionKind]dynamic.NamespaceableResourceInterface{},\n\t\t},\n\t\tinformers: map[schema.GroupVersionKind]cache.SharedIndexInformer{},\n\t}\n\n\tfor _, ig := range igs {\n\t\ta.informers[ig.GroupVersionKind()] = ig.Informer()\n\t}\n\n\treturn a\n}\n\ntype apply struct {\n\tclients   *clients\n\tinformers map[schema.GroupVersionKind]cache.SharedIndexInformer\n}\n\ntype clients struct {\n\tsync.Mutex\n\n\tclientFactory ClientFactory\n\tdiscovery     discovery.DiscoveryInterface\n\tnamespaced    map[schema.GroupVersionKind]bool\n\tgvkToGVR      map[schema.GroupVersionKind]schema.GroupVersionResource\n\tclients       map[schema.GroupVersionKind]dynamic.NamespaceableResourceInterface\n}\n\nfunc (c *clients) IsNamespaced(gvk schema.GroupVersionKind) (bool, error) {\n\tc.Lock()\n\tok, exists := c.namespaced[gvk]\n\tc.Unlock()\n\n\tif exists {\n\t\treturn ok, nil\n\t}\n\t_, err := c.client(gvk)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tc.Lock()\n\tdefer c.Unlock()\n\treturn c.namespaced[gvk], nil\n}\n\nfunc (c *clients) gvr(gvk schema.GroupVersionKind) schema.GroupVersionResource {\n\tc.Lock()\n\tdefer c.Unlock()\n\treturn c.gvkToGVR[gvk]\n}\n\nfunc (c *clients) client(gvk schema.GroupVersionKind) (dynamic.NamespaceableResourceInterface, error) {\n\tc.Lock()\n\tdefer c.Unlock()\n\n\tif client, ok := c.clients[gvk]; ok {\n\t\treturn client, nil\n\t}\n\n\tresources, err := c.discovery.ServerResourcesForGroupVersion(gvk.GroupVersion().String())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, resource := range resources.APIResources {\n\t\tif resource.Kind != gvk.Kind {\n\t\t\tcontinue\n\t\t}\n\n\t\tclient, err := c.clientFactory(gvk.GroupVersion().WithResource(resource.Name))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tc.namespaced[gvk] = resource.Namespaced\n\t\tc.clients[gvk] = client\n\t\tc.gvkToGVR[gvk] = gvk.GroupVersion().WithResource(resource.Name)\n\t\treturn client, nil\n\t}\n\n\treturn nil, fmt.Errorf(\"failed to discover client for %s\", gvk)\n}\n\nfunc (a *apply) newDesiredSet() desiredSet {\n\treturn desiredSet{\n\t\ta:                a,\n\t\tdefaultNamespace: defaultNamespace,\n\t\tctx:              context.Background(),\n\t\tratelimitingQPS:  1,\n\t\treconcilers:      defaultReconcilers,\n\t\tstrictCaching:    true,\n\t}\n}\n\nfunc (a *apply) DryRun(objs ...runtime.Object) (Plan, error) {\n\treturn a.newDesiredSet().DryRun(objs...)\n}\n\nfunc (a *apply) Apply(set *objectset.ObjectSet) error {\n\treturn a.newDesiredSet().Apply(set)\n}\n\nfunc (a *apply) ApplyObjects(objs ...runtime.Object) error {\n\tos := objectset.NewObjectSet()\n\tos.Add(objs...)\n\treturn a.newDesiredSet().Apply(os)\n}\n\nfunc (a *apply) WithSetID(id string) Apply {\n\treturn a.newDesiredSet().WithSetID(id)\n}\n\nfunc (a *apply) WithOwner(obj runtime.Object) Apply {\n\treturn a.newDesiredSet().WithOwner(obj)\n}\n\nfunc (a *apply) WithOwnerKey(key string, gvk schema.GroupVersionKind) Apply {\n\treturn a.newDesiredSet().WithOwnerKey(key, gvk)\n}\n\nfunc (a *apply) WithInjector(injs ...injectors.ConfigInjector) Apply {\n\treturn a.newDesiredSet().WithInjector(injs...)\n}\n\nfunc (a *apply) WithInjectorName(injs ...string) Apply {\n\treturn a.newDesiredSet().WithInjectorName(injs...)\n}\n\nfunc (a *apply) WithCacheTypes(igs ...InformerGetter) Apply {\n\treturn a.newDesiredSet().WithCacheTypes(igs...)\n}\n\nfunc (a *apply) WithCacheTypeFactory(factory InformerFactory) Apply {\n\treturn a.newDesiredSet().WithCacheTypeFactory(factory)\n}\n\nfunc (a *apply) WithGVK(gvks ...schema.GroupVersionKind) Apply {\n\treturn a.newDesiredSet().WithGVK(gvks...)\n}\n\nfunc (a *apply) WithPatcher(gvk schema.GroupVersionKind, patcher Patcher) Apply {\n\treturn a.newDesiredSet().WithPatcher(gvk, patcher)\n}\n\nfunc (a *apply) WithReconciler(gvk schema.GroupVersionKind, reconciler Reconciler) Apply {\n\treturn a.newDesiredSet().WithReconciler(gvk, reconciler)\n}\n\nfunc (a *apply) WithStrictCaching() Apply {\n\treturn a.newDesiredSet().WithStrictCaching()\n}\n\nfunc (a *apply) WithDynamicLookup() Apply {\n\treturn a.newDesiredSet().WithDynamicLookup()\n}\n\nfunc (a *apply) WithRestrictClusterScoped() Apply {\n\treturn a.newDesiredSet().WithRestrictClusterScoped()\n}\n\nfunc (a *apply) WithDefaultNamespace(ns string) Apply {\n\treturn a.newDesiredSet().WithDefaultNamespace(ns)\n}\n\nfunc (a *apply) WithListerNamespace(ns string) Apply {\n\treturn a.newDesiredSet().WithListerNamespace(ns)\n}\n\nfunc (a *apply) WithRateLimiting(ratelimitingQPS float32) Apply {\n\treturn a.newDesiredSet().WithRateLimiting(ratelimitingQPS)\n}\n\nfunc (a *apply) WithNoDelete() Apply {\n\treturn a.newDesiredSet().WithNoDelete()\n}\n\nfunc (a *apply) WithNoDeleteGVK(gvks ...schema.GroupVersionKind) Apply {\n\treturn a.newDesiredSet().WithNoDeleteGVK(gvks...)\n}\n\nfunc (a *apply) WithSetOwnerReference(controller, block bool) Apply {\n\treturn a.newDesiredSet().WithSetOwnerReference(controller, block)\n}\n\nfunc (a *apply) WithContext(ctx context.Context) Apply {\n\treturn a.newDesiredSet().WithContext(ctx)\n}\n\nfunc (a *apply) WithIgnorePreviousApplied() Apply {\n\treturn a.newDesiredSet().WithIgnorePreviousApplied()\n}\n\nfunc (a *apply) FindOwner(obj runtime.Object) (runtime.Object, error) {\n\treturn a.newDesiredSet().FindOwner(obj)\n}\n\nfunc (a *apply) PurgeOrphan(obj runtime.Object) error {\n\treturn a.newDesiredSet().PurgeOrphan(obj)\n}\n\nfunc (a *apply) WithDiffPatch(gvk schema.GroupVersionKind, namespace, name string, patch []byte) Apply {\n\treturn a.newDesiredSet().WithDiffPatch(gvk, namespace, name, patch)\n}\n"
  },
  {
    "path": "pkg/apply/client_factory.go",
    "content": "package apply\n\nimport (\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/client-go/dynamic\"\n\t\"k8s.io/client-go/rest\"\n)\n\nfunc NewClientFactory(config *rest.Config) ClientFactory {\n\treturn func(gvr schema.GroupVersionResource) (dynamic.NamespaceableResourceInterface, error) {\n\t\tclient, err := dynamic.NewForConfig(config)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn client.Resource(gvr), nil\n\t}\n}\n"
  },
  {
    "path": "pkg/apply/desiredset.go",
    "content": "package apply\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply/injectors\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\t\"github.com/rancher/wrangler/v3/pkg/merr\"\n\t\"github.com/rancher/wrangler/v3/pkg/objectset\"\n\t\"github.com/sirupsen/logrus\"\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\n// Indexer name added for cached types\nconst byHash = \"wrangler.byObjectSetHash\"\n\ntype patchKey struct {\n\tschema.GroupVersionKind\n\tobjectset.ObjectKey\n}\n\ntype desiredSet struct {\n\ta                        *apply\n\tctx                      context.Context\n\tdefaultNamespace         string\n\tlisterNamespace          string\n\tignorePreviousApplied    bool\n\tsetOwnerReference        bool\n\townerReferenceController bool\n\townerReferenceBlock      bool\n\tstrictCaching            bool\n\trestrictClusterScoped    bool\n\tpruneTypes               map[schema.GroupVersionKind]cache.SharedIndexInformer\n\tpatchers                 map[schema.GroupVersionKind]Patcher\n\treconcilers              map[schema.GroupVersionKind]Reconciler\n\tdiffPatches              map[patchKey][][]byte\n\tinformerFactory          InformerFactory\n\tremove                   bool\n\tnoDelete                 bool\n\tnoDeleteGVK              map[schema.GroupVersionKind]struct{}\n\tsetID                    string\n\tobjs                     *objectset.ObjectSet\n\towner                    runtime.Object\n\tinjectors                []injectors.ConfigInjector\n\tratelimitingQPS          float32\n\tinjectorNames            []string\n\terrs                     []error\n\n\tcreatePlan bool\n\tplan       Plan\n}\n\nfunc (o *desiredSet) err(err error) error {\n\to.errs = append(o.errs, err)\n\treturn o.Err()\n}\n\nfunc (o desiredSet) Err() error {\n\treturn merr.NewErrors(append(o.errs, o.objs.Err())...)\n}\n\nfunc (o desiredSet) DryRun(objs ...runtime.Object) (Plan, error) {\n\to.objs = objectset.NewObjectSet()\n\to.objs.Add(objs...)\n\treturn o.dryRun()\n}\n\nfunc (o desiredSet) Apply(set *objectset.ObjectSet) error {\n\tif set == nil {\n\t\tset = objectset.NewObjectSet()\n\t}\n\to.objs = set\n\treturn o.apply()\n}\n\nfunc (o desiredSet) ApplyObjects(objs ...runtime.Object) error {\n\tos := objectset.NewObjectSet()\n\tos.Add(objs...)\n\treturn o.Apply(os)\n}\n\nfunc (o desiredSet) WithDiffPatch(gvk schema.GroupVersionKind, namespace, name string, patch []byte) Apply {\n\tpatches := map[patchKey][][]byte{}\n\tfor k, v := range o.diffPatches {\n\t\tpatches[k] = v\n\t}\n\tkey := patchKey{\n\t\tGroupVersionKind: gvk,\n\t\tObjectKey: objectset.ObjectKey{\n\t\t\tName:      name,\n\t\t\tNamespace: namespace,\n\t\t},\n\t}\n\tpatches[key] = append(patches[key], patch)\n\to.diffPatches = patches\n\treturn o\n}\n\n// WithGVK uses a known listing of existing gvks to modify the the prune types to allow for deletion of objects\nfunc (o desiredSet) WithGVK(gvks ...schema.GroupVersionKind) Apply {\n\tpruneTypes := make(map[schema.GroupVersionKind]cache.SharedIndexInformer, len(gvks))\n\tfor k, v := range o.pruneTypes {\n\t\tpruneTypes[k] = v\n\t}\n\tfor _, gvk := range gvks {\n\t\tpruneTypes[gvk] = nil\n\t}\n\to.pruneTypes = pruneTypes\n\treturn o\n}\n\nfunc (o desiredSet) WithSetID(id string) Apply {\n\to.setID = id\n\treturn o\n}\n\nfunc (o desiredSet) WithOwnerKey(key string, gvk schema.GroupVersionKind) Apply {\n\tobj := &v1.PartialObjectMetadata{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(gvk)\n\to.owner = obj\n\treturn o\n}\n\nfunc (o desiredSet) WithOwner(obj runtime.Object) Apply {\n\to.owner = obj\n\treturn o\n}\n\nfunc (o desiredSet) WithSetOwnerReference(controller, block bool) Apply {\n\to.setOwnerReference = true\n\to.ownerReferenceController = controller\n\to.ownerReferenceBlock = block\n\treturn o\n}\n\nfunc (o desiredSet) WithInjector(injs ...injectors.ConfigInjector) Apply {\n\to.injectors = append(o.injectors, injs...)\n\treturn o\n}\n\nfunc (o desiredSet) WithInjectorName(injs ...string) Apply {\n\to.injectorNames = append(o.injectorNames, injs...)\n\treturn o\n}\n\nfunc (o desiredSet) WithCacheTypeFactory(factory InformerFactory) Apply {\n\to.informerFactory = factory\n\treturn o\n}\n\nfunc (o desiredSet) WithIgnorePreviousApplied() Apply {\n\to.ignorePreviousApplied = true\n\treturn o\n}\n\nfunc (o desiredSet) WithCacheTypes(igs ...InformerGetter) Apply {\n\tpruneTypes := make(map[schema.GroupVersionKind]cache.SharedIndexInformer, len(igs))\n\tfor k, v := range o.pruneTypes {\n\t\tpruneTypes[k] = v\n\t}\n\n\tfor _, ig := range igs {\n\t\tinformer := ig.Informer()\n\t\tif err := addIndexerByHash(informer.GetIndexer()); err != nil {\n\t\t\t// Ignore repeatedly adding the same indexer for different types\n\t\t\tif !errors.Is(err, errIndexerAlreadyExists) {\n\t\t\t\tlogrus.Warnf(\"Problem adding hash indexer to informer [%s]: %v\", ig.GroupVersionKind().Kind, err)\n\t\t\t}\n\t\t}\n\t\tpruneTypes[ig.GroupVersionKind()] = informer\n\t}\n\n\to.pruneTypes = pruneTypes\n\treturn o\n}\n\n// addIndexerByHash an Informer to index objects by the hash annotation value\nfunc addIndexerByHash(indexer cache.Indexer) error {\n\tif _, alreadyAdded := indexer.GetIndexers()[byHash]; alreadyAdded {\n\t\treturn fmt.Errorf(\"adding indexer %q: %w\", byHash, errIndexerAlreadyExists)\n\t}\n\treturn indexer.AddIndexers(map[string]cache.IndexFunc{\n\t\tbyHash: func(obj interface{}) ([]string, error) {\n\t\t\tmetadata, err := meta.Accessor(obj)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tlabels := metadata.GetLabels()\n\t\t\tif labels == nil || labels[LabelHash] == \"\" {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t\treturn []string{labels[LabelHash]}, nil\n\t\t},\n\t})\n}\n\nfunc (o desiredSet) WithPatcher(gvk schema.GroupVersionKind, patcher Patcher) Apply {\n\tpatchers := map[schema.GroupVersionKind]Patcher{}\n\tfor k, v := range o.patchers {\n\t\tpatchers[k] = v\n\t}\n\tpatchers[gvk] = patcher\n\to.patchers = patchers\n\treturn o\n}\n\nfunc (o desiredSet) WithReconciler(gvk schema.GroupVersionKind, reconciler Reconciler) Apply {\n\treconcilers := map[schema.GroupVersionKind]Reconciler{}\n\tfor k, v := range o.reconcilers {\n\t\treconcilers[k] = v\n\t}\n\treconcilers[gvk] = reconciler\n\to.reconcilers = reconcilers\n\treturn o\n}\n\nfunc (o desiredSet) WithStrictCaching() Apply {\n\to.strictCaching = true\n\treturn o\n}\nfunc (o desiredSet) WithDynamicLookup() Apply {\n\to.strictCaching = false\n\treturn o\n}\n\nfunc (o desiredSet) WithRestrictClusterScoped() Apply {\n\to.restrictClusterScoped = true\n\treturn o\n}\n\nfunc (o desiredSet) WithDefaultNamespace(ns string) Apply {\n\tif ns == \"\" {\n\t\to.defaultNamespace = defaultNamespace\n\t} else {\n\t\to.defaultNamespace = ns\n\t}\n\treturn o\n}\n\nfunc (o desiredSet) WithListerNamespace(ns string) Apply {\n\to.listerNamespace = ns\n\treturn o\n}\n\nfunc (o desiredSet) WithRateLimiting(ratelimitingQPS float32) Apply {\n\to.ratelimitingQPS = ratelimitingQPS\n\treturn o\n}\n\nfunc (o desiredSet) WithNoDelete() Apply {\n\to.noDelete = true\n\treturn o\n}\n\nfunc (o desiredSet) WithNoDeleteGVK(gvks ...schema.GroupVersionKind) Apply {\n\tif o.noDeleteGVK == nil {\n\t\to.noDeleteGVK = make(map[schema.GroupVersionKind]struct{})\n\t}\n\tfor _, curr := range gvks {\n\t\to.noDeleteGVK[curr] = struct{}{}\n\t}\n\treturn o\n}\n\nfunc (o desiredSet) WithContext(ctx context.Context) Apply {\n\to.ctx = ctx\n\treturn o\n}\n"
  },
  {
    "path": "pkg/apply/desiredset_apply.go",
    "content": "package apply\n\nimport (\n\t\"crypto/sha1\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/sirupsen/logrus\"\n\n\tgvk2 \"github.com/rancher/wrangler/v3/pkg/gvk\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply/injectors\"\n\t\"github.com/rancher/wrangler/v3/pkg/objectset\"\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/selection\"\n\t\"k8s.io/client-go/util/flowcontrol\"\n)\n\nconst (\n\tLabelID        = \"objectset.rio.cattle.io/id\"\n\tLabelGVK       = \"objectset.rio.cattle.io/owner-gvk\"\n\tLabelName      = \"objectset.rio.cattle.io/owner-name\"\n\tLabelNamespace = \"objectset.rio.cattle.io/owner-namespace\"\n\tLabelHash      = \"objectset.rio.cattle.io/hash\"\n\tLabelPrefix    = \"objectset.rio.cattle.io/\"\n\tLabelPrune     = \"objectset.rio.cattle.io/prune\"\n)\n\nvar (\n\thashOrder = []string{\n\t\tLabelID,\n\t\tLabelGVK,\n\t\tLabelName,\n\t\tLabelNamespace,\n\t}\n\trls                     = map[string]flowcontrol.RateLimiter{}\n\trlsLock                 sync.Mutex\n\terrIndexerAlreadyExists = errors.New(\"an indexer with the same already exists\")\n)\n\nfunc (o *desiredSet) getRateLimit(labelHash string) flowcontrol.RateLimiter {\n\tvar rl flowcontrol.RateLimiter\n\n\trlsLock.Lock()\n\tdefer rlsLock.Unlock()\n\tif o.remove {\n\t\tdelete(rls, labelHash)\n\t} else {\n\t\trl = rls[labelHash]\n\t\tif rl == nil {\n\t\t\trl = flowcontrol.NewTokenBucketRateLimiter(o.ratelimitingQPS, 10)\n\t\t\trls[labelHash] = rl\n\t\t}\n\t}\n\n\treturn rl\n}\n\nfunc (o *desiredSet) dryRun() (Plan, error) {\n\to.createPlan = true\n\to.plan.Create = objectset.ObjectKeyByGVK{}\n\to.plan.Update = PatchByGVK{}\n\to.plan.Delete = objectset.ObjectKeyByGVK{}\n\terr := o.apply()\n\treturn o.plan, err\n}\n\nfunc (o *desiredSet) apply() error {\n\tif o.objs == nil || o.objs.Len() == 0 {\n\t\to.remove = true\n\t}\n\n\tif err := o.Err(); err != nil {\n\t\treturn err\n\t}\n\n\tlabelSet, annotationSet, err := GetLabelsAndAnnotations(o.setID, o.owner)\n\tif err != nil {\n\t\treturn o.err(err)\n\t}\n\n\trl := o.getRateLimit(labelSet[LabelHash])\n\tif rl != nil {\n\t\tt := time.Now()\n\t\trl.Accept()\n\t\tif d := time.Now().Sub(t); d.Seconds() > 1 {\n\t\t\tlogrus.Infof(\"rate limited %s(%s) %s\", o.setID, labelSet, d)\n\t\t}\n\t}\n\n\tobjList, err := o.injectLabelsAndAnnotations(labelSet, annotationSet)\n\tif err != nil {\n\t\treturn o.err(err)\n\t}\n\n\tobjList, err = o.runInjectors(objList)\n\tif err != nil {\n\t\treturn o.err(err)\n\t}\n\n\tobjs := o.collect(objList)\n\n\tdebugID := o.debugID()\n\tsel, err := GetSelector(labelSet)\n\tif err != nil {\n\t\treturn o.err(err)\n\t}\n\n\tfor _, gvk := range o.objs.GVKOrder(o.knownGVK()...) {\n\t\to.process(debugID, sel, gvk, objs[gvk])\n\t}\n\n\treturn o.Err()\n}\n\nfunc (o *desiredSet) knownGVK() (ret []schema.GroupVersionKind) {\n\tfor k := range o.pruneTypes {\n\t\tret = append(ret, k)\n\t}\n\treturn\n}\n\nfunc (o *desiredSet) debugID() string {\n\tif o.owner == nil {\n\t\treturn o.setID\n\t}\n\tmetadata, err := meta.Accessor(o.owner)\n\tif err != nil {\n\t\treturn o.setID\n\t}\n\n\treturn fmt.Sprintf(\"%s %s\", o.setID, objectset.ObjectKey{\n\t\tNamespace: metadata.GetNamespace(),\n\t\tName:      metadata.GetName(),\n\t})\n}\n\nfunc (o *desiredSet) collect(objList []runtime.Object) objectset.ObjectByGVK {\n\tresult := objectset.ObjectByGVK{}\n\tfor _, obj := range objList {\n\t\t_, _ = result.Add(obj)\n\t}\n\treturn result\n}\n\nfunc (o *desiredSet) runInjectors(objList []runtime.Object) ([]runtime.Object, error) {\n\tvar err error\n\n\tfor _, inj := range o.injectors {\n\t\tif inj == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tobjList, err = inj(objList)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tfor _, name := range o.injectorNames {\n\t\tinj := injectors.Get(name)\n\t\tif inj == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tobjList, err = inj(objList)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn objList, nil\n}\n\n// GetSelectorFromOwner returns the label selector for the owner object which is useful\n// to list the dependents\nfunc GetSelectorFromOwner(setID string, owner runtime.Object) (labels.Selector, error) {\n\t// Build the labels, we want the hash label for the lister\n\townerLabel, _, err := GetLabelsAndAnnotations(setID, owner)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn GetSelector(ownerLabel)\n}\n\nfunc GetSelector(labelSet map[string]string) (labels.Selector, error) {\n\treq, err := labels.NewRequirement(LabelHash, selection.Equals, []string{labelSet[LabelHash]})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn labels.NewSelector().Add(*req), nil\n}\n\nfunc GetLabelsAndAnnotations(setID string, owner runtime.Object) (map[string]string, map[string]string, error) {\n\tif setID == \"\" && owner == nil {\n\t\treturn nil, nil, fmt.Errorf(\"set ID or owner must be set\")\n\t}\n\n\tannotations := map[string]string{\n\t\tLabelID: setID,\n\t}\n\n\tif owner != nil {\n\t\tgvk, err := gvk2.Get(owner)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tannotations[LabelGVK] = gvk.String()\n\t\tmetadata, err := meta.Accessor(owner)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to get metadata for %s\", gvk)\n\t\t}\n\t\tannotations[LabelName] = metadata.GetName()\n\t\tannotations[LabelNamespace] = metadata.GetNamespace()\n\t}\n\n\tlabels := map[string]string{\n\t\tLabelHash: objectSetHash(annotations),\n\t}\n\n\treturn labels, annotations, nil\n}\n\nfunc (o *desiredSet) injectLabelsAndAnnotations(labels, annotations map[string]string) ([]runtime.Object, error) {\n\tvar result []runtime.Object\n\n\tfor _, objMap := range o.objs.ObjectsByGVK() {\n\t\tfor key, obj := range objMap {\n\t\t\tobj = obj.DeepCopyObject()\n\t\t\tmeta, err := meta.Accessor(obj)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to get metadata for %s: %w\", key, err)\n\t\t\t}\n\n\t\t\tsetLabels(meta, labels)\n\t\t\tsetAnnotations(meta, annotations)\n\n\t\t\tresult = append(result, obj)\n\t\t}\n\t}\n\n\treturn result, nil\n}\n\nfunc setAnnotations(meta metav1.Object, annotations map[string]string) {\n\tobjAnn := meta.GetAnnotations()\n\tif objAnn == nil {\n\t\tobjAnn = map[string]string{}\n\t}\n\tdelete(objAnn, LabelApplied)\n\tfor k, v := range annotations {\n\t\tobjAnn[k] = v\n\t}\n\tmeta.SetAnnotations(objAnn)\n}\n\nfunc setLabels(meta metav1.Object, labels map[string]string) {\n\tobjLabels := meta.GetLabels()\n\tif objLabels == nil {\n\t\tobjLabels = map[string]string{}\n\t}\n\tfor k, v := range labels {\n\t\tobjLabels[k] = v\n\t}\n\tmeta.SetLabels(objLabels)\n}\n\nfunc objectSetHash(labels map[string]string) string {\n\tdig := sha1.New()\n\tfor _, key := range hashOrder {\n\t\tdig.Write([]byte(labels[key]))\n\t}\n\treturn hex.EncodeToString(dig.Sum(nil))\n}\n"
  },
  {
    "path": "pkg/apply/desiredset_compare.go",
    "content": "package apply\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"strings\"\n\t\"sync\"\n\n\tjsonpatch \"github.com/evanphx/json-patch\"\n\tdata2 \"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n\t\"github.com/rancher/wrangler/v3/pkg/objectset\"\n\tpatch2 \"github.com/rancher/wrangler/v3/pkg/patch\"\n\t\"github.com/sirupsen/logrus\"\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"k8s.io/apimachinery/pkg/util/json\"\n\t\"k8s.io/apimachinery/pkg/util/jsonmergepatch\"\n\t\"k8s.io/apimachinery/pkg/util/strategicpatch\"\n\t\"k8s.io/client-go/dynamic\"\n)\n\nconst (\n\tLabelApplied = \"objectset.rio.cattle.io/applied\"\n)\n\nvar (\n\tknownListKeys = map[string]bool{\n\t\t\"apiVersion\":    true,\n\t\t\"containerPort\": true,\n\t\t\"devicePath\":    true,\n\t\t\"ip\":            true,\n\t\t\"kind\":          true,\n\t\t\"mountPath\":     true,\n\t\t\"name\":          true,\n\t\t\"port\":          true,\n\t\t\"topologyKey\":   true,\n\t\t\"type\":          true,\n\t}\n\n\t// Pools used by gzip writers\n\n\tbuffersPool = sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\treturn new(bytes.Buffer)\n\t\t},\n\t}\n\tgzipWritersPool = sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\t// Initialize a new writer with discarding destination\n\t\t\t// It should be reconfigured prior usage with an actual buffer using Reset()\n\t\t\treturn gzip.NewWriter(io.Discard)\n\t\t},\n\t}\n)\n\nfunc prepareObjectForCreate(gvk schema.GroupVersionKind, obj runtime.Object) (runtime.Object, error) {\n\tserialized, err := serializeApplied(obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tobj = obj.DeepCopyObject()\n\tm, err := meta.Accessor(obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tannotations := m.GetAnnotations()\n\tif annotations == nil {\n\t\tannotations = map[string]string{}\n\t}\n\n\tannotations[LabelApplied] = appliedToAnnotation(serialized)\n\tm.SetAnnotations(annotations)\n\n\ttyped, err := meta.TypeAccessor(obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tapiVersion, kind := gvk.ToAPIVersionAndKind()\n\ttyped.SetAPIVersion(apiVersion)\n\ttyped.SetKind(kind)\n\n\treturn obj, nil\n}\n\nfunc originalAndModified(gvk schema.GroupVersionKind, oldMetadata v1.Object, newObject runtime.Object) ([]byte, []byte, error) {\n\toriginal, err := getOriginalBytes(gvk, oldMetadata)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tnewObject, err = prepareObjectForCreate(gvk, newObject)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tmodified, err := json.Marshal(newObject)\n\n\treturn original, modified, err\n}\n\nfunc emptyMaps(data map[string]interface{}, keys ...string) bool {\n\tfor _, key := range append(keys, \"__invalid_key__\") {\n\t\tif len(data) == 0 {\n\t\t\t// map is empty so all children are empty too\n\t\t\treturn true\n\t\t} else if len(data) > 1 {\n\t\t\t// map has more than one key so not empty\n\t\t\treturn false\n\t\t}\n\n\t\tvalue, ok := data[key]\n\t\tif !ok {\n\t\t\t// map has one key but not what we are expecting so not considered empty\n\t\t\treturn false\n\t\t}\n\n\t\tdata = convert.ToMapInterface(value)\n\t}\n\n\treturn true\n}\n\nfunc sanitizePatch(patch []byte, removeObjectSetAnnotation bool) ([]byte, error) {\n\tmod := false\n\tdata := map[string]interface{}{}\n\terr := json.Unmarshal(patch, &data)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif _, ok := data[\"kind\"]; ok {\n\t\tmod = true\n\t\tdelete(data, \"kind\")\n\t}\n\n\tif _, ok := data[\"apiVersion\"]; ok {\n\t\tmod = true\n\t\tdelete(data, \"apiVersion\")\n\t}\n\n\tif _, ok := data[\"status\"]; ok {\n\t\tmod = true\n\t\tdelete(data, \"status\")\n\t}\n\n\tif deleted := removeCreationTimestamp(data); deleted {\n\t\tmod = true\n\t}\n\n\tif removeObjectSetAnnotation {\n\t\tmetadata := convert.ToMapInterface(data2.GetValueN(data, \"metadata\"))\n\t\tannotations := convert.ToMapInterface(data2.GetValueN(data, \"metadata\", \"annotations\"))\n\t\tfor k := range annotations {\n\t\t\tif strings.HasPrefix(k, LabelPrefix) {\n\t\t\t\tmod = true\n\t\t\t\tdelete(annotations, k)\n\t\t\t}\n\t\t}\n\t\tif mod && len(annotations) == 0 {\n\t\t\tdelete(metadata, \"annotations\")\n\t\t\tif len(metadata) == 0 {\n\t\t\t\tdelete(data, \"metadata\")\n\t\t\t}\n\t\t}\n\t}\n\n\tif emptyMaps(data, \"metadata\", \"annotations\") {\n\t\treturn []byte(\"{}\"), nil\n\t}\n\n\tif !mod {\n\t\treturn patch, nil\n\t}\n\n\treturn json.Marshal(data)\n}\n\nfunc applyPatch(gvk schema.GroupVersionKind, reconciler Reconciler, patcher Patcher, debugID string, ignoreOriginal bool, oldObject, newObject runtime.Object, diffPatches [][]byte) (bool, error) {\n\toldMetadata, err := meta.Accessor(oldObject)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\toriginal, modified, err := originalAndModified(gvk, oldMetadata, newObject)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tif ignoreOriginal {\n\t\toriginal = nil\n\t}\n\n\tcurrent, err := json.Marshal(oldObject)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tpatchType, patch, err := doPatch(gvk, original, modified, current, diffPatches)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"patch generation: %w\", err)\n\t}\n\n\tif string(patch) == \"{}\" {\n\t\treturn false, nil\n\t}\n\n\tpatch, err = sanitizePatch(patch, false)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tif string(patch) == \"{}\" {\n\t\treturn false, nil\n\t}\n\n\tlogrus.Debugf(\"DesiredSet - Patch %s %s/%s for %s -- [PATCH:%s, ORIGINAL:%s, MODIFIED:%s, CURRENT:%s]\", gvk, oldMetadata.GetNamespace(), oldMetadata.GetName(), debugID, patch, original, modified, current)\n\tif reconciler != nil {\n\t\tnewObject, err := prepareObjectForCreate(gvk, newObject)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\toriginalObject, err := getOriginalObject(gvk, oldMetadata)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\tif originalObject == nil {\n\t\t\toriginalObject = oldObject\n\t\t}\n\t\thandled, err := reconciler(originalObject, newObject)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\tif handled {\n\t\t\treturn true, nil\n\t\t}\n\t}\n\n\tlogrus.Debugf(\"DesiredSet - Updated %s %s/%s for %s -- %s %s\", gvk, oldMetadata.GetNamespace(), oldMetadata.GetName(), debugID, patchType, patch)\n\t_, err = patcher(oldMetadata.GetNamespace(), oldMetadata.GetName(), patchType, patch)\n\n\treturn true, err\n}\n\nfunc (o *desiredSet) compareObjects(gvk schema.GroupVersionKind, reconciler Reconciler, patcher Patcher, client dynamic.NamespaceableResourceInterface, debugID string, oldObject, newObject runtime.Object, force bool) error {\n\toldMetadata, err := meta.Accessor(oldObject)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif o.createPlan {\n\t\to.plan.Objects = append(o.plan.Objects, oldObject)\n\t}\n\n\tdiffPatches := o.diffPatches[patchKey{\n\t\tGroupVersionKind: gvk,\n\t\tObjectKey: objectset.ObjectKey{\n\t\t\tNamespace: oldMetadata.GetNamespace(),\n\t\t\tName:      oldMetadata.GetName(),\n\t\t},\n\t}]\n\tdiffPatches = append(diffPatches, o.diffPatches[patchKey{\n\t\tGroupVersionKind: gvk,\n\t}]...)\n\n\tif ran, err := applyPatch(gvk, reconciler, patcher, debugID, o.ignorePreviousApplied, oldObject, newObject, diffPatches); err != nil {\n\t\treturn err\n\t} else if !ran {\n\t\tlogrus.Debugf(\"DesiredSet - No change(2) %s %s/%s for %s\", gvk, oldMetadata.GetNamespace(), oldMetadata.GetName(), debugID)\n\t}\n\n\treturn nil\n}\n\nfunc removeCreationTimestamp(data map[string]interface{}) bool {\n\tmetadata, ok := data[\"metadata\"]\n\tif !ok {\n\t\treturn false\n\t}\n\n\tdata = convert.ToMapInterface(metadata)\n\tif _, ok := data[\"creationTimestamp\"]; ok {\n\t\tdelete(data, \"creationTimestamp\")\n\t\treturn true\n\t}\n\n\treturn false\n}\n\nfunc getOriginalObject(gvk schema.GroupVersionKind, obj v1.Object) (runtime.Object, error) {\n\toriginal := appliedFromAnnotation(obj.GetAnnotations()[LabelApplied])\n\tif len(original) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tmapObj := map[string]interface{}{}\n\terr := json.Unmarshal(original, &mapObj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tremoveCreationTimestamp(mapObj)\n\treturn prepareObjectForCreate(gvk, &unstructured.Unstructured{\n\t\tObject: mapObj,\n\t})\n}\n\nfunc getOriginalBytes(gvk schema.GroupVersionKind, obj v1.Object) ([]byte, error) {\n\tobjCopy, err := getOriginalObject(gvk, obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif objCopy == nil {\n\t\treturn []byte(\"{}\"), nil\n\t}\n\treturn json.Marshal(objCopy)\n}\n\nfunc appliedFromAnnotation(str string) []byte {\n\tif len(str) == 0 || str[0] == '{' {\n\t\treturn []byte(str)\n\t}\n\n\tb, err := base64.RawStdEncoding.DecodeString(str)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tr, err := gzip.NewReader(bytes.NewBuffer(b))\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tb, err = ioutil.ReadAll(r)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\treturn b\n}\n\nfunc pruneList(data []interface{}) []interface{} {\n\tresult := make([]interface{}, 0, len(data))\n\tfor _, v := range data {\n\t\tswitch typed := v.(type) {\n\t\tcase map[string]interface{}:\n\t\t\tresult = append(result, pruneValues(typed, true))\n\t\tcase []interface{}:\n\t\t\tresult = append(result, pruneList(typed))\n\t\tdefault:\n\t\t\tresult = append(result, v)\n\t\t}\n\t}\n\treturn result\n}\n\nfunc pruneValues(data map[string]interface{}, isList bool) map[string]interface{} {\n\tresult := map[string]interface{}{}\n\tfor k, v := range data {\n\t\tswitch typed := v.(type) {\n\t\tcase map[string]interface{}:\n\t\t\tresult[k] = pruneValues(typed, false)\n\t\tcase []interface{}:\n\t\t\tresult[k] = pruneList(typed)\n\t\tdefault:\n\t\t\tif isList && knownListKeys[k] {\n\t\t\t\tresult[k] = v\n\t\t\t} else {\n\t\t\t\tswitch x := v.(type) {\n\t\t\t\tcase string:\n\t\t\t\t\tif len(x) > 64 {\n\t\t\t\t\t\tresult[k] = x[:64]\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult[k] = v\n\t\t\t\t\t}\n\t\t\t\tcase []byte:\n\t\t\t\t\tresult[k] = nil\n\t\t\t\tdefault:\n\t\t\t\t\tresult[k] = v\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn result\n}\n\nfunc serializeApplied(obj runtime.Object) ([]byte, error) {\n\tdata, err := convert.EncodeToMap(obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = pruneValues(data, false)\n\treturn json.Marshal(data)\n}\n\nfunc appliedToAnnotation(b []byte) string {\n\treturn compressAndEncode(b)\n}\n\nfunc compressAndEncode(b []byte) string {\n\tbuf := buffersPool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\tdefer buffersPool.Put(buf)\n\n\tw := gzipWritersPool.Get().(*gzip.Writer)\n\tw.Reset(buf)\n\tdefer gzipWritersPool.Put(w)\n\n\tif _, err := w.Write(b); err != nil {\n\t\treturn string(b)\n\t}\n\tif err := w.Close(); err != nil {\n\t\treturn string(b)\n\t}\n\treturn base64.RawStdEncoding.EncodeToString(buf.Bytes())\n}\n\nfunc stripIgnores(original, modified, current []byte, patches [][]byte) ([]byte, []byte, []byte, error) {\n\tfor _, patch := range patches {\n\t\tpatch, err := jsonpatch.DecodePatch(patch)\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, err\n\t\t}\n\t\tif len(original) > 0 {\n\t\t\tb, err := patch.Apply(original)\n\t\t\tif err == nil {\n\t\t\t\toriginal = b\n\t\t\t}\n\t\t}\n\t\tb, err := patch.Apply(modified)\n\t\tif err == nil {\n\t\t\tmodified = b\n\t\t}\n\t\tb, err = patch.Apply(current)\n\t\tif err == nil {\n\t\t\tcurrent = b\n\t\t}\n\t}\n\n\treturn original, modified, current, nil\n}\n\n// doPatch is adapted from \"kubectl apply\"\nfunc doPatch(gvk schema.GroupVersionKind, original, modified, current []byte, diffPatch [][]byte) (types.PatchType, []byte, error) {\n\tvar (\n\t\tpatchType types.PatchType\n\t\tpatch     []byte\n\t)\n\n\toriginal, modified, current, err := stripIgnores(original, modified, current, diffPatch)\n\tif err != nil {\n\t\treturn patchType, nil, err\n\t}\n\n\tpatchType, lookupPatchMeta, err := patch2.GetMergeStyle(gvk)\n\tif err != nil {\n\t\treturn patchType, nil, err\n\t}\n\n\tif patchType == types.StrategicMergePatchType {\n\t\tpatch, err = strategicpatch.CreateThreeWayMergePatch(original, modified, current, lookupPatchMeta, true)\n\t} else {\n\t\tpatch, err = jsonmergepatch.CreateThreeWayJSONMergePatch(original, modified, current)\n\t}\n\n\tif err != nil {\n\t\tlogrus.Errorf(\"Failed to calcuated patch: %v\", err)\n\t}\n\n\treturn patchType, patch, err\n}\n"
  },
  {
    "path": "pkg/apply/desiredset_compare_test.go",
    "content": "package apply\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestCompressAndEncode(t *testing.T) {\n\ttestCases := []struct {\n\t\tname     string\n\t\tinput    []byte\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tname:     \"Empty string\",\n\t\t\tinput:    []byte(\"\"),\n\t\t\texpected: \"H4sIAAAAAAAA/wEAAP//AAAAAAAAAAA\",\n\t\t},\n\t\t{\n\t\t\tname:     \"Short string\",\n\t\t\tinput:    []byte(\"hello world\"),\n\t\t\texpected: \"H4sIAAAAAAAA/8pIzcnJVyjPL8pJAQQAAP//hRFKDQsAAAA\",\n\t\t},\n\t\t{\n\t\t\tname:     \"JSON payload\",\n\t\t\tinput:    []byte(`{\"id\": 123, \"status\": \"active\", \"message\": \"hello\"}`),\n\t\t\texpected: \"H4sIAAAAAAAA/6pWykxRslIwNDLWUVAqLkksKS1WslJQSkwuySxLVdJRUMpNLS5OTE8FCWak5uTkK9UCAgAA//9XG2xwMwAAAA\",\n\t\t},\n\t\t{\n\t\t\tname:     \"Longer repeating string\",\n\t\t\tinput:    bytes.Repeat([]byte(\"test data \"), 10),\n\t\t\texpected: \"H4sIAAAAAAAA/ypJLS5RSEksSVSgHQsQAAD//02/IfBkAAAA\",\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t// Compare the result against our hardcoded golden value\n\t\t\tif got, want := compressAndEncode(tc.input), tc.expected; got != want {\n\t\t\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/apply/desiredset_crud.go",
    "content": "package apply\n\nimport (\n\t\"bytes\"\n\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/util/json\"\n\t\"k8s.io/client-go/dynamic\"\n)\n\nvar (\n\tdeletePolicy = v1.DeletePropagationBackground\n)\n\nfunc (o *desiredSet) toUnstructured(obj runtime.Object) (*unstructured.Unstructured, error) {\n\tunstruct, ok := obj.(*unstructured.Unstructured)\n\tif ok {\n\t\treturn unstruct, nil\n\t}\n\n\tbuf := &bytes.Buffer{}\n\tif err := json.NewEncoder(buf).Encode(obj); err != nil {\n\t\treturn nil, err\n\t}\n\n\tunstruct = &unstructured.Unstructured{\n\t\tObject: map[string]interface{}{},\n\t}\n\n\treturn unstruct, json.Unmarshal(buf.Bytes(), &unstruct.Object)\n}\n\nfunc (o *desiredSet) create(nsed bool, namespace string, client dynamic.NamespaceableResourceInterface, obj runtime.Object) (runtime.Object, error) {\n\tunstr, err := o.toUnstructured(obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif nsed {\n\t\treturn client.Namespace(namespace).Create(o.ctx, unstr, v1.CreateOptions{})\n\t}\n\treturn client.Create(o.ctx, unstr, v1.CreateOptions{})\n}\n\nfunc (o *desiredSet) get(nsed bool, namespace, name string, client dynamic.NamespaceableResourceInterface) (runtime.Object, error) {\n\tif nsed {\n\t\treturn client.Namespace(namespace).Get(o.ctx, name, v1.GetOptions{})\n\t}\n\treturn client.Get(o.ctx, name, v1.GetOptions{})\n}\n\nfunc (o *desiredSet) delete(nsed bool, namespace, name string, client dynamic.NamespaceableResourceInterface, force bool, gvk schema.GroupVersionKind) error {\n\tif !force {\n\t\tif o.noDelete {\n\t\t\treturn nil\n\t\t}\n\t\tif _, ok := o.noDeleteGVK[gvk]; ok {\n\t\t\treturn nil\n\t\t}\n\t}\n\topts := v1.DeleteOptions{\n\t\tPropagationPolicy: &deletePolicy,\n\t}\n\tif nsed {\n\t\treturn client.Namespace(namespace).Delete(o.ctx, name, opts)\n\t}\n\n\treturn client.Delete(o.ctx, name, opts)\n}\n"
  },
  {
    "path": "pkg/apply/desiredset_owner.go",
    "content": "package apply\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/gvk\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\n\tnamer \"github.com/rancher/wrangler/v3/pkg/name\"\n\tapierrors \"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/client-go/dynamic\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\nvar (\n\tErrOwnerNotFound   = errors.New(\"owner not found\")\n\tErrNoInformerFound = errors.New(\"informer not found\")\n)\n\nfunc notFound(name string, gvk schema.GroupVersionKind) error {\n\t// this is not proper, but does it really matter that much? If you find this\n\t// line while researching a bug, then the answer is probably yes.\n\tresource := namer.GuessPluralName(strings.ToLower(gvk.Kind))\n\treturn apierrors.NewNotFound(schema.GroupResource{\n\t\tGroup:    gvk.Group,\n\t\tResource: resource,\n\t}, name)\n}\n\nfunc getGVK(gvkLabel string, gvk *schema.GroupVersionKind) error {\n\tparts := strings.Split(gvkLabel, \", Kind=\")\n\tif len(parts) != 2 {\n\t\treturn fmt.Errorf(\"invalid GVK format: %s\", gvkLabel)\n\t}\n\tgvk.Group, gvk.Version = kv.Split(parts[0], \"/\")\n\tgvk.Kind = parts[1]\n\treturn nil\n}\n\nfunc (o desiredSet) FindOwner(obj runtime.Object) (runtime.Object, error) {\n\tif obj == nil {\n\t\treturn nil, ErrOwnerNotFound\n\t}\n\tmeta, err := meta.Accessor(obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar (\n\t\tdebugID   = fmt.Sprintf(\"%s/%s\", meta.GetNamespace(), meta.GetName())\n\t\tgvkLabel  = meta.GetAnnotations()[LabelGVK]\n\t\tnamespace = meta.GetAnnotations()[LabelNamespace]\n\t\tname      = meta.GetAnnotations()[LabelName]\n\t\tgvk       schema.GroupVersionKind\n\t)\n\n\tif gvkLabel == \"\" {\n\t\treturn nil, ErrOwnerNotFound\n\t}\n\n\tif err := getGVK(gvkLabel, &gvk); err != nil {\n\t\treturn nil, err\n\t}\n\n\tcache, client, err := o.getControllerAndClient(debugID, gvk)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif cache != nil {\n\t\treturn o.fromCache(cache, namespace, name, gvk)\n\t}\n\n\treturn o.fromClient(client, namespace, name, gvk)\n}\n\nfunc (o *desiredSet) fromClient(client dynamic.NamespaceableResourceInterface, namespace, name string, gvk schema.GroupVersionKind) (runtime.Object, error) {\n\tvar (\n\t\terr error\n\t\tobj interface{}\n\t)\n\tif namespace == \"\" {\n\t\tobj, err = client.Get(o.ctx, name, metav1.GetOptions{})\n\t} else {\n\t\tobj, err = client.Namespace(namespace).Get(o.ctx, name, metav1.GetOptions{})\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif ro, ok := obj.(runtime.Object); ok {\n\t\treturn ro, nil\n\t}\n\treturn nil, notFound(name, gvk)\n}\n\nfunc (o *desiredSet) fromCache(cache cache.SharedInformer, namespace, name string, gvk schema.GroupVersionKind) (runtime.Object, error) {\n\tvar key string\n\tif namespace == \"\" {\n\t\tkey = name\n\t} else {\n\t\tkey = namespace + \"/\" + name\n\t}\n\titem, ok, err := cache.GetStore().GetByKey(key)\n\tif err != nil {\n\t\treturn nil, err\n\t} else if !ok {\n\t\treturn nil, notFound(name, gvk)\n\t} else if ro, ok := item.(runtime.Object); ok {\n\t\treturn ro, nil\n\t}\n\treturn nil, notFound(name, gvk)\n}\n\nfunc (o desiredSet) PurgeOrphan(obj runtime.Object) error {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\n\tmeta, err := meta.Accessor(obj)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif _, err := o.FindOwner(obj); apierrors.IsNotFound(err) {\n\t\tgvk, err := gvk.Get(obj)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\to.strictCaching = false\n\t\t_, client, err := o.getControllerAndClient(meta.GetName(), gvk)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif meta.GetNamespace() == \"\" {\n\t\t\treturn client.Delete(o.ctx, meta.GetName(), metav1.DeleteOptions{})\n\t\t}\n\t\treturn client.Namespace(meta.GetNamespace()).Delete(o.ctx, meta.GetName(), metav1.DeleteOptions{})\n\t} else if err == ErrOwnerNotFound {\n\t\treturn nil\n\t} else if err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/apply/desiredset_process.go",
    "content": "package apply\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"sort\"\n\t\"sync\"\n\n\tgvk2 \"github.com/rancher/wrangler/v3/pkg/gvk\"\n\t\"github.com/rancher/wrangler/v3/pkg/merr\"\n\t\"github.com/rancher/wrangler/v3/pkg/objectset\"\n\t\"github.com/sirupsen/logrus\"\n\t\"golang.org/x/sync/errgroup\"\n\terrors2 \"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\ttypes2 \"k8s.io/apimachinery/pkg/types\"\n\t\"k8s.io/client-go/dynamic\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\nvar (\n\tErrReplace      = errors.New(\"replace object with changes\")\n\tReplaceOnChange = func(name string, o runtime.Object, patchType types2.PatchType, data []byte, subresources ...string) (runtime.Object, error) {\n\t\treturn nil, ErrReplace\n\t}\n)\n\nfunc (o *desiredSet) getControllerAndClient(debugID string, gvk schema.GroupVersionKind) (cache.SharedIndexInformer, dynamic.NamespaceableResourceInterface, error) {\n\t// client needs to be accessed first so that the gvk->gvr mapping gets cached\n\tclient, err := o.a.clients.client(gvk)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tinformer, ok := o.pruneTypes[gvk]\n\tif !ok {\n\t\tinformer = o.a.informers[gvk]\n\t}\n\tif informer == nil && o.informerFactory != nil {\n\t\tnewInformer, err := o.informerFactory.Get(gvk, o.a.clients.gvr(gvk))\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to construct informer for %v for %s: %w\", gvk, debugID, err)\n\t\t}\n\t\tinformer = newInformer\n\t}\n\tif informer == nil && o.strictCaching {\n\t\treturn nil, nil, fmt.Errorf(\"failed to find informer for %s for %s: %w\", gvk, debugID, ErrNoInformerFound)\n\t}\n\n\treturn informer, client, nil\n}\n\nfunc (o *desiredSet) assignOwnerReference(gvk schema.GroupVersionKind, objs objectset.ObjectByKey) error {\n\tif o.owner == nil {\n\t\treturn fmt.Errorf(\"no owner set to assign owner reference\")\n\t}\n\townerMeta, err := meta.Accessor(o.owner)\n\tif err != nil {\n\t\treturn err\n\t}\n\townerGVK, err := gvk2.Get(o.owner)\n\tif err != nil {\n\t\treturn err\n\t}\n\townerNSed, err := o.a.clients.IsNamespaced(ownerGVK)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor k, v := range objs {\n\t\t// can't set owners across boundaries\n\t\tif ownerNSed {\n\t\t\tif nsed, err := o.a.clients.IsNamespaced(gvk); err != nil {\n\t\t\t\treturn err\n\t\t\t} else if !nsed {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tassignNS := false\n\t\tassignOwner := true\n\t\tif nsed, err := o.a.clients.IsNamespaced(gvk); err != nil {\n\t\t\treturn err\n\t\t} else if nsed {\n\t\t\tif k.Namespace == \"\" {\n\t\t\t\tassignNS = true\n\t\t\t} else if k.Namespace != ownerMeta.GetNamespace() && ownerNSed {\n\t\t\t\tassignOwner = false\n\t\t\t}\n\t\t}\n\n\t\tif !assignOwner {\n\t\t\tcontinue\n\t\t}\n\n\t\tv = v.DeepCopyObject()\n\t\tmeta, err := meta.Accessor(v)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif assignNS {\n\t\t\tmeta.SetNamespace(ownerMeta.GetNamespace())\n\t\t}\n\n\t\tshouldSet := true\n\t\tfor _, of := range meta.GetOwnerReferences() {\n\t\t\tif ownerMeta.GetUID() == of.UID {\n\t\t\t\tshouldSet = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif shouldSet {\n\t\t\tmeta.SetOwnerReferences(append(meta.GetOwnerReferences(), v1.OwnerReference{\n\t\t\t\tAPIVersion:         ownerGVK.GroupVersion().String(),\n\t\t\t\tKind:               ownerGVK.Kind,\n\t\t\t\tName:               ownerMeta.GetName(),\n\t\t\t\tUID:                ownerMeta.GetUID(),\n\t\t\t\tController:         &o.ownerReferenceController,\n\t\t\t\tBlockOwnerDeletion: &o.ownerReferenceBlock,\n\t\t\t}))\n\t\t}\n\n\t\tobjs[k] = v\n\n\t\tif assignNS {\n\t\t\tdelete(objs, k)\n\t\t\tk.Namespace = ownerMeta.GetNamespace()\n\t\t\tobjs[k] = v\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (o *desiredSet) adjustNamespace(gvk schema.GroupVersionKind, objs objectset.ObjectByKey) error {\n\tfor k, v := range objs {\n\t\tif k.Namespace != \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tv = v.DeepCopyObject()\n\t\tmeta, err := meta.Accessor(v)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tmeta.SetNamespace(o.defaultNamespace)\n\t\tdelete(objs, k)\n\t\tk.Namespace = o.defaultNamespace\n\t\tobjs[k] = v\n\t}\n\n\treturn nil\n}\n\nfunc (o *desiredSet) clearNamespace(objs objectset.ObjectByKey) error {\n\tfor k, v := range objs {\n\t\tif k.Namespace == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tv = v.DeepCopyObject()\n\t\tmeta, err := meta.Accessor(v)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tmeta.SetNamespace(\"\")\n\n\t\tdelete(objs, k)\n\t\tk.Namespace = \"\"\n\t\tobjs[k] = v\n\t}\n\n\treturn nil\n}\n\nfunc (o *desiredSet) createPatcher(client dynamic.NamespaceableResourceInterface) Patcher {\n\treturn func(namespace, name string, pt types2.PatchType, data []byte) (object runtime.Object, e error) {\n\t\tif namespace != \"\" {\n\t\t\treturn client.Namespace(namespace).Patch(o.ctx, name, pt, data, v1.PatchOptions{})\n\t\t}\n\t\treturn client.Patch(o.ctx, name, pt, data, v1.PatchOptions{})\n\t}\n}\n\nfunc (o *desiredSet) filterCrossVersion(gvk schema.GroupVersionKind, keys []objectset.ObjectKey) []objectset.ObjectKey {\n\tresult := make([]objectset.ObjectKey, 0, len(keys))\n\tgk := gvk.GroupKind()\n\tfor _, key := range keys {\n\t\tif o.objs.Contains(gk, key) {\n\t\t\tcontinue\n\t\t}\n\t\tif key.Namespace == o.defaultNamespace && o.objs.Contains(gk, objectset.ObjectKey{Name: key.Name}) {\n\t\t\tcontinue\n\t\t}\n\t\tresult = append(result, key)\n\t}\n\treturn result\n}\n\nfunc (o *desiredSet) process(debugID string, set labels.Selector, gvk schema.GroupVersionKind, objs objectset.ObjectByKey) {\n\tcontroller, client, err := o.getControllerAndClient(debugID, gvk)\n\tif err != nil {\n\t\to.err(err)\n\t\treturn\n\t}\n\n\tnsed, err := o.a.clients.IsNamespaced(gvk)\n\tif err != nil {\n\t\to.err(err)\n\t\treturn\n\t}\n\n\tif !nsed && o.restrictClusterScoped {\n\t\to.err(fmt.Errorf(\"invalid cluster scoped gvk: %v\", gvk))\n\t\treturn\n\t}\n\n\tif o.setOwnerReference && o.owner != nil {\n\t\tif err := o.assignOwnerReference(gvk, objs); err != nil {\n\t\t\to.err(err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif nsed {\n\t\tif err := o.adjustNamespace(gvk, objs); err != nil {\n\t\t\to.err(err)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tif err := o.clearNamespace(objs); err != nil {\n\t\t\to.err(err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tpatcher, ok := o.patchers[gvk]\n\tif !ok {\n\t\tpatcher = o.createPatcher(client)\n\t}\n\n\treconciler := o.reconcilers[gvk]\n\n\texisting, err := o.list(nsed, controller, client, set, objs)\n\tif err != nil {\n\t\to.err(fmt.Errorf(\"failed to list %s for %s: %w\", gvk, debugID, err))\n\t\treturn\n\t}\n\n\ttoCreate, toDelete, toUpdate := compareSets(existing, objs)\n\n\t// check for resources in the objectset but under a different version of the same group/kind\n\ttoDelete = o.filterCrossVersion(gvk, toDelete)\n\n\tif o.createPlan {\n\t\to.plan.Create[gvk] = toCreate\n\t\to.plan.Delete[gvk] = toDelete\n\n\t\treconciler = nil\n\t\tpatcher = func(namespace, name string, pt types2.PatchType, data []byte) (runtime.Object, error) {\n\t\t\tdata, err := sanitizePatch(data, true)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif string(data) != \"{}\" {\n\t\t\t\to.plan.Update.Add(gvk, namespace, name, string(data))\n\t\t\t}\n\t\t\treturn nil, nil\n\t\t}\n\n\t\ttoCreate = nil\n\t\ttoDelete = nil\n\t}\n\n\tcreateF := func(k objectset.ObjectKey) {\n\t\tobj := objs[k]\n\t\tobj, err := prepareObjectForCreate(gvk, obj)\n\t\tif err != nil {\n\t\t\to.err(fmt.Errorf(\"failed to prepare create %s %s for %s: %w\", k, gvk, debugID, err))\n\t\t\treturn\n\t\t}\n\n\t\t_, err = o.create(nsed, k.Namespace, client, obj)\n\t\tif errors2.IsAlreadyExists(err) {\n\t\t\t// Taking over an object that wasn't previously managed by us\n\t\t\texistingObj, err := o.get(nsed, k.Namespace, k.Name, client)\n\t\t\tif err == nil {\n\t\t\t\ttoUpdate = append(toUpdate, k)\n\t\t\t\texisting[k] = existingObj\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tif err != nil {\n\t\t\to.err(fmt.Errorf(\"failed to create %s %s for %s: %w\", k, gvk, debugID, err))\n\t\t\treturn\n\t\t}\n\t\tlogrus.Debugf(\"DesiredSet - Created %s %s for %s\", gvk, k, debugID)\n\t}\n\n\tdeleteF := func(k objectset.ObjectKey, force bool) {\n\t\tif err := o.delete(nsed, k.Namespace, k.Name, client, force, gvk); err != nil {\n\t\t\to.err(fmt.Errorf(\"failed to delete %s %s for %s: %w\", k, gvk, debugID, err))\n\t\t\treturn\n\t\t}\n\t\tlogrus.Debugf(\"DesiredSet - Delete %s %s for %s\", gvk, k, debugID)\n\t}\n\n\tupdateF := func(k objectset.ObjectKey) {\n\t\terr := o.compareObjects(gvk, reconciler, patcher, client, debugID, existing[k], objs[k], len(toCreate) > 0 || len(toDelete) > 0)\n\t\tif err == ErrReplace {\n\t\t\tdeleteF(k, true)\n\t\t\to.err(fmt.Errorf(\"DesiredSet - Replace Wait %s %s for %s\", gvk, k, debugID))\n\t\t} else if err != nil {\n\t\t\to.err(fmt.Errorf(\"failed to update %s %s for %s: %w\", k, gvk, debugID, err))\n\t\t}\n\t}\n\n\tfor _, k := range toCreate {\n\t\tcreateF(k)\n\t}\n\n\tfor _, k := range toUpdate {\n\t\tupdateF(k)\n\t}\n\n\tfor _, k := range toDelete {\n\t\tdeleteF(k, false)\n\t}\n}\n\nfunc (o *desiredSet) list(namespaced bool, informer cache.SharedIndexInformer, client dynamic.NamespaceableResourceInterface, selector labels.Selector, desiredObjects objectset.ObjectByKey) (map[objectset.ObjectKey]runtime.Object, error) {\n\tvar (\n\t\terrs []error\n\t\tobjs = objectset.ObjectByKey{}\n\t)\n\n\tif informer == nil {\n\t\t// If a lister namespace is set, assume all objects belong to the listerNamespace.  If the\n\t\t// desiredSet has an owner but no lister namespace, list objects from all namespaces to ensure\n\t\t// we're cleaning up any owned resources.  Otherwise, search only objects from the namespaces\n\t\t// used by the objects.  Note: desiredSets without owners will never return objects to delete;\n\t\t// deletion requires an owner to track object references across multiple apply runs.\n\t\tvar namespaces []string\n\t\tif o.listerNamespace != \"\" {\n\t\t\tnamespaces = append(namespaces, o.listerNamespace)\n\t\t} else {\n\t\t\tnamespaces = desiredObjects.Namespaces()\n\t\t}\n\n\t\tif o.owner != nil && o.listerNamespace == \"\" {\n\t\t\t// owner set and unspecified lister namespace, search all namespaces\n\t\t\terr := allNamespaceList(o.ctx, client, selector, func(obj unstructured.Unstructured) {\n\t\t\t\tif err := addObjectToMap(objs, &obj); err != nil {\n\t\t\t\t\terrs = append(errs, err)\n\t\t\t\t}\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\terrs = append(errs, err)\n\t\t\t}\n\t\t} else {\n\t\t\t// no owner or lister namespace intentionally restricted; only search in specified namespaces\n\t\t\terr := multiNamespaceList(o.ctx, namespaces, client, selector, func(obj unstructured.Unstructured) {\n\t\t\t\tif err := addObjectToMap(objs, &obj); err != nil {\n\t\t\t\t\terrs = append(errs, err)\n\t\t\t\t}\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\terrs = append(errs, err)\n\t\t\t}\n\t\t}\n\n\t\treturn objs, merr.NewErrors(errs...)\n\t}\n\n\tvar namespace string\n\tif namespaced {\n\t\tnamespace = o.listerNamespace\n\t}\n\n\t// Special case for listing only by hash using indexers\n\tindexer := informer.GetIndexer()\n\tif hash, ok := getIndexableHash(indexer, selector); ok {\n\t\treturn listByHash(indexer, hash, namespace)\n\t}\n\n\tif err := cache.ListAllByNamespace(indexer, namespace, selector, func(obj interface{}) {\n\t\tif err := addObjectToMap(objs, obj); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}); err != nil {\n\t\terrs = append(errs, err)\n\t}\n\n\treturn objs, merr.NewErrors(errs...)\n}\n\nfunc shouldPrune(obj runtime.Object) bool {\n\tmeta, err := meta.Accessor(obj)\n\tif err != nil {\n\t\treturn true\n\t}\n\treturn meta.GetLabels()[LabelPrune] != \"false\"\n}\n\nfunc compareSets(existingSet, newSet objectset.ObjectByKey) (toCreate, toDelete, toUpdate []objectset.ObjectKey) {\n\tfor k := range newSet {\n\t\tif _, ok := existingSet[k]; ok {\n\t\t\ttoUpdate = append(toUpdate, k)\n\t\t} else {\n\t\t\ttoCreate = append(toCreate, k)\n\t\t}\n\t}\n\n\tfor k, obj := range existingSet {\n\t\tif _, ok := newSet[k]; !ok {\n\t\t\tif shouldPrune(obj) {\n\t\t\t\ttoDelete = append(toDelete, k)\n\t\t\t}\n\t\t}\n\t}\n\n\tsortObjectKeys(toCreate)\n\tsortObjectKeys(toDelete)\n\tsortObjectKeys(toUpdate)\n\n\treturn\n}\n\nfunc sortObjectKeys(keys []objectset.ObjectKey) {\n\tsort.Slice(keys, func(i, j int) bool {\n\t\treturn keys[i].String() < keys[j].String()\n\t})\n}\n\nfunc addObjectToMap(objs objectset.ObjectByKey, obj interface{}) error {\n\tmetadata, err := meta.Accessor(obj)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tobjs[objectset.ObjectKey{\n\t\tNamespace: metadata.GetNamespace(),\n\t\tName:      metadata.GetName(),\n\t}] = obj.(runtime.Object)\n\n\treturn nil\n}\n\n// allNamespaceList lists objects across all namespaces.\nfunc allNamespaceList(ctx context.Context, baseClient dynamic.NamespaceableResourceInterface, selector labels.Selector, appendFn func(obj unstructured.Unstructured)) error {\n\tlist, err := baseClient.List(ctx, v1.ListOptions{\n\t\tLabelSelector: selector.String(),\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, obj := range list.Items {\n\t\tappendFn(obj)\n\t}\n\treturn nil\n}\n\n// multiNamespaceList lists objects across all given namespaces, because requests are concurrent it is possible for appendFn to be called before errors are reported.\nfunc multiNamespaceList(ctx context.Context, namespaces []string, baseClient dynamic.NamespaceableResourceInterface, selector labels.Selector, appendFn func(obj unstructured.Unstructured)) error {\n\tvar mu sync.Mutex\n\twg, _ctx := errgroup.WithContext(ctx)\n\n\t// list all namespaces concurrently\n\tfor _, namespace := range namespaces {\n\t\tnamespace := namespace\n\t\twg.Go(func() error {\n\t\t\tlist, err := baseClient.Namespace(namespace).List(_ctx, v1.ListOptions{\n\t\t\t\tLabelSelector: selector.String(),\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tmu.Lock()\n\t\t\tfor _, obj := range list.Items {\n\t\t\t\tappendFn(obj)\n\t\t\t}\n\t\t\tmu.Unlock()\n\n\t\t\treturn nil\n\t\t})\n\t}\n\n\treturn wg.Wait()\n}\n\n// getIndexableHash detects if provided selector can be replaced by using the hash index, if configured, in which case returns the hash value\nfunc getIndexableHash(indexer cache.Indexer, selector labels.Selector) (string, bool) {\n\t// Check if indexer was added\n\tif indexer == nil || indexer.GetIndexers()[byHash] == nil {\n\t\treturn \"\", false\n\t}\n\n\t// Check specific case of listing with exact hash label selector\n\tif req, selectable := selector.Requirements(); len(req) != 1 || !selectable {\n\t\treturn \"\", false\n\t}\n\n\treturn selector.RequiresExactMatch(LabelHash)\n}\n\n// inNamespace checks whether a given object is a Kubernetes object and is part of the provided namespace\nfunc inNamespace(namespace string, obj interface{}) bool {\n\tmetadata, err := meta.Accessor(obj)\n\treturn err == nil && metadata.GetNamespace() == namespace\n}\n\n// listByHash use a pre-configured indexer to list objects of a certain type by their hash label\nfunc listByHash(indexer cache.Indexer, hash string, namespace string) (map[objectset.ObjectKey]runtime.Object, error) {\n\tvar (\n\t\terrs []error\n\t\tobjs = objectset.ObjectByKey{}\n\t)\n\tres, err := indexer.ByIndex(byHash, hash)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, obj := range res {\n\t\tif namespace != \"\" && !inNamespace(namespace, obj) {\n\t\t\tcontinue\n\t\t}\n\t\tif err := addObjectToMap(objs, obj); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\treturn objs, merr.NewErrors(errs...)\n}\n"
  },
  {
    "path": "pkg/apply/desiredset_process_test.go",
    "content": "package apply\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/objectset\"\n\n\t\"github.com/stretchr/testify/assert\"\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/client-go/dynamic/fake\"\n\tk8stesting \"k8s.io/client-go/testing\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\nfunc Test_multiNamespaceList(t *testing.T) {\n\tresults := map[string]*unstructured.UnstructuredList{\n\t\t\"ns1\": {Items: []unstructured.Unstructured{\n\t\t\t{Object: map[string]interface{}{\"name\": \"o1\", \"namespace\": \"ns1\"}},\n\t\t\t{Object: map[string]interface{}{\"name\": \"o2\", \"namespace\": \"ns1\"}},\n\t\t\t{Object: map[string]interface{}{\"name\": \"o3\", \"namespace\": \"ns1\"}},\n\t\t}},\n\t\t\"ns2\": {Items: []unstructured.Unstructured{\n\t\t\t{Object: map[string]interface{}{\"name\": \"o4\", \"namespace\": \"ns2\"}},\n\t\t\t{Object: map[string]interface{}{\"name\": \"o5\", \"namespace\": \"ns2\"}},\n\t\t}},\n\t\t\"ns3\": {Items: []unstructured.Unstructured{}},\n\t}\n\ts := runtime.NewScheme()\n\terr := appsv1.SchemeBuilder.AddToScheme(s)\n\tassert.NoError(t, err, \"Failed to build schema.\")\n\tbaseClient := fake.NewSimpleDynamicClient(s)\n\tbaseClient.PrependReactor(\"list\", \"*\", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) {\n\t\tif strings.Contains(action.GetNamespace(), \"error\") {\n\t\t\treturn true, nil, errors.New(\"simulated failure\")\n\t\t}\n\n\t\treturn true, results[action.GetNamespace()], nil\n\t})\n\n\ttype args struct {\n\t\tnamespaces []string\n\t}\n\ttests := []struct {\n\t\tname          string\n\t\targs          args\n\t\texpectedCalls int\n\t\texpectError   bool\n\t}{\n\t\t{\n\t\t\tname: \"no namespaces\",\n\t\t\targs: args{\n\t\t\t\tnamespaces: []string{},\n\t\t\t},\n\t\t\texpectError:   false,\n\t\t\texpectedCalls: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"1 namespace\",\n\t\t\targs: args{\n\t\t\t\tnamespaces: []string{\"ns1\"},\n\t\t\t},\n\t\t\texpectError:   false,\n\t\t\texpectedCalls: 3,\n\t\t},\n\t\t{\n\t\t\tname: \"many namespaces\",\n\t\t\targs: args{\n\t\t\t\tnamespaces: []string{\"ns1\", \"ns2\", \"ns3\"},\n\t\t\t},\n\t\t\texpectError:   false,\n\t\t\texpectedCalls: 5,\n\t\t},\n\t\t{\n\t\t\tname: \"1 namespace error\",\n\t\t\targs: args{\n\t\t\t\tnamespaces: []string{\"error\", \"ns2\", \"ns3\"},\n\t\t\t},\n\t\t\texpectError:   true,\n\t\t\texpectedCalls: -1,\n\t\t},\n\t\t{\n\t\t\tname: \"many namespace errors\",\n\t\t\targs: args{\n\t\t\t\tnamespaces: []string{\"error\", \"error1\", \"error2\"},\n\t\t\t},\n\t\t\texpectError:   true,\n\t\t\texpectedCalls: -1,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar calls int\n\t\t\terr := multiNamespaceList(context.TODO(), tt.args.namespaces, baseClient.Resource(appsv1.SchemeGroupVersion.WithResource(\"deployments\")), labels.NewSelector(), func(obj unstructured.Unstructured) {\n\t\t\t\tcalls += 1\n\t\t\t})\n\n\t\t\tif tt.expectError {\n\t\t\t\tassert.Error(t, err)\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, err)\n\t\t\t}\n\n\t\t\tif tt.expectedCalls >= 0 {\n\t\t\t\tassert.Equal(t, tt.expectedCalls, calls)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_getIndexableHash(t *testing.T) {\n\tconst hash = \"somehash\"\n\thashSelector, err := GetSelector(map[string]string{LabelHash: hash})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tenvLabelSelector, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{MatchLabels: map[string]string{\"env\": \"dev\"}})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tindexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{byHash: func(obj interface{}) ([]string, error) {\n\t\treturn nil, nil\n\t}})\n\ttype args struct {\n\t\tindexer  cache.Indexer\n\t\tselector labels.Selector\n\t}\n\ttests := []struct {\n\t\tname     string\n\t\targs     args\n\t\twantHash string\n\t\twant     bool\n\t}{\n\t\t{name: \"indexer configured\", args: args{\n\t\t\tindexer:  indexer,\n\t\t\tselector: hashSelector,\n\t\t}, wantHash: hash, want: true},\n\t\t{name: \"indexer not configured\", args: args{\n\t\t\tindexer:  cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}),\n\t\t\tselector: hashSelector,\n\t\t}, wantHash: \"\", want: false},\n\t\t{name: \"using Everything selector\", args: args{\n\t\t\tindexer:  indexer,\n\t\t\tselector: labels.Everything(),\n\t\t}, wantHash: \"\", want: false},\n\t\t{name: \"using other label selectors\", args: args{\n\t\t\tindexer:  indexer,\n\t\t\tselector: envLabelSelector,\n\t\t}, wantHash: \"\", want: false},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgotHash, got := getIndexableHash(tt.args.indexer, tt.args.selector)\n\t\t\tassert.Equalf(t, tt.wantHash, gotHash, \"getIndexableHash(%v, %v)\", tt.args.indexer, tt.args.selector)\n\t\t\tassert.Equalf(t, tt.want, got, \"getIndexableHash(%v, %v)\", tt.args.indexer, tt.args.selector)\n\t\t})\n\t}\n}\n\nfunc Test_inNamespace(t *testing.T) {\n\ttype args struct {\n\t\tnamespace string\n\t\tobj       interface{}\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant bool\n\t}{\n\t\t{name: \"object in namespace\", args: args{\n\t\t\tnamespace: \"ns\", obj: &metav1.ObjectMeta{\n\t\t\t\tNamespace: \"ns\",\n\t\t\t},\n\t\t}, want: true},\n\t\t{name: \"object not in namespace\", args: args{\n\t\t\tnamespace: \"ns\", obj: &metav1.ObjectMeta{\n\t\t\t\tNamespace: \"another-ns\",\n\t\t\t},\n\t\t}, want: false},\n\t\t{name: \"object not namespaced\", args: args{\n\t\t\tnamespace: \"ns\", obj: &corev1.Namespace{},\n\t\t}, want: false},\n\t\t{name: \"non k8s object\", args: args{\n\t\t\tnamespace: \"ns\", obj: &struct{}{},\n\t\t}, want: false},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tassert.Equalf(t, tt.want, inNamespace(tt.args.namespace, tt.args.obj), \"inNamespace(%v, %v)\", tt.args.namespace, tt.args.obj)\n\t\t})\n\t}\n}\n\nfunc Test_listByHash(t *testing.T) {\n\tindexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})\n\tif err := addIndexerByHash(indexer); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\taddObject := func(name, namespace, hash string) *corev1.Pod {\n\t\tt.Helper()\n\t\tobj := &corev1.Pod{\n\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\tName:      name,\n\t\t\t\tNamespace: namespace,\n\t\t\t\tLabels:    map[string]string{LabelHash: hash},\n\t\t\t},\n\t\t}\n\t\tif err := indexer.Add(obj); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\treturn obj\n\t}\n\tnamespace := \"ns\"\n\tobjects := []*corev1.Pod{\n\t\t// 3 objects with the same hash\n\t\taddObject(\"obj0\", namespace, \"hash0\"),\n\t\taddObject(\"obj01\", namespace, \"hash0\"),\n\t\taddObject(\"obj02\", \"another-ns\", \"hash0\"),\n\t\t// Single object for hash\n\t\taddObject(\"obj1\", namespace, \"hash1\"),\n\t}\n\n\ttype args struct {\n\t\thash      string\n\t\tnamespace string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant map[objectset.ObjectKey]runtime.Object\n\t}{\n\t\t{name: \"finds object by hash in all namespaces\",\n\t\t\targs: args{\n\t\t\t\thash: \"hash0\",\n\t\t\t}, want: map[objectset.ObjectKey]runtime.Object{\n\t\t\t\tobjectset.NewObjectKey(objects[0]): objects[0],\n\t\t\t\tobjectset.NewObjectKey(objects[1]): objects[1],\n\t\t\t\tobjectset.NewObjectKey(objects[2]): objects[2],\n\t\t\t}},\n\t\t{name: \"finds object by hash in namespace\",\n\t\t\targs: args{\n\t\t\t\thash:      \"hash0\",\n\t\t\t\tnamespace: namespace,\n\t\t\t}, want: map[objectset.ObjectKey]runtime.Object{\n\t\t\t\tobjectset.NewObjectKey(objects[0]): objects[0],\n\t\t\t\tobjectset.NewObjectKey(objects[1]): objects[1],\n\t\t\t}},\n\t\t{name: \"returns empty if namespace does not match\",\n\t\t\targs: args{\n\t\t\t\thash:      \"hash1\",\n\t\t\t\tnamespace: \"another-ns\",\n\t\t\t}, want: map[objectset.ObjectKey]runtime.Object{}},\n\t\t{name: \"finds object by hash\",\n\t\t\targs: args{\n\t\t\t\thash:      \"hash1\",\n\t\t\t\tnamespace: namespace,\n\t\t\t}, want: map[objectset.ObjectKey]runtime.Object{\n\t\t\t\tobjectset.NewObjectKey(objects[3]): objects[3],\n\t\t\t}},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := listByHash(indexer, tt.args.hash, tt.args.namespace)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equalf(t, tt.want, got, \"listByHash(%v, %v, %v)\", indexer, tt.args.hash, tt.args.namespace)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/apply/fake/apply.go",
    "content": "package fake\n\nimport (\n\t\"context\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/apply/injectors\"\n\t\"github.com/rancher/wrangler/v3/pkg/objectset\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nvar _ apply.Apply = (*FakeApply)(nil)\n\ntype FakeApply struct {\n\tObjects []*objectset.ObjectSet\n\tCount   int\n}\n\nfunc (f *FakeApply) Apply(set *objectset.ObjectSet) error {\n\tf.Objects = append(f.Objects, set)\n\tf.Count++\n\treturn nil\n}\n\nfunc (f *FakeApply) ApplyObjects(objs ...runtime.Object) error {\n\tos := objectset.NewObjectSet()\n\tos.Add(objs...)\n\treturn f.Apply(os)\n}\n\nfunc (f *FakeApply) WithCacheTypes(igs ...apply.InformerGetter) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithIgnorePreviousApplied() apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithGVK(gvks ...schema.GroupVersionKind) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithSetID(id string) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithOwner(obj runtime.Object) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithInjector(injs ...injectors.ConfigInjector) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithInjectorName(injs ...string) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithPatcher(gvk schema.GroupVersionKind, patchers apply.Patcher) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithReconciler(gvk schema.GroupVersionKind, reconciler apply.Reconciler) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithStrictCaching() apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithDynamicLookup() apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithDefaultNamespace(ns string) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithListerNamespace(ns string) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithRestrictClusterScoped() apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithSetOwnerReference(controller, block bool) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithRateLimiting(ratelimitingQPS float32) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithNoDelete() apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithNoDeleteGVK(gvks ...schema.GroupVersionKind) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithContext(ctx context.Context) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithCacheTypeFactory(factory apply.InformerFactory) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) DryRun(objs ...runtime.Object) (apply.Plan, error) {\n\treturn apply.Plan{}, nil\n}\n\nfunc (f *FakeApply) FindOwner(obj runtime.Object) (runtime.Object, error) {\n\treturn nil, nil\n}\n\nfunc (f *FakeApply) PurgeOrphan(obj runtime.Object) error {\n\treturn nil\n}\n\nfunc (f *FakeApply) WithOwnerKey(key string, gvk schema.GroupVersionKind) apply.Apply {\n\treturn f\n}\n\nfunc (f *FakeApply) WithDiffPatch(gvk schema.GroupVersionKind, namespace, name string, patch []byte) apply.Apply {\n\treturn f\n}\n"
  },
  {
    "path": "pkg/apply/injectors/registry.go",
    "content": "package injectors\n\nimport \"k8s.io/apimachinery/pkg/runtime\"\n\nvar (\n\tinjectors = map[string]ConfigInjector{}\n\torder     []string\n)\n\ntype ConfigInjector func(config []runtime.Object) ([]runtime.Object, error)\n\nfunc Register(name string, injector ConfigInjector) {\n\tif _, ok := injectors[name]; !ok {\n\t\torder = append(order, name)\n\t}\n\tinjectors[name] = injector\n}\n\nfunc Get(name string) ConfigInjector {\n\treturn injectors[name]\n}\n"
  },
  {
    "path": "pkg/apply/reconcilers.go",
    "content": "package apply\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tbatchv1 \"k8s.io/api/batch/v1\"\n\tv1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nvar (\n\tdefaultReconcilers = map[schema.GroupVersionKind]Reconciler{\n\t\tv1.SchemeGroupVersion.WithKind(\"Secret\"):         reconcileSecret,\n\t\tv1.SchemeGroupVersion.WithKind(\"Service\"):        reconcileService,\n\t\tbatchv1.SchemeGroupVersion.WithKind(\"Job\"):       reconcileJob,\n\t\tappsv1.SchemeGroupVersion.WithKind(\"Deployment\"): reconcileDeployment,\n\t\tappsv1.SchemeGroupVersion.WithKind(\"DaemonSet\"):  reconcileDaemonSet,\n\t}\n)\n\nfunc reconcileDaemonSet(oldObj, newObj runtime.Object) (bool, error) {\n\toldSvc, ok := oldObj.(*appsv1.DaemonSet)\n\tif !ok {\n\t\toldSvc = &appsv1.DaemonSet{}\n\t\tif err := convertObj(oldObj, oldSvc); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\tnewSvc, ok := newObj.(*appsv1.DaemonSet)\n\tif !ok {\n\t\tnewSvc = &appsv1.DaemonSet{}\n\t\tif err := convertObj(newObj, newSvc); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\tif !equality.Semantic.DeepEqual(oldSvc.Spec.Selector, newSvc.Spec.Selector) {\n\t\treturn false, ErrReplace\n\t}\n\n\treturn false, nil\n}\n\nfunc reconcileDeployment(oldObj, newObj runtime.Object) (bool, error) {\n\toldSvc, ok := oldObj.(*appsv1.Deployment)\n\tif !ok {\n\t\toldSvc = &appsv1.Deployment{}\n\t\tif err := convertObj(oldObj, oldSvc); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\tnewSvc, ok := newObj.(*appsv1.Deployment)\n\tif !ok {\n\t\tnewSvc = &appsv1.Deployment{}\n\t\tif err := convertObj(newObj, newSvc); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\tif !equality.Semantic.DeepEqual(oldSvc.Spec.Selector, newSvc.Spec.Selector) {\n\t\treturn false, ErrReplace\n\t}\n\n\treturn false, nil\n}\n\nfunc reconcileSecret(oldObj, newObj runtime.Object) (bool, error) {\n\toldSvc, ok := oldObj.(*v1.Secret)\n\tif !ok {\n\t\toldSvc = &v1.Secret{}\n\t\tif err := convertObj(oldObj, oldSvc); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\tnewSvc, ok := newObj.(*v1.Secret)\n\tif !ok {\n\t\tnewSvc = &v1.Secret{}\n\t\tif err := convertObj(newObj, newSvc); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\tif newSvc.Type != \"\" && oldSvc.Type != newSvc.Type {\n\t\treturn false, ErrReplace\n\t}\n\n\treturn false, nil\n}\n\nfunc reconcileService(oldObj, newObj runtime.Object) (bool, error) {\n\toldSvc, ok := oldObj.(*v1.Service)\n\tif !ok {\n\t\toldSvc = &v1.Service{}\n\t\tif err := convertObj(oldObj, oldSvc); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\tnewSvc, ok := newObj.(*v1.Service)\n\tif !ok {\n\t\tnewSvc = &v1.Service{}\n\t\tif err := convertObj(newObj, newSvc); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\tif newSvc.Spec.Type != \"\" && oldSvc.Spec.Type != newSvc.Spec.Type {\n\t\treturn false, ErrReplace\n\t}\n\n\treturn false, nil\n}\n\nfunc reconcileJob(oldObj, newObj runtime.Object) (bool, error) {\n\toldJob, ok := oldObj.(*batchv1.Job)\n\tif !ok {\n\t\toldJob = &batchv1.Job{}\n\t\tif err := convertObj(oldObj, oldJob); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\tnewJob, ok := newObj.(*batchv1.Job)\n\tif !ok {\n\t\tnewJob = &batchv1.Job{}\n\t\tif err := convertObj(newObj, newJob); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\t// We round trip the object here because when serializing to the applied\n\t// annotation values are truncated to 64 bytes.\n\tprunedJob, err := getOriginalObject(newJob.GroupVersionKind(), newJob)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tnewPrunedJob := &batchv1.Job{}\n\tif err := convertObj(prunedJob, newPrunedJob); err != nil {\n\t\treturn false, err\n\t}\n\n\tif !equality.Semantic.DeepEqual(oldJob.Spec.Template, newPrunedJob.Spec.Template) {\n\t\treturn false, ErrReplace\n\t}\n\n\treturn false, nil\n}\n\nfunc convertObj(src interface{}, obj interface{}) error {\n\tuObj, ok := src.(*unstructured.Unstructured)\n\tif !ok {\n\t\treturn fmt.Errorf(\"expected unstructured but got %v\", reflect.TypeOf(src))\n\t}\n\n\tbytes, err := uObj.MarshalJSON()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn json.Unmarshal(bytes, obj)\n}\n"
  },
  {
    "path": "pkg/broadcast/generic.go",
    "content": "package broadcast\n\nimport (\n\t\"context\"\n\t\"sync\"\n)\n\ntype ConnectFunc func() (chan interface{}, error)\n\ntype Broadcaster struct {\n\tsync.Mutex\n\trunning bool\n\tsubs    map[chan interface{}]struct{}\n}\n\nfunc (b *Broadcaster) Subscribe(ctx context.Context, connect ConnectFunc) (chan interface{}, error) {\n\tb.Lock()\n\tdefer b.Unlock()\n\n\tif !b.running {\n\t\tif err := b.start(connect); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tsub := make(chan interface{}, 100)\n\tif b.subs == nil {\n\t\tb.subs = map[chan interface{}]struct{}{}\n\t}\n\tb.subs[sub] = struct{}{}\n\tgo func() {\n\t\t<-ctx.Done()\n\t\tb.unsub(sub, true)\n\t}()\n\n\treturn sub, nil\n}\n\nfunc (b *Broadcaster) unsub(sub chan interface{}, lock bool) {\n\tif lock {\n\t\tb.Lock()\n\t}\n\tif _, ok := b.subs[sub]; ok {\n\t\tclose(sub)\n\t\tdelete(b.subs, sub)\n\t}\n\tif lock {\n\t\tb.Unlock()\n\t}\n}\n\nfunc (b *Broadcaster) start(connect ConnectFunc) error {\n\tc, err := connect()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tgo b.stream(c)\n\tb.running = true\n\treturn nil\n}\n\nfunc (b *Broadcaster) stream(input chan interface{}) {\n\tfor item := range input {\n\t\tb.Lock()\n\t\tfor sub := range b.subs {\n\t\t\tselect {\n\t\t\tcase sub <- item:\n\t\t\tdefault:\n\t\t\t\t// Slow consumer, drop\n\t\t\t\tgo b.unsub(sub, true)\n\t\t\t}\n\t\t}\n\t\tb.Unlock()\n\t}\n\n\tb.Lock()\n\tfor sub := range b.subs {\n\t\tb.unsub(sub, false)\n\t}\n\tb.running = false\n\tb.Unlock()\n}\n"
  },
  {
    "path": "pkg/cleanup/cleanup.go",
    "content": "package cleanup\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nfunc Cleanup(path string) error {\n\treturn filepath.Walk(path, func(path string, info os.FileInfo, err error) error {\n\t\tfmt.Println(path)\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif strings.Contains(path, \"vendor\") {\n\t\t\treturn filepath.SkipDir\n\t\t}\n\n\t\tif strings.HasPrefix(info.Name(), \"zz_generated\") {\n\t\t\tfmt.Println(\"Removing\", path)\n\t\t\tif err := os.Remove(path); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "pkg/clients/clients.go",
    "content": "package clients\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/lasso/pkg/dynamic\"\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\tadmissionreg \"github.com/rancher/wrangler/v3/pkg/generated/controllers/admissionregistration.k8s.io\"\n\tadmissionregcontrollers \"github.com/rancher/wrangler/v3/pkg/generated/controllers/admissionregistration.k8s.io/v1\"\n\t\"github.com/rancher/wrangler/v3/pkg/generated/controllers/apiextensions.k8s.io\"\n\tcrdcontrollers \"github.com/rancher/wrangler/v3/pkg/generated/controllers/apiextensions.k8s.io/v1\"\n\t\"github.com/rancher/wrangler/v3/pkg/generated/controllers/apiregistration.k8s.io\"\n\tapicontrollers \"github.com/rancher/wrangler/v3/pkg/generated/controllers/apiregistration.k8s.io/v1\"\n\t\"github.com/rancher/wrangler/v3/pkg/generated/controllers/apps\"\n\tappcontrollers \"github.com/rancher/wrangler/v3/pkg/generated/controllers/apps/v1\"\n\t\"github.com/rancher/wrangler/v3/pkg/generated/controllers/batch\"\n\tbatchcontrollers \"github.com/rancher/wrangler/v3/pkg/generated/controllers/batch/v1\"\n\t\"github.com/rancher/wrangler/v3/pkg/generated/controllers/core\"\n\tcorecontrollers \"github.com/rancher/wrangler/v3/pkg/generated/controllers/core/v1\"\n\t\"github.com/rancher/wrangler/v3/pkg/generated/controllers/rbac\"\n\trbaccontrollers \"github.com/rancher/wrangler/v3/pkg/generated/controllers/rbac/v1\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/ratelimit\"\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\t\"k8s.io/client-go/discovery\"\n\t\"k8s.io/client-go/discovery/cached/memory\"\n\t\"k8s.io/client-go/kubernetes\"\n\t\"k8s.io/client-go/rest\"\n\t\"k8s.io/client-go/restmapper\"\n\t\"k8s.io/client-go/tools/clientcmd\"\n)\n\ntype Clients struct {\n\tK8s       kubernetes.Interface\n\tCore      corecontrollers.Interface\n\tRBAC      rbaccontrollers.Interface\n\tApps      appcontrollers.Interface\n\tCRD       crdcontrollers.Interface\n\tAPI       apicontrollers.Interface\n\tAdmission admissionregcontrollers.Interface\n\tBatch     batchcontrollers.Interface\n\tApply     apply.Apply\n\n\tDynamic                 *dynamic.Controller\n\tClientConfig            clientcmd.ClientConfig\n\tRESTConfig              *rest.Config\n\tCachedDiscovery         discovery.CachedDiscoveryInterface\n\tSharedControllerFactory controller.SharedControllerFactory\n\tRESTMapper              meta.RESTMapper\n\tFactoryOptions          *generic.FactoryOptions\n}\n\nfunc ensureSharedFactory(cfg *rest.Config, opts *generic.FactoryOptions) (*generic.FactoryOptions, error) {\n\tif opts == nil {\n\t\topts = &generic.FactoryOptions{}\n\t}\n\n\tcopy := *opts\n\tfactory, err := generic.NewFactoryFromConfigWithOptions(cfg, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcopy.SharedControllerFactory = factory.ControllerFactory()\n\tcopy.SharedCacheFactory = factory.ControllerFactory().SharedCacheFactory()\n\treturn &copy, nil\n}\n\nfunc New(clientConfig clientcmd.ClientConfig, opts *generic.FactoryOptions) (*Clients, error) {\n\tcfg, err := clientConfig.ClientConfig()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tclients, err := NewFromConfig(cfg, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tclients.ClientConfig = clientConfig\n\n\treturn clients, nil\n}\n\nfunc NewFromConfig(cfg *rest.Config, opts *generic.FactoryOptions) (*Clients, error) {\n\tcfg = restConfigDefaults(cfg)\n\n\topts, err := ensureSharedFactory(cfg, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcore, err := core.NewFactoryFromConfigWithOptions(cfg, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trbac, err := rbac.NewFactoryFromConfigWithOptions(cfg, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tapps, err := apps.NewFactoryFromConfigWithOptions(cfg, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tapi, err := apiregistration.NewFactoryFromConfigWithOptions(cfg, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcrd, err := apiextensions.NewFactoryFromConfigWithOptions(cfg, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tadminReg, err := admissionreg.NewFactoryFromConfigWithOptions(cfg, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tk8s, err := kubernetes.NewForConfig(cfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tbatch, err := batch.NewFactoryFromConfigWithOptions(cfg, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcache := memory.NewMemCacheClient(k8s.Discovery())\n\trestMapper := restmapper.NewDeferredDiscoveryRESTMapper(cache)\n\n\tapply, err := apply.NewForConfig(cfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Clients{\n\t\tK8s:                     k8s,\n\t\tCore:                    core.Core().V1(),\n\t\tRBAC:                    rbac.Rbac().V1(),\n\t\tApps:                    apps.Apps().V1(),\n\t\tCRD:                     crd.Apiextensions().V1(),\n\t\tAPI:                     api.Apiregistration().V1(),\n\t\tAdmission:               adminReg.Admissionregistration().V1(),\n\t\tBatch:                   batch.Batch().V1(),\n\t\tApply:                   apply.WithSetOwnerReference(false, false),\n\t\tRESTConfig:              cfg,\n\t\tCachedDiscovery:         cache,\n\t\tSharedControllerFactory: opts.SharedControllerFactory,\n\t\tRESTMapper:              restMapper,\n\t\tFactoryOptions:          opts,\n\t\tDynamic:                 dynamic.New(k8s.Discovery()),\n\t}, nil\n}\n\nfunc (c *Clients) ToRawKubeConfigLoader() clientcmd.ClientConfig {\n\treturn c.ClientConfig\n}\n\nfunc (c *Clients) ToRESTConfig() (*rest.Config, error) {\n\treturn c.RESTConfig, nil\n}\n\nfunc (c *Clients) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {\n\treturn c.CachedDiscovery, nil\n}\n\nfunc (c *Clients) ToRESTMapper() (meta.RESTMapper, error) {\n\treturn c.RESTMapper, nil\n}\n\nfunc (c *Clients) Start(ctx context.Context) error {\n\tif err := c.Dynamic.Register(ctx, c.SharedControllerFactory); err != nil {\n\t\treturn err\n\t}\n\treturn c.SharedControllerFactory.Start(ctx, 5)\n}\n\nfunc restConfigDefaults(cfg *rest.Config) *rest.Config {\n\tcfg = rest.CopyConfig(cfg)\n\tcfg.Timeout = 15 * time.Minute\n\tcfg.RateLimiter = ratelimit.None\n\n\treturn cfg\n}\n"
  },
  {
    "path": "pkg/codegen/main.go",
    "content": "package main\n\nimport (\n\tcontrollergen \"github.com/rancher/wrangler/v3/pkg/controller-gen\"\n\t\"github.com/rancher/wrangler/v3/pkg/controller-gen/args\"\n\tadmissionregistrationv1 \"k8s.io/api/admissionregistration/v1\"\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tbatchv1 \"k8s.io/api/batch/v1\"\n\tcoordinationv1 \"k8s.io/api/coordination/v1\"\n\tv1 \"k8s.io/api/core/v1\"\n\tdiscoveryv1 \"k8s.io/api/discovery/v1\"\n\textensionsv1beta1 \"k8s.io/api/extensions/v1beta1\"\n\tnetworkingv1 \"k8s.io/api/networking/v1\"\n\trbacv1 \"k8s.io/api/rbac/v1\"\n\tstoragev1 \"k8s.io/api/storage/v1\"\n\tapiextv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\tapiv1 \"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1\"\n)\n\nfunc main() {\n\tcontrollergen.Run(args.Options{\n\t\tImportPackage: \"github.com/rancher/wrangler/v3/pkg/generated\",\n\t\tOutputPackage: \"github.com/rancher/wrangler/pkg/generated\",\n\t\tBoilerplate:   \"scripts/boilerplate.go.txt\",\n\t\tGroups: map[string]args.Group{\n\t\t\tv1.GroupName: {\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\tv1.Event{},\n\t\t\t\t\tv1.Node{},\n\t\t\t\t\tv1.Namespace{},\n\t\t\t\t\tv1.Secret{},\n\t\t\t\t\tv1.Service{},\n\t\t\t\t\tv1.ServiceAccount{},\n\t\t\t\t\tv1.Endpoints{},\n\t\t\t\t\tv1.ConfigMap{},\n\t\t\t\t\tv1.PersistentVolume{},\n\t\t\t\t\tv1.PersistentVolumeClaim{},\n\t\t\t\t\tv1.Pod{},\n\t\t\t\t\tv1.LimitRange{},\n\t\t\t\t\tv1.ResourceQuota{},\n\t\t\t\t},\n\t\t\t},\n\t\t\tdiscoveryv1.GroupName: {\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\tdiscoveryv1.EndpointSlice{},\n\t\t\t\t},\n\t\t\t\tOutputControllerPackageName: \"discovery\",\n\t\t\t},\n\t\t\textensionsv1beta1.GroupName: {\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\textensionsv1beta1.Ingress{},\n\t\t\t\t},\n\t\t\t},\n\t\t\trbacv1.GroupName: {\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\trbacv1.Role{},\n\t\t\t\t\trbacv1.RoleBinding{},\n\t\t\t\t\trbacv1.ClusterRole{},\n\t\t\t\t\trbacv1.ClusterRoleBinding{},\n\t\t\t\t},\n\t\t\t\tOutputControllerPackageName: \"rbac\",\n\t\t\t},\n\t\t\tappsv1.GroupName: {\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\tappsv1.Deployment{},\n\t\t\t\t\tappsv1.DaemonSet{},\n\t\t\t\t\tappsv1.StatefulSet{},\n\t\t\t\t},\n\t\t\t},\n\t\t\tstoragev1.GroupName: {\n\t\t\t\tOutputControllerPackageName: \"storage\",\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\tstoragev1.StorageClass{},\n\t\t\t\t},\n\t\t\t},\n\t\t\tapiextv1.GroupName: {\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\tapiextv1.CustomResourceDefinition{},\n\t\t\t\t},\n\t\t\t},\n\t\t\tapiv1.GroupName: {\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\tapiv1.APIService{},\n\t\t\t\t},\n\t\t\t},\n\t\t\tbatchv1.GroupName: {\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\tbatchv1.Job{},\n\t\t\t\t},\n\t\t\t},\n\t\t\tnetworkingv1.GroupName: {\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\tnetworkingv1.NetworkPolicy{},\n\t\t\t\t},\n\t\t\t},\n\t\t\tadmissionregistrationv1.GroupName: {\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\tadmissionregistrationv1.ValidatingWebhookConfiguration{},\n\t\t\t\t\tadmissionregistrationv1.MutatingWebhookConfiguration{},\n\t\t\t\t},\n\t\t\t},\n\t\t\tcoordinationv1.GroupName: {\n\t\t\t\tTypes: []interface{}{\n\t\t\t\t\tcoordinationv1.Lease{},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "pkg/condition/condition.go",
    "content": "package condition\n\nimport (\n\t\"reflect\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/sirupsen/logrus\"\n)\n\ntype Cond string\n\nfunc (c Cond) GetStatus(obj interface{}) string {\n\treturn getStatus(obj, string(c))\n}\n\nfunc (c Cond) SetError(obj interface{}, reason string, err error) {\n\tif err == nil || err == generic.ErrSkip {\n\t\tc.True(obj)\n\t\tc.Message(obj, \"\")\n\t\tc.Reason(obj, reason)\n\t\treturn\n\t}\n\tif reason == \"\" {\n\t\treason = \"Error\"\n\t}\n\tc.False(obj)\n\tc.Message(obj, err.Error())\n\tc.Reason(obj, reason)\n}\n\nfunc (c Cond) MatchesError(obj interface{}, reason string, err error) bool {\n\tif err == nil {\n\t\treturn c.IsTrue(obj) &&\n\t\t\tc.GetMessage(obj) == \"\" &&\n\t\t\tc.GetReason(obj) == reason\n\t}\n\tif reason == \"\" {\n\t\treason = \"Error\"\n\t}\n\treturn c.IsFalse(obj) &&\n\t\tc.GetMessage(obj) == err.Error() &&\n\t\tc.GetReason(obj) == reason\n}\n\nfunc (c Cond) SetStatus(obj interface{}, status string) {\n\tsetStatus(obj, string(c), status)\n}\n\nfunc (c Cond) SetStatusBool(obj interface{}, val bool) {\n\tif val {\n\t\tsetStatus(obj, string(c), \"True\")\n\t} else {\n\t\tsetStatus(obj, string(c), \"False\")\n\t}\n}\n\nfunc (c Cond) True(obj interface{}) {\n\tsetStatus(obj, string(c), \"True\")\n}\n\nfunc (c Cond) IsTrue(obj interface{}) bool {\n\treturn getStatus(obj, string(c)) == \"True\"\n}\n\nfunc (c Cond) False(obj interface{}) {\n\tsetStatus(obj, string(c), \"False\")\n}\n\nfunc (c Cond) IsFalse(obj interface{}) bool {\n\treturn getStatus(obj, string(c)) == \"False\"\n}\n\nfunc (c Cond) Unknown(obj interface{}) {\n\tsetStatus(obj, string(c), \"Unknown\")\n}\n\nfunc (c Cond) IsUnknown(obj interface{}) bool {\n\treturn getStatus(obj, string(c)) == \"Unknown\"\n}\n\nfunc (c Cond) LastUpdated(obj interface{}, ts string) {\n\tsetTS(obj, string(c), ts)\n}\n\nfunc (c Cond) GetLastUpdated(obj interface{}) string {\n\treturn getTS(obj, string(c))\n}\n\nfunc (c Cond) CreateUnknownIfNotExists(obj interface{}) {\n\tcondSlice := getValue(obj, \"Status\", \"Conditions\")\n\tif !condSlice.IsValid() {\n\t\tcondSlice = getValue(obj, \"Conditions\")\n\t}\n\tcond := findCond(obj, condSlice, string(c))\n\tif cond == nil {\n\t\tc.Unknown(obj)\n\t}\n}\n\nfunc (c Cond) Reason(obj interface{}, reason string) {\n\tcond := findOrCreateCond(obj, string(c))\n\tgetFieldValue(cond, \"Reason\").SetString(reason)\n}\n\nfunc (c Cond) GetReason(obj interface{}) string {\n\tcond := findOrNotCreateCond(obj, string(c))\n\tif cond == nil {\n\t\treturn \"\"\n\t}\n\treturn getFieldValue(*cond, \"Reason\").String()\n}\n\nfunc (c Cond) SetMessageIfBlank(obj interface{}, message string) {\n\tif c.GetMessage(obj) == \"\" {\n\t\tc.Message(obj, message)\n\t}\n}\n\nfunc (c Cond) Message(obj interface{}, message string) {\n\tcond := findOrCreateCond(obj, string(c))\n\tsetValue(cond, \"Message\", message)\n}\n\nfunc (c Cond) GetMessage(obj interface{}) string {\n\tcond := findOrNotCreateCond(obj, string(c))\n\tif cond == nil {\n\t\treturn \"\"\n\t}\n\treturn getFieldValue(*cond, \"Message\").String()\n}\n\nfunc touchTS(value reflect.Value) {\n\tnow := time.Now().UTC().Format(time.RFC3339)\n\tgetFieldValue(value, \"LastUpdateTime\").SetString(now)\n}\n\nfunc getStatus(obj interface{}, condName string) string {\n\tcond := findOrNotCreateCond(obj, condName)\n\tif cond == nil {\n\t\treturn \"\"\n\t}\n\treturn getFieldValue(*cond, \"Status\").String()\n}\n\nfunc setTS(obj interface{}, condName, ts string) {\n\tcond := findOrCreateCond(obj, condName)\n\tgetFieldValue(cond, \"LastUpdateTime\").SetString(ts)\n}\n\nfunc getTS(obj interface{}, condName string) string {\n\tcond := findOrNotCreateCond(obj, condName)\n\tif cond == nil {\n\t\treturn \"\"\n\t}\n\treturn getFieldValue(*cond, \"LastUpdateTime\").String()\n}\n\nfunc setStatus(obj interface{}, condName, status string) {\n\tif reflect.TypeOf(obj).Kind() != reflect.Ptr {\n\t\tpanic(\"obj passed must be a pointer\")\n\t}\n\tcond := findOrCreateCond(obj, condName)\n\tsetValue(cond, \"Status\", status)\n}\n\nfunc setValue(cond reflect.Value, fieldName, newValue string) {\n\tvalue := getFieldValue(cond, fieldName)\n\tif value.String() != newValue {\n\t\tvalue.SetString(newValue)\n\t\ttouchTS(cond)\n\t}\n}\n\nfunc findOrNotCreateCond(obj interface{}, condName string) *reflect.Value {\n\tcondSlice := getValue(obj, \"Status\", \"Conditions\")\n\tif !condSlice.IsValid() {\n\t\tcondSlice = getValue(obj, \"Conditions\")\n\t}\n\treturn findCond(obj, condSlice, condName)\n}\n\nfunc findOrCreateCond(obj interface{}, condName string) reflect.Value {\n\tcondSlice := getValue(obj, \"Status\", \"Conditions\")\n\tif !condSlice.IsValid() {\n\t\tcondSlice = getValue(obj, \"Conditions\")\n\t}\n\tcond := findCond(obj, condSlice, condName)\n\tif cond != nil {\n\t\treturn *cond\n\t}\n\n\tnewCond := reflect.New(condSlice.Type().Elem()).Elem()\n\tnewCond.FieldByName(\"Type\").SetString(condName)\n\tnewCond.FieldByName(\"Status\").SetString(\"Unknown\")\n\tcondSlice.Set(reflect.Append(condSlice, newCond))\n\treturn *findCond(obj, condSlice, condName)\n}\n\nfunc findCond(obj interface{}, val reflect.Value, name string) *reflect.Value {\n\tdefer func() {\n\t\tif recover() != nil {\n\t\t\tlogrus.Fatalf(\"failed to find .Status.Conditions field on %v\", reflect.TypeOf(obj))\n\t\t}\n\t}()\n\n\tfor i := 0; i < val.Len(); i++ {\n\t\tcond := val.Index(i)\n\t\ttypeVal := getFieldValue(cond, \"Type\")\n\t\tif typeVal.String() == name {\n\t\t\treturn &cond\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc getValue(obj interface{}, name ...string) reflect.Value {\n\tif obj == nil {\n\t\treturn reflect.Value{}\n\t}\n\tv := reflect.ValueOf(obj)\n\tt := v.Type()\n\tif t.Kind() == reflect.Ptr {\n\t\tv = v.Elem()\n\t}\n\n\tfield := v.FieldByName(name[0])\n\tif len(name) == 1 {\n\t\treturn field\n\t}\n\treturn getFieldValue(field, name[1:]...)\n}\n\nfunc getFieldValue(v reflect.Value, name ...string) reflect.Value {\n\tif !v.IsValid() {\n\t\treturn v\n\t}\n\tfield := v.FieldByName(name[0])\n\tif len(name) == 1 {\n\t\treturn field\n\t}\n\treturn getFieldValue(field, name[1:]...)\n}\n\nfunc Error(reason string, err error) error {\n\treturn &conditionError{\n\t\treason:  reason,\n\t\tmessage: err.Error(),\n\t}\n}\n\ntype conditionError struct {\n\treason  string\n\tmessage string\n}\n\nfunc (e *conditionError) Error() string {\n\treturn e.message\n}\n"
  },
  {
    "path": "pkg/controller-gen/OWNERS",
    "content": "# See the OWNERS docs at https://go.k8s.io/owners\n\napprovers:\n- lavalamp\n- wojtek-t\n- caesarxuchao\nreviewers:\n- lavalamp\n- wojtek-t\n- caesarxuchao\n"
  },
  {
    "path": "pkg/controller-gen/README.md",
    "content": "See [generating-clientset.md](https://git.k8s.io/community/contributors/devel/sig-api-machinery/generating-clientset.md)\n\n\n[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/staging/src/k8s.io/code-generator/client-gen/README.md?pixel)]()\n"
  },
  {
    "path": "pkg/controller-gen/args/args.go",
    "content": "package args\n\nimport (\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/gengo/v2/types\"\n)\n\ntype CustomArgs struct {\n\t// Package is the directory path where generated code output will be located\n\tPackage       string\n\tImportPackage string\n\tTypesByGroup  map[schema.GroupVersion][]*types.Name\n\tOptions       Options\n\tOutputBase    string\n\t// BoilerplateContent is the actual boilerplate content that has been\n\t// read. It will be added to every generated files.\n\tBoilerplateContent []byte\n}\n\ntype Options struct {\n\tImportPackage string\n\t// OutputPackage is the directory path where generated code output will be located\n\tOutputPackage string\n\tGroups        map[string]Group\n\t// Boilerplate is the filepath to a boilerplate file whose content will\n\t// be added to every generated files.\n\tBoilerplate string\n}\n\ntype Type struct {\n\tVersion string\n\tPackage string\n\tName    string\n}\n\ntype Group struct {\n\t// Types is a slice of the following types\n\t// Instance of any struct: used for reflection to describe the type\n\t// string: a directory that will be listed (non-recursively) for types\n\t// Type: a description of a type\n\tTypes         []interface{}\n\tGenerateTypes bool\n\t// Generate clientsets\n\tGenerateClients             bool\n\tOutputControllerPackageName string\n\t// Generate listers\n\tGenerateListers bool\n\t// Generate informers\n\tGenerateInformers bool\n\t// Generate openapi\n\tGenerateOpenAPI bool\n\t// Open API model package name\n\tOpenAPIModelPackageName string\n\t// OpenAPI extra dependencies\n\tOpenAPIDependencies []string\n\t// The package name of the API types\n\tPackageName string\n}\n"
  },
  {
    "path": "pkg/controller-gen/args/groupversion.go",
    "content": "package args\n\nimport (\n\tgotypes \"go/types\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"golang.org/x/tools/go/packages\"\n\t\"golang.org/x/tools/imports\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/code-generator/cmd/client-gen/generators/util\"\n\t\"k8s.io/gengo/v2/types\"\n)\n\nconst (\n\tneedsComment = `\n\t\t// +genclient\n\t\t// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n\t`\n\tobjectComment = \"+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\"\n)\n\nfunc translate(types []Type, err error) (result []interface{}, _ error) {\n\tfor _, v := range types {\n\t\tresult = append(result, v)\n\t}\n\treturn result, err\n}\n\nfunc ObjectsToGroupVersion(group string, objs []interface{}, ret map[schema.GroupVersion][]*types.Name) error {\n\tfor _, obj := range objs {\n\t\tif s, ok := obj.(string); ok {\n\t\t\ttypes, err := translate(ScanDirectory(s))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err := ObjectsToGroupVersion(group, types, ret); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tversion, t := toVersionType(obj)\n\t\tgv := schema.GroupVersion{\n\t\t\tGroup:   group,\n\t\t\tVersion: version,\n\t\t}\n\t\tret[gv] = append(ret[gv], t)\n\t}\n\n\treturn nil\n}\n\nfunc toVersionType(obj interface{}) (string, *types.Name) {\n\tswitch v := obj.(type) {\n\tcase Type:\n\t\treturn v.Version, &types.Name{\n\t\t\tPackage: v.Package,\n\t\t\tName:    v.Name,\n\t\t}\n\tcase *Type:\n\t\treturn v.Version, &types.Name{\n\t\t\tPackage: v.Package,\n\t\t\tName:    v.Name,\n\t\t}\n\t}\n\n\tt := reflect.TypeOf(obj)\n\tif t.Kind() == reflect.Ptr {\n\t\tt = t.Elem()\n\t}\n\tpkg := imports.VendorlessPath(t.PkgPath())\n\treturn versionFromPackage(pkg), &types.Name{\n\t\tPackage: pkg,\n\t\tName:    t.Name(),\n\t}\n}\n\nfunc versionFromPackage(pkg string) string {\n\tparts := strings.Split(pkg, \"/\")\n\treturn parts[len(parts)-1]\n}\n\nfunc CheckType(passedType *types.Type) {\n\ttags := util.MustParseClientGenTags(passedType.SecondClosestCommentLines)\n\tif !tags.GenerateClient {\n\t\tpanic(\"Type \" + passedType.String() + \" is missing comment \" + needsComment)\n\t}\n\tfound := false\n\tfor _, line := range passedType.SecondClosestCommentLines {\n\t\tif strings.Contains(line, objectComment) {\n\t\t\tfound = true\n\t\t}\n\t}\n\tif !found {\n\t\tpanic(\"Type \" + passedType.String() + \" is missing comment \" + objectComment)\n\t}\n}\n\nfunc ScanDirectory(pkgPath string) (result []Type, err error) {\n\tpkgs, err := packages.Load(&packages.Config{\n\t\tMode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax,\n\t\tDir:  pkgPath,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, v := range pkgs[0].TypesInfo.Defs {\n\t\ttypeAndName, ok := v.(*gotypes.TypeName)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\ts, ok := typeAndName.Type().Underlying().(*gotypes.Struct)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tfor i := 0; i < s.NumFields(); i++ {\n\t\t\tf := s.Field(i)\n\t\t\tif f.Embedded() && f.Type().String() == \"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta\" {\n\t\t\t\tpkgPath := imports.VendorlessPath(pkgs[0].PkgPath)\n\t\t\t\tresult = append(result, Type{\n\t\t\t\t\tPackage: pkgPath,\n\t\t\t\t\tVersion: versionFromPackage(pkgPath),\n\t\t\t\t\tName:    typeAndName.Name(),\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\tsort.Slice(result, func(i, j int) bool {\n\t\treturn result[i].Name < result[j].Name\n\t})\n\treturn\n}\n"
  },
  {
    "path": "pkg/controller-gen/args/groupversion_test.go",
    "content": "package args\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestScan(t *testing.T) {\n\tcwd, _ := os.Getwd()\n\tfmt.Println(cwd)\n\t_, _ = ScanDirectory(\"./testdata\")\n}\n"
  },
  {
    "path": "pkg/controller-gen/args/testdata/test.go",
    "content": "package testdata\n\nimport (\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\ntype SomeStruct struct {\n\tmetav1.TypeMeta   `json:\",inline\"`\n\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n}\n"
  },
  {
    "path": "pkg/controller-gen/generators/client_generator.go",
    "content": "/*\nCopyright 2015 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage generators\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\targs \"github.com/rancher/wrangler/v3/pkg/controller-gen/args\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/gengo/v2/generator\"\n\t\"k8s.io/gengo/v2/types\"\n)\n\ntype ClientGenerator struct {\n\tFakes map[string][]string\n}\n\nfunc NewClientGenerator() *ClientGenerator {\n\treturn &ClientGenerator{\n\t\tFakes: make(map[string][]string),\n\t}\n}\n\n// Packages makes the client package definition.\nfunc (cg *ClientGenerator) GetTargets(context *generator.Context, customArgs *args.CustomArgs) []generator.Target {\n\tgenerateTypesGroups := map[string]bool{}\n\n\tfor groupName, group := range customArgs.Options.Groups {\n\t\tif group.GenerateTypes {\n\t\t\tgenerateTypesGroups[groupName] = true\n\t\t}\n\t}\n\n\tvar (\n\t\tpackageList []generator.Target\n\t\tgroups      = map[string]bool{}\n\t)\n\n\tfor gv, types := range customArgs.TypesByGroup {\n\t\tif !groups[gv.Group] {\n\t\t\tpackageList = append(packageList, cg.groupPackage(gv.Group, customArgs))\n\t\t\tif generateTypesGroups[gv.Group] {\n\t\t\t\tpackageList = append(packageList, cg.typesGroupPackage(types[0], gv, customArgs))\n\t\t\t}\n\t\t}\n\t\tgroups[gv.Group] = true\n\t\tpackageList = append(packageList, cg.groupVersionPackage(gv, customArgs))\n\n\t\tif generateTypesGroups[gv.Group] {\n\t\t\tpackageList = append(packageList, cg.typesGroupVersionPackage(types[0], gv, customArgs))\n\t\t\tpackageList = append(packageList, cg.typesGroupVersionDocPackage(types[0], gv, customArgs))\n\t\t}\n\t}\n\n\treturn packageList\n}\n\nfunc (cg *ClientGenerator) typesGroupPackage(name *types.Name, gv schema.GroupVersion, customArgs *args.CustomArgs) generator.Target {\n\tpackagePath := strings.TrimSuffix(name.Package, \"/\"+gv.Version)\n\treturn Target(customArgs, packagePath, func(context *generator.Context) []generator.Generator {\n\t\treturn []generator.Generator{\n\t\t\tRegisterGroupGo(gv.Group, customArgs),\n\t\t}\n\t})\n}\n\nfunc (cg *ClientGenerator) typesGroupVersionDocPackage(name *types.Name, gv schema.GroupVersion, customArgs *args.CustomArgs) generator.Target {\n\tpackagePath := name.Package\n\tp := Target(customArgs, packagePath, func(context *generator.Context) []generator.Generator {\n\t\treturn []generator.Generator{\n\t\t\tgenerator.GoGenerator{\n\t\t\t\tOutputFilename: \"doc.go\",\n\t\t\t},\n\t\t\tRegisterGroupVersionGo(gv, customArgs),\n\t\t\tListTypesGo(gv, customArgs),\n\t\t}\n\t})\n\n\topenAPIDirective := \"\"\n\tif customArgs.Options.Groups[gv.Group].GenerateOpenAPI {\n\t\topenAPIDirective = fmt.Sprintf(\"\\n// +k8s:openapi-gen=true\")\n\t}\n\n\tif customArgs.Options.Groups[gv.Group].OpenAPIModelPackageName != \"\" {\n\t\topenAPIDirective += fmt.Sprintf(\"\\n// +k8s:openapi-model-package=%s\", customArgs.Options.Groups[gv.Group].OpenAPIModelPackageName)\n\t}\n\n\tp.HeaderComment = []byte(fmt.Sprintf(`\n%s\n%s\n// +k8s:deepcopy-gen=package\n// +groupName=%s\n`, string(customArgs.BoilerplateContent), openAPIDirective, gv.Group))\n\n\treturn p\n}\n\nfunc (cg *ClientGenerator) typesGroupVersionPackage(name *types.Name, gv schema.GroupVersion, customArgs *args.CustomArgs) generator.Target {\n\tpackagePath := name.Package\n\treturn Target(customArgs, packagePath, func(context *generator.Context) []generator.Generator {\n\t\treturn []generator.Generator{\n\t\t\tRegisterGroupVersionGo(gv, customArgs),\n\t\t\tListTypesGo(gv, customArgs),\n\t\t}\n\t})\n}\n\nfunc (cg *ClientGenerator) groupPackage(group string, customArgs *args.CustomArgs) generator.Target {\n\tpackagePath := filepath.Join(customArgs.Package, \"controllers\", groupPackageName(group, customArgs.Options.Groups[group].OutputControllerPackageName))\n\treturn Target(customArgs, packagePath, func(context *generator.Context) []generator.Generator {\n\t\treturn []generator.Generator{\n\t\t\tFactoryGo(group, customArgs),\n\t\t\tGroupInterfaceGo(group, customArgs),\n\t\t}\n\t})\n}\n\nfunc (cg *ClientGenerator) groupVersionPackage(gv schema.GroupVersion, customArgs *args.CustomArgs) generator.Target {\n\tpackagePath := filepath.Join(customArgs.Package, \"controllers\", groupPackageName(gv.Group, customArgs.Options.Groups[gv.Group].OutputControllerPackageName), gv.Version)\n\n\treturn Target(customArgs, packagePath, func(context *generator.Context) []generator.Generator {\n\t\tgenerators := []generator.Generator{\n\t\t\tGroupVersionInterfaceGo(gv, customArgs),\n\t\t}\n\n\t\tfor _, t := range customArgs.TypesByGroup[gv] {\n\t\t\tgenerators = append(generators, TypeGo(gv, t, customArgs))\n\t\t\tcg.Fakes[packagePath] = append(cg.Fakes[packagePath], t.Name)\n\t\t}\n\n\t\treturn generators\n\t})\n}\n"
  },
  {
    "path": "pkg/controller-gen/generators/factory_go.go",
    "content": "package generators\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/controller-gen/args\"\n\t\"k8s.io/gengo/v2/generator\"\n)\n\nfunc FactoryGo(group string, customArgs *args.CustomArgs) generator.Generator {\n\treturn &factory{\n\t\tgroup:      group,\n\t\tcustomArgs: customArgs,\n\t\tGoGenerator: generator.GoGenerator{\n\t\t\tOutputFilename: \"factory.go\",\n\t\t\tOptionalBody:   []byte(factoryBody),\n\t\t},\n\t}\n}\n\ntype factory struct {\n\tgenerator.GoGenerator\n\n\tgroup      string\n\tcustomArgs *args.CustomArgs\n}\n\nfunc (f *factory) Imports(*generator.Context) []string {\n\timports := Imports\n\n\tfor gv, types := range f.customArgs.TypesByGroup {\n\t\tif f.group == gv.Group && len(types) > 0 {\n\t\t\timports = append(imports,\n\t\t\t\tfmt.Sprintf(\"%s \\\"%s\\\"\", gv.Version, types[0].Package))\n\t\t}\n\t}\n\n\treturn imports\n}\n\nfunc (f *factory) Init(c *generator.Context, w io.Writer) error {\n\tif err := f.GoGenerator.Init(c, w); err != nil {\n\t\treturn err\n\t}\n\n\tsw := generator.NewSnippetWriter(w, c, \"{{\", \"}}\")\n\tm := map[string]interface{}{\n\t\t\"groupName\": upperLowercase(f.group),\n\t}\n\n\tsw.Do(\"\\n\\nfunc (c *Factory) {{.groupName}}() Interface {\\n\", m)\n\tsw.Do(\"\treturn New(c.ControllerFactory())\\n\", m)\n\tsw.Do(\"}\\n\\n\", m)\n\n\tsw.Do(\"\\n\\nfunc (c *Factory) WithAgent(userAgent string) Interface {\\n\", m)\n\tsw.Do(\"\treturn New(controller.NewSharedControllerFactoryWithAgent(userAgent, c.ControllerFactory()))\\n\", m)\n\tsw.Do(\"}\\n\\n\", m)\n\n\treturn sw.Error()\n}\n\nvar factoryBody = `\ntype Factory struct {\n\t*generic.Factory\n}\n\nfunc NewFactoryFromConfigOrDie(config *rest.Config) *Factory {\n\tf, err := NewFactoryFromConfig(config)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFactoryFromConfig(config *rest.Config) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, nil)\n}\n\nfunc NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, &FactoryOptions{\n\t\tNamespace: namespace,\n\t})\n}\n\ntype FactoryOptions = generic.FactoryOptions\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tf, err := generic.NewFactoryFromConfigWithOptions(config, opts)\n\treturn &Factory{\n\t\tFactory: f,\n\t}, err\n}\n\nfunc NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory {\n    f, err := NewFactoryFromConfigWithOptions(config, opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\n`\n"
  },
  {
    "path": "pkg/controller-gen/generators/group_interface_go.go",
    "content": "package generators\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/controller-gen/args\"\n\t\"k8s.io/gengo/v2/generator\"\n\t\"k8s.io/gengo/v2/namer\"\n)\n\nfunc GroupInterfaceGo(group string, customArgs *args.CustomArgs) generator.Generator {\n\treturn &interfaceGo{\n\t\tgroup:      group,\n\t\tcustomArgs: customArgs,\n\t\tGoGenerator: generator.GoGenerator{\n\t\t\tOutputFilename: \"interface.go\",\n\t\t\tOptionalBody:   []byte(interfaceBody),\n\t\t},\n\t}\n}\n\ntype interfaceGo struct {\n\tgenerator.GoGenerator\n\n\tgroup      string\n\tcustomArgs *args.CustomArgs\n}\n\nfunc (f *interfaceGo) Imports(context *generator.Context) []string {\n\tpackages := Imports\n\n\tfor gv := range f.customArgs.TypesByGroup {\n\t\tif gv.Group != f.group {\n\t\t\tcontinue\n\t\t}\n\t\tpkg := f.customArgs.ImportPackage\n\t\tif pkg == \"\" {\n\t\t\tpkg = f.customArgs.Package\n\t\t}\n\t\tpackages = append(packages, fmt.Sprintf(\"%s \\\"%s/controllers/%s/%s\\\"\", gv.Version, pkg,\n\t\t\tgroupPackageName(gv.Group, f.customArgs.Options.Groups[gv.Group].OutputControllerPackageName), gv.Version))\n\t}\n\n\treturn packages\n}\n\nfunc (f *interfaceGo) Init(c *generator.Context, w io.Writer) error {\n\tsw := generator.NewSnippetWriter(w, c, \"{{\", \"}}\")\n\tsw.Do(\"type Interface interface {\\n\", nil)\n\tfor gv := range f.customArgs.TypesByGroup {\n\t\tif gv.Group != f.group {\n\t\t\tcontinue\n\t\t}\n\n\t\tsw.Do(\"{{.upperVersion}}() {{.version}}.Interface\\n\", map[string]interface{}{\n\t\t\t\"upperVersion\": namer.IC(gv.Version),\n\t\t\t\"version\":      gv.Version,\n\t\t})\n\t}\n\tsw.Do(\"}\\n\", nil)\n\n\tif err := f.GoGenerator.Init(c, w); err != nil {\n\t\treturn err\n\t}\n\n\tfor gv := range f.customArgs.TypesByGroup {\n\t\tif gv.Group != f.group {\n\t\t\tcontinue\n\t\t}\n\n\t\tm := map[string]interface{}{\n\t\t\t\"upperGroup\":   upperLowercase(f.group),\n\t\t\t\"upperVersion\": namer.IC(gv.Version),\n\t\t\t\"version\":      gv.Version,\n\t\t}\n\t\tsw.Do(\"\\nfunc (g *group) {{.upperVersion}}() {{.version}}.Interface {\\n\", m)\n\t\tsw.Do(\"return {{.version}}.New(g.controllerFactory)\\n\", m)\n\t\tsw.Do(\"}\\n\", m)\n\t}\n\n\treturn sw.Error()\n}\n\nvar interfaceBody = `\ntype group struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n// New returns a new Interface.\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &group{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n`\n"
  },
  {
    "path": "pkg/controller-gen/generators/group_version_interface_go.go",
    "content": "package generators\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/controller-gen/args\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/gengo/v2/generator\"\n\t\"k8s.io/gengo/v2/namer\"\n\t\"k8s.io/gengo/v2/types\"\n)\n\nfunc GroupVersionInterfaceGo(gv schema.GroupVersion, customArgs *args.CustomArgs) generator.Generator {\n\treturn &groupInterfaceGo{\n\t\tgv:         gv,\n\t\tcustomArgs: customArgs,\n\t\tGoGenerator: generator.GoGenerator{\n\t\t\tOutputFilename: \"interface.go\",\n\t\t},\n\t}\n}\n\ntype groupInterfaceGo struct {\n\tgenerator.GoGenerator\n\n\tgv         schema.GroupVersion\n\tcustomArgs *args.CustomArgs\n}\n\nfunc (f *groupInterfaceGo) Imports(context *generator.Context) []string {\n\tfirstType := f.customArgs.TypesByGroup[f.gv][0]\n\n\tpackages := append(Imports,\n\t\tfmt.Sprintf(\"%s \\\"%s\\\"\", f.gv.Version, firstType.Package))\n\n\treturn packages\n}\n\nvar (\n\tpluralExceptions = map[string]string{\n\t\t\"Endpoints\": \"Endpoints\",\n\t}\n\tplural = namer.NewPublicPluralNamer(pluralExceptions)\n)\n\nfunc (f *groupInterfaceGo) Init(c *generator.Context, w io.Writer) error {\n\tsw := generator.NewSnippetWriter(w, c, \"{{\", \"}}\")\n\n\torderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)}\n\n\tvar types []*types.Type\n\tfor _, name := range f.customArgs.TypesByGroup[f.gv] {\n\t\ttypes = append(types, c.Universe.Type(*name))\n\t}\n\ttypes = orderer.OrderTypes(types)\n\n\tsw.Do(\"func init() {\\n\", nil)\n\tsw.Do(\"schemes.Register(\"+f.gv.Version+\".AddToScheme)\\n\", nil)\n\tsw.Do(\"}\\n\", nil)\n\n\tsw.Do(\"type Interface interface {\\n\", nil)\n\tfor _, t := range types {\n\t\tm := map[string]interface{}{\n\t\t\t\"type\": t.Name.Name,\n\t\t}\n\t\tsw.Do(\"{{.type}}() {{.type}}Controller\\n\", m)\n\t}\n\tsw.Do(\"}\\n\", nil)\n\n\tm := map[string]interface{}{\n\t\t\"version\":      f.gv.Version,\n\t\t\"versionUpper\": namer.IC(f.gv.Version),\n\t\t\"groupUpper\":   upperLowercase(f.gv.Group),\n\t}\n\tsw.Do(groupInterfaceBody, m)\n\n\tfor _, t := range types {\n\t\tm := map[string]interface{}{\n\t\t\t\"type\":         t.Name.Name,\n\t\t\t\"plural\":       plural.Name(t),\n\t\t\t\"pluralLower\":  strings.ToLower(plural.Name(t)),\n\t\t\t\"version\":      f.gv.Version,\n\t\t\t\"group\":        f.gv.Group,\n\t\t\t\"namespaced\":   namespaced(t),\n\t\t\t\"versionUpper\": namer.IC(f.gv.Version),\n\t\t\t\"groupUpper\":   upperLowercase(f.gv.Group),\n\t\t}\n\t\tbody := `\n\t\tfunc (v *version) {{.type}}() {{.type}}Controller {\n\t\t\treturn generic.New{{ if not .namespaced}}NonNamespaced{{end}}Controller[*{{.version}}.{{.type}}, *{{.version}}.{{.type}}List](schema.GroupVersionKind{Group: \"{{.group}}\", Version: \"{{.version}}\", Kind: \"{{.type}}\"}, \"{{.pluralLower}}\", {{ if .namespaced}}true, {{end}}v.controllerFactory)\n\t\t}\n\t\t`\n\t\tsw.Do(body, m)\n\t}\n\n\treturn sw.Error()\n}\n\nvar groupInterfaceBody = `\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &version{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\ntype version struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n`\n"
  },
  {
    "path": "pkg/controller-gen/generators/list_type_go.go",
    "content": "package generators\n\nimport (\n\t\"io\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/controller-gen/args\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/gengo/v2/generator\"\n)\n\nfunc ListTypesGo(gv schema.GroupVersion, customArgs *args.CustomArgs) generator.Generator {\n\treturn &listTypesGo{\n\t\tgv:         gv,\n\t\tcustomArgs: customArgs,\n\t\tGoGenerator: generator.GoGenerator{\n\t\t\tOutputFilename: \"zz_generated_list_types.go\",\n\t\t},\n\t}\n}\n\ntype listTypesGo struct {\n\tgenerator.GoGenerator\n\n\tgv         schema.GroupVersion\n\tcustomArgs *args.CustomArgs\n}\n\nfunc (f *listTypesGo) Imports(*generator.Context) []string {\n\treturn Imports\n}\n\nfunc (f *listTypesGo) Init(c *generator.Context, w io.Writer) error {\n\tsw := generator.NewSnippetWriter(w, c, \"{{\", \"}}\")\n\n\tfor _, t := range f.customArgs.TypesByGroup[f.gv] {\n\t\tm := map[string]interface{}{\n\t\t\t\"type\": t.Name,\n\t\t}\n\t\targs.CheckType(c.Universe.Type(*t))\n\t\tsw.Do(string(listTypesBody), m)\n\t}\n\n\treturn sw.Error()\n}\n\nvar listTypesBody = `\n// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n\n// {{.type}}List is a list of {{.type}} resources\ntype {{.type}}List struct {\n\tmetav1.TypeMeta ` + \"`\" + `json:\",inline\"` + \"`\" + `\n\tmetav1.ListMeta ` + \"`\" + `json:\"metadata\"` + \"`\" + `\n\n\tItems []{{.type}} ` + \"`\" + `json:\"items\"` + \"`\" + `\n}\n\nfunc New{{.type}}(namespace, name string, obj {{.type}}) *{{.type}} {\n\tobj.APIVersion, obj.Kind = SchemeGroupVersion.WithKind(\"{{.type}}\").ToAPIVersionAndKind()\n\tobj.Name = name\n\tobj.Namespace = namespace\n\treturn &obj\n}\n`\n"
  },
  {
    "path": "pkg/controller-gen/generators/register_group_go.go",
    "content": "package generators\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/controller-gen/args\"\n\t\"k8s.io/gengo/v2/generator\"\n)\n\nfunc RegisterGroupGo(group string, customArgs *args.CustomArgs) generator.Generator {\n\treturn &registerGroupGo{\n\t\tgroup:      group,\n\t\tcustomArgs: customArgs,\n\t\tGoGenerator: generator.GoGenerator{\n\t\t\tOutputFilename: \"zz_generated_register.go\",\n\t\t},\n\t}\n}\n\ntype registerGroupGo struct {\n\tgenerator.GoGenerator\n\n\tgroup      string\n\tcustomArgs *args.CustomArgs\n}\n\nfunc (f *registerGroupGo) Name() string {\n\t// Keep the old behavior of generating comments without the .go suffix\n\treturn strings.TrimSuffix(f.Filename(), \".go\")\n}\n\nfunc (f *registerGroupGo) PackageConsts(*generator.Context) []string {\n\treturn []string{\n\t\tfmt.Sprintf(\"GroupName = \\\"%s\\\"\", f.group),\n\t}\n}\n"
  },
  {
    "path": "pkg/controller-gen/generators/register_group_version_go.go",
    "content": "package generators\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/controller-gen/args\"\n\t\"github.com/rancher/wrangler/v3/pkg/name\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/gengo/v2/generator\"\n\t\"k8s.io/gengo/v2/namer\"\n\t\"k8s.io/gengo/v2/types\"\n)\n\nfunc RegisterGroupVersionGo(gv schema.GroupVersion, customArgs *args.CustomArgs) generator.Generator {\n\treturn &registerGroupVersionGo{\n\t\tgv:         gv,\n\t\tcustomArgs: customArgs,\n\t\tGoGenerator: generator.GoGenerator{\n\t\t\tOutputFilename: \"zz_generated_register.go\",\n\t\t},\n\t}\n}\n\ntype registerGroupVersionGo struct {\n\tgenerator.GoGenerator\n\n\tgv         schema.GroupVersion\n\tcustomArgs *args.CustomArgs\n}\n\nfunc (f *registerGroupVersionGo) Imports(*generator.Context) []string {\n\tfirstType := f.customArgs.TypesByGroup[f.gv][0]\n\ttypeGroupPath := strings.TrimSuffix(firstType.Package, \"/\"+f.gv.Version)\n\n\tpackages := append(Imports,\n\t\tfmt.Sprintf(\"%s \\\"%s\\\"\", groupPath(f.gv.Group), typeGroupPath))\n\n\treturn packages\n}\n\nfunc (f *registerGroupVersionGo) Init(c *generator.Context, w io.Writer) error {\n\tvar (\n\t\ttypes   []*types.Type\n\t\torderer = namer.Orderer{Namer: namer.NewPrivateNamer(0)}\n\t\tsw      = generator.NewSnippetWriter(w, c, \"{{\", \"}}\")\n\t)\n\n\tfor _, name := range f.customArgs.TypesByGroup[f.gv] {\n\t\ttypes = append(types, c.Universe.Type(*name))\n\t}\n\ttypes = orderer.OrderTypes(types)\n\n\tm := map[string]interface{}{\n\t\t\"version\":   f.gv.Version,\n\t\t\"groupPath\": groupPath(f.gv.Group),\n\t}\n\n\tsw.Do(\"var (\\n\", nil)\n\tfor _, t := range types {\n\t\tm := map[string]interface{}{\n\t\t\t\"name\":   t.Name.Name + \"ResourceName\",\n\t\t\t\"plural\": name.GuessPluralName(strings.ToLower(t.Name.Name)),\n\t\t}\n\n\t\tsw.Do(\"{{.name}} = \\\"{{.plural}}\\\"\\n\", m)\n\t}\n\tsw.Do(\")\\n\", nil)\n\n\tsw.Do(registerGroupVersionBody, m)\n\n\tfor _, t := range types {\n\t\tm := map[string]interface{}{\n\t\t\t\"type\": t.Name.Name,\n\t\t}\n\n\t\tsw.Do(\"&{{.type}}{},\\n\", m)\n\t\tsw.Do(\"&{{.type}}List{},\\n\", m)\n\t}\n\n\tsw.Do(registerGroupVersionBodyEnd, nil)\n\n\treturn sw.Error()\n}\n\nvar registerGroupVersionBody = `\n// SchemeGroupVersion is group version used to register these objects\nvar SchemeGroupVersion = schema.GroupVersion{Group: {{.groupPath}}.GroupName, Version: \"{{.version}}\"}\n\n// Kind takes an unqualified kind and returns back a Group qualified GroupKind\nfunc Kind(kind string) schema.GroupKind {\n\treturn SchemeGroupVersion.WithKind(kind).GroupKind()\n}\n\n// Resource takes an unqualified resource and returns a Group qualified GroupResource\nfunc Resource(resource string) schema.GroupResource {\n\treturn SchemeGroupVersion.WithResource(resource).GroupResource()\n}\n\nvar (\n\tSchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)\n\tAddToScheme   = SchemeBuilder.AddToScheme\n)\n\n// Adds the list of known types to Scheme.\nfunc addKnownTypes(scheme *runtime.Scheme) error {\n\tscheme.AddKnownTypes(SchemeGroupVersion,\n`\n\nvar registerGroupVersionBodyEnd = `\n\t)\n\tmetav1.AddToGroupVersion(scheme, SchemeGroupVersion)\n\treturn nil\n}\n`\n"
  },
  {
    "path": "pkg/controller-gen/generators/target.go",
    "content": "package generators\n\nimport (\n\t\"path/filepath\"\n\t\"strings\"\n\n\targs \"github.com/rancher/wrangler/v3/pkg/controller-gen/args\"\n\t\"k8s.io/gengo/v2/generator\"\n)\n\nfunc Target(customArgs *args.CustomArgs, name string, generators func(context *generator.Context) []generator.Generator) generator.SimpleTarget {\n\tparts := strings.Split(name, \"/\")\n\treturn generator.SimpleTarget{\n\t\tPkgName:        groupPath(parts[len(parts)-1]),\n\t\tPkgPath:        name,\n\t\tPkgDir:         filepath.Join(customArgs.OutputBase, name),\n\t\tHeaderComment:  customArgs.BoilerplateContent,\n\t\tGeneratorsFunc: generators,\n\t}\n}\n"
  },
  {
    "path": "pkg/controller-gen/generators/type_go.go",
    "content": "package generators\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/controller-gen/args\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/gengo/v2/generator\"\n\t\"k8s.io/gengo/v2/namer\"\n\t\"k8s.io/gengo/v2/types\"\n)\n\nfunc TypeGo(gv schema.GroupVersion, name *types.Name, customArgs *args.CustomArgs) generator.Generator {\n\treturn &typeGo{\n\t\tname:       name,\n\t\tgv:         gv,\n\t\tcustomArgs: customArgs,\n\t\tGoGenerator: generator.GoGenerator{\n\t\t\tOutputFilename: fmt.Sprintf(\"%s.go\", strings.ToLower(name.Name)),\n\t\t},\n\t}\n}\n\ntype typeGo struct {\n\tgenerator.GoGenerator\n\n\tname       *types.Name\n\tgv         schema.GroupVersion\n\tcustomArgs *args.CustomArgs\n}\n\nfunc (f *typeGo) Imports(context *generator.Context) []string {\n\tpackages := append(Imports,\n\t\tfmt.Sprintf(\"%s \\\"%s\\\"\", f.gv.Version, f.name.Package))\n\n\treturn packages\n}\n\nfunc (f *typeGo) Init(c *generator.Context, w io.Writer) error {\n\tsw := generator.NewSnippetWriter(w, c, \"{{\", \"}}\")\n\n\tif err := f.GoGenerator.Init(c, w); err != nil {\n\t\treturn err\n\t}\n\n\tt := c.Universe.Type(*f.name)\n\tm := map[string]interface{}{\n\t\t\"type\":       f.name.Name,\n\t\t\"lowerName\":  namer.IL(f.name.Name),\n\t\t\"plural\":     plural.Name(t),\n\t\t\"version\":    f.gv.Version,\n\t\t\"namespaced\": namespaced(t),\n\t\t\"hasStatus\":  hasStatus(t),\n\t\t\"statusType\": statusType(t),\n\t}\n\n\tsw.Do(typeBody, m)\n\treturn sw.Error()\n}\n\nfunc statusType(t *types.Type) string {\n\tfor _, m := range t.Members {\n\t\tif m.Name == \"Status\" {\n\t\t\treturn m.Type.Name.Name\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc hasStatus(t *types.Type) bool {\n\tfor _, m := range t.Members {\n\t\tif m.Name == \"Status\" && m.Type.Name.Package == t.Name.Package {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nvar typeBody = `\n// {{.type}}Controller interface for managing {{.type}} resources.\ntype {{.type}}Controller interface {\n    generic.{{ if not .namespaced}}NonNamespaced{{end}}ControllerInterface[*{{.version}}.{{.type}}, *{{.version}}.{{.type}}List]\n}\n\n// {{.type}}Client interface for managing {{.type}} resources in Kubernetes.\ntype {{.type}}Client interface {\n\tgeneric.{{ if not .namespaced}}NonNamespaced{{end}}ClientInterface[*{{.version}}.{{.type}}, *{{.version}}.{{.type}}List]\n}\n\n// {{.type}}Cache interface for retrieving {{.type}} resources in memory.\ntype {{.type}}Cache interface {\n\tgeneric.{{ if not .namespaced}}NonNamespaced{{end}}CacheInterface[*{{.version}}.{{.type}}]\n}\n\n{{ if .hasStatus -}}\n// {{.type}}StatusHandler is executed for every added or modified {{.type}}. Should return the new status to be updated\ntype {{.type}}StatusHandler func(obj *{{.version}}.{{.type}}, status {{.version}}.{{.statusType}}) ({{.version}}.{{.statusType}}, error)\n\n// {{.type}}GeneratingHandler is the top-level handler that is executed for every {{.type}} event. It extends {{.type}}StatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype {{.type}}GeneratingHandler func(obj *{{.version}}.{{.type}}, status {{.version}}.{{.statusType}}) ([]runtime.Object, {{.version}}.{{.statusType}}, error)\n\n// Register{{.type}}StatusHandler configures a {{.type}}Controller to execute a {{.type}}StatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc Register{{.type}}StatusHandler(ctx context.Context, controller {{.type}}Controller, condition condition.Cond, name string, handler {{.type}}StatusHandler) {\n\tstatusHandler := &{{.lowerName}}StatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// Register{{.type}}GeneratingHandler configures a {{.type}}Controller to execute a {{.type}}GeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc Register{{.type}}GeneratingHandler(ctx context.Context, controller {{.type}}Controller, apply apply.Apply,\n\tcondition condition.Cond, name string, handler {{.type}}GeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &{{.lowerName}}GeneratingHandler{\n\t\t{{.type}}GeneratingHandler: handler,\n\t\tapply:                            apply,\n\t\tname:                             name,\n\t\tgvk:                              controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegister{{.type}}StatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype {{.lowerName}}StatusHandler struct {\n\tclient    {{.type}}Client\n\tcondition condition.Cond\n\thandler   {{.type}}StatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *{{.lowerName}}StatusHandler) sync(key string, obj *{{.version}}.{{.type}}) (*{{.version}}.{{.type}}, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype {{.lowerName}}GeneratingHandler struct {\n\t{{.type}}GeneratingHandler\n\tapply      apply.Apply\n\topts       generic.GeneratingHandlerOptions\n\tgvk        schema.GroupVersionKind\n\tname       string\n\tseen       sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *{{.lowerName}}GeneratingHandler) Remove(key string, obj *{{.version}}.{{.type}}) (*{{.version}}.{{.type}}, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &{{.version}}.{{.type}}{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured {{.type}}GeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *{{.lowerName}}GeneratingHandler) Handle(obj *{{.version}}.{{.type}}, status {{.version}}.{{.statusType}}) ({{.version}}.{{.statusType}}, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.{{.type}}GeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t    return newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed. \n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *{{.lowerName}}GeneratingHandler) isNewResourceVersion(obj *{{.version}}.{{.type}}) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *{{.lowerName}}GeneratingHandler) storeResourceVersion(obj *{{.version}}.{{.type}}) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n{{- end }}\n`\n"
  },
  {
    "path": "pkg/controller-gen/generators/util.go",
    "content": "package generators\n\nimport (\n\t\"strings\"\n\n\t\"k8s.io/code-generator/cmd/client-gen/generators/util\"\n\t\"k8s.io/gengo/v2/namer\"\n\t\"k8s.io/gengo/v2/types\"\n)\n\nvar (\n\tImports = []string{\n\t\t\"context\",\n\t\t\"sync\",\n\t\t\"time\",\n\t\t\"k8s.io/client-go/rest\",\n\t\t\"github.com/rancher/wrangler/v3/pkg/apply\",\n\t\t\"github.com/rancher/lasso/pkg/controller\",\n\t\t\"github.com/rancher/wrangler/v3/pkg/condition\",\n\t\t\"github.com/rancher/wrangler/v3/pkg/schemes\",\n\t\t\"github.com/rancher/wrangler/v3/pkg/generic\",\n\t\t\"github.com/rancher/wrangler/v3/pkg/kv\",\n\t\t\"k8s.io/apimachinery/pkg/api/equality\",\n\t\t\"k8s.io/apimachinery/pkg/api/errors\",\n\t\t\"metav1 \\\"k8s.io/apimachinery/pkg/apis/meta/v1\\\"\",\n\t\t\"k8s.io/apimachinery/pkg/labels\",\n\t\t\"k8s.io/apimachinery/pkg/runtime\",\n\t\t\"k8s.io/apimachinery/pkg/runtime/schema\",\n\t\t\"k8s.io/apimachinery/pkg/types\",\n\t\t\"k8s.io/apimachinery/pkg/watch\",\n\t}\n)\n\nfunc namespaced(t *types.Type) bool {\n\tif util.MustParseClientGenTags(t.SecondClosestCommentLines).NonNamespaced {\n\t\treturn false\n\t}\n\n\tkubeBuilder := false\n\tfor _, line := range t.SecondClosestCommentLines {\n\t\tif strings.HasPrefix(line, \"+kubebuilder:resource:path=\") {\n\t\t\tkubeBuilder = true\n\t\t\tif strings.Contains(line, \"scope=Namespaced\") {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn !kubeBuilder\n}\n\nfunc groupPath(group string) string {\n\tg := strings.Replace(strings.Split(group, \".\")[0], \"-\", \"\", -1)\n\treturn groupPackageName(g, \"\")\n}\n\nfunc groupPackageName(group, groupPackageName string) string {\n\tif groupPackageName != \"\" {\n\t\treturn groupPackageName\n\t}\n\tif group == \"\" {\n\t\treturn \"core\"\n\t}\n\treturn group\n}\n\nfunc upperLowercase(name string) string {\n\treturn namer.IC(strings.ToLower(groupPath(name)))\n}\n"
  },
  {
    "path": "pkg/controller-gen/main.go",
    "content": "package controllergen\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"k8s.io/gengo/args\"\n\t\"k8s.io/gengo/v2\"\n\t\"k8s.io/gengo/v2/generator\"\n\t\"k8s.io/gengo/v2/types\"\n\n\tcgargs \"github.com/rancher/wrangler/v3/pkg/controller-gen/args\"\n\t\"github.com/rancher/wrangler/v3/pkg/controller-gen/generators\"\n\t\"github.com/sirupsen/logrus\"\n\t\"golang.org/x/tools/imports\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\tcsargs \"k8s.io/code-generator/cmd/client-gen/args\"\n\n\tcs \"k8s.io/code-generator/cmd/client-gen/generators\"\n\ttypes2 \"k8s.io/code-generator/cmd/client-gen/types\"\n\tdpargs \"k8s.io/code-generator/cmd/deepcopy-gen/args\"\n\tdp \"k8s.io/code-generator/cmd/deepcopy-gen/generators\"\n\tinfargs \"k8s.io/code-generator/cmd/informer-gen/args\"\n\tinf \"k8s.io/code-generator/cmd/informer-gen/generators\"\n\tlsargs \"k8s.io/code-generator/cmd/lister-gen/args\"\n\tls \"k8s.io/code-generator/cmd/lister-gen/generators\"\n\toaargs \"k8s.io/kube-openapi/cmd/openapi-gen/args\"\n\toa \"k8s.io/kube-openapi/pkg/generators\"\n)\n\nfunc Run(opts cgargs.Options) {\n\tgenericArgs := args.Default().WithoutDefaultFlagParsing()\n\tgenericArgs.GoHeaderFilePath = opts.Boilerplate\n\tif genericArgs.OutputBase == \"./\" { //go modules\n\t\ttempDir, err := os.MkdirTemp(\"\", \"\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tgenericArgs.OutputBase = tempDir\n\t\tdefer os.RemoveAll(tempDir)\n\t}\n\n\tboilerplate, err := genericArgs.LoadGoBoilerplate()\n\tif err != nil {\n\t\tlogrus.Fatalf(\"Loading boilerplate: %v\", err)\n\t}\n\n\tcustomArgs := &cgargs.CustomArgs{\n\t\tImportPackage:      opts.ImportPackage,\n\t\tOptions:            opts,\n\t\tTypesByGroup:       map[schema.GroupVersion][]*types.Name{},\n\t\tPackage:            opts.OutputPackage,\n\t\tOutputBase:         genericArgs.OutputBase,\n\t\tBoilerplateContent: boilerplate,\n\t}\n\tinputDirs := parseTypes(customArgs)\n\n\tclientGen := generators.NewClientGenerator()\n\n\tgetTargets := func(context *generator.Context) []generator.Target {\n\t\t// replace the default formatter options to ensure unused imports are pruned.\n\t\t// ref: https://github.com/kubernetes/gengo/pull/277#issuecomment-2557462569\n\t\tgoGenerator := generator.NewGoFile()\n\t\tgoGenerator.Format = func(src []byte) ([]byte, error) {\n\t\t\treturn imports.Process(\"\", src, nil)\n\t\t}\n\t\tcontext.FileTypes[generator.GoFileType] = goGenerator\n\t\treturn clientGen.GetTargets(context, customArgs)\n\t}\n\tif err := gengo.Execute(\n\t\tcs.NameSystems(nil),\n\t\tcs.DefaultNameSystem(),\n\t\tgetTargets,\n\t\tgengo.StdBuildTag,\n\t\tinputDirs,\n\t); err != nil {\n\t\tlogrus.Fatalf(\"Error: %v\", err)\n\t}\n\n\tgroups := map[string]bool{}\n\tlisterGroups := map[string]bool{}\n\tinformerGroups := map[string]bool{}\n\tdeepCopygroups := map[string]bool{}\n\topenAPIGroups := map[string]bool{}\n\tfor groupName, group := range customArgs.Options.Groups {\n\t\tif group.GenerateTypes {\n\t\t\tdeepCopygroups[groupName] = true\n\t\t}\n\t\tif group.GenerateClients {\n\t\t\tgroups[groupName] = true\n\t\t}\n\t\tif group.GenerateListers {\n\t\t\tlisterGroups[groupName] = true\n\t\t}\n\t\tif group.GenerateInformers {\n\t\t\tinformerGroups[groupName] = true\n\t\t}\n\t\tif group.GenerateOpenAPI {\n\t\t\topenAPIGroups[groupName] = true\n\t\t}\n\t}\n\n\tif len(deepCopygroups) == 0 && len(groups) == 0 && len(listerGroups) == 0 && len(informerGroups) == 0 && len(openAPIGroups) == 0 {\n\t\tif err := copyGoPathToModules(customArgs); err != nil {\n\t\t\tlogrus.Fatalf(\"go modules copy failed: %v\", err)\n\t\t}\n\t\treturn\n\t}\n\n\tif err := copyGoPathToModules(customArgs); err != nil {\n\t\tlogrus.Fatalf(\"go modules copy failed: %v\", err)\n\t}\n\n\tif err := generateDeepcopy(deepCopygroups, customArgs); err != nil {\n\t\tlogrus.Fatalf(\"deepcopy failed: %v\", err)\n\t}\n\n\tif err := generateClientset(groups, customArgs); err != nil {\n\t\tlogrus.Fatalf(\"clientset failed: %v\", err)\n\t}\n\n\tif err := generateListers(listerGroups, customArgs); err != nil {\n\t\tlogrus.Fatalf(\"listers failed: %v\", err)\n\t}\n\n\tif err := generateInformers(informerGroups, customArgs); err != nil {\n\t\tlogrus.Fatalf(\"informers failed: %v\", err)\n\t}\n\n\tif err := generateOpenAPI(openAPIGroups, customArgs); err != nil {\n\t\tlogrus.Fatalf(\"openapi failed: %v\", err)\n\t}\n\n\tif err := copyGoPathToModules(customArgs); err != nil {\n\t\tlogrus.Fatalf(\"go modules copy failed: %v\", err)\n\t}\n}\n\nfunc sourcePackagePath(customArgs *cgargs.CustomArgs, pkgName string) string {\n\tpkgSplit := strings.Split(pkgName, string(os.PathSeparator))\n\tpkg := filepath.Join(customArgs.OutputBase, strings.Join(pkgSplit[:3], string(os.PathSeparator)))\n\treturn pkg\n}\n\n// until k8s code-gen supports gopath\nfunc copyGoPathToModules(customArgs *cgargs.CustomArgs) error {\n\n\tpathsToCopy := map[string]bool{}\n\tfor _, types := range customArgs.TypesByGroup {\n\t\tfor _, names := range types {\n\t\t\tpkg := sourcePackagePath(customArgs, names.Package)\n\t\t\tpathsToCopy[pkg] = true\n\t\t}\n\t}\n\n\tpkg := sourcePackagePath(customArgs, customArgs.Package)\n\tpathsToCopy[pkg] = true\n\n\tfor pkg := range pathsToCopy {\n\t\tif _, err := os.Stat(pkg); os.IsNotExist(err) {\n\t\t\tcontinue\n\t\t}\n\n\t\treturn filepath.Walk(pkg, func(path string, info os.FileInfo, err error) error {\n\t\t\tnewPath := strings.Replace(path, pkg, \".\", 1)\n\t\t\tif info.IsDir() {\n\t\t\t\treturn os.MkdirAll(newPath, info.Mode())\n\t\t\t}\n\n\t\t\treturn copyFile(path, newPath)\n\t\t})\n\t}\n\n\treturn nil\n}\n\nfunc copyFile(src, dst string) error {\n\tvar err error\n\tvar srcfd *os.File\n\tvar dstfd *os.File\n\tvar srcinfo os.FileInfo\n\n\tif srcfd, err = os.Open(src); err != nil {\n\t\treturn err\n\t}\n\tdefer srcfd.Close()\n\n\tif dstfd, err = os.Create(dst); err != nil {\n\t\treturn err\n\t}\n\tdefer dstfd.Close()\n\n\tif _, err = io.Copy(dstfd, srcfd); err != nil {\n\t\treturn err\n\t}\n\tif srcinfo, err = os.Stat(src); err != nil {\n\t\treturn err\n\t}\n\treturn os.Chmod(dst, srcinfo.Mode())\n}\n\nfunc generateDeepcopy(groups map[string]bool, customArgs *cgargs.CustomArgs) error {\n\tif len(groups) == 0 {\n\t\treturn nil\n\t}\n\n\tdeepCopyArgs := dpargs.New()\n\tdeepCopyArgs.OutputFile = \"zz_generated_deepcopy.go\"\n\tdeepCopyArgs.GoHeaderFile = customArgs.Options.Boilerplate\n\n\tinputDirs := []string{}\n\tfor gv, names := range customArgs.TypesByGroup {\n\t\tif !groups[gv.Group] {\n\t\t\tcontinue\n\t\t}\n\t\tinputDirs = append(inputDirs, names[0].Package)\n\t\tdeepCopyArgs.BoundingDirs = append(deepCopyArgs.BoundingDirs, names[0].Package)\n\t}\n\n\tgetTargets := func(context *generator.Context) []generator.Target {\n\t\treturn dp.GetTargets(context, deepCopyArgs)\n\t}\n\n\treturn gengo.Execute(\n\t\tdp.NameSystems(),\n\t\tdp.DefaultNameSystem(),\n\t\tgetTargets,\n\t\tgengo.StdBuildTag,\n\t\tinputDirs,\n\t)\n}\n\nfunc generateClientset(groups map[string]bool, customArgs *cgargs.CustomArgs) error {\n\tif len(groups) == 0 {\n\t\treturn nil\n\t}\n\n\tclientSetArgs := csargs.New()\n\tclientSetArgs.ClientsetName = \"versioned\"\n\tclientSetArgs.OutputDir = filepath.Join(customArgs.OutputBase, customArgs.Package, \"clientset\")\n\tclientSetArgs.OutputPkg = filepath.Join(customArgs.Package, \"clientset\")\n\tclientSetArgs.GoHeaderFile = customArgs.Options.Boilerplate\n\n\tvar order []schema.GroupVersion\n\n\tfor gv := range customArgs.TypesByGroup {\n\t\tif !groups[gv.Group] {\n\t\t\tcontinue\n\t\t}\n\t\torder = append(order, gv)\n\t}\n\tsort.Slice(order, func(i, j int) bool {\n\t\treturn order[i].Group < order[j].Group\n\t})\n\n\tinputDirs := []string{}\n\tfor _, gv := range order {\n\t\tpackageName := customArgs.Options.Groups[gv.Group].PackageName\n\t\tif packageName == \"\" {\n\t\t\tpackageName = gv.Group\n\t\t}\n\t\tnames := customArgs.TypesByGroup[gv]\n\t\tinputDirs = append(inputDirs, names[0].Package)\n\t\tclientSetArgs.Groups = append(clientSetArgs.Groups, types2.GroupVersions{\n\t\t\tPackageName: packageName,\n\t\t\tGroup:       types2.Group(gv.Group),\n\t\t\tVersions: []types2.PackageVersion{\n\t\t\t\t{\n\t\t\t\t\tVersion: types2.Version(gv.Version),\n\t\t\t\t\tPackage: names[0].Package,\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\t}\n\tgetTargets := setGenClient(groups, customArgs.TypesByGroup, func(context *generator.Context) []generator.Target {\n\t\treturn cs.GetTargets(context, clientSetArgs)\n\t})\n\treturn gengo.Execute(\n\t\tcs.NameSystems(nil),\n\t\tcs.DefaultNameSystem(),\n\t\tgetTargets,\n\t\tgengo.StdBuildTag,\n\t\tinputDirs,\n\t)\n}\n\nfunc generateOpenAPI(groups map[string]bool, customArgs *cgargs.CustomArgs) error {\n\tif len(groups) == 0 {\n\t\treturn nil\n\t}\n\n\topenAPIArgs := oaargs.New()\n\topenAPIArgs.OutputDir = filepath.Join(customArgs.OutputBase, customArgs.Options.OutputPackage, \"openapi\")\n\topenAPIArgs.OutputFile = \"zz_generated_openapi.go\"\n\topenAPIArgs.OutputPkg = customArgs.Options.OutputPackage + \"/openapi\"\n\topenAPIArgs.GoHeaderFile = customArgs.Options.Boilerplate\n\topenAPIArgs.OutputModelNameFile = \"zz_generated.model_name.go\"\n\n\tif err := openAPIArgs.Validate(); err != nil {\n\t\treturn err\n\t}\n\n\tinputDirsMap := map[string]bool{}\n\tinputDirs := []string{}\n\tinputModelDirsMap := map[string]bool{}\n\tinputModelDirs := []string{}\n\n\tfor gv, names := range customArgs.TypesByGroup {\n\t\tif !groups[gv.Group] {\n\t\t\tcontinue\n\t\t}\n\t\tgroup := customArgs.Options.Groups[gv.Group]\n\n\t\tif _, found := inputDirsMap[names[0].Package]; !found {\n\t\t\tinputDirsMap[names[0].Package] = true\n\t\t\tinputDirs = append(inputDirs, names[0].Package)\n\t\t\tif group.OpenAPIModelPackageName != \"\" {\n\t\t\t\tinputModelDirsMap[names[0].Package] = true\n\t\t\t\tinputModelDirs = append(inputModelDirs, names[0].Package)\n\t\t\t}\n\t\t}\n\n\t\tfor _, dep := range group.OpenAPIDependencies {\n\t\t\tif _, found := inputDirsMap[dep]; !found {\n\t\t\t\tinputDirsMap[dep] = true\n\t\t\t\tinputDirs = append(inputDirs, dep)\n\t\t\t}\n\t\t}\n\t}\n\n\tboilerplate, err := gengo.GoBoilerplate(openAPIArgs.GoHeaderFile, gengo.StdBuildTag, gengo.StdGeneratedBy)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tgetTargets := func(context *generator.Context) []generator.Target {\n\t\treturn oa.GetOpenAPITargets(context, openAPIArgs, boilerplate)\n\t}\n\n\tif err := gengo.Execute(\n\t\toa.NameSystems(),\n\t\toa.DefaultNameSystem(),\n\t\tgetTargets,\n\t\tgengo.StdBuildTag,\n\t\tinputDirs,\n\t); err != nil {\n\t\treturn err\n\t}\n\n\tif len(inputModelDirs) <= 0 {\n\t\treturn nil\n\t}\n\n\tgetModelNameTargets := func(context *generator.Context) []generator.Target {\n\t\treturn oa.GetModelNameTargets(context, openAPIArgs, boilerplate)\n\t}\n\n\treturn gengo.Execute(\n\t\toa.NameSystems(),\n\t\toa.DefaultNameSystem(),\n\t\tgetModelNameTargets,\n\t\tgengo.StdBuildTag,\n\t\tinputModelDirs,\n\t)\n}\n\nfunc setGenClient(\n\tgroups map[string]bool,\n\ttypesByGroup map[schema.GroupVersion][]*types.Name,\n\tf func(*generator.Context) []generator.Target,\n) func(*generator.Context) []generator.Target {\n\treturn func(context *generator.Context) []generator.Target {\n\t\tfor gv, names := range typesByGroup {\n\t\t\tif !groups[gv.Group] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor _, name := range names {\n\t\t\t\tvar (\n\t\t\t\t\tp           = context.Universe.Package(name.Package)\n\t\t\t\t\tt           = p.Type(name.Name)\n\t\t\t\t\tstatus      bool\n\t\t\t\t\tnsed        bool\n\t\t\t\t\tkubebuilder bool\n\t\t\t\t)\n\n\t\t\t\tfor _, line := range append(t.SecondClosestCommentLines, t.CommentLines...) {\n\t\t\t\t\tswitch {\n\t\t\t\t\tcase strings.Contains(line, \"+kubebuilder:object:root=true\"):\n\t\t\t\t\t\tkubebuilder = true\n\t\t\t\t\t\tt.SecondClosestCommentLines = append(t.SecondClosestCommentLines, \"+genclient\")\n\t\t\t\t\tcase strings.Contains(line, \"+kubebuilder:subresource:status\"):\n\t\t\t\t\t\tstatus = true\n\t\t\t\t\tcase strings.Contains(line, \"+kubebuilder:resource:\") && strings.Contains(line, \"scope=Namespaced\"):\n\t\t\t\t\t\tnsed = true\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif kubebuilder {\n\t\t\t\t\tif !nsed {\n\t\t\t\t\t\tt.SecondClosestCommentLines = append(t.SecondClosestCommentLines, \"+genclient:nonNamespaced\")\n\t\t\t\t\t}\n\t\t\t\t\tif !status {\n\t\t\t\t\t\tt.SecondClosestCommentLines = append(t.SecondClosestCommentLines, \"+genclient:noStatus\")\n\t\t\t\t\t}\n\n\t\t\t\t\tfoundGroup := false\n\t\t\t\t\tfor _, comment := range p.DocComments {\n\t\t\t\t\t\tif strings.Contains(comment, \"+groupName=\") {\n\t\t\t\t\t\t\tfoundGroup = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif !foundGroup {\n\t\t\t\t\t\tp.DocComments = append(p.DocComments, \"+groupName=\"+gv.Group)\n\t\t\t\t\t\tp.Comments = append(p.Comments, \"+groupName=\"+gv.Group)\n\t\t\t\t\t\tfmt.Println(gv.Group, p.DocComments, p.Comments, p.Path)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn f(context)\n\t}\n}\n\nfunc generateInformers(groups map[string]bool, customArgs *cgargs.CustomArgs) error {\n\tif len(groups) == 0 {\n\t\treturn nil\n\t}\n\n\tinformerArgs := infargs.New()\n\tinformerArgs.VersionedClientSetPackage = filepath.Join(customArgs.Package, \"clientset/versioned\")\n\tinformerArgs.ListersPackage = filepath.Join(customArgs.Package, \"listers\")\n\tinformerArgs.OutputDir = filepath.Join(customArgs.OutputBase, customArgs.Package, \"informers\")\n\tinformerArgs.OutputPkg = filepath.Join(customArgs.Package, \"informers\")\n\tinformerArgs.GoHeaderFile = customArgs.Options.Boilerplate\n\n\tinputDirs := []string{}\n\tfor gv, names := range customArgs.TypesByGroup {\n\t\tif !groups[gv.Group] {\n\t\t\tcontinue\n\t\t}\n\t\tinputDirs = append(inputDirs, names[0].Package)\n\t}\n\n\tgetTargets := setGenClient(groups, customArgs.TypesByGroup, func(context *generator.Context) []generator.Target {\n\t\treturn inf.GetTargets(context, informerArgs)\n\t})\n\n\treturn gengo.Execute(\n\t\tinf.NameSystems(nil),\n\t\tinf.DefaultNameSystem(),\n\t\tgetTargets,\n\t\tgengo.StdBuildTag,\n\t\tinputDirs,\n\t)\n}\n\nfunc generateListers(groups map[string]bool, customArgs *cgargs.CustomArgs) error {\n\tif len(groups) == 0 {\n\t\treturn nil\n\t}\n\n\tlisterArgs := lsargs.New()\n\tlisterArgs.OutputDir = filepath.Join(customArgs.OutputBase, customArgs.Package, \"listers\")\n\tlisterArgs.OutputPkg = filepath.Join(customArgs.Package, \"listers\")\n\tlisterArgs.GoHeaderFile = customArgs.Options.Boilerplate\n\n\tinputDirs := []string{}\n\tfor gv, names := range customArgs.TypesByGroup {\n\t\tif !groups[gv.Group] {\n\t\t\tcontinue\n\t\t}\n\t\tinputDirs = append(inputDirs, names[0].Package)\n\t}\n\n\tgetTargets := setGenClient(groups, customArgs.TypesByGroup, func(context *generator.Context) []generator.Target {\n\t\treturn ls.GetTargets(context, listerArgs)\n\t})\n\treturn gengo.Execute(\n\t\tls.NameSystems(nil),\n\t\tls.DefaultNameSystem(),\n\t\tgetTargets,\n\t\tgengo.StdBuildTag,\n\t\tinputDirs,\n\t)\n}\n\nfunc parseTypes(customArgs *cgargs.CustomArgs) []string {\n\tfor groupName, group := range customArgs.Options.Groups {\n\t\tif group.GenerateTypes || group.GenerateClients {\n\t\t\tcustomArgs.Options.Groups[groupName] = group\n\t\t}\n\t}\n\n\tfor groupName, group := range customArgs.Options.Groups {\n\t\tif err := cgargs.ObjectsToGroupVersion(groupName, group.Types, customArgs.TypesByGroup); err != nil {\n\t\t\t// sorry, should really handle this better\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\tvar inputDirs []string\n\tfor _, names := range customArgs.TypesByGroup {\n\t\tinputDirs = append(inputDirs, names[0].Package)\n\t}\n\n\treturn inputDirs\n}\n"
  },
  {
    "path": "pkg/crd/crd.go",
    "content": "// Package crd handles the dynamic creation and modification of CustomResourceDefinitions.\npackage crd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/sirupsen/logrus\"\n\tapiextv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\tclientv1 \"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1\"\n\tapierrors \"k8s.io/apimachinery/pkg/api/errors\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n\t\"k8s.io/apimachinery/pkg/util/wait\"\n)\n\nconst waitInterval = 500 * time.Millisecond\n\n// BatchCreateCRDs will create or update the list of crds if they do not exist.\n// It will wait for a specified amount of time for the CRD status to be established before returning an error.\n// labelSelector can be used to scope the CRDs listed by this function.\nfunc BatchCreateCRDs(ctx context.Context, crdClient clientv1.CustomResourceDefinitionInterface, labelSelector labels.Selector, waitDuration time.Duration, crds []*apiextv1.CustomResourceDefinition) error {\n\texistingCRDs, err := getExistingCRDs(ctx, crdClient, labelSelector)\n\tif err != nil {\n\t\t// we do not need to return this error because we can still attempt to create the CRDs if the list fails\n\t\tlogrus.Warnf(\"unable to list existing CRDs: %s\", err.Error())\n\t}\n\n\t// ensure each CRD exist\n\tfor _, crd := range crds {\n\t\terr = ensureCRD(ctx, crd, crdClient, existingCRDs)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\texistingCRDs, err = getExistingCRDs(ctx, crdClient, labelSelector)\n\tif err != nil {\n\t\t// we do not need to return this error because we can still attempt to create the CRDs if the list fails\n\t\tlogrus.Warnf(\"unable to list existing CRDs: %s\", err.Error())\n\t}\n\t// ensure each CRD is ready\n\tfor _, crd := range crds {\n\t\tif existingCRD, ok := existingCRDs[crd.Name]; ok && crdIsReady(existingCRD) {\n\t\t\tcontinue\n\t\t}\n\t\t// wait for the CRD to be ready\n\t\terr := waitCRD(ctx, crd.Name, crdClient, waitDuration)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed waiting on CRD '%s': %w\", crd.Name, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// getExistingCRDs returns a map of all CRD resource on the cluster.\nfunc getExistingCRDs(ctx context.Context, crdClient clientv1.CustomResourceDefinitionInterface, labelSelector labels.Selector) (map[string]*apiextv1.CustomResourceDefinition, error) {\n\tlistOpt := metav1.ListOptions{}\n\tif labelSelector != nil {\n\t\tlistOpt.LabelSelector = labelSelector.String()\n\t}\n\tstoredCRDs, err := crdClient.List(ctx, listOpt)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to list crds: %w\", err)\n\t}\n\n\t// convert existingCRDs to a map for faster lookup\n\texistingCRDs := make(map[string]*apiextv1.CustomResourceDefinition, len(storedCRDs.Items))\n\tfor i := range storedCRDs.Items {\n\t\tcrd := &storedCRDs.Items[i]\n\t\texistingCRDs[crd.Name] = crd\n\t}\n\treturn existingCRDs, nil\n}\n\n// ensureCRD checks if there is an existing CRD that matches if not it will attempt to create or update the CRD.\nfunc ensureCRD(ctx context.Context, crd *apiextv1.CustomResourceDefinition, crdClient clientv1.CustomResourceDefinitionInterface, existingCRDs map[string]*apiextv1.CustomResourceDefinition) error {\n\texistingCRD, exist := existingCRDs[crd.Name]\n\tif !exist {\n\t\tlogrus.Infof(\"Creating embedded CRD %s\", crd.Name)\n\t\t_, err := crdClient.Create(ctx, crd, metav1.CreateOptions{})\n\t\tif err == nil {\n\t\t\treturn nil\n\t\t}\n\t\tif !apierrors.IsAlreadyExists(err) {\n\t\t\treturn fmt.Errorf(\"failed to create crd '%s': %w\", crd.Name, err)\n\t\t}\n\t\t// item was not in initial list but does exist so we will attempt to get the latest resourceVersion\n\t\texistingCRD, err = crdClient.Get(ctx, crd.Name, metav1.GetOptions{})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get exiting '%s' CRD: %w\", crd.Name, err)\n\t\t}\n\t\t// fallthrough and attempt an update\n\t}\n\t// only keep the resource version for the desired object\n\tcrd.ResourceVersion = existingCRD.ResourceVersion\n\t// if the CRD exists attempt to update it\n\tlogrus.Infof(\"Updating embedded CRD %s\", crd.Name)\n\t_, err := crdClient.Update(ctx, crd, metav1.UpdateOptions{})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update crd '%s': %w\", crd.Name, err)\n\t}\n\treturn nil\n}\n\n// waitCRD repeatably checks CRD status until it is established.\nfunc waitCRD(ctx context.Context, crdName string, crdClient clientv1.CustomResourceDefinitionInterface, waitDuration time.Duration) error {\n\tlogrus.Infof(\"Waiting for CRD %s to become available\", crdName)\n\tdefer logrus.Infof(\"Done waiting for CRD %s to become available\", crdName)\n\n\treturn wait.PollImmediate(waitInterval, waitDuration, func() (bool, error) {\n\t\tcrd, err := crdClient.Get(ctx, crdName, metav1.GetOptions{})\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"failed to get CRD '%s' for status checking: %w\", crdName, err)\n\t\t}\n\t\tif crdIsReady(crd) {\n\t\t\treturn true, nil\n\t\t}\n\n\t\treturn false, nil\n\t})\n}\n\nfunc crdIsReady(crd *apiextv1.CustomResourceDefinition) bool {\n\tfor i := range crd.Status.Conditions {\n\t\tcond := &crd.Status.Conditions[i]\n\t\tswitch cond.Type {\n\t\tcase apiextv1.Established:\n\t\t\tif cond.Status == apiextv1.ConditionTrue {\n\t\t\t\treturn true\n\t\t\t}\n\t\tcase apiextv1.NamesAccepted:\n\t\t\tif cond.Status == apiextv1.ConditionFalse {\n\t\t\t\tlogrus.Infof(\"Name conflict for CRD %s: %s\", crd.Name, cond.Reason)\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "pkg/crd/crd_test.go",
    "content": "package crd\n\n//go:generate mockgen --build_flags=--mod=mod -package crd -destination ./mockCRDClient_test.go \"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1\" CustomResourceDefinitionInterface\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.uber.org/mock/gomock\"\n\t\"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions\"\n\tapiextv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\t\"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation\"\n\tapierrors \"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/api/resource\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/util/intstr\"\n\t\"k8s.io/apimachinery/pkg/util/validation/field\"\n)\n\nvar errTest = errors.New(\"test Error\")\n\nfunc TestBatchCreateCRDs(t *testing.T) {\n\tt.Parallel()\n\t// decrease the ready wait duration for tests\n\twaitDuration := time.Second * 5\n\n\t// create 3 CRDs no status clone and update status in setup\n\tcrd1 := &apiextv1.CustomResourceDefinition{}\n\tcrd1.Name = \"crd1s.testGroup\"\n\tcrd1.Spec.Group = \"testGroup\"\n\tcrd1.Spec.Names.Plural = \"crd1s\"\n\tcrd1.Spec.Names.Kind = \"CRD1\"\n\tcrd1.Spec.Scope = apiextv1.ClusterScoped\n\tcrd1.Spec.Versions = []apiextv1.CustomResourceDefinitionVersion{{Name: \"v1\"}}\n\n\tcrd2 := &apiextv1.CustomResourceDefinition{}\n\tcrd2.Name = \"crd2s.testGroup\"\n\tcrd2.Spec.Group = \"testGroup\"\n\tcrd2.Spec.Names.Plural = \"crd2s\"\n\tcrd2.Spec.Names.Kind = \"CRD2\"\n\tcrd2.Spec.Scope = apiextv1.ClusterScoped\n\tcrd2.Spec.Versions = []apiextv1.CustomResourceDefinitionVersion{{Name: \"v1\"}}\n\n\tcrd3 := &apiextv1.CustomResourceDefinition{}\n\tcrd3.Name = \"crd3s.testGroup\"\n\tcrd3.Spec.Group = \"testGroup\"\n\tcrd3.Spec.Names.Plural = \"crd3s\"\n\tcrd3.Spec.Names.Kind = \"CRD3\"\n\tcrd3.Spec.Scope = apiextv1.ClusterScoped\n\tcrd3.Spec.Versions = []apiextv1.CustomResourceDefinitionVersion{{Name: \"v1\"}}\n\n\ttests := []struct {\n\t\tname         string\n\t\ttoCreateCRDs []*apiextv1.CustomResourceDefinition\n\t\tselector     labels.Selector\n\t\twantErr      bool\n\t\tsetupMock    func(*MockCustomResourceDefinitionInterface)\n\t}{\n\t\t{\n\t\t\tname:         \"create single CRD\",\n\t\t\ttoCreateCRDs: []*apiextv1.CustomResourceDefinition{crd1},\n\t\t\tselector:     labels.Nothing(),\n\t\t\tsetupMock: func(mock *MockCustomResourceDefinitionInterface) {\n\t\t\t\tlist := &apiextv1.CustomResourceDefinitionList{}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{LabelSelector: labels.Nothing().String()}).Return(list, nil)\n\n\t\t\t\tmock.EXPECT().Create(gomock.Any(), crd1, gomock.Any())\n\n\t\t\t\treadyCRD := crd1.DeepCopy()\n\t\t\t\treadyCRD.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\t\t\t\tlist2 := &apiextv1.CustomResourceDefinitionList{Items: []apiextv1.CustomResourceDefinition{*readyCRD}}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list2, nil)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:         \"create multiple CRDs\",\n\t\t\ttoCreateCRDs: []*apiextv1.CustomResourceDefinition{crd1, crd2, crd3},\n\t\t\tsetupMock: func(mock *MockCustomResourceDefinitionInterface) {\n\t\t\t\t// initial list should be allowed to fail.\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(nil, errTest)\n\n\t\t\t\tmock.EXPECT().Create(gomock.Any(), crd1, gomock.Any())\n\t\t\t\tmock.EXPECT().Create(gomock.Any(), crd2, gomock.Any())\n\t\t\t\tmock.EXPECT().Create(gomock.Any(), crd3, gomock.Any())\n\n\t\t\t\treadyCRD1 := crd1.DeepCopy()\n\t\t\t\treadyCRD1.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\t\t\t\treadyCRD2 := crd2.DeepCopy()\n\t\t\t\treadyCRD2.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\t\t\t\treadyCRD3 := crd3.DeepCopy()\n\t\t\t\treadyCRD3.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\n\t\t\t\tlist2 := &apiextv1.CustomResourceDefinitionList{\n\t\t\t\t\tItems: []apiextv1.CustomResourceDefinition{*readyCRD1, *readyCRD2, *readyCRD3},\n\t\t\t\t}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list2, nil)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:         \"create already exist CRD\",\n\t\t\ttoCreateCRDs: []*apiextv1.CustomResourceDefinition{crd1},\n\t\t\tsetupMock: func(mock *MockCustomResourceDefinitionInterface) {\n\t\t\t\tlist := &apiextv1.CustomResourceDefinitionList{}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list, nil)\n\n\t\t\t\tmock.EXPECT().Create(gomock.Any(), crd1, gomock.Any()).Return(nil, apierrors.NewAlreadyExists(apiextv1.Resource(\"customeresourcedefinitions\"), crd1.Name))\n\t\t\t\tmock.EXPECT().Get(gomock.Any(), crd1.Name, gomock.Any()).Return(crd1, nil)\n\t\t\t\tmock.EXPECT().Update(gomock.Any(), crd1, gomock.Any())\n\n\t\t\t\treadyCRD := crd1.DeepCopy()\n\t\t\t\treadyCRD.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\t\t\t\tlist2 := &apiextv1.CustomResourceDefinitionList{Items: []apiextv1.CustomResourceDefinition{*readyCRD}}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list2, nil)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:         \"create failed\",\n\t\t\twantErr:      true,\n\t\t\ttoCreateCRDs: []*apiextv1.CustomResourceDefinition{crd1},\n\t\t\tsetupMock: func(mock *MockCustomResourceDefinitionInterface) {\n\t\t\t\tlist := &apiextv1.CustomResourceDefinitionList{}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list, nil)\n\n\t\t\t\tmock.EXPECT().Create(gomock.Any(), crd1, gomock.Any()).Return(nil, errTest)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:         \"update CRD\",\n\t\t\ttoCreateCRDs: []*apiextv1.CustomResourceDefinition{crd1},\n\t\t\tsetupMock: func(mock *MockCustomResourceDefinitionInterface) {\n\t\t\t\tlist := &apiextv1.CustomResourceDefinitionList{Items: []apiextv1.CustomResourceDefinition{*crd1}}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list, nil)\n\n\t\t\t\tmock.EXPECT().Update(gomock.Any(), crd1, gomock.Any())\n\n\t\t\t\treadyCRD := crd1.DeepCopy()\n\t\t\t\treadyCRD.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\t\t\t\tlist2 := &apiextv1.CustomResourceDefinitionList{Items: []apiextv1.CustomResourceDefinition{*readyCRD}}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list2, nil)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:         \"update Failed\",\n\t\t\twantErr:      true,\n\t\t\ttoCreateCRDs: []*apiextv1.CustomResourceDefinition{crd1},\n\t\t\tsetupMock: func(mock *MockCustomResourceDefinitionInterface) {\n\t\t\t\tlist := &apiextv1.CustomResourceDefinitionList{Items: []apiextv1.CustomResourceDefinition{*crd1}}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list, nil)\n\n\t\t\t\tmock.EXPECT().Update(gomock.Any(), crd1, gomock.Any()).Return(nil, errTest)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:         \"update multiple CRDs\",\n\t\t\ttoCreateCRDs: []*apiextv1.CustomResourceDefinition{crd1, crd2, crd3},\n\t\t\tsetupMock: func(mock *MockCustomResourceDefinitionInterface) {\n\t\t\t\tlist := &apiextv1.CustomResourceDefinitionList{\n\t\t\t\t\tItems: []apiextv1.CustomResourceDefinition{*crd1, *crd2, *crd3},\n\t\t\t\t}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list, nil)\n\n\t\t\t\tmock.EXPECT().Update(gomock.Any(), crd1, gomock.Any())\n\t\t\t\tmock.EXPECT().Update(gomock.Any(), crd2, gomock.Any())\n\t\t\t\tmock.EXPECT().Update(gomock.Any(), crd3, gomock.Any())\n\n\t\t\t\treadyCRD1 := crd1.DeepCopy()\n\t\t\t\treadyCRD1.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\t\t\t\treadyCRD2 := crd2.DeepCopy()\n\t\t\t\treadyCRD2.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\t\t\t\treadyCRD3 := crd3.DeepCopy()\n\t\t\t\treadyCRD3.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\n\t\t\t\tlist2 := &apiextv1.CustomResourceDefinitionList{\n\t\t\t\t\tItems: []apiextv1.CustomResourceDefinition{*readyCRD1, *readyCRD2, *readyCRD3},\n\t\t\t\t}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list2, nil)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:         \"create and update Multiple CRDs\",\n\t\t\ttoCreateCRDs: []*apiextv1.CustomResourceDefinition{crd1, crd2, crd3},\n\t\t\tsetupMock: func(mock *MockCustomResourceDefinitionInterface) {\n\t\t\t\tlist := &apiextv1.CustomResourceDefinitionList{\n\t\t\t\t\tItems: []apiextv1.CustomResourceDefinition{*crd2, *crd3},\n\t\t\t\t}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list, nil)\n\n\t\t\t\tmock.EXPECT().Create(gomock.Any(), crd1, gomock.Any())\n\t\t\t\tmock.EXPECT().Update(gomock.Any(), crd2, gomock.Any())\n\t\t\t\tmock.EXPECT().Update(gomock.Any(), crd3, gomock.Any())\n\n\t\t\t\treadyCRD1 := crd1.DeepCopy()\n\t\t\t\treadyCRD1.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\t\t\t\treadyCRD2 := crd2.DeepCopy()\n\t\t\t\treadyCRD2.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\t\t\t\treadyCRD3 := crd3.DeepCopy()\n\t\t\t\treadyCRD3.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\n\t\t\t\tlist2 := &apiextv1.CustomResourceDefinitionList{\n\t\t\t\t\tItems: []apiextv1.CustomResourceDefinition{*readyCRD1, *readyCRD2, *readyCRD3},\n\t\t\t\t}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list2, nil)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:         \"wait for Multiple CRDs\",\n\t\t\ttoCreateCRDs: []*apiextv1.CustomResourceDefinition{crd1, crd2, crd3},\n\t\t\tsetupMock: func(mock *MockCustomResourceDefinitionInterface) {\n\t\t\t\treadyCRD1 := crd1.DeepCopy()\n\t\t\t\treadyCRD1.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\t\t\t\treadyCRD2 := crd2.DeepCopy()\n\t\t\t\treadyCRD2.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\t\t\t\treadyCRD3 := crd3.DeepCopy()\n\t\t\t\treadyCRD3.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionTrue},\n\t\t\t\t}\n\t\t\t\tlist := &apiextv1.CustomResourceDefinitionList{\n\t\t\t\t\tItems: []apiextv1.CustomResourceDefinition{*readyCRD1, *readyCRD2, *readyCRD3},\n\t\t\t\t}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list, nil)\n\n\t\t\t\tmock.EXPECT().Update(gomock.Any(), crd1, gomock.Any())\n\t\t\t\tmock.EXPECT().Update(gomock.Any(), crd2, gomock.Any())\n\t\t\t\tmock.EXPECT().Update(gomock.Any(), crd3, gomock.Any())\n\n\t\t\t\tnotReadyCRD1 := crd1.DeepCopy()\n\t\t\t\tnotReadyCRD1.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionFalse},\n\t\t\t\t}\n\t\t\t\tnotReadyCRD2 := crd2.DeepCopy()\n\t\t\t\tnotReadyCRD2.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionFalse},\n\t\t\t\t}\n\t\t\t\tnotReadyCRD3 := crd3.DeepCopy()\n\t\t\t\tnotReadyCRD3.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionFalse},\n\t\t\t\t}\n\n\t\t\t\tlist2 := &apiextv1.CustomResourceDefinitionList{\n\t\t\t\t\tItems: []apiextv1.CustomResourceDefinition{*readyCRD1, *readyCRD2, *readyCRD3},\n\t\t\t\t}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list2, nil)\n\n\t\t\t\tmock.EXPECT().Get(gomock.Any(), crd1.Name, gomock.Any()).Return(readyCRD1, nil).AnyTimes()\n\t\t\t\tmock.EXPECT().Get(gomock.Any(), crd1.Name, gomock.Any()).Return(readyCRD2, nil).AnyTimes()\n\t\t\t\tmock.EXPECT().Get(gomock.Any(), crd1.Name, gomock.Any()).Return(readyCRD3, nil).AnyTimes()\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:         \"wait for CRD that doesn't resolve\",\n\t\t\ttoCreateCRDs: []*apiextv1.CustomResourceDefinition{crd1},\n\t\t\tsetupMock: func(mock *MockCustomResourceDefinitionInterface) {\n\t\t\t\tlist := &apiextv1.CustomResourceDefinitionList{}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list, nil)\n\t\t\t\tmock.EXPECT().Create(gomock.Any(), crd1, gomock.Any())\n\t\t\t\tnotReadyCRD := crd1.DeepCopy()\n\t\t\t\tnotReadyCRD.Status.Conditions = []apiextv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t{Type: apiextv1.Established, Status: apiextv1.ConditionFalse},\n\t\t\t\t}\n\t\t\t\tlist2 := &apiextv1.CustomResourceDefinitionList{Items: []apiextv1.CustomResourceDefinition{*notReadyCRD}}\n\t\t\t\tmock.EXPECT().List(gomock.Any(), metav1.ListOptions{}).Return(list2, nil)\n\t\t\t\tmock.EXPECT().Get(gomock.Any(), crd1.Name, gomock.Any()).Return(notReadyCRD, nil).AnyTimes()\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor i := range tests {\n\t\ttt := &tests[i]\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tctrl := gomock.NewController(t)\n\t\t\tmock := NewMockCustomResourceDefinitionInterface(ctrl)\n\t\t\tif tt.setupMock != nil {\n\t\t\t\ttt.setupMock(mock)\n\t\t\t}\n\t\t\tif err := BatchCreateCRDs(context.Background(), mock, tt.selector, waitDuration, tt.toCreateCRDs); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"BatchCreateCRDs() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCreateCRDWithColumns(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\twantErr bool\n\t\tcolumns []apiextv1.CustomResourceColumnDefinition\n\t\tcrd     func() CRD\n\t}{\n\t\t{\n\t\t\tname:    \"Basic CRD with no printer column\",\n\t\t\twantErr: false,\n\t\t\tcrd: func() CRD {\n\t\t\t\ttype ExampleSpec struct {\n\t\t\t\t\tSource   string `json:\"source,omitempty\"`\n\t\t\t\t\tChecksum string `json:\"checksum,omitempty\"`\n\t\t\t\t}\n\n\t\t\t\ttype Example struct {\n\t\t\t\t\tmetav1.TypeMeta   `json:\",inline\"`\n\t\t\t\t\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\n\t\t\t\t\tSpec ExampleSpec `json:\"spec,omitempty\"`\n\t\t\t\t}\n\n\t\t\t\texample := Example{}\n\t\t\t\treturn NamespacedType(\"Example.example.com/v1\").WithSchemaFromStruct(example).WithColumnsFromStruct(example)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"Basic CRD with single printer column\",\n\t\t\twantErr: false,\n\t\t\tcolumns: []apiextv1.CustomResourceColumnDefinition{\n\t\t\t\t{Name: \"Source\", Type: \"string\", Format: \"\", Description: \"\", Priority: 0, JSONPath: \".spec.source\"},\n\t\t\t},\n\t\t\tcrd: func() CRD {\n\t\t\t\ttype ExampleSpec struct {\n\t\t\t\t\tSource   string `json:\"source,omitempty\" column:\"\"`\n\t\t\t\t\tChecksum string `json:\"checksum,omitempty\"`\n\t\t\t\t}\n\n\t\t\t\ttype Example struct {\n\t\t\t\t\tmetav1.TypeMeta   `json:\",inline\"`\n\t\t\t\t\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\n\t\t\t\t\tSpec ExampleSpec `json:\"spec,omitempty\"`\n\t\t\t\t}\n\n\t\t\t\texample := Example{}\n\t\t\t\treturn NamespacedType(\"Example.example.com/v1\").WithSchemaFromStruct(example).WithColumnsFromStruct(example)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"Basic CRD with single printer column and custom name\",\n\t\t\twantErr: false,\n\t\t\tcolumns: []apiextv1.CustomResourceColumnDefinition{\n\t\t\t\t{Name: \"ExampleSource\", Type: \"string\", Format: \"\", Description: \"\", Priority: 0, JSONPath: \".spec.source\"},\n\t\t\t},\n\t\t\tcrd: func() CRD {\n\t\t\t\ttype ExampleSpec struct {\n\t\t\t\t\tSource   string `json:\"source,omitempty\" column:\"name=ExampleSource\"`\n\t\t\t\t\tChecksum string `json:\"checksum,omitempty\"`\n\t\t\t\t}\n\n\t\t\t\ttype Example struct {\n\t\t\t\t\tmetav1.TypeMeta   `json:\",inline\"`\n\t\t\t\t\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\n\t\t\t\t\tSpec ExampleSpec `json:\"spec,omitempty\"`\n\t\t\t\t}\n\n\t\t\t\texample := Example{}\n\t\t\t\treturn NamespacedType(\"Example.example.com/v1\").WithSchemaFromStruct(example).WithColumnsFromStruct(example)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"Basic CRD with struct field columns\",\n\t\t\twantErr: false,\n\t\t\tcolumns: []apiextv1.CustomResourceColumnDefinition{\n\t\t\t\t{Name: \"Time\", Type: \"string\", Format: \"date-time\", Description: \"\", Priority: 0, JSONPath: \".spec.time\"},\n\t\t\t\t{Name: \"Quantity\", Type: \"string\", Format: \"\", Description: \"\", Priority: 0, JSONPath: \".spec.quantity\"},\n\t\t\t},\n\t\t\tcrd: func() CRD {\n\t\t\t\ttype ExampleSpec struct {\n\t\t\t\t\tTime     *metav1.Time       `json:\"time,omitempty\" column:\"\"`\n\t\t\t\t\tQuantity *resource.Quantity `json:\"quantity,omitempty\" column:\"\"`\n\t\t\t\t\tChecksum string             `json:\"checksum,omitempty\"`\n\t\t\t\t}\n\n\t\t\t\ttype Example struct {\n\t\t\t\t\tmetav1.TypeMeta   `json:\",inline\"`\n\t\t\t\t\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\n\t\t\t\t\tSpec ExampleSpec `json:\"spec,omitempty\"`\n\t\t\t\t}\n\n\t\t\t\texample := Example{}\n\t\t\t\treturn NamespacedType(\"Example.example.com/v1\").WithSchemaFromStruct(example).WithColumnsFromStruct(example)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"Complex CRD with mix of struct and basic field columns\",\n\t\t\twantErr: false,\n\t\t\tcolumns: []apiextv1.CustomResourceColumnDefinition{\n\t\t\t\t{Name: \"Time\", Type: \"string\", Format: \"date-time\", Description: \"\", Priority: 0, JSONPath: \".spec.time\"},\n\t\t\t\t{Name: \"Quantity\", Type: \"string\", Format: \"\", Description: \"\", Priority: 0, JSONPath: \".spec.quantity\"},\n\t\t\t\t{Name: \"Byte\", Type: \"string\", Format: \"byte\", Description: \"\", Priority: 0, JSONPath: \".status.checksum\"},\n\t\t\t\t{Name: \"Password\", Type: \"string\", Format: \"password\", Description: \"\", Priority: 0, JSONPath: \".status.password\"},\n\t\t\t\t{Name: \"Boolean\", Type: \"boolean\", Format: \"\", Description: \"\", Priority: 0, JSONPath: \".status.boolean\"},\n\t\t\t\t{Name: \"Float\", Type: \"number\", Format: \"\", Description: \"\", Priority: 0, JSONPath: \".status.float\"},\n\t\t\t\t{Name: \"Integer\", Type: \"integer\", Format: \"\", Description: \"\", Priority: 0, JSONPath: \".status.integer\"},\n\t\t\t\t{Name: \"IntOrString\", Type: \"string\", Format: \"\", Description: \"\", Priority: 0, JSONPath: \".status.intOrString\"},\n\t\t\t},\n\t\t\tcrd: func() CRD {\n\t\t\t\ttype ExampleSpec struct {\n\t\t\t\t\tTime     *metav1.Time       `json:\"time,omitempty\" column:\"\"`\n\t\t\t\t\tQuantity *resource.Quantity `json:\"quantity,omitempty\" column:\"\"`\n\t\t\t\t}\n\n\t\t\t\ttype ExampleStatus struct {\n\t\t\t\t\tByte        string              `json:\"checksum,omitempty\" column:\"format=byte\"`\n\t\t\t\t\tPassword    string              `json:\"password,omitempty\" column:\"format=password\"`\n\t\t\t\t\tBoolean     *bool               `json:\"boolean,omitempty\" column:\"\"`\n\t\t\t\t\tFloat       *float32            `json:\"float,omitempty\" column:\"\"`\n\t\t\t\t\tInteger     *int32              `json:\"integer,omitempty\" column:\"\"`\n\t\t\t\t\tIntOrString *intstr.IntOrString `json:\"intOrString,omitempty\" column:\"\"`\n\t\t\t\t}\n\n\t\t\t\ttype Example struct {\n\t\t\t\t\tmetav1.TypeMeta   `json:\",inline\"`\n\t\t\t\t\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\n\t\t\t\t\tSpec   ExampleSpec   `json:\"spec,omitempty\"`\n\t\t\t\t\tStatus ExampleStatus `json:\"status,omitempty\"`\n\t\t\t\t}\n\n\t\t\t\texample := Example{}\n\t\t\t\treturn NamespacedType(\"Example.example.com/v1\").WithSchemaFromStruct(example).WithColumnsFromStruct(example)\n\t\t\t},\n\t\t},\n\t}\n\n\tfor i := range tests {\n\t\ttt := &tests[i]\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\to, err := tt.crd().ToCustomResourceDefinition()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Fatalf(\"ToCustomResourceDefinition() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t\tunstructuredCRD, ok := o.(*unstructured.Unstructured)\n\t\t\tif !ok {\n\t\t\t\tt.Fatal(\"could not convert CRD runtime.Object to *unstructured.Unstructured\")\n\t\t\t}\n\t\t\tvar v1CRD *apiextv1.CustomResourceDefinition\n\t\t\tif err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredCRD.UnstructuredContent(), &v1CRD); err != nil {\n\t\t\t\tt.Fatalf(\"Failed to convert CRD *unstructured.Unstructured to *apiextv1.CustomResourceDefinition: %v\", err)\n\t\t\t}\n\n\t\t\tif len(v1CRD.Spec.Versions) == 0 {\n\t\t\t\tt.Errorf(\"CRD has no schema versions\")\n\t\t\t}\n\n\t\t\tfldPath := field.NewPath(\"spec\")\n\t\t\tfor _, version := range v1CRD.Spec.Versions {\n\t\t\t\tfor i := range version.AdditionalPrinterColumns {\n\t\t\t\t\tapc := &apiextensions.CustomResourceColumnDefinition{}\n\t\t\t\t\tif err := apiextv1.Convert_v1_CustomResourceColumnDefinition_To_apiextensions_CustomResourceColumnDefinition(&version.AdditionalPrinterColumns[i], apc, nil); err != nil {\n\t\t\t\t\t\tt.Errorf(\"Failed to convert apiextv1.CustomResourceColumnDefinition to apiextensions.CustomResourceColumnDefinition for validation: %v\", err)\n\t\t\t\t\t}\n\t\t\t\t\tif errs := validation.ValidateCustomResourceColumnDefinition(apc, fldPath.Child(\"additionalPrinterColumns\").Index(i)); len(errs) > 0 {\n\t\t\t\t\t\tt.Errorf(\"AdditionalPrinterColumn definition validation failed: %s\", errs.ToAggregate().Error())\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif !reflect.DeepEqual(tt.columns, version.AdditionalPrinterColumns) {\n\t\t\t\t\tt.Errorf(\"AdditionalPrinterColumns = %#v,\\n\\t\\twanted columns = %#v\", version.AdditionalPrinterColumns, tt.columns)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/crd/init.go",
    "content": "package crd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\t\"github.com/rancher/wrangler/v3/pkg/name\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemas/openapi\"\n\t\"github.com/sirupsen/logrus\"\n\tapiext \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions\"\n\tapiextv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\tapiextv1beta1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1\"\n\t\"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset\"\n\tapierrors \"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/util/sets\"\n\t\"k8s.io/apimachinery/pkg/util/wait\"\n\t\"k8s.io/client-go/rest\"\n\n\t// Ensure the gvks are loaded so that apply works correctly\n\t_ \"github.com/rancher/wrangler/v3/pkg/generated/controllers/apiextensions.k8s.io/v1\"\n)\n\nvar (\n\t// Ref: https://github.com/kubernetes/apiextensions-apiserver/blob/v0.28.0/pkg/apis/apiextensions/validation/validation.go#L53\n\tcustomResourceColumnDefinitionFormats = sets.NewString(\"int32\", \"int64\", \"float\", \"double\", \"byte\", \"date\", \"date-time\", \"password\")\n)\n\nconst CRDKind = \"CustomResourceDefinition\"\n\ntype Factory struct {\n\twg        sync.WaitGroup\n\terr       error\n\tCRDClient clientset.Interface\n\tapply     apply.Apply\n}\n\n// CRD defines information about a CRD that can be used to create a CustomResourceDefinition runtime.Object\n// Deprecated: Rancher does not plan to continue support for dynamically defined CRDs\ntype CRD struct {\n\tGVK          schema.GroupVersionKind\n\tPluralName   string\n\tSingularName string\n\tNonNamespace bool\n\tSchema       *apiextv1.JSONSchemaProps\n\tSchemaObject interface{}\n\tColumns      []apiextv1.CustomResourceColumnDefinition\n\tStatus       bool\n\tScale        bool\n\tCategories   []string\n\tShortNames   []string\n\tLabels       map[string]string\n\tAnnotations  map[string]string\n\n\tOverride runtime.Object\n}\n\nfunc (c CRD) WithSchema(schema *apiextv1.JSONSchemaProps) CRD {\n\tc.Schema = schema\n\treturn c\n}\n\nfunc (c CRD) WithSchemaFromStruct(obj interface{}) CRD {\n\tc.SchemaObject = obj\n\treturn c\n}\n\nfunc (c CRD) WithColumn(name, path string) CRD {\n\tc.Columns = append(c.Columns, apiextv1.CustomResourceColumnDefinition{\n\t\tName:     name,\n\t\tType:     \"string\",\n\t\tPriority: 0,\n\t\tJSONPath: path,\n\t})\n\treturn c\n}\n\nfunc getType(obj interface{}) reflect.Type {\n\tif t, ok := obj.(reflect.Type); ok {\n\t\treturn t\n\t}\n\n\tt := reflect.TypeOf(obj)\n\tif t.Kind() == reflect.Ptr {\n\t\tt = t.Elem()\n\t}\n\treturn t\n}\n\nfunc (c CRD) WithColumnsFromStruct(obj interface{}) CRD {\n\tc.Columns = append(c.Columns, readCustomColumns(getType(obj), \".\")...)\n\treturn c\n}\n\nfunc fieldName(f reflect.StructField) string {\n\tjsonTag := f.Tag.Get(\"json\")\n\tif jsonTag == \"-\" {\n\t\treturn \"\"\n\t}\n\tname := strings.Split(jsonTag, \",\")[0]\n\tif name == \"\" {\n\t\treturn f.Name\n\t}\n\treturn name\n}\n\nfunc tagToColumn(f reflect.StructField, kind reflect.Kind, format, path string) (apiextv1.CustomResourceColumnDefinition, bool) {\n\t// Column definitions support only a subset of the full OpenAPI schema formats\n\tif !customResourceColumnDefinitionFormats.Has(format) {\n\t\tformat = \"\"\n\t}\n\n\tc := apiextv1.CustomResourceColumnDefinition{\n\t\tName:     f.Name,\n\t\tType:     kindToType(kind),\n\t\tFormat:   format,\n\t\tJSONPath: path,\n\t}\n\n\tcolumnDef, ok := f.Tag.Lookup(\"column\")\n\tif !ok {\n\t\treturn c, false\n\t}\n\n\tfor k, v := range kv.SplitMap(columnDef, \",\") {\n\t\tswitch k {\n\t\tcase \"name\":\n\t\t\tc.Name = v\n\t\tcase \"type\":\n\t\t\tc.Type = v\n\t\tcase \"format\":\n\t\t\tc.Format = v\n\t\tcase \"description\":\n\t\t\tc.Description = v\n\t\tcase \"priority\":\n\t\t\tp, _ := strconv.Atoi(v)\n\t\t\tc.Priority = int32(p)\n\t\tcase \"jsonpath\":\n\t\t\tc.JSONPath = v\n\t\t}\n\t}\n\n\treturn c, true\n}\n\nfunc kindToType(k reflect.Kind) string {\n\t// Ref: https://github.com/kubernetes/apiserver/blob/v0.28.0/pkg/endpoints/installer.go#L1178\n\tswitch s := k.String(); s {\n\tcase \"bool\", \"*bool\":\n\t\treturn \"boolean\"\n\tcase \"uint8\", \"*uint8\", \"int\", \"*int\", \"int32\", \"*int32\", \"int64\", \"*int64\", \"uint32\", \"*uint32\", \"uint64\", \"*uint64\":\n\t\treturn \"integer\"\n\tcase \"float64\", \"*float64\", \"float32\", \"*float32\":\n\t\treturn \"number\"\n\tcase \"byte\", \"*byte\":\n\t\treturn \"string\"\n\tcase \"[]string\", \"[]*string\":\n\t\treturn \"string\"\n\tcase \"[]int32\", \"[]*int32\":\n\t\treturn \"integer\"\n\tdefault:\n\t\treturn s\n\t}\n}\n\ntype openAPISchemaProvider interface {\n\tOpenAPISchemaType() []string\n\tOpenAPISchemaFormat() string\n}\n\nfunc openAPISchema(t reflect.Type) (reflect.Kind, string) {\n\tvar format string\n\tif o, ok := reflect.New(t).Interface().(openAPISchemaProvider); ok {\n\t\tformat = o.OpenAPISchemaFormat()\n\t\tif st := o.OpenAPISchemaType(); len(st) > 0 {\n\t\t\tswitch st[0] {\n\t\t\tcase \"string\":\n\t\t\t\treturn reflect.String, format\n\t\t\t}\n\t\t}\n\t}\n\treturn reflect.Invalid, format\n}\n\nfunc readCustomColumns(t reflect.Type, path string) (result []apiextv1.CustomResourceColumnDefinition) {\n\tfor i := 0; i < t.NumField(); i++ {\n\t\tf := t.Field(i)\n\t\tfieldName := fieldName(f)\n\t\tif fieldName == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tt := f.Type\n\t\tif t.Kind() == reflect.Ptr {\n\t\t\tt = t.Elem()\n\t\t}\n\t\tif kind, format := openAPISchema(t); kind != reflect.Invalid {\n\t\t\tif col, ok := tagToColumn(f, kind, format, path+fieldName); ok {\n\t\t\t\tresult = append(result, col)\n\t\t\t}\n\t\t} else if t.Kind() == reflect.Struct {\n\t\t\tif f.Anonymous {\n\t\t\t\tresult = append(result, readCustomColumns(t, path)...)\n\t\t\t} else {\n\t\t\t\tresult = append(result, readCustomColumns(t, path+fieldName+\".\")...)\n\t\t\t}\n\t\t} else {\n\t\t\tif col, ok := tagToColumn(f, t.Kind(), \"\", path+fieldName); ok {\n\t\t\t\tresult = append(result, col)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result\n}\n\nfunc (c CRD) WithCustomColumn(columns ...apiextv1.CustomResourceColumnDefinition) CRD {\n\tc.Columns = append(c.Columns, columns...)\n\treturn c\n}\n\nfunc (c CRD) WithStatus() CRD {\n\tc.Status = true\n\treturn c\n}\n\nfunc (c CRD) WithScale() CRD {\n\tc.Scale = true\n\treturn c\n}\n\nfunc (c CRD) WithCategories(categories ...string) CRD {\n\tc.Categories = categories\n\treturn c\n}\n\nfunc (c CRD) WithGroup(group string) CRD {\n\tc.GVK.Group = group\n\treturn c\n}\n\nfunc (c CRD) WithShortNames(shortNames ...string) CRD {\n\tc.ShortNames = shortNames\n\treturn c\n}\n\nfunc (c CRD) ToCustomResourceDefinition() (runtime.Object, error) {\n\tif c.Override != nil {\n\t\treturn c.Override, nil\n\t}\n\n\tif c.SchemaObject != nil && c.GVK.Kind == \"\" {\n\t\tt := getType(c.SchemaObject)\n\t\tc.GVK.Kind = t.Name()\n\t}\n\n\tif c.SchemaObject != nil && c.GVK.Version == \"\" {\n\t\tt := getType(c.SchemaObject)\n\t\tc.GVK.Version = filepath.Base(t.PkgPath())\n\t}\n\n\tif c.SchemaObject != nil && c.GVK.Group == \"\" {\n\t\tt := getType(c.SchemaObject)\n\t\tc.GVK.Group = filepath.Base(filepath.Dir(t.PkgPath()))\n\t}\n\n\tplural := c.PluralName\n\tif plural == \"\" {\n\t\tplural = strings.ToLower(name.GuessPluralName(c.GVK.Kind))\n\t}\n\n\tsingular := c.SingularName\n\tif singular == \"\" {\n\t\tsingular = strings.ToLower(c.GVK.Kind)\n\t}\n\n\tname := c.Name()\n\n\tcrd := apiextv1.CustomResourceDefinition{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName: name,\n\t\t},\n\t\tSpec: apiextv1.CustomResourceDefinitionSpec{\n\t\t\tGroup: c.GVK.Group,\n\t\t\tVersions: []apiextv1.CustomResourceDefinitionVersion{\n\t\t\t\t{\n\t\t\t\t\tName:                     c.GVK.Version,\n\t\t\t\t\tStorage:                  true,\n\t\t\t\t\tServed:                   true,\n\t\t\t\t\tAdditionalPrinterColumns: c.Columns,\n\t\t\t\t},\n\t\t\t},\n\t\t\tNames: apiextv1.CustomResourceDefinitionNames{\n\t\t\t\tPlural:     plural,\n\t\t\t\tSingular:   singular,\n\t\t\t\tKind:       c.GVK.Kind,\n\t\t\t\tCategories: c.Categories,\n\t\t\t\tShortNames: c.ShortNames,\n\t\t\t},\n\t\t\tPreserveUnknownFields: false,\n\t\t},\n\t}\n\n\tif c.Schema != nil {\n\t\tcrd.Spec.Versions[0].Schema = &apiextv1.CustomResourceValidation{\n\t\t\tOpenAPIV3Schema: c.Schema,\n\t\t}\n\t}\n\n\tif c.SchemaObject != nil {\n\t\tschema, err := openapi.ToOpenAPIFromStruct(c.SchemaObject)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcrd.Spec.Versions[0].Schema = &apiextv1.CustomResourceValidation{\n\t\t\tOpenAPIV3Schema: schema,\n\t\t}\n\t}\n\n\t// add a dummy schema because v1 requires OpenAPIV3Schema to be set\n\tif crd.Spec.Versions[0].Schema == nil {\n\t\tcrd.Spec.Versions[0].Schema = &apiextv1.CustomResourceValidation{\n\t\t\tOpenAPIV3Schema: &apiextv1.JSONSchemaProps{\n\t\t\t\tType: \"object\",\n\t\t\t\tProperties: map[string]apiextv1.JSONSchemaProps{\n\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\tXPreserveUnknownFields: &[]bool{true}[0],\n\t\t\t\t\t},\n\t\t\t\t\t\"status\": {\n\t\t\t\t\t\tXPreserveUnknownFields: &[]bool{true}[0],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t}\n\n\tif c.Status {\n\t\tcrd.Spec.Versions[0].Subresources = &apiextv1.CustomResourceSubresources{\n\t\t\tStatus: &apiextv1.CustomResourceSubresourceStatus{},\n\t\t}\n\t\tif c.Scale {\n\t\t\tsel := \"Spec.Selector\"\n\t\t\tcrd.Spec.Versions[0].Subresources.Scale = &apiextv1.CustomResourceSubresourceScale{\n\t\t\t\tSpecReplicasPath:   \"Spec.Replicas\",\n\t\t\t\tStatusReplicasPath: \"Status.Replicas\",\n\t\t\t\tLabelSelectorPath:  &sel,\n\t\t\t}\n\t\t}\n\t}\n\n\tif c.NonNamespace {\n\t\tcrd.Spec.Scope = apiextv1.ClusterScoped\n\t} else {\n\t\tcrd.Spec.Scope = apiextv1.NamespaceScoped\n\t}\n\n\tcrd.Labels = c.Labels\n\tcrd.Annotations = c.Annotations\n\n\t// Convert to unstructured to ensure that PreserveUnknownFields=false is set because the struct will omit false\n\tmapData, err := convert.EncodeToMap(crd)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmapData[\"kind\"] = CRDKind\n\tmapData[\"apiVersion\"] = apiextv1.SchemeGroupVersion.String()\n\n\treturn &unstructured.Unstructured{\n\t\tObject: mapData,\n\t}, unstructured.SetNestedField(mapData, false, \"spec\", \"preserveUnknownFields\")\n}\n\nfunc (c CRD) ToCustomResourceDefinitionV1Beta1() (*apiextv1beta1.CustomResourceDefinition, error) {\n\ttoConvertCRD, err := c.ToCustomResourceDefinition()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif toConvertCRD == nil {\n\t\treturn nil, fmt.Errorf(\"cannot convert empty CRD runtime object to apiextensions v1beta1 CRD object\")\n\t}\n\n\tunstructuredCRD, ok := toConvertCRD.(*unstructured.Unstructured)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"could not convert CRD runtime object to *unstructured.Unstructured\")\n\t}\n\tvar v1CRD *apiextv1.CustomResourceDefinition\n\tif err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredCRD.UnstructuredContent(), &v1CRD); err != nil {\n\t\treturn nil, err\n\t}\n\n\tinternalCRD := &apiext.CustomResourceDefinition{}\n\tif err := apiextv1.Convert_v1_CustomResourceDefinition_To_apiextensions_CustomResourceDefinition(v1CRD, internalCRD, nil); err != nil {\n\t\treturn nil, err\n\t}\n\tv1beta1CRD := &apiextv1beta1.CustomResourceDefinition{}\n\tif err := apiextv1beta1.Convert_apiextensions_CustomResourceDefinition_To_v1beta1_CustomResourceDefinition(internalCRD, v1beta1CRD, nil); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// GVK is dropped during conversion, so we must add it.\n\tv1beta1CRD.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{\n\t\tGroup:   apiextv1beta1.SchemeGroupVersion.Group,\n\t\tVersion: apiextv1beta1.SchemeGroupVersion.Version,\n\t\tKind:    CRDKind,\n\t})\n\treturn v1beta1CRD, nil\n}\n\n// Name resolves the Name for the given CRD.\nfunc (c *CRD) Name() string {\n\tif meta, ok := c.Override.(metav1.Object); ok && c.Override != nil {\n\t\treturn meta.GetName()\n\t}\n\tkind := c.GVK.Kind\n\tif c.SchemaObject != nil && kind == \"\" {\n\t\tt := getType(c.SchemaObject)\n\t\tkind = t.Name()\n\t}\n\n\tgroup := c.GVK.Group\n\tif c.SchemaObject != nil && group == \"\" {\n\t\tt := getType(c.SchemaObject)\n\t\tgroup = filepath.Base(filepath.Dir(t.PkgPath()))\n\t}\n\n\tplural := c.PluralName\n\tif plural == \"\" {\n\t\tplural = strings.ToLower(name.GuessPluralName(kind))\n\t}\n\n\treturn strings.ToLower(plural + \".\" + group)\n}\n\nfunc NamespacedType(name string) CRD {\n\tkindGroup, version := kv.Split(name, \"/\")\n\tkind, group := kv.Split(kindGroup, \".\")\n\tkind = convert.Capitalize(kind)\n\tgroup = strings.ToLower(group)\n\n\treturn FromGV(schema.GroupVersion{\n\t\tGroup:   group,\n\t\tVersion: version,\n\t}, kind)\n}\n\nfunc New(group, version string) CRD {\n\treturn CRD{\n\t\tGVK: schema.GroupVersionKind{\n\t\t\tGroup:   group,\n\t\t\tVersion: version,\n\t\t},\n\t\tPluralName:   \"\",\n\t\tNonNamespace: false,\n\t\tSchema:       nil,\n\t\tSchemaObject: nil,\n\t\tColumns:      nil,\n\t\tStatus:       false,\n\t\tScale:        false,\n\t\tCategories:   nil,\n\t\tShortNames:   nil,\n\t}\n}\n\nfunc NamespacedTypes(names ...string) (ret []CRD) {\n\tfor _, name := range names {\n\t\tret = append(ret, NamespacedType(name))\n\t}\n\treturn\n}\n\nfunc NonNamespacedType(name string) CRD {\n\tcrd := NamespacedType(name)\n\tcrd.NonNamespace = true\n\treturn crd\n}\n\nfunc NonNamespacedTypes(names ...string) (ret []CRD) {\n\tfor _, name := range names {\n\t\tret = append(ret, NonNamespacedType(name))\n\t}\n\treturn\n}\n\nfunc FromGV(gv schema.GroupVersion, kind string) CRD {\n\treturn CRD{\n\t\tGVK: gv.WithKind(kind),\n\t}\n}\n\nfunc NewFactoryFromClient(config *rest.Config) (*Factory, error) {\n\tapply, err := apply.NewForConfig(config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tf, err := clientset.NewForConfig(config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Factory{\n\t\tCRDClient: f,\n\t\tapply:     apply.WithDynamicLookup().WithNoDelete(),\n\t}, nil\n}\n\nfunc (f *Factory) BatchWait() error {\n\tf.wg.Wait()\n\treturn f.err\n}\n\nfunc (f *Factory) BatchCreateCRDs(ctx context.Context, crds ...CRD) *Factory {\n\tf.wg.Add(1)\n\tgo func() {\n\t\tdefer f.wg.Done()\n\t\tif _, err := f.CreateCRDs(ctx, crds...); err != nil && f.err == nil {\n\t\t\tf.err = err\n\t\t}\n\t}()\n\treturn f\n}\n\nfunc (f *Factory) CreateCRDs(ctx context.Context, crds ...CRD) (map[schema.GroupVersionKind]*apiextv1.CustomResourceDefinition, error) {\n\tif len(crds) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tif ok, err := f.ensureAccess(ctx); err != nil {\n\t\treturn nil, err\n\t} else if !ok {\n\t\tlogrus.Infof(\"No access to list CRDs, assuming CRDs are pre-created.\")\n\t\treturn nil, err\n\t}\n\n\tcrdStatus := map[schema.GroupVersionKind]*apiextv1.CustomResourceDefinition{}\n\n\tready, err := f.getReadyCRDs(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, crdDef := range crds {\n\t\tcrd, err := f.createCRD(ctx, crdDef, ready)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcrdStatus[crdDef.GVK] = crd\n\t}\n\n\tready, err = f.getReadyCRDs(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor gvk, crd := range crdStatus {\n\t\tif readyCrd, ok := ready[crd.Name]; ok {\n\t\t\tcrdStatus[gvk] = readyCrd\n\t\t} else {\n\t\t\tif err := f.waitCRD(ctx, crd.Name, gvk, crdStatus); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn crdStatus, nil\n}\n\nfunc (f *Factory) waitCRD(ctx context.Context, crdName string, gvk schema.GroupVersionKind, crdStatus map[schema.GroupVersionKind]*apiextv1.CustomResourceDefinition) error {\n\tlogrus.Infof(\"Waiting for CRD %s to become available\", crdName)\n\tdefer logrus.Infof(\"Done waiting for CRD %s to become available\", crdName)\n\n\tfirst := true\n\treturn wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) {\n\t\tif !first {\n\t\t\tlogrus.Infof(\"Waiting for CRD %s to become available\", crdName)\n\t\t}\n\t\tfirst = false\n\n\t\tcrd, err := f.CRDClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, crdName, metav1.GetOptions{})\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\n\t\tfor _, cond := range crd.Status.Conditions {\n\t\t\tswitch cond.Type {\n\t\t\tcase apiextv1.Established:\n\t\t\t\tif cond.Status == apiextv1.ConditionTrue {\n\t\t\t\t\tcrdStatus[gvk] = crd\n\t\t\t\t\treturn true, err\n\t\t\t\t}\n\t\t\tcase apiextv1.NamesAccepted:\n\t\t\t\tif cond.Status == apiextv1.ConditionFalse {\n\t\t\t\t\tlogrus.Infof(\"Name conflict on %s: %v\\n\", crdName, cond.Reason)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false, ctx.Err()\n\t})\n}\n\nfunc (f *Factory) createCRD(ctx context.Context, crdDef CRD, ready map[string]*apiextv1.CustomResourceDefinition) (*apiextv1.CustomResourceDefinition, error) {\n\tcrd, err := crdDef.ToCustomResourceDefinition()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmeta, err := meta.Accessor(crd)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlogrus.Infof(\"Applying CRD %s\", meta.GetName())\n\tif err := f.apply.WithOwner(crd).ApplyObjects(crd); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn f.CRDClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, meta.GetName(), metav1.GetOptions{})\n}\n\nfunc (f *Factory) ensureAccess(ctx context.Context) (bool, error) {\n\t_, err := f.CRDClient.ApiextensionsV1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{})\n\tif apierrors.IsForbidden(err) {\n\t\treturn false, nil\n\t}\n\treturn true, err\n}\n\nfunc (f *Factory) getReadyCRDs(ctx context.Context) (map[string]*apiextv1.CustomResourceDefinition, error) {\n\tlist, err := f.CRDClient.ApiextensionsV1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tresult := map[string]*apiextv1.CustomResourceDefinition{}\n\n\tfor i, crd := range list.Items {\n\t\tfor _, cond := range crd.Status.Conditions {\n\t\t\tswitch cond.Type {\n\t\t\tcase apiextv1.Established:\n\t\t\t\tif cond.Status == apiextv1.ConditionTrue {\n\t\t\t\t\tresult[crd.Name] = &list.Items[i]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result, nil\n}\n"
  },
  {
    "path": "pkg/crd/mockCRDClient_test.go",
    "content": "// Code generated by MockGen. DO NOT EDIT.\n// Source: k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1 (interfaces: CustomResourceDefinitionInterface)\n//\n// Generated by this command:\n//\n//\tmockgen --build_flags=--mod=mod -package crd -destination ./mockCRDClient_test.go k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1 CustomResourceDefinitionInterface\n//\n\n// Package crd is a generated GoMock package.\npackage crd\n\nimport (\n\tcontext \"context\"\n\treflect \"reflect\"\n\n\tgomock \"go.uber.org/mock/gomock\"\n\tv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\tv10 \"k8s.io/apiextensions-apiserver/pkg/client/applyconfiguration/apiextensions/v1\"\n\tv11 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\ttypes \"k8s.io/apimachinery/pkg/types\"\n\twatch \"k8s.io/apimachinery/pkg/watch\"\n)\n\n// MockCustomResourceDefinitionInterface is a mock of CustomResourceDefinitionInterface interface.\ntype MockCustomResourceDefinitionInterface struct {\n\tctrl     *gomock.Controller\n\trecorder *MockCustomResourceDefinitionInterfaceMockRecorder\n\tisgomock struct{}\n}\n\n// MockCustomResourceDefinitionInterfaceMockRecorder is the mock recorder for MockCustomResourceDefinitionInterface.\ntype MockCustomResourceDefinitionInterfaceMockRecorder struct {\n\tmock *MockCustomResourceDefinitionInterface\n}\n\n// NewMockCustomResourceDefinitionInterface creates a new mock instance.\nfunc NewMockCustomResourceDefinitionInterface(ctrl *gomock.Controller) *MockCustomResourceDefinitionInterface {\n\tmock := &MockCustomResourceDefinitionInterface{ctrl: ctrl}\n\tmock.recorder = &MockCustomResourceDefinitionInterfaceMockRecorder{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use.\nfunc (m *MockCustomResourceDefinitionInterface) EXPECT() *MockCustomResourceDefinitionInterfaceMockRecorder {\n\treturn m.recorder\n}\n\n// Apply mocks base method.\nfunc (m *MockCustomResourceDefinitionInterface) Apply(ctx context.Context, customResourceDefinition *v10.CustomResourceDefinitionApplyConfiguration, opts v11.ApplyOptions) (*v1.CustomResourceDefinition, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Apply\", ctx, customResourceDefinition, opts)\n\tret0, _ := ret[0].(*v1.CustomResourceDefinition)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Apply indicates an expected call of Apply.\nfunc (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Apply(ctx, customResourceDefinition, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Apply\", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Apply), ctx, customResourceDefinition, opts)\n}\n\n// ApplyStatus mocks base method.\nfunc (m *MockCustomResourceDefinitionInterface) ApplyStatus(ctx context.Context, customResourceDefinition *v10.CustomResourceDefinitionApplyConfiguration, opts v11.ApplyOptions) (*v1.CustomResourceDefinition, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"ApplyStatus\", ctx, customResourceDefinition, opts)\n\tret0, _ := ret[0].(*v1.CustomResourceDefinition)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// ApplyStatus indicates an expected call of ApplyStatus.\nfunc (mr *MockCustomResourceDefinitionInterfaceMockRecorder) ApplyStatus(ctx, customResourceDefinition, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"ApplyStatus\", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).ApplyStatus), ctx, customResourceDefinition, opts)\n}\n\n// Create mocks base method.\nfunc (m *MockCustomResourceDefinitionInterface) Create(ctx context.Context, customResourceDefinition *v1.CustomResourceDefinition, opts v11.CreateOptions) (*v1.CustomResourceDefinition, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Create\", ctx, customResourceDefinition, opts)\n\tret0, _ := ret[0].(*v1.CustomResourceDefinition)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Create indicates an expected call of Create.\nfunc (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Create(ctx, customResourceDefinition, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Create\", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Create), ctx, customResourceDefinition, opts)\n}\n\n// Delete mocks base method.\nfunc (m *MockCustomResourceDefinitionInterface) Delete(ctx context.Context, name string, opts v11.DeleteOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Delete\", ctx, name, opts)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// Delete indicates an expected call of Delete.\nfunc (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Delete(ctx, name, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Delete\", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Delete), ctx, name, opts)\n}\n\n// DeleteCollection mocks base method.\nfunc (m *MockCustomResourceDefinitionInterface) DeleteCollection(ctx context.Context, opts v11.DeleteOptions, listOpts v11.ListOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"DeleteCollection\", ctx, opts, listOpts)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// DeleteCollection indicates an expected call of DeleteCollection.\nfunc (mr *MockCustomResourceDefinitionInterfaceMockRecorder) DeleteCollection(ctx, opts, listOpts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"DeleteCollection\", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).DeleteCollection), ctx, opts, listOpts)\n}\n\n// Get mocks base method.\nfunc (m *MockCustomResourceDefinitionInterface) Get(ctx context.Context, name string, opts v11.GetOptions) (*v1.CustomResourceDefinition, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Get\", ctx, name, opts)\n\tret0, _ := ret[0].(*v1.CustomResourceDefinition)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Get indicates an expected call of Get.\nfunc (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Get(ctx, name, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Get\", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Get), ctx, name, opts)\n}\n\n// List mocks base method.\nfunc (m *MockCustomResourceDefinitionInterface) List(ctx context.Context, opts v11.ListOptions) (*v1.CustomResourceDefinitionList, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"List\", ctx, opts)\n\tret0, _ := ret[0].(*v1.CustomResourceDefinitionList)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// List indicates an expected call of List.\nfunc (mr *MockCustomResourceDefinitionInterfaceMockRecorder) List(ctx, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"List\", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).List), ctx, opts)\n}\n\n// Patch mocks base method.\nfunc (m *MockCustomResourceDefinitionInterface) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v11.PatchOptions, subresources ...string) (*v1.CustomResourceDefinition, error) {\n\tm.ctrl.T.Helper()\n\tvarargs := []any{ctx, name, pt, data, opts}\n\tfor _, a := range subresources {\n\t\tvarargs = append(varargs, a)\n\t}\n\tret := m.ctrl.Call(m, \"Patch\", varargs...)\n\tret0, _ := ret[0].(*v1.CustomResourceDefinition)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Patch indicates an expected call of Patch.\nfunc (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Patch(ctx, name, pt, data, opts any, subresources ...any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\tvarargs := append([]any{ctx, name, pt, data, opts}, subresources...)\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Patch\", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Patch), varargs...)\n}\n\n// Update mocks base method.\nfunc (m *MockCustomResourceDefinitionInterface) Update(ctx context.Context, customResourceDefinition *v1.CustomResourceDefinition, opts v11.UpdateOptions) (*v1.CustomResourceDefinition, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Update\", ctx, customResourceDefinition, opts)\n\tret0, _ := ret[0].(*v1.CustomResourceDefinition)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Update indicates an expected call of Update.\nfunc (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Update(ctx, customResourceDefinition, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Update\", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Update), ctx, customResourceDefinition, opts)\n}\n\n// UpdateStatus mocks base method.\nfunc (m *MockCustomResourceDefinitionInterface) UpdateStatus(ctx context.Context, customResourceDefinition *v1.CustomResourceDefinition, opts v11.UpdateOptions) (*v1.CustomResourceDefinition, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"UpdateStatus\", ctx, customResourceDefinition, opts)\n\tret0, _ := ret[0].(*v1.CustomResourceDefinition)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// UpdateStatus indicates an expected call of UpdateStatus.\nfunc (mr *MockCustomResourceDefinitionInterfaceMockRecorder) UpdateStatus(ctx, customResourceDefinition, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"UpdateStatus\", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).UpdateStatus), ctx, customResourceDefinition, opts)\n}\n\n// Watch mocks base method.\nfunc (m *MockCustomResourceDefinitionInterface) Watch(ctx context.Context, opts v11.ListOptions) (watch.Interface, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Watch\", ctx, opts)\n\tret0, _ := ret[0].(watch.Interface)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Watch indicates an expected call of Watch.\nfunc (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Watch(ctx, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Watch\", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Watch), ctx, opts)\n}\n"
  },
  {
    "path": "pkg/crd/print.go",
    "content": "package crd\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/yaml\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/client-go/rest\"\n)\n\nfunc WriteFile(filename string, crds []CRD) error {\n\tif err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {\n\t\treturn err\n\t}\n\tf, err := os.Create(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\n\treturn Print(f, crds)\n}\n\nfunc Print(out io.Writer, crds []CRD) error {\n\tobj, err := Objects(crds)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdata, err := yaml.Export(obj...)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = out.Write(data)\n\treturn err\n}\n\nfunc Objects(crds []CRD) (result []runtime.Object, err error) {\n\tfor _, crdDef := range crds {\n\t\tif crdDef.Override == nil {\n\t\t\tcrd, err := crdDef.ToCustomResourceDefinition()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tresult = append(result, crd)\n\t\t} else {\n\t\t\tresult = append(result, crdDef.Override)\n\t\t}\n\t}\n\treturn\n}\n\nfunc Create(ctx context.Context, cfg *rest.Config, crds []CRD) error {\n\tfactory, err := NewFactoryFromClient(cfg)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn factory.BatchCreateCRDs(ctx, crds...).BatchWait()\n}\n"
  },
  {
    "path": "pkg/data/convert/convert.go",
    "content": "package convert\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode\"\n\n\t\"golang.org/x/text/cases\"\n\t\"golang.org/x/text/language\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n)\n\nfunc Singular(value interface{}) interface{} {\n\tif slice, ok := value.([]string); ok {\n\t\tif len(slice) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\treturn slice[0]\n\t}\n\tif slice, ok := value.([]interface{}); ok {\n\t\tif len(slice) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\treturn slice[0]\n\t}\n\treturn value\n}\n\nfunc ToStringNoTrim(value interface{}) string {\n\tif t, ok := value.(time.Time); ok {\n\t\treturn t.Format(time.RFC3339)\n\t}\n\tsingle := Singular(value)\n\tif single == nil {\n\t\treturn \"\"\n\t}\n\treturn fmt.Sprint(single)\n}\n\nfunc ToString(value interface{}) string {\n\treturn strings.TrimSpace(ToStringNoTrim(value))\n}\n\nfunc ToTimestamp(value interface{}) (int64, error) {\n\tstr := ToString(value)\n\tif str == \"\" {\n\t\treturn 0, errors.New(\"invalid date\")\n\t}\n\tt, err := time.Parse(time.RFC3339, str)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn t.UnixNano() / 1000000, nil\n}\n\nfunc ToBool(value interface{}) bool {\n\tvalue = Singular(value)\n\n\tb, ok := value.(bool)\n\tif ok {\n\t\treturn b\n\t}\n\n\tstr := strings.ToLower(ToString(value))\n\treturn str == \"true\" || str == \"t\" || str == \"yes\" || str == \"y\"\n}\n\nfunc ToNumber(value interface{}) (int64, error) {\n\tvalue = Singular(value)\n\n\ti, ok := value.(int64)\n\tif ok {\n\t\treturn i, nil\n\t}\n\tf, ok := value.(float64)\n\tif ok {\n\t\treturn int64(f), nil\n\t}\n\tif n, ok := value.(json.Number); ok {\n\t\ti, err := n.Int64()\n\t\tif err == nil {\n\t\t\treturn i, nil\n\t\t}\n\t\tf, err := n.Float64()\n\t\treturn int64(f), err\n\t}\n\treturn strconv.ParseInt(ToString(value), 10, 64)\n}\n\nfunc ToFloat(value interface{}) (float64, error) {\n\tvalue = Singular(value)\n\n\tf64, ok := value.(float64)\n\tif ok {\n\t\treturn f64, nil\n\t}\n\n\tf32, ok := value.(float32)\n\tif ok {\n\t\treturn float64(f32), nil\n\t}\n\n\tif n, ok := value.(json.Number); ok {\n\t\ti, err := n.Int64()\n\t\tif err == nil {\n\t\t\treturn float64(i), nil\n\t\t}\n\t\tf, err := n.Float64()\n\t\treturn float64(f), err\n\t}\n\treturn strconv.ParseFloat(ToString(value), 64)\n}\n\nfunc Capitalize(s string) string {\n\tif len(s) <= 1 {\n\t\treturn strings.ToUpper(s)\n\t}\n\n\treturn strings.ToUpper(s[:1]) + s[1:]\n}\n\nfunc Uncapitalize(s string) string {\n\tif len(s) <= 1 {\n\t\treturn strings.ToLower(s)\n\t}\n\n\treturn strings.ToLower(s[:1]) + s[1:]\n}\n\nfunc LowerTitle(input string) string {\n\trunes := []rune(input)\n\tfor i := 0; i < len(runes); i++ {\n\t\tif unicode.IsUpper(runes[i]) &&\n\t\t\t(i == 0 ||\n\t\t\t\ti == len(runes)-1 ||\n\t\t\t\tunicode.IsUpper(runes[i+1])) {\n\t\t\trunes[i] = unicode.ToLower(runes[i])\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn string(runes)\n}\n\nfunc IsEmptyValue(v interface{}) bool {\n\tif v == nil || v == \"\" || v == 0 || v == false {\n\t\treturn true\n\t}\n\tif m, ok := v.(map[string]interface{}); ok {\n\t\treturn len(m) == 0\n\t}\n\tif s, ok := v.([]interface{}); ok {\n\t\treturn len(s) == 0\n\t}\n\treturn false\n}\n\nfunc ToMapInterface(obj interface{}) map[string]interface{} {\n\tv, _ := obj.(map[string]interface{})\n\treturn v\n}\n\nfunc ToInterfaceSlice(obj interface{}) []interface{} {\n\tif v, ok := obj.([]interface{}); ok {\n\t\treturn v\n\t}\n\treturn nil\n}\n\nfunc ToMapSlice(obj interface{}) []map[string]interface{} {\n\tif v, ok := obj.([]map[string]interface{}); ok {\n\t\treturn v\n\t}\n\tvs, _ := obj.([]interface{})\n\tvar result []map[string]interface{}\n\tfor _, item := range vs {\n\t\tif v, ok := item.(map[string]interface{}); ok {\n\t\t\tresult = append(result, v)\n\t\t} else {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn result\n}\n\nfunc ToStringSlice(data interface{}) []string {\n\tif v, ok := data.([]string); ok {\n\t\treturn v\n\t}\n\tif v, ok := data.([]interface{}); ok {\n\t\tvar result []string\n\t\tfor _, item := range v {\n\t\t\tresult = append(result, ToString(item))\n\t\t}\n\t\treturn result\n\t}\n\tif v, ok := data.(string); ok {\n\t\treturn []string{v}\n\t}\n\treturn nil\n}\n\nfunc ToObj(data interface{}, into interface{}) error {\n\tbytes, err := json.Marshal(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn json.Unmarshal(bytes, into)\n}\n\nfunc EncodeToMap(obj interface{}) (map[string]interface{}, error) {\n\tif m, ok := obj.(map[string]interface{}); ok {\n\t\treturn m, nil\n\t}\n\n\tif unstr, ok := obj.(*unstructured.Unstructured); ok {\n\t\treturn unstr.Object, nil\n\t}\n\n\tb, err := json.Marshal(obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresult := map[string]interface{}{}\n\tdec := json.NewDecoder(bytes.NewBuffer(b))\n\tdec.UseNumber()\n\treturn result, dec.Decode(&result)\n}\n\nfunc ToJSONKey(str string) string {\n\tparts := strings.Split(str, \"_\")\n\tfor i := 1; i < len(parts); i++ {\n\t\tcaser := cases.Title(language.English)\n\t\tparts[i] = caser.String(parts[i])\n\t}\n\n\treturn strings.Join(parts, \"\")\n}\n\nfunc ToYAMLKey(str string) string {\n\tvar result []rune\n\tcap := false\n\n\tfor i, r := range []rune(str) {\n\t\tif i == 0 {\n\t\t\tif unicode.IsUpper(r) {\n\t\t\t\tcap = true\n\t\t\t}\n\t\t\tresult = append(result, unicode.ToLower(r))\n\t\t\tcontinue\n\t\t}\n\n\t\tif unicode.IsUpper(r) {\n\t\t\tif cap {\n\t\t\t\tresult = append(result, unicode.ToLower(r))\n\t\t\t} else {\n\t\t\t\tresult = append(result, '_', unicode.ToLower(r))\n\t\t\t}\n\t\t} else {\n\t\t\tcap = false\n\t\t\tresult = append(result, r)\n\t\t}\n\t}\n\n\treturn string(result)\n}\n\nfunc ToArgKey(str string) string {\n\tvar (\n\t\tresult []rune\n\t\tinput  = []rune(str)\n\t)\n\tcap := false\n\n\tfor i := 0; i < len(input); i++ {\n\t\tr := input[i]\n\t\tif i == 0 {\n\t\t\tif unicode.IsUpper(r) {\n\t\t\t\tcap = true\n\t\t\t}\n\t\t\tresult = append(result, unicode.ToLower(r))\n\t\t\tcontinue\n\t\t}\n\n\t\tif unicode.IsUpper(r) {\n\t\t\tif cap {\n\t\t\t\tresult = append(result, unicode.ToLower(r))\n\t\t\t} else if len(input) > i+2 &&\n\t\t\t\tunicode.IsUpper(input[i]) &&\n\t\t\t\tunicode.IsUpper(input[i+1]) &&\n\t\t\t\tunicode.IsUpper(input[i+2]) {\n\t\t\t\tresult = append(result, '-',\n\t\t\t\t\tunicode.ToLower(input[i]),\n\t\t\t\t\tunicode.ToLower(input[i+1]),\n\t\t\t\t\tunicode.ToLower(input[i+2]))\n\t\t\t\ti += 2\n\t\t\t} else {\n\t\t\t\tresult = append(result, '-', unicode.ToLower(r))\n\t\t\t}\n\t\t} else {\n\t\t\tcap = false\n\t\t\tresult = append(result, r)\n\t\t}\n\t}\n\n\treturn \"--\" + string(result)\n}\n"
  },
  {
    "path": "pkg/data/convert/convert_test.go",
    "content": "package convert\n\nimport (\n\t\"testing\"\n)\n\ntype data struct {\n\tTTLMillis int `json:\"ttl\"`\n}\n\nfunc TestJSON(t *testing.T) {\n\td := &data{\n\t\tTTLMillis: 57600000,\n\t}\n\n\tm, err := EncodeToMap(d)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ti, _ := ToNumber(m[\"ttl\"])\n\tif i != 57600000 {\n\t\tt.Fatal(\"not\", 57600000, \"got\", m[\"ttl\"])\n\t}\n}\n\nfunc TestArgKey(t *testing.T) {\n\tdata := []struct {\n\t\tinput  string\n\t\toutput string\n\t}{\n\t\t{\n\t\t\tinput:  \"disableOpenAPIValidation\",\n\t\t\toutput: \"--disable-open-api-validation\",\n\t\t},\n\t\t{\n\t\t\tinput:  \"skipCRDs\",\n\t\t\toutput: \"--skip-crds\",\n\t\t},\n\t}\n\n\tfor _, data := range data {\n\t\tif ToArgKey(data.input) != data.output {\n\t\t\tt.Errorf(\"expected %s, got %s\", data.output, ToArgKey(data.input))\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pkg/data/data.go",
    "content": "package data\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n)\n\ntype List []map[string]interface{}\n\ntype Object map[string]interface{}\n\nfunc New() Object {\n\treturn map[string]interface{}{}\n}\n\nfunc Convert(obj interface{}) (Object, error) {\n\tdata, err := convert.EncodeToMap(obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn data, nil\n}\n\nfunc (o Object) Map(names ...string) Object {\n\tv := GetValueN(o, names...)\n\tm := convert.ToMapInterface(v)\n\treturn m\n}\n\nfunc (o Object) Slice(names ...string) (result []Object) {\n\tv := GetValueN(o, names...)\n\tfor _, item := range convert.ToInterfaceSlice(v) {\n\t\tresult = append(result, convert.ToMapInterface(item))\n\t}\n\treturn\n}\n\nfunc (o Object) Values() (result []Object) {\n\tfor k := range o {\n\t\tresult = append(result, o.Map(k))\n\t}\n\treturn\n}\n\nfunc (o Object) String(names ...string) string {\n\tv := GetValueN(o, names...)\n\treturn convert.ToString(v)\n}\n\nfunc (o Object) StringSlice(names ...string) []string {\n\tv := GetValueN(o, names...)\n\treturn convert.ToStringSlice(v)\n}\n\nfunc (o Object) Set(key string, obj interface{}) {\n\tif o == nil {\n\t\treturn\n\t}\n\to[key] = obj\n}\n\nfunc (o Object) SetNested(obj interface{}, key ...string) {\n\tPutValue(o, obj, key...)\n}\n\nfunc (o Object) Bool(key ...string) bool {\n\treturn convert.ToBool(GetValueN(o, key...))\n}\n"
  },
  {
    "path": "pkg/data/merge.go",
    "content": "package data\n\nfunc MergeMaps(base, overlay map[string]interface{}) map[string]interface{} {\n\tresult := map[string]interface{}{}\n\tfor k, v := range base {\n\t\tresult[k] = v\n\t}\n\tfor k, v := range overlay {\n\t\tif baseMap, overlayMap, bothMaps := bothMaps(result[k], v); bothMaps {\n\t\t\tv = MergeMaps(baseMap, overlayMap)\n\t\t}\n\t\tresult[k] = v\n\t}\n\treturn result\n}\n\nfunc bothMaps(left, right interface{}) (map[string]interface{}, map[string]interface{}, bool) {\n\tleftMap, ok := left.(map[string]interface{})\n\tif !ok {\n\t\treturn nil, nil, false\n\t}\n\trightMap, ok := right.(map[string]interface{})\n\treturn leftMap, rightMap, ok\n}\n\nfunc bothSlices(left, right interface{}) ([]interface{}, []interface{}, bool) {\n\tleftSlice, ok := left.([]interface{})\n\tif !ok {\n\t\treturn nil, nil, false\n\t}\n\trightSlice, ok := right.([]interface{})\n\treturn leftSlice, rightSlice, ok\n}\n\nfunc MergeMapsConcatSlice(base, overlay map[string]interface{}) map[string]interface{} {\n\tresult := map[string]interface{}{}\n\tfor k, v := range base {\n\t\tresult[k] = v\n\t}\n\tfor k, v := range overlay {\n\t\tif baseMap, overlayMap, bothMaps := bothMaps(result[k], v); bothMaps {\n\t\t\tv = MergeMaps(baseMap, overlayMap)\n\t\t} else if baseSlice, overlaySlice, bothSlices := bothSlices(result[k], v); bothSlices {\n\t\t\ts := make([]interface{}, 0, len(baseSlice)+len(overlaySlice))\n\t\t\ts = append(s, baseSlice...)\n\t\t\ts = append(s, overlaySlice...)\n\t\t\tv = s\n\t\t}\n\t\tresult[k] = v\n\t}\n\treturn result\n\n}\n"
  },
  {
    "path": "pkg/data/values.go",
    "content": "// Package data contains functions for working with unstructured values like []interface or map[string]interface{}.\n// It allows reading/writing to these values without having to convert to structured items.\npackage data\n\nimport (\n\t\"strconv\"\n)\n\n// RemoveValue removes a value from data. Keys should be in order denoting the path to the value in the nested\n// structure of the map. For example, passing []string{\"metadata\", \"annotations\"} will make the function remove the\n// \"annotations\" key from the \"metadata\" sub-map. Returns the removed value (if any) and a bool indicating if the value\n// was found.\nfunc RemoveValue(data map[string]interface{}, keys ...string) (interface{}, bool) {\n\tfor i, key := range keys {\n\t\tif i == len(keys)-1 {\n\t\t\tval, ok := data[key]\n\t\t\tdelete(data, key)\n\t\t\treturn val, ok\n\t\t}\n\t\tdata, _ = data[key].(map[string]interface{})\n\t}\n\n\treturn nil, false\n}\n\nfunc GetValueN(data map[string]interface{}, keys ...string) interface{} {\n\tval, _ := GetValue(data, keys...)\n\treturn val\n}\n\n// GetValue works similar to GetValueFromAny, but can only process maps. Kept this way to avoid breaking changes with\n// the previous interface, GetValueFromAny should be used in most cases since that can handle slices as well.\nfunc GetValue(data map[string]interface{}, keys ...string) (interface{}, bool) {\n\tfor i, key := range keys {\n\t\tif i == len(keys)-1 {\n\t\t\tval, ok := data[key]\n\t\t\treturn val, ok\n\t\t}\n\t\tdata, _ = data[key].(map[string]interface{})\n\t}\n\treturn nil, false\n}\n\n// GetValueFromAny retrieves a value from the provided collection, which must be a map[string]interface, []interface, or []string (as a final value)\n// Keys are always strings.\n// For a map, a key denotes the key in the map whose value we want to retrieve.\n// For the slice, it denotes the index (starting at 0) of the value we want to retrieve.\n// Returns the retrieved value (if any) and a bool indicating if the value was found.\nfunc GetValueFromAny(data interface{}, keys ...string) (interface{}, bool) {\n\tif len(keys) == 0 {\n\t\treturn nil, false\n\t}\n\tfor _, key := range keys {\n\t\tif d2, ok := data.(map[string]interface{}); ok {\n\t\t\tdata, ok = d2[key]\n\t\t\tif !ok {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\t// So it must be an array. Verify the index and then continue\n\t\t// with a type-assertion switch block for the two different types of arrays we expect\n\t\tkeyInt, err := strconv.Atoi(key)\n\t\tif err != nil || keyInt < 0 {\n\t\t\treturn nil, false\n\t\t}\n\t\tswitch node := data.(type) {\n\t\tcase []interface{}:\n\t\t\tif keyInt >= len(node) {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t\tdata = node[keyInt]\n\t\tcase []string:\n\t\t\tif keyInt >= len(node) {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t\tdata = node[keyInt]\n\t\t\t// If we're at the end of the keys, we'll return the value at the end of this function\n\t\t\t// Otherwise we'll try to index into the string and hit the default case,\n\t\t\t// and return <nil, false>\n\t\t\t// See the \"keys nested too far on a string array\" test.\n\t\tdefault:\n\t\t\treturn nil, false\n\t\t}\n\t}\n\n\treturn data, true\n}\n\n// PutValue updates the value of a given map at the index specified by keys that denote the path to the value in the\n// nested structure of the map. If there is no current entry at a key, a new map is created for that value.\nfunc PutValue(data map[string]interface{}, val interface{}, keys ...string) {\n\tif data == nil {\n\t\treturn\n\t}\n\n\t// This is so ugly\n\tfor i, key := range keys {\n\t\tif i == len(keys)-1 {\n\t\t\tdata[key] = val\n\t\t} else {\n\t\t\tnewData, ok := data[key]\n\t\t\tif ok {\n\t\t\t\tnewMap, ok := newData.(map[string]interface{})\n\t\t\t\tif ok {\n\t\t\t\t\tdata = newMap\n\t\t\t\t} else {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnewMap := map[string]interface{}{}\n\t\t\t\tdata[key] = newMap\n\t\t\t\tdata = newMap\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pkg/data/values_test.go",
    "content": "package data\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestGetValueFromAny(t *testing.T) {\n\tt.Parallel()\n\ttests := []struct {\n\t\tname        string\n\t\tdata        interface{}\n\t\tkeys        []string\n\t\twantValue   interface{}\n\t\twantSuccess bool\n\t}{\n\t\t{\n\t\t\tname:        \"nil map\",\n\t\t\tdata:        nil,\n\t\t\tkeys:        []string{\"somekey\"},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t\t{\n\t\t\tname: \"key is not in map\",\n\t\t\tdata: map[string]interface{}{\n\t\t\t\t\"realKey\": \"realVal\",\n\t\t\t},\n\t\t\tkeys:        []string{\"badKey\"},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t\t{\n\t\t\tname: \"key is in first level of map\",\n\t\t\tdata: map[string]interface{}{\n\t\t\t\t\"realKey\": \"realVal\",\n\t\t\t},\n\t\t\tkeys:        []string{\"realKey\"},\n\t\t\twantValue:   \"realVal\",\n\t\t\twantSuccess: true,\n\t\t},\n\t\t{\n\t\t\tname: \"key is nested in map\",\n\t\t\tdata: map[string]interface{}{\n\t\t\t\t\"parent\": map[string]interface{}{\n\t\t\t\t\t\"child\": map[string]interface{}{\n\t\t\t\t\t\t\"grandchild\": \"someValue\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tkeys:        []string{\"parent\", \"child\", \"grandchild\"},\n\t\t\twantValue:   \"someValue\",\n\t\t\twantSuccess: true,\n\t\t},\n\t\t{\n\t\t\tname: \"incorrected nested key\",\n\t\t\tdata: map[string]interface{}{\n\t\t\t\t\"parent\": map[string]interface{}{\n\t\t\t\t\t\"child\": map[string]interface{}{\n\t\t\t\t\t\t\"grandchild\": \"someValue\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tkeys:        []string{\"parent\", \"grandchild\", \"child\"},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t\t{\n\t\t\tname: \"get index of slice\",\n\t\t\tdata: map[string]interface{}{\n\t\t\t\t\"parent\": map[string]interface{}{\n\t\t\t\t\t\"children\": []interface{}{\n\t\t\t\t\t\t\"alice\",\n\t\t\t\t\t\t\"bob\",\n\t\t\t\t\t\t\"eve\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tkeys:        []string{\"parent\", \"children\", \"2\"},\n\t\t\twantValue:   \"eve\",\n\t\t\twantSuccess: true,\n\t\t},\n\t\t{\n\t\t\tname: \"get index of top level slice\",\n\t\t\tdata: []interface{}{\n\t\t\t\t\"alice\",\n\t\t\t\t\"bob\",\n\t\t\t\t\"eve\",\n\t\t\t},\n\t\t\tkeys:        []string{\"2\"},\n\t\t\twantValue:   \"eve\",\n\t\t\twantSuccess: true,\n\t\t},\n\t\t{\n\t\t\tname: \"slice of maps\",\n\t\t\tdata: []interface{}{\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"notthisone\": \"val\",\n\t\t\t\t},\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"parent\": map[string]interface{}{\n\t\t\t\t\t\t\"children\": []interface{}{\n\t\t\t\t\t\t\t\"alice\",\n\t\t\t\t\t\t\t\"bob\",\n\t\t\t\t\t\t\t\"eve\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tkeys:        []string{\"1\", \"parent\", \"children\", \"0\"},\n\t\t\twantValue:   \"alice\",\n\t\t\twantSuccess: true,\n\t\t},\n\t\t{\n\t\t\tname: \"index is too big\",\n\t\t\tdata: map[string]interface{}{\n\t\t\t\t\"parent\": map[string]interface{}{\n\t\t\t\t\t\"children\": []interface{}{\n\t\t\t\t\t\t\"alice\",\n\t\t\t\t\t\t\"bob\",\n\t\t\t\t\t\t\"eve\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tkeys:        []string{\"parent\", \"children\", \"3\"},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t\t{\n\t\t\tname: \"index is negative\",\n\t\t\tdata: map[string]interface{}{\n\t\t\t\t\"parent\": map[string]interface{}{\n\t\t\t\t\t\"children\": []interface{}{\n\t\t\t\t\t\t\"alice\",\n\t\t\t\t\t\t\"bob\",\n\t\t\t\t\t\t\"eve\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tkeys:        []string{\"parent\", \"children\", \"-3\"},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t\t{\n\t\t\tname: \"index not parseable to int\",\n\t\t\tdata: map[string]interface{}{\n\t\t\t\t\"parent\": map[string]interface{}{\n\t\t\t\t\t\"children\": []interface{}{\n\t\t\t\t\t\t\"alice\",\n\t\t\t\t\t\t\"bob\",\n\t\t\t\t\t\t\"eve\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tkeys:        []string{\"parent\", \"children\", \"notanint\"},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t\t{\n\t\t\tname: \"slice blank index\",\n\t\t\tdata: []interface{}{\n\t\t\t\t\"bob\",\n\t\t\t},\n\t\t\tkeys:        []string{\"\"},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t\t{\n\t\t\tname: \"slice no index\",\n\t\t\tdata: []interface{}{\n\t\t\t\t\"bob\",\n\t\t\t},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t\t{\n\t\t\tname: \"keys nested too far\",\n\t\t\tdata: []interface{}{\n\t\t\t\t\"alice\",\n\t\t\t\t\"bob\",\n\t\t\t\t\"eve\",\n\t\t\t},\n\t\t\tkeys:        []string{\"2\", \"1\"},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t\t{\n\t\t\tname: \"keys nested too far on a string array\",\n\t\t\tdata: map[string]interface{}{\n\t\t\t\t\"block1\": []string{\n\t\t\t\t\t\"ink\",\n\t\t\t\t\t\"wink\",\n\t\t\t\t\t\"blink\",\n\t\t\t\t},\n\t\t\t\t\"block2\": []string{\n\t\t\t\t\t\"ball\",\n\t\t\t\t\t\"bell\",\n\t\t\t\t\t\"bill\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tkeys:        []string{\"block1\", \"2\", \"3\"},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t\t{\n\t\t\tname: \"map blank key with value\",\n\t\t\tdata: map[string]interface{}{\n\t\t\t\t\"\": \"bob\",\n\t\t\t},\n\t\t\tkeys:        []string{\"\"},\n\t\t\twantValue:   \"bob\",\n\t\t\twantSuccess: true,\n\t\t},\n\t\t{\n\t\t\tname: \"map blank key no value\",\n\t\t\tdata: map[string]interface{}{\n\t\t\t\t\"alice\": \"bob\",\n\t\t\t},\n\t\t\tkeys:        []string{\"\"},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t\t{\n\t\t\tname: \"map no key\",\n\t\t\tdata: map[string]interface{}{\n\t\t\t\t\"\": \"bob\",\n\t\t\t},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t\t{\n\t\t\tname: \"contains an array of strings at top-level\",\n\t\t\tdata: map[string]interface{}{\n\t\t\t\t\"kind\": \"apple\",\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"name\": \"granny-smith\",\n\t\t\t\t\t\"fields\": []string{\n\t\t\t\t\t\t\"a3\",\n\t\t\t\t\t\t\"position2\",\n\t\t\t\t\t\t\"more...\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"data\": map[string]interface{}{\n\t\t\t\t\t\"color\": \"green\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tkeys:        []string{\"metadata\", \"fields\", \"1\"},\n\t\t\twantValue:   \"position2\",\n\t\t\twantSuccess: true,\n\t\t},\n\t\t{\n\t\t\tname: \"contains an array of strings at top-level\",\n\t\t\tdata: []string{\n\t\t\t\t\"a4\",\n\t\t\t\t\"position4\",\n\t\t\t\t\"more...\",\n\t\t\t},\n\t\t\tkeys:        []string{\"2\"},\n\t\t\twantValue:   \"more...\",\n\t\t\twantSuccess: true,\n\t\t},\n\t\t{\n\t\t\tname: \"index out of bounds for top-level array\",\n\t\t\tdata: []string{\n\t\t\t\t\"a4\",\n\t\t\t\t\"position4\",\n\t\t\t\t\"more...\",\n\t\t\t},\n\t\t\tkeys:        []string{\"-5\"},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t\t{\n\t\t\tname:        \"doesn't handle array of ints\",\n\t\t\tdata:        []int{1, 3, 5},\n\t\t\tkeys:        []string{\"1\"},\n\t\t\twantValue:   nil,\n\t\t\twantSuccess: false,\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\ttest := test\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tgotValue, gotSuccess := GetValueFromAny(test.data, test.keys...)\n\t\t\tassert.Equal(t, test.wantValue, gotValue)\n\t\t\tassert.Equal(t, test.wantSuccess, gotSuccess)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/generated/controllers/admissionregistration.k8s.io/factory.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage admissionregistration\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"k8s.io/client-go/rest\"\n)\n\ntype Factory struct {\n\t*generic.Factory\n}\n\nfunc NewFactoryFromConfigOrDie(config *rest.Config) *Factory {\n\tf, err := NewFactoryFromConfig(config)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFactoryFromConfig(config *rest.Config) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, nil)\n}\n\nfunc NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, &FactoryOptions{\n\t\tNamespace: namespace,\n\t})\n}\n\ntype FactoryOptions = generic.FactoryOptions\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tf, err := generic.NewFactoryFromConfigWithOptions(config, opts)\n\treturn &Factory{\n\t\tFactory: f,\n\t}, err\n}\n\nfunc NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory {\n\tf, err := NewFactoryFromConfigWithOptions(config, opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc (c *Factory) Admissionregistration() Interface {\n\treturn New(c.ControllerFactory())\n}\n\nfunc (c *Factory) WithAgent(userAgent string) Interface {\n\treturn New(controller.NewSharedControllerFactoryWithAgent(userAgent, c.ControllerFactory()))\n}\n"
  },
  {
    "path": "pkg/generated/controllers/admissionregistration.k8s.io/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage admissionregistration\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\tv1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/admissionregistration.k8s.io/v1\"\n)\n\ntype Interface interface {\n\tV1() v1.Interface\n}\n\ntype group struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n// New returns a new Interface.\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &group{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\nfunc (g *group) V1() v1.Interface {\n\treturn v1.New(g.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/admissionregistration.k8s.io/v1/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\tv1 \"k8s.io/api/admissionregistration/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc init() {\n\tschemes.Register(v1.AddToScheme)\n}\n\ntype Interface interface {\n\tMutatingWebhookConfiguration() MutatingWebhookConfigurationController\n\tValidatingWebhookConfiguration() ValidatingWebhookConfigurationController\n}\n\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &version{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\ntype version struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\nfunc (v *version) MutatingWebhookConfiguration() MutatingWebhookConfigurationController {\n\treturn generic.NewNonNamespacedController[*v1.MutatingWebhookConfiguration, *v1.MutatingWebhookConfigurationList](schema.GroupVersionKind{Group: \"admissionregistration.k8s.io\", Version: \"v1\", Kind: \"MutatingWebhookConfiguration\"}, \"mutatingwebhookconfigurations\", v.controllerFactory)\n}\n\nfunc (v *version) ValidatingWebhookConfiguration() ValidatingWebhookConfigurationController {\n\treturn generic.NewNonNamespacedController[*v1.ValidatingWebhookConfiguration, *v1.ValidatingWebhookConfigurationList](schema.GroupVersionKind{Group: \"admissionregistration.k8s.io\", Version: \"v1\", Kind: \"ValidatingWebhookConfiguration\"}, \"validatingwebhookconfigurations\", v.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/admissionregistration.k8s.io/v1/mutatingwebhookconfiguration.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/admissionregistration/v1\"\n)\n\n// MutatingWebhookConfigurationController interface for managing MutatingWebhookConfiguration resources.\ntype MutatingWebhookConfigurationController interface {\n\tgeneric.NonNamespacedControllerInterface[*v1.MutatingWebhookConfiguration, *v1.MutatingWebhookConfigurationList]\n}\n\n// MutatingWebhookConfigurationClient interface for managing MutatingWebhookConfiguration resources in Kubernetes.\ntype MutatingWebhookConfigurationClient interface {\n\tgeneric.NonNamespacedClientInterface[*v1.MutatingWebhookConfiguration, *v1.MutatingWebhookConfigurationList]\n}\n\n// MutatingWebhookConfigurationCache interface for retrieving MutatingWebhookConfiguration resources in memory.\ntype MutatingWebhookConfigurationCache interface {\n\tgeneric.NonNamespacedCacheInterface[*v1.MutatingWebhookConfiguration]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/admissionregistration.k8s.io/v1/validatingwebhookconfiguration.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/admissionregistration/v1\"\n)\n\n// ValidatingWebhookConfigurationController interface for managing ValidatingWebhookConfiguration resources.\ntype ValidatingWebhookConfigurationController interface {\n\tgeneric.NonNamespacedControllerInterface[*v1.ValidatingWebhookConfiguration, *v1.ValidatingWebhookConfigurationList]\n}\n\n// ValidatingWebhookConfigurationClient interface for managing ValidatingWebhookConfiguration resources in Kubernetes.\ntype ValidatingWebhookConfigurationClient interface {\n\tgeneric.NonNamespacedClientInterface[*v1.ValidatingWebhookConfiguration, *v1.ValidatingWebhookConfigurationList]\n}\n\n// ValidatingWebhookConfigurationCache interface for retrieving ValidatingWebhookConfiguration resources in memory.\ntype ValidatingWebhookConfigurationCache interface {\n\tgeneric.NonNamespacedCacheInterface[*v1.ValidatingWebhookConfiguration]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apiextensions.k8s.io/factory.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage apiextensions\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"k8s.io/client-go/rest\"\n)\n\ntype Factory struct {\n\t*generic.Factory\n}\n\nfunc NewFactoryFromConfigOrDie(config *rest.Config) *Factory {\n\tf, err := NewFactoryFromConfig(config)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFactoryFromConfig(config *rest.Config) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, nil)\n}\n\nfunc NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, &FactoryOptions{\n\t\tNamespace: namespace,\n\t})\n}\n\ntype FactoryOptions = generic.FactoryOptions\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tf, err := generic.NewFactoryFromConfigWithOptions(config, opts)\n\treturn &Factory{\n\t\tFactory: f,\n\t}, err\n}\n\nfunc NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory {\n\tf, err := NewFactoryFromConfigWithOptions(config, opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc (c *Factory) Apiextensions() Interface {\n\treturn New(c.ControllerFactory())\n}\n\nfunc (c *Factory) WithAgent(userAgent string) Interface {\n\treturn New(controller.NewSharedControllerFactoryWithAgent(userAgent, c.ControllerFactory()))\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apiextensions.k8s.io/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage apiextensions\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\tv1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/apiextensions.k8s.io/v1\"\n)\n\ntype Interface interface {\n\tV1() v1.Interface\n}\n\ntype group struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n// New returns a new Interface.\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &group{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\nfunc (g *group) V1() v1.Interface {\n\treturn v1.New(g.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apiextensions.k8s.io/v1/customresourcedefinition.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\tv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// CustomResourceDefinitionController interface for managing CustomResourceDefinition resources.\ntype CustomResourceDefinitionController interface {\n\tgeneric.NonNamespacedControllerInterface[*v1.CustomResourceDefinition, *v1.CustomResourceDefinitionList]\n}\n\n// CustomResourceDefinitionClient interface for managing CustomResourceDefinition resources in Kubernetes.\ntype CustomResourceDefinitionClient interface {\n\tgeneric.NonNamespacedClientInterface[*v1.CustomResourceDefinition, *v1.CustomResourceDefinitionList]\n}\n\n// CustomResourceDefinitionCache interface for retrieving CustomResourceDefinition resources in memory.\ntype CustomResourceDefinitionCache interface {\n\tgeneric.NonNamespacedCacheInterface[*v1.CustomResourceDefinition]\n}\n\n// CustomResourceDefinitionStatusHandler is executed for every added or modified CustomResourceDefinition. Should return the new status to be updated\ntype CustomResourceDefinitionStatusHandler func(obj *v1.CustomResourceDefinition, status v1.CustomResourceDefinitionStatus) (v1.CustomResourceDefinitionStatus, error)\n\n// CustomResourceDefinitionGeneratingHandler is the top-level handler that is executed for every CustomResourceDefinition event. It extends CustomResourceDefinitionStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype CustomResourceDefinitionGeneratingHandler func(obj *v1.CustomResourceDefinition, status v1.CustomResourceDefinitionStatus) ([]runtime.Object, v1.CustomResourceDefinitionStatus, error)\n\n// RegisterCustomResourceDefinitionStatusHandler configures a CustomResourceDefinitionController to execute a CustomResourceDefinitionStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterCustomResourceDefinitionStatusHandler(ctx context.Context, controller CustomResourceDefinitionController, condition condition.Cond, name string, handler CustomResourceDefinitionStatusHandler) {\n\tstatusHandler := &customResourceDefinitionStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterCustomResourceDefinitionGeneratingHandler configures a CustomResourceDefinitionController to execute a CustomResourceDefinitionGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterCustomResourceDefinitionGeneratingHandler(ctx context.Context, controller CustomResourceDefinitionController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler CustomResourceDefinitionGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &customResourceDefinitionGeneratingHandler{\n\t\tCustomResourceDefinitionGeneratingHandler: handler,\n\t\tapply: apply,\n\t\tname:  name,\n\t\tgvk:   controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterCustomResourceDefinitionStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype customResourceDefinitionStatusHandler struct {\n\tclient    CustomResourceDefinitionClient\n\tcondition condition.Cond\n\thandler   CustomResourceDefinitionStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *customResourceDefinitionStatusHandler) sync(key string, obj *v1.CustomResourceDefinition) (*v1.CustomResourceDefinition, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype customResourceDefinitionGeneratingHandler struct {\n\tCustomResourceDefinitionGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *customResourceDefinitionGeneratingHandler) Remove(key string, obj *v1.CustomResourceDefinition) (*v1.CustomResourceDefinition, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1.CustomResourceDefinition{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured CustomResourceDefinitionGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *customResourceDefinitionGeneratingHandler) Handle(obj *v1.CustomResourceDefinition, status v1.CustomResourceDefinitionStatus) (v1.CustomResourceDefinitionStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.CustomResourceDefinitionGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *customResourceDefinitionGeneratingHandler) isNewResourceVersion(obj *v1.CustomResourceDefinition) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *customResourceDefinitionGeneratingHandler) storeResourceVersion(obj *v1.CustomResourceDefinition) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apiextensions.k8s.io/v1/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\tv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc init() {\n\tschemes.Register(v1.AddToScheme)\n}\n\ntype Interface interface {\n\tCustomResourceDefinition() CustomResourceDefinitionController\n}\n\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &version{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\ntype version struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\nfunc (v *version) CustomResourceDefinition() CustomResourceDefinitionController {\n\treturn generic.NewNonNamespacedController[*v1.CustomResourceDefinition, *v1.CustomResourceDefinitionList](schema.GroupVersionKind{Group: \"apiextensions.k8s.io\", Version: \"v1\", Kind: \"CustomResourceDefinition\"}, \"customresourcedefinitions\", v.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apiregistration.k8s.io/factory.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage apiregistration\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"k8s.io/client-go/rest\"\n)\n\ntype Factory struct {\n\t*generic.Factory\n}\n\nfunc NewFactoryFromConfigOrDie(config *rest.Config) *Factory {\n\tf, err := NewFactoryFromConfig(config)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFactoryFromConfig(config *rest.Config) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, nil)\n}\n\nfunc NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, &FactoryOptions{\n\t\tNamespace: namespace,\n\t})\n}\n\ntype FactoryOptions = generic.FactoryOptions\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tf, err := generic.NewFactoryFromConfigWithOptions(config, opts)\n\treturn &Factory{\n\t\tFactory: f,\n\t}, err\n}\n\nfunc NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory {\n\tf, err := NewFactoryFromConfigWithOptions(config, opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc (c *Factory) Apiregistration() Interface {\n\treturn New(c.ControllerFactory())\n}\n\nfunc (c *Factory) WithAgent(userAgent string) Interface {\n\treturn New(controller.NewSharedControllerFactoryWithAgent(userAgent, c.ControllerFactory()))\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apiregistration.k8s.io/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage apiregistration\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\tv1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/apiregistration.k8s.io/v1\"\n)\n\ntype Interface interface {\n\tV1() v1.Interface\n}\n\ntype group struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n// New returns a new Interface.\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &group{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\nfunc (g *group) V1() v1.Interface {\n\treturn v1.New(g.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apiregistration.k8s.io/v1/apiservice.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\tv1 \"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1\"\n)\n\n// APIServiceController interface for managing APIService resources.\ntype APIServiceController interface {\n\tgeneric.NonNamespacedControllerInterface[*v1.APIService, *v1.APIServiceList]\n}\n\n// APIServiceClient interface for managing APIService resources in Kubernetes.\ntype APIServiceClient interface {\n\tgeneric.NonNamespacedClientInterface[*v1.APIService, *v1.APIServiceList]\n}\n\n// APIServiceCache interface for retrieving APIService resources in memory.\ntype APIServiceCache interface {\n\tgeneric.NonNamespacedCacheInterface[*v1.APIService]\n}\n\n// APIServiceStatusHandler is executed for every added or modified APIService. Should return the new status to be updated\ntype APIServiceStatusHandler func(obj *v1.APIService, status v1.APIServiceStatus) (v1.APIServiceStatus, error)\n\n// APIServiceGeneratingHandler is the top-level handler that is executed for every APIService event. It extends APIServiceStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype APIServiceGeneratingHandler func(obj *v1.APIService, status v1.APIServiceStatus) ([]runtime.Object, v1.APIServiceStatus, error)\n\n// RegisterAPIServiceStatusHandler configures a APIServiceController to execute a APIServiceStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterAPIServiceStatusHandler(ctx context.Context, controller APIServiceController, condition condition.Cond, name string, handler APIServiceStatusHandler) {\n\tstatusHandler := &aPIServiceStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterAPIServiceGeneratingHandler configures a APIServiceController to execute a APIServiceGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterAPIServiceGeneratingHandler(ctx context.Context, controller APIServiceController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler APIServiceGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &aPIServiceGeneratingHandler{\n\t\tAPIServiceGeneratingHandler: handler,\n\t\tapply:                       apply,\n\t\tname:                        name,\n\t\tgvk:                         controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterAPIServiceStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype aPIServiceStatusHandler struct {\n\tclient    APIServiceClient\n\tcondition condition.Cond\n\thandler   APIServiceStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *aPIServiceStatusHandler) sync(key string, obj *v1.APIService) (*v1.APIService, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype aPIServiceGeneratingHandler struct {\n\tAPIServiceGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *aPIServiceGeneratingHandler) Remove(key string, obj *v1.APIService) (*v1.APIService, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1.APIService{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured APIServiceGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *aPIServiceGeneratingHandler) Handle(obj *v1.APIService, status v1.APIServiceStatus) (v1.APIServiceStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.APIServiceGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *aPIServiceGeneratingHandler) isNewResourceVersion(obj *v1.APIService) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *aPIServiceGeneratingHandler) storeResourceVersion(obj *v1.APIService) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apiregistration.k8s.io/v1/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\tv1 \"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1\"\n)\n\nfunc init() {\n\tschemes.Register(v1.AddToScheme)\n}\n\ntype Interface interface {\n\tAPIService() APIServiceController\n}\n\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &version{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\ntype version struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\nfunc (v *version) APIService() APIServiceController {\n\treturn generic.NewNonNamespacedController[*v1.APIService, *v1.APIServiceList](schema.GroupVersionKind{Group: \"apiregistration.k8s.io\", Version: \"v1\", Kind: \"APIService\"}, \"apiservices\", v.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apps/factory.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage apps\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"k8s.io/client-go/rest\"\n)\n\ntype Factory struct {\n\t*generic.Factory\n}\n\nfunc NewFactoryFromConfigOrDie(config *rest.Config) *Factory {\n\tf, err := NewFactoryFromConfig(config)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFactoryFromConfig(config *rest.Config) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, nil)\n}\n\nfunc NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, &FactoryOptions{\n\t\tNamespace: namespace,\n\t})\n}\n\ntype FactoryOptions = generic.FactoryOptions\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tf, err := generic.NewFactoryFromConfigWithOptions(config, opts)\n\treturn &Factory{\n\t\tFactory: f,\n\t}, err\n}\n\nfunc NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory {\n\tf, err := NewFactoryFromConfigWithOptions(config, opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc (c *Factory) Apps() Interface {\n\treturn New(c.ControllerFactory())\n}\n\nfunc (c *Factory) WithAgent(userAgent string) Interface {\n\treturn New(controller.NewSharedControllerFactoryWithAgent(userAgent, c.ControllerFactory()))\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apps/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage apps\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\tv1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/apps/v1\"\n)\n\ntype Interface interface {\n\tV1() v1.Interface\n}\n\ntype group struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n// New returns a new Interface.\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &group{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\nfunc (g *group) V1() v1.Interface {\n\treturn v1.New(g.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apps/v1/daemonset.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\tv1 \"k8s.io/api/apps/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// DaemonSetController interface for managing DaemonSet resources.\ntype DaemonSetController interface {\n\tgeneric.ControllerInterface[*v1.DaemonSet, *v1.DaemonSetList]\n}\n\n// DaemonSetClient interface for managing DaemonSet resources in Kubernetes.\ntype DaemonSetClient interface {\n\tgeneric.ClientInterface[*v1.DaemonSet, *v1.DaemonSetList]\n}\n\n// DaemonSetCache interface for retrieving DaemonSet resources in memory.\ntype DaemonSetCache interface {\n\tgeneric.CacheInterface[*v1.DaemonSet]\n}\n\n// DaemonSetStatusHandler is executed for every added or modified DaemonSet. Should return the new status to be updated\ntype DaemonSetStatusHandler func(obj *v1.DaemonSet, status v1.DaemonSetStatus) (v1.DaemonSetStatus, error)\n\n// DaemonSetGeneratingHandler is the top-level handler that is executed for every DaemonSet event. It extends DaemonSetStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype DaemonSetGeneratingHandler func(obj *v1.DaemonSet, status v1.DaemonSetStatus) ([]runtime.Object, v1.DaemonSetStatus, error)\n\n// RegisterDaemonSetStatusHandler configures a DaemonSetController to execute a DaemonSetStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterDaemonSetStatusHandler(ctx context.Context, controller DaemonSetController, condition condition.Cond, name string, handler DaemonSetStatusHandler) {\n\tstatusHandler := &daemonSetStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterDaemonSetGeneratingHandler configures a DaemonSetController to execute a DaemonSetGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterDaemonSetGeneratingHandler(ctx context.Context, controller DaemonSetController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler DaemonSetGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &daemonSetGeneratingHandler{\n\t\tDaemonSetGeneratingHandler: handler,\n\t\tapply:                      apply,\n\t\tname:                       name,\n\t\tgvk:                        controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterDaemonSetStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype daemonSetStatusHandler struct {\n\tclient    DaemonSetClient\n\tcondition condition.Cond\n\thandler   DaemonSetStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *daemonSetStatusHandler) sync(key string, obj *v1.DaemonSet) (*v1.DaemonSet, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype daemonSetGeneratingHandler struct {\n\tDaemonSetGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *daemonSetGeneratingHandler) Remove(key string, obj *v1.DaemonSet) (*v1.DaemonSet, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1.DaemonSet{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured DaemonSetGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *daemonSetGeneratingHandler) Handle(obj *v1.DaemonSet, status v1.DaemonSetStatus) (v1.DaemonSetStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.DaemonSetGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *daemonSetGeneratingHandler) isNewResourceVersion(obj *v1.DaemonSet) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *daemonSetGeneratingHandler) storeResourceVersion(obj *v1.DaemonSet) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apps/v1/deployment.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\tv1 \"k8s.io/api/apps/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// DeploymentController interface for managing Deployment resources.\ntype DeploymentController interface {\n\tgeneric.ControllerInterface[*v1.Deployment, *v1.DeploymentList]\n}\n\n// DeploymentClient interface for managing Deployment resources in Kubernetes.\ntype DeploymentClient interface {\n\tgeneric.ClientInterface[*v1.Deployment, *v1.DeploymentList]\n}\n\n// DeploymentCache interface for retrieving Deployment resources in memory.\ntype DeploymentCache interface {\n\tgeneric.CacheInterface[*v1.Deployment]\n}\n\n// DeploymentStatusHandler is executed for every added or modified Deployment. Should return the new status to be updated\ntype DeploymentStatusHandler func(obj *v1.Deployment, status v1.DeploymentStatus) (v1.DeploymentStatus, error)\n\n// DeploymentGeneratingHandler is the top-level handler that is executed for every Deployment event. It extends DeploymentStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype DeploymentGeneratingHandler func(obj *v1.Deployment, status v1.DeploymentStatus) ([]runtime.Object, v1.DeploymentStatus, error)\n\n// RegisterDeploymentStatusHandler configures a DeploymentController to execute a DeploymentStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterDeploymentStatusHandler(ctx context.Context, controller DeploymentController, condition condition.Cond, name string, handler DeploymentStatusHandler) {\n\tstatusHandler := &deploymentStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterDeploymentGeneratingHandler configures a DeploymentController to execute a DeploymentGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterDeploymentGeneratingHandler(ctx context.Context, controller DeploymentController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler DeploymentGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &deploymentGeneratingHandler{\n\t\tDeploymentGeneratingHandler: handler,\n\t\tapply:                       apply,\n\t\tname:                        name,\n\t\tgvk:                         controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterDeploymentStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype deploymentStatusHandler struct {\n\tclient    DeploymentClient\n\tcondition condition.Cond\n\thandler   DeploymentStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *deploymentStatusHandler) sync(key string, obj *v1.Deployment) (*v1.Deployment, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype deploymentGeneratingHandler struct {\n\tDeploymentGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *deploymentGeneratingHandler) Remove(key string, obj *v1.Deployment) (*v1.Deployment, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1.Deployment{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured DeploymentGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *deploymentGeneratingHandler) Handle(obj *v1.Deployment, status v1.DeploymentStatus) (v1.DeploymentStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.DeploymentGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *deploymentGeneratingHandler) isNewResourceVersion(obj *v1.Deployment) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *deploymentGeneratingHandler) storeResourceVersion(obj *v1.Deployment) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apps/v1/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\tv1 \"k8s.io/api/apps/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc init() {\n\tschemes.Register(v1.AddToScheme)\n}\n\ntype Interface interface {\n\tDaemonSet() DaemonSetController\n\tDeployment() DeploymentController\n\tStatefulSet() StatefulSetController\n}\n\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &version{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\ntype version struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\nfunc (v *version) DaemonSet() DaemonSetController {\n\treturn generic.NewController[*v1.DaemonSet, *v1.DaemonSetList](schema.GroupVersionKind{Group: \"apps\", Version: \"v1\", Kind: \"DaemonSet\"}, \"daemonsets\", true, v.controllerFactory)\n}\n\nfunc (v *version) Deployment() DeploymentController {\n\treturn generic.NewController[*v1.Deployment, *v1.DeploymentList](schema.GroupVersionKind{Group: \"apps\", Version: \"v1\", Kind: \"Deployment\"}, \"deployments\", true, v.controllerFactory)\n}\n\nfunc (v *version) StatefulSet() StatefulSetController {\n\treturn generic.NewController[*v1.StatefulSet, *v1.StatefulSetList](schema.GroupVersionKind{Group: \"apps\", Version: \"v1\", Kind: \"StatefulSet\"}, \"statefulsets\", true, v.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/apps/v1/statefulset.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\tv1 \"k8s.io/api/apps/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// StatefulSetController interface for managing StatefulSet resources.\ntype StatefulSetController interface {\n\tgeneric.ControllerInterface[*v1.StatefulSet, *v1.StatefulSetList]\n}\n\n// StatefulSetClient interface for managing StatefulSet resources in Kubernetes.\ntype StatefulSetClient interface {\n\tgeneric.ClientInterface[*v1.StatefulSet, *v1.StatefulSetList]\n}\n\n// StatefulSetCache interface for retrieving StatefulSet resources in memory.\ntype StatefulSetCache interface {\n\tgeneric.CacheInterface[*v1.StatefulSet]\n}\n\n// StatefulSetStatusHandler is executed for every added or modified StatefulSet. Should return the new status to be updated\ntype StatefulSetStatusHandler func(obj *v1.StatefulSet, status v1.StatefulSetStatus) (v1.StatefulSetStatus, error)\n\n// StatefulSetGeneratingHandler is the top-level handler that is executed for every StatefulSet event. It extends StatefulSetStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype StatefulSetGeneratingHandler func(obj *v1.StatefulSet, status v1.StatefulSetStatus) ([]runtime.Object, v1.StatefulSetStatus, error)\n\n// RegisterStatefulSetStatusHandler configures a StatefulSetController to execute a StatefulSetStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterStatefulSetStatusHandler(ctx context.Context, controller StatefulSetController, condition condition.Cond, name string, handler StatefulSetStatusHandler) {\n\tstatusHandler := &statefulSetStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterStatefulSetGeneratingHandler configures a StatefulSetController to execute a StatefulSetGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterStatefulSetGeneratingHandler(ctx context.Context, controller StatefulSetController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler StatefulSetGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &statefulSetGeneratingHandler{\n\t\tStatefulSetGeneratingHandler: handler,\n\t\tapply:                        apply,\n\t\tname:                         name,\n\t\tgvk:                          controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterStatefulSetStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype statefulSetStatusHandler struct {\n\tclient    StatefulSetClient\n\tcondition condition.Cond\n\thandler   StatefulSetStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *statefulSetStatusHandler) sync(key string, obj *v1.StatefulSet) (*v1.StatefulSet, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype statefulSetGeneratingHandler struct {\n\tStatefulSetGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *statefulSetGeneratingHandler) Remove(key string, obj *v1.StatefulSet) (*v1.StatefulSet, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1.StatefulSet{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured StatefulSetGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *statefulSetGeneratingHandler) Handle(obj *v1.StatefulSet, status v1.StatefulSetStatus) (v1.StatefulSetStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.StatefulSetGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *statefulSetGeneratingHandler) isNewResourceVersion(obj *v1.StatefulSet) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *statefulSetGeneratingHandler) storeResourceVersion(obj *v1.StatefulSet) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/batch/factory.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage batch\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"k8s.io/client-go/rest\"\n)\n\ntype Factory struct {\n\t*generic.Factory\n}\n\nfunc NewFactoryFromConfigOrDie(config *rest.Config) *Factory {\n\tf, err := NewFactoryFromConfig(config)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFactoryFromConfig(config *rest.Config) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, nil)\n}\n\nfunc NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, &FactoryOptions{\n\t\tNamespace: namespace,\n\t})\n}\n\ntype FactoryOptions = generic.FactoryOptions\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tf, err := generic.NewFactoryFromConfigWithOptions(config, opts)\n\treturn &Factory{\n\t\tFactory: f,\n\t}, err\n}\n\nfunc NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory {\n\tf, err := NewFactoryFromConfigWithOptions(config, opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc (c *Factory) Batch() Interface {\n\treturn New(c.ControllerFactory())\n}\n\nfunc (c *Factory) WithAgent(userAgent string) Interface {\n\treturn New(controller.NewSharedControllerFactoryWithAgent(userAgent, c.ControllerFactory()))\n}\n"
  },
  {
    "path": "pkg/generated/controllers/batch/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage batch\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\tv1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/batch/v1\"\n)\n\ntype Interface interface {\n\tV1() v1.Interface\n}\n\ntype group struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n// New returns a new Interface.\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &group{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\nfunc (g *group) V1() v1.Interface {\n\treturn v1.New(g.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/batch/v1/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\tv1 \"k8s.io/api/batch/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc init() {\n\tschemes.Register(v1.AddToScheme)\n}\n\ntype Interface interface {\n\tJob() JobController\n}\n\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &version{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\ntype version struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\nfunc (v *version) Job() JobController {\n\treturn generic.NewController[*v1.Job, *v1.JobList](schema.GroupVersionKind{Group: \"batch\", Version: \"v1\", Kind: \"Job\"}, \"jobs\", true, v.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/batch/v1/job.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\tv1 \"k8s.io/api/batch/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// JobController interface for managing Job resources.\ntype JobController interface {\n\tgeneric.ControllerInterface[*v1.Job, *v1.JobList]\n}\n\n// JobClient interface for managing Job resources in Kubernetes.\ntype JobClient interface {\n\tgeneric.ClientInterface[*v1.Job, *v1.JobList]\n}\n\n// JobCache interface for retrieving Job resources in memory.\ntype JobCache interface {\n\tgeneric.CacheInterface[*v1.Job]\n}\n\n// JobStatusHandler is executed for every added or modified Job. Should return the new status to be updated\ntype JobStatusHandler func(obj *v1.Job, status v1.JobStatus) (v1.JobStatus, error)\n\n// JobGeneratingHandler is the top-level handler that is executed for every Job event. It extends JobStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype JobGeneratingHandler func(obj *v1.Job, status v1.JobStatus) ([]runtime.Object, v1.JobStatus, error)\n\n// RegisterJobStatusHandler configures a JobController to execute a JobStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterJobStatusHandler(ctx context.Context, controller JobController, condition condition.Cond, name string, handler JobStatusHandler) {\n\tstatusHandler := &jobStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterJobGeneratingHandler configures a JobController to execute a JobGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterJobGeneratingHandler(ctx context.Context, controller JobController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler JobGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &jobGeneratingHandler{\n\t\tJobGeneratingHandler: handler,\n\t\tapply:                apply,\n\t\tname:                 name,\n\t\tgvk:                  controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterJobStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype jobStatusHandler struct {\n\tclient    JobClient\n\tcondition condition.Cond\n\thandler   JobStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *jobStatusHandler) sync(key string, obj *v1.Job) (*v1.Job, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype jobGeneratingHandler struct {\n\tJobGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *jobGeneratingHandler) Remove(key string, obj *v1.Job) (*v1.Job, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1.Job{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured JobGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *jobGeneratingHandler) Handle(obj *v1.Job, status v1.JobStatus) (v1.JobStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.JobGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *jobGeneratingHandler) isNewResourceVersion(obj *v1.Job) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *jobGeneratingHandler) storeResourceVersion(obj *v1.Job) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/coordination.k8s.io/factory.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage coordination\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"k8s.io/client-go/rest\"\n)\n\ntype Factory struct {\n\t*generic.Factory\n}\n\nfunc NewFactoryFromConfigOrDie(config *rest.Config) *Factory {\n\tf, err := NewFactoryFromConfig(config)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFactoryFromConfig(config *rest.Config) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, nil)\n}\n\nfunc NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, &FactoryOptions{\n\t\tNamespace: namespace,\n\t})\n}\n\ntype FactoryOptions = generic.FactoryOptions\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tf, err := generic.NewFactoryFromConfigWithOptions(config, opts)\n\treturn &Factory{\n\t\tFactory: f,\n\t}, err\n}\n\nfunc NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory {\n\tf, err := NewFactoryFromConfigWithOptions(config, opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc (c *Factory) Coordination() Interface {\n\treturn New(c.ControllerFactory())\n}\n\nfunc (c *Factory) WithAgent(userAgent string) Interface {\n\treturn New(controller.NewSharedControllerFactoryWithAgent(userAgent, c.ControllerFactory()))\n}\n"
  },
  {
    "path": "pkg/generated/controllers/coordination.k8s.io/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage coordination\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\tv1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/coordination.k8s.io/v1\"\n)\n\ntype Interface interface {\n\tV1() v1.Interface\n}\n\ntype group struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n// New returns a new Interface.\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &group{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\nfunc (g *group) V1() v1.Interface {\n\treturn v1.New(g.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/coordination.k8s.io/v1/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\tv1 \"k8s.io/api/coordination/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc init() {\n\tschemes.Register(v1.AddToScheme)\n}\n\ntype Interface interface {\n\tLease() LeaseController\n}\n\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &version{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\ntype version struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\nfunc (v *version) Lease() LeaseController {\n\treturn generic.NewController[*v1.Lease, *v1.LeaseList](schema.GroupVersionKind{Group: \"coordination.k8s.io\", Version: \"v1\", Kind: \"Lease\"}, \"leases\", true, v.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/coordination.k8s.io/v1/lease.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/coordination/v1\"\n)\n\n// LeaseController interface for managing Lease resources.\ntype LeaseController interface {\n\tgeneric.ControllerInterface[*v1.Lease, *v1.LeaseList]\n}\n\n// LeaseClient interface for managing Lease resources in Kubernetes.\ntype LeaseClient interface {\n\tgeneric.ClientInterface[*v1.Lease, *v1.LeaseList]\n}\n\n// LeaseCache interface for retrieving Lease resources in memory.\ntype LeaseCache interface {\n\tgeneric.CacheInterface[*v1.Lease]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/factory.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage core\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"k8s.io/client-go/rest\"\n)\n\ntype Factory struct {\n\t*generic.Factory\n}\n\nfunc NewFactoryFromConfigOrDie(config *rest.Config) *Factory {\n\tf, err := NewFactoryFromConfig(config)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFactoryFromConfig(config *rest.Config) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, nil)\n}\n\nfunc NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, &FactoryOptions{\n\t\tNamespace: namespace,\n\t})\n}\n\ntype FactoryOptions = generic.FactoryOptions\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tf, err := generic.NewFactoryFromConfigWithOptions(config, opts)\n\treturn &Factory{\n\t\tFactory: f,\n\t}, err\n}\n\nfunc NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory {\n\tf, err := NewFactoryFromConfigWithOptions(config, opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc (c *Factory) Core() Interface {\n\treturn New(c.ControllerFactory())\n}\n\nfunc (c *Factory) WithAgent(userAgent string) Interface {\n\treturn New(controller.NewSharedControllerFactoryWithAgent(userAgent, c.ControllerFactory()))\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage core\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\tv1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/core/v1\"\n)\n\ntype Interface interface {\n\tV1() v1.Interface\n}\n\ntype group struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n// New returns a new Interface.\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &group{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\nfunc (g *group) V1() v1.Interface {\n\treturn v1.New(g.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/configmap.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/core/v1\"\n)\n\n// ConfigMapController interface for managing ConfigMap resources.\ntype ConfigMapController interface {\n\tgeneric.ControllerInterface[*v1.ConfigMap, *v1.ConfigMapList]\n}\n\n// ConfigMapClient interface for managing ConfigMap resources in Kubernetes.\ntype ConfigMapClient interface {\n\tgeneric.ClientInterface[*v1.ConfigMap, *v1.ConfigMapList]\n}\n\n// ConfigMapCache interface for retrieving ConfigMap resources in memory.\ntype ConfigMapCache interface {\n\tgeneric.CacheInterface[*v1.ConfigMap]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/endpoints.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/core/v1\"\n)\n\n// EndpointsController interface for managing Endpoints resources.\ntype EndpointsController interface {\n\tgeneric.ControllerInterface[*v1.Endpoints, *v1.EndpointsList]\n}\n\n// EndpointsClient interface for managing Endpoints resources in Kubernetes.\ntype EndpointsClient interface {\n\tgeneric.ClientInterface[*v1.Endpoints, *v1.EndpointsList]\n}\n\n// EndpointsCache interface for retrieving Endpoints resources in memory.\ntype EndpointsCache interface {\n\tgeneric.CacheInterface[*v1.Endpoints]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/event.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/core/v1\"\n)\n\n// EventController interface for managing Event resources.\ntype EventController interface {\n\tgeneric.ControllerInterface[*v1.Event, *v1.EventList]\n}\n\n// EventClient interface for managing Event resources in Kubernetes.\ntype EventClient interface {\n\tgeneric.ClientInterface[*v1.Event, *v1.EventList]\n}\n\n// EventCache interface for retrieving Event resources in memory.\ntype EventCache interface {\n\tgeneric.CacheInterface[*v1.Event]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\tv1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc init() {\n\tschemes.Register(v1.AddToScheme)\n}\n\ntype Interface interface {\n\tConfigMap() ConfigMapController\n\tEndpoints() EndpointsController\n\tEvent() EventController\n\tLimitRange() LimitRangeController\n\tNamespace() NamespaceController\n\tNode() NodeController\n\tPersistentVolume() PersistentVolumeController\n\tPersistentVolumeClaim() PersistentVolumeClaimController\n\tPod() PodController\n\tResourceQuota() ResourceQuotaController\n\tSecret() SecretController\n\tService() ServiceController\n\tServiceAccount() ServiceAccountController\n}\n\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &version{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\ntype version struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\nfunc (v *version) ConfigMap() ConfigMapController {\n\treturn generic.NewController[*v1.ConfigMap, *v1.ConfigMapList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"ConfigMap\"}, \"configmaps\", true, v.controllerFactory)\n}\n\nfunc (v *version) Endpoints() EndpointsController {\n\treturn generic.NewController[*v1.Endpoints, *v1.EndpointsList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"Endpoints\"}, \"endpoints\", true, v.controllerFactory)\n}\n\nfunc (v *version) Event() EventController {\n\treturn generic.NewController[*v1.Event, *v1.EventList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"Event\"}, \"events\", true, v.controllerFactory)\n}\n\nfunc (v *version) LimitRange() LimitRangeController {\n\treturn generic.NewController[*v1.LimitRange, *v1.LimitRangeList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"LimitRange\"}, \"limitranges\", true, v.controllerFactory)\n}\n\nfunc (v *version) Namespace() NamespaceController {\n\treturn generic.NewNonNamespacedController[*v1.Namespace, *v1.NamespaceList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"Namespace\"}, \"namespaces\", v.controllerFactory)\n}\n\nfunc (v *version) Node() NodeController {\n\treturn generic.NewNonNamespacedController[*v1.Node, *v1.NodeList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"Node\"}, \"nodes\", v.controllerFactory)\n}\n\nfunc (v *version) PersistentVolume() PersistentVolumeController {\n\treturn generic.NewNonNamespacedController[*v1.PersistentVolume, *v1.PersistentVolumeList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"PersistentVolume\"}, \"persistentvolumes\", v.controllerFactory)\n}\n\nfunc (v *version) PersistentVolumeClaim() PersistentVolumeClaimController {\n\treturn generic.NewController[*v1.PersistentVolumeClaim, *v1.PersistentVolumeClaimList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"PersistentVolumeClaim\"}, \"persistentvolumeclaims\", true, v.controllerFactory)\n}\n\nfunc (v *version) Pod() PodController {\n\treturn generic.NewController[*v1.Pod, *v1.PodList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"Pod\"}, \"pods\", true, v.controllerFactory)\n}\n\nfunc (v *version) ResourceQuota() ResourceQuotaController {\n\treturn generic.NewController[*v1.ResourceQuota, *v1.ResourceQuotaList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"ResourceQuota\"}, \"resourcequotas\", true, v.controllerFactory)\n}\n\nfunc (v *version) Secret() SecretController {\n\treturn generic.NewController[*v1.Secret, *v1.SecretList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"Secret\"}, \"secrets\", true, v.controllerFactory)\n}\n\nfunc (v *version) Service() ServiceController {\n\treturn generic.NewController[*v1.Service, *v1.ServiceList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"Service\"}, \"services\", true, v.controllerFactory)\n}\n\nfunc (v *version) ServiceAccount() ServiceAccountController {\n\treturn generic.NewController[*v1.ServiceAccount, *v1.ServiceAccountList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"ServiceAccount\"}, \"serviceaccounts\", true, v.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/limitrange.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/core/v1\"\n)\n\n// LimitRangeController interface for managing LimitRange resources.\ntype LimitRangeController interface {\n\tgeneric.ControllerInterface[*v1.LimitRange, *v1.LimitRangeList]\n}\n\n// LimitRangeClient interface for managing LimitRange resources in Kubernetes.\ntype LimitRangeClient interface {\n\tgeneric.ClientInterface[*v1.LimitRange, *v1.LimitRangeList]\n}\n\n// LimitRangeCache interface for retrieving LimitRange resources in memory.\ntype LimitRangeCache interface {\n\tgeneric.CacheInterface[*v1.LimitRange]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/namespace.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\tv1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// NamespaceController interface for managing Namespace resources.\ntype NamespaceController interface {\n\tgeneric.NonNamespacedControllerInterface[*v1.Namespace, *v1.NamespaceList]\n}\n\n// NamespaceClient interface for managing Namespace resources in Kubernetes.\ntype NamespaceClient interface {\n\tgeneric.NonNamespacedClientInterface[*v1.Namespace, *v1.NamespaceList]\n}\n\n// NamespaceCache interface for retrieving Namespace resources in memory.\ntype NamespaceCache interface {\n\tgeneric.NonNamespacedCacheInterface[*v1.Namespace]\n}\n\n// NamespaceStatusHandler is executed for every added or modified Namespace. Should return the new status to be updated\ntype NamespaceStatusHandler func(obj *v1.Namespace, status v1.NamespaceStatus) (v1.NamespaceStatus, error)\n\n// NamespaceGeneratingHandler is the top-level handler that is executed for every Namespace event. It extends NamespaceStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype NamespaceGeneratingHandler func(obj *v1.Namespace, status v1.NamespaceStatus) ([]runtime.Object, v1.NamespaceStatus, error)\n\n// RegisterNamespaceStatusHandler configures a NamespaceController to execute a NamespaceStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterNamespaceStatusHandler(ctx context.Context, controller NamespaceController, condition condition.Cond, name string, handler NamespaceStatusHandler) {\n\tstatusHandler := &namespaceStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterNamespaceGeneratingHandler configures a NamespaceController to execute a NamespaceGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterNamespaceGeneratingHandler(ctx context.Context, controller NamespaceController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler NamespaceGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &namespaceGeneratingHandler{\n\t\tNamespaceGeneratingHandler: handler,\n\t\tapply:                      apply,\n\t\tname:                       name,\n\t\tgvk:                        controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterNamespaceStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype namespaceStatusHandler struct {\n\tclient    NamespaceClient\n\tcondition condition.Cond\n\thandler   NamespaceStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *namespaceStatusHandler) sync(key string, obj *v1.Namespace) (*v1.Namespace, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype namespaceGeneratingHandler struct {\n\tNamespaceGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *namespaceGeneratingHandler) Remove(key string, obj *v1.Namespace) (*v1.Namespace, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1.Namespace{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured NamespaceGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *namespaceGeneratingHandler) Handle(obj *v1.Namespace, status v1.NamespaceStatus) (v1.NamespaceStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.NamespaceGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *namespaceGeneratingHandler) isNewResourceVersion(obj *v1.Namespace) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *namespaceGeneratingHandler) storeResourceVersion(obj *v1.Namespace) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/node.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\tv1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// NodeController interface for managing Node resources.\ntype NodeController interface {\n\tgeneric.NonNamespacedControllerInterface[*v1.Node, *v1.NodeList]\n}\n\n// NodeClient interface for managing Node resources in Kubernetes.\ntype NodeClient interface {\n\tgeneric.NonNamespacedClientInterface[*v1.Node, *v1.NodeList]\n}\n\n// NodeCache interface for retrieving Node resources in memory.\ntype NodeCache interface {\n\tgeneric.NonNamespacedCacheInterface[*v1.Node]\n}\n\n// NodeStatusHandler is executed for every added or modified Node. Should return the new status to be updated\ntype NodeStatusHandler func(obj *v1.Node, status v1.NodeStatus) (v1.NodeStatus, error)\n\n// NodeGeneratingHandler is the top-level handler that is executed for every Node event. It extends NodeStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype NodeGeneratingHandler func(obj *v1.Node, status v1.NodeStatus) ([]runtime.Object, v1.NodeStatus, error)\n\n// RegisterNodeStatusHandler configures a NodeController to execute a NodeStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterNodeStatusHandler(ctx context.Context, controller NodeController, condition condition.Cond, name string, handler NodeStatusHandler) {\n\tstatusHandler := &nodeStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterNodeGeneratingHandler configures a NodeController to execute a NodeGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterNodeGeneratingHandler(ctx context.Context, controller NodeController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler NodeGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &nodeGeneratingHandler{\n\t\tNodeGeneratingHandler: handler,\n\t\tapply:                 apply,\n\t\tname:                  name,\n\t\tgvk:                   controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterNodeStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype nodeStatusHandler struct {\n\tclient    NodeClient\n\tcondition condition.Cond\n\thandler   NodeStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *nodeStatusHandler) sync(key string, obj *v1.Node) (*v1.Node, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype nodeGeneratingHandler struct {\n\tNodeGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *nodeGeneratingHandler) Remove(key string, obj *v1.Node) (*v1.Node, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1.Node{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured NodeGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *nodeGeneratingHandler) Handle(obj *v1.Node, status v1.NodeStatus) (v1.NodeStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.NodeGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *nodeGeneratingHandler) isNewResourceVersion(obj *v1.Node) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *nodeGeneratingHandler) storeResourceVersion(obj *v1.Node) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/persistentvolume.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\tv1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// PersistentVolumeController interface for managing PersistentVolume resources.\ntype PersistentVolumeController interface {\n\tgeneric.NonNamespacedControllerInterface[*v1.PersistentVolume, *v1.PersistentVolumeList]\n}\n\n// PersistentVolumeClient interface for managing PersistentVolume resources in Kubernetes.\ntype PersistentVolumeClient interface {\n\tgeneric.NonNamespacedClientInterface[*v1.PersistentVolume, *v1.PersistentVolumeList]\n}\n\n// PersistentVolumeCache interface for retrieving PersistentVolume resources in memory.\ntype PersistentVolumeCache interface {\n\tgeneric.NonNamespacedCacheInterface[*v1.PersistentVolume]\n}\n\n// PersistentVolumeStatusHandler is executed for every added or modified PersistentVolume. Should return the new status to be updated\ntype PersistentVolumeStatusHandler func(obj *v1.PersistentVolume, status v1.PersistentVolumeStatus) (v1.PersistentVolumeStatus, error)\n\n// PersistentVolumeGeneratingHandler is the top-level handler that is executed for every PersistentVolume event. It extends PersistentVolumeStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype PersistentVolumeGeneratingHandler func(obj *v1.PersistentVolume, status v1.PersistentVolumeStatus) ([]runtime.Object, v1.PersistentVolumeStatus, error)\n\n// RegisterPersistentVolumeStatusHandler configures a PersistentVolumeController to execute a PersistentVolumeStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterPersistentVolumeStatusHandler(ctx context.Context, controller PersistentVolumeController, condition condition.Cond, name string, handler PersistentVolumeStatusHandler) {\n\tstatusHandler := &persistentVolumeStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterPersistentVolumeGeneratingHandler configures a PersistentVolumeController to execute a PersistentVolumeGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterPersistentVolumeGeneratingHandler(ctx context.Context, controller PersistentVolumeController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler PersistentVolumeGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &persistentVolumeGeneratingHandler{\n\t\tPersistentVolumeGeneratingHandler: handler,\n\t\tapply:                             apply,\n\t\tname:                              name,\n\t\tgvk:                               controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterPersistentVolumeStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype persistentVolumeStatusHandler struct {\n\tclient    PersistentVolumeClient\n\tcondition condition.Cond\n\thandler   PersistentVolumeStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *persistentVolumeStatusHandler) sync(key string, obj *v1.PersistentVolume) (*v1.PersistentVolume, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype persistentVolumeGeneratingHandler struct {\n\tPersistentVolumeGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *persistentVolumeGeneratingHandler) Remove(key string, obj *v1.PersistentVolume) (*v1.PersistentVolume, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1.PersistentVolume{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured PersistentVolumeGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *persistentVolumeGeneratingHandler) Handle(obj *v1.PersistentVolume, status v1.PersistentVolumeStatus) (v1.PersistentVolumeStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.PersistentVolumeGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *persistentVolumeGeneratingHandler) isNewResourceVersion(obj *v1.PersistentVolume) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *persistentVolumeGeneratingHandler) storeResourceVersion(obj *v1.PersistentVolume) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/persistentvolumeclaim.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\tv1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// PersistentVolumeClaimController interface for managing PersistentVolumeClaim resources.\ntype PersistentVolumeClaimController interface {\n\tgeneric.ControllerInterface[*v1.PersistentVolumeClaim, *v1.PersistentVolumeClaimList]\n}\n\n// PersistentVolumeClaimClient interface for managing PersistentVolumeClaim resources in Kubernetes.\ntype PersistentVolumeClaimClient interface {\n\tgeneric.ClientInterface[*v1.PersistentVolumeClaim, *v1.PersistentVolumeClaimList]\n}\n\n// PersistentVolumeClaimCache interface for retrieving PersistentVolumeClaim resources in memory.\ntype PersistentVolumeClaimCache interface {\n\tgeneric.CacheInterface[*v1.PersistentVolumeClaim]\n}\n\n// PersistentVolumeClaimStatusHandler is executed for every added or modified PersistentVolumeClaim. Should return the new status to be updated\ntype PersistentVolumeClaimStatusHandler func(obj *v1.PersistentVolumeClaim, status v1.PersistentVolumeClaimStatus) (v1.PersistentVolumeClaimStatus, error)\n\n// PersistentVolumeClaimGeneratingHandler is the top-level handler that is executed for every PersistentVolumeClaim event. It extends PersistentVolumeClaimStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype PersistentVolumeClaimGeneratingHandler func(obj *v1.PersistentVolumeClaim, status v1.PersistentVolumeClaimStatus) ([]runtime.Object, v1.PersistentVolumeClaimStatus, error)\n\n// RegisterPersistentVolumeClaimStatusHandler configures a PersistentVolumeClaimController to execute a PersistentVolumeClaimStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterPersistentVolumeClaimStatusHandler(ctx context.Context, controller PersistentVolumeClaimController, condition condition.Cond, name string, handler PersistentVolumeClaimStatusHandler) {\n\tstatusHandler := &persistentVolumeClaimStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterPersistentVolumeClaimGeneratingHandler configures a PersistentVolumeClaimController to execute a PersistentVolumeClaimGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterPersistentVolumeClaimGeneratingHandler(ctx context.Context, controller PersistentVolumeClaimController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler PersistentVolumeClaimGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &persistentVolumeClaimGeneratingHandler{\n\t\tPersistentVolumeClaimGeneratingHandler: handler,\n\t\tapply:                                  apply,\n\t\tname:                                   name,\n\t\tgvk:                                    controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterPersistentVolumeClaimStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype persistentVolumeClaimStatusHandler struct {\n\tclient    PersistentVolumeClaimClient\n\tcondition condition.Cond\n\thandler   PersistentVolumeClaimStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *persistentVolumeClaimStatusHandler) sync(key string, obj *v1.PersistentVolumeClaim) (*v1.PersistentVolumeClaim, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype persistentVolumeClaimGeneratingHandler struct {\n\tPersistentVolumeClaimGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *persistentVolumeClaimGeneratingHandler) Remove(key string, obj *v1.PersistentVolumeClaim) (*v1.PersistentVolumeClaim, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1.PersistentVolumeClaim{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured PersistentVolumeClaimGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *persistentVolumeClaimGeneratingHandler) Handle(obj *v1.PersistentVolumeClaim, status v1.PersistentVolumeClaimStatus) (v1.PersistentVolumeClaimStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.PersistentVolumeClaimGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *persistentVolumeClaimGeneratingHandler) isNewResourceVersion(obj *v1.PersistentVolumeClaim) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *persistentVolumeClaimGeneratingHandler) storeResourceVersion(obj *v1.PersistentVolumeClaim) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/pod.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\tv1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// PodController interface for managing Pod resources.\ntype PodController interface {\n\tgeneric.ControllerInterface[*v1.Pod, *v1.PodList]\n}\n\n// PodClient interface for managing Pod resources in Kubernetes.\ntype PodClient interface {\n\tgeneric.ClientInterface[*v1.Pod, *v1.PodList]\n}\n\n// PodCache interface for retrieving Pod resources in memory.\ntype PodCache interface {\n\tgeneric.CacheInterface[*v1.Pod]\n}\n\n// PodStatusHandler is executed for every added or modified Pod. Should return the new status to be updated\ntype PodStatusHandler func(obj *v1.Pod, status v1.PodStatus) (v1.PodStatus, error)\n\n// PodGeneratingHandler is the top-level handler that is executed for every Pod event. It extends PodStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype PodGeneratingHandler func(obj *v1.Pod, status v1.PodStatus) ([]runtime.Object, v1.PodStatus, error)\n\n// RegisterPodStatusHandler configures a PodController to execute a PodStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterPodStatusHandler(ctx context.Context, controller PodController, condition condition.Cond, name string, handler PodStatusHandler) {\n\tstatusHandler := &podStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterPodGeneratingHandler configures a PodController to execute a PodGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterPodGeneratingHandler(ctx context.Context, controller PodController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler PodGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &podGeneratingHandler{\n\t\tPodGeneratingHandler: handler,\n\t\tapply:                apply,\n\t\tname:                 name,\n\t\tgvk:                  controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterPodStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype podStatusHandler struct {\n\tclient    PodClient\n\tcondition condition.Cond\n\thandler   PodStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *podStatusHandler) sync(key string, obj *v1.Pod) (*v1.Pod, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype podGeneratingHandler struct {\n\tPodGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *podGeneratingHandler) Remove(key string, obj *v1.Pod) (*v1.Pod, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1.Pod{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured PodGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *podGeneratingHandler) Handle(obj *v1.Pod, status v1.PodStatus) (v1.PodStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.PodGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *podGeneratingHandler) isNewResourceVersion(obj *v1.Pod) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *podGeneratingHandler) storeResourceVersion(obj *v1.Pod) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/resourcequota.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\tv1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// ResourceQuotaController interface for managing ResourceQuota resources.\ntype ResourceQuotaController interface {\n\tgeneric.ControllerInterface[*v1.ResourceQuota, *v1.ResourceQuotaList]\n}\n\n// ResourceQuotaClient interface for managing ResourceQuota resources in Kubernetes.\ntype ResourceQuotaClient interface {\n\tgeneric.ClientInterface[*v1.ResourceQuota, *v1.ResourceQuotaList]\n}\n\n// ResourceQuotaCache interface for retrieving ResourceQuota resources in memory.\ntype ResourceQuotaCache interface {\n\tgeneric.CacheInterface[*v1.ResourceQuota]\n}\n\n// ResourceQuotaStatusHandler is executed for every added or modified ResourceQuota. Should return the new status to be updated\ntype ResourceQuotaStatusHandler func(obj *v1.ResourceQuota, status v1.ResourceQuotaStatus) (v1.ResourceQuotaStatus, error)\n\n// ResourceQuotaGeneratingHandler is the top-level handler that is executed for every ResourceQuota event. It extends ResourceQuotaStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype ResourceQuotaGeneratingHandler func(obj *v1.ResourceQuota, status v1.ResourceQuotaStatus) ([]runtime.Object, v1.ResourceQuotaStatus, error)\n\n// RegisterResourceQuotaStatusHandler configures a ResourceQuotaController to execute a ResourceQuotaStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterResourceQuotaStatusHandler(ctx context.Context, controller ResourceQuotaController, condition condition.Cond, name string, handler ResourceQuotaStatusHandler) {\n\tstatusHandler := &resourceQuotaStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterResourceQuotaGeneratingHandler configures a ResourceQuotaController to execute a ResourceQuotaGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterResourceQuotaGeneratingHandler(ctx context.Context, controller ResourceQuotaController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler ResourceQuotaGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &resourceQuotaGeneratingHandler{\n\t\tResourceQuotaGeneratingHandler: handler,\n\t\tapply:                          apply,\n\t\tname:                           name,\n\t\tgvk:                            controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterResourceQuotaStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype resourceQuotaStatusHandler struct {\n\tclient    ResourceQuotaClient\n\tcondition condition.Cond\n\thandler   ResourceQuotaStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *resourceQuotaStatusHandler) sync(key string, obj *v1.ResourceQuota) (*v1.ResourceQuota, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype resourceQuotaGeneratingHandler struct {\n\tResourceQuotaGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *resourceQuotaGeneratingHandler) Remove(key string, obj *v1.ResourceQuota) (*v1.ResourceQuota, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1.ResourceQuota{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured ResourceQuotaGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *resourceQuotaGeneratingHandler) Handle(obj *v1.ResourceQuota, status v1.ResourceQuotaStatus) (v1.ResourceQuotaStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.ResourceQuotaGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *resourceQuotaGeneratingHandler) isNewResourceVersion(obj *v1.ResourceQuota) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *resourceQuotaGeneratingHandler) storeResourceVersion(obj *v1.ResourceQuota) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/secret.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/core/v1\"\n)\n\n// SecretController interface for managing Secret resources.\ntype SecretController interface {\n\tgeneric.ControllerInterface[*v1.Secret, *v1.SecretList]\n}\n\n// SecretClient interface for managing Secret resources in Kubernetes.\ntype SecretClient interface {\n\tgeneric.ClientInterface[*v1.Secret, *v1.SecretList]\n}\n\n// SecretCache interface for retrieving Secret resources in memory.\ntype SecretCache interface {\n\tgeneric.CacheInterface[*v1.Secret]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/service.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\tv1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// ServiceController interface for managing Service resources.\ntype ServiceController interface {\n\tgeneric.ControllerInterface[*v1.Service, *v1.ServiceList]\n}\n\n// ServiceClient interface for managing Service resources in Kubernetes.\ntype ServiceClient interface {\n\tgeneric.ClientInterface[*v1.Service, *v1.ServiceList]\n}\n\n// ServiceCache interface for retrieving Service resources in memory.\ntype ServiceCache interface {\n\tgeneric.CacheInterface[*v1.Service]\n}\n\n// ServiceStatusHandler is executed for every added or modified Service. Should return the new status to be updated\ntype ServiceStatusHandler func(obj *v1.Service, status v1.ServiceStatus) (v1.ServiceStatus, error)\n\n// ServiceGeneratingHandler is the top-level handler that is executed for every Service event. It extends ServiceStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype ServiceGeneratingHandler func(obj *v1.Service, status v1.ServiceStatus) ([]runtime.Object, v1.ServiceStatus, error)\n\n// RegisterServiceStatusHandler configures a ServiceController to execute a ServiceStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterServiceStatusHandler(ctx context.Context, controller ServiceController, condition condition.Cond, name string, handler ServiceStatusHandler) {\n\tstatusHandler := &serviceStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterServiceGeneratingHandler configures a ServiceController to execute a ServiceGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterServiceGeneratingHandler(ctx context.Context, controller ServiceController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler ServiceGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &serviceGeneratingHandler{\n\t\tServiceGeneratingHandler: handler,\n\t\tapply:                    apply,\n\t\tname:                     name,\n\t\tgvk:                      controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterServiceStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype serviceStatusHandler struct {\n\tclient    ServiceClient\n\tcondition condition.Cond\n\thandler   ServiceStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *serviceStatusHandler) sync(key string, obj *v1.Service) (*v1.Service, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype serviceGeneratingHandler struct {\n\tServiceGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *serviceGeneratingHandler) Remove(key string, obj *v1.Service) (*v1.Service, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1.Service{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured ServiceGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *serviceGeneratingHandler) Handle(obj *v1.Service, status v1.ServiceStatus) (v1.ServiceStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.ServiceGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *serviceGeneratingHandler) isNewResourceVersion(obj *v1.Service) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *serviceGeneratingHandler) storeResourceVersion(obj *v1.Service) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/core/v1/serviceaccount.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/core/v1\"\n)\n\n// ServiceAccountController interface for managing ServiceAccount resources.\ntype ServiceAccountController interface {\n\tgeneric.ControllerInterface[*v1.ServiceAccount, *v1.ServiceAccountList]\n}\n\n// ServiceAccountClient interface for managing ServiceAccount resources in Kubernetes.\ntype ServiceAccountClient interface {\n\tgeneric.ClientInterface[*v1.ServiceAccount, *v1.ServiceAccountList]\n}\n\n// ServiceAccountCache interface for retrieving ServiceAccount resources in memory.\ntype ServiceAccountCache interface {\n\tgeneric.CacheInterface[*v1.ServiceAccount]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/discovery/factory.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage discovery\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"k8s.io/client-go/rest\"\n)\n\ntype Factory struct {\n\t*generic.Factory\n}\n\nfunc NewFactoryFromConfigOrDie(config *rest.Config) *Factory {\n\tf, err := NewFactoryFromConfig(config)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFactoryFromConfig(config *rest.Config) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, nil)\n}\n\nfunc NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, &FactoryOptions{\n\t\tNamespace: namespace,\n\t})\n}\n\ntype FactoryOptions = generic.FactoryOptions\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tf, err := generic.NewFactoryFromConfigWithOptions(config, opts)\n\treturn &Factory{\n\t\tFactory: f,\n\t}, err\n}\n\nfunc NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory {\n\tf, err := NewFactoryFromConfigWithOptions(config, opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc (c *Factory) Discovery() Interface {\n\treturn New(c.ControllerFactory())\n}\n\nfunc (c *Factory) WithAgent(userAgent string) Interface {\n\treturn New(controller.NewSharedControllerFactoryWithAgent(userAgent, c.ControllerFactory()))\n}\n"
  },
  {
    "path": "pkg/generated/controllers/discovery/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage discovery\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\tv1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/discovery/v1\"\n)\n\ntype Interface interface {\n\tV1() v1.Interface\n}\n\ntype group struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n// New returns a new Interface.\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &group{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\nfunc (g *group) V1() v1.Interface {\n\treturn v1.New(g.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/discovery/v1/endpointslice.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/discovery/v1\"\n)\n\n// EndpointSliceController interface for managing EndpointSlice resources.\ntype EndpointSliceController interface {\n\tgeneric.ControllerInterface[*v1.EndpointSlice, *v1.EndpointSliceList]\n}\n\n// EndpointSliceClient interface for managing EndpointSlice resources in Kubernetes.\ntype EndpointSliceClient interface {\n\tgeneric.ClientInterface[*v1.EndpointSlice, *v1.EndpointSliceList]\n}\n\n// EndpointSliceCache interface for retrieving EndpointSlice resources in memory.\ntype EndpointSliceCache interface {\n\tgeneric.CacheInterface[*v1.EndpointSlice]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/discovery/v1/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\tv1 \"k8s.io/api/discovery/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc init() {\n\tschemes.Register(v1.AddToScheme)\n}\n\ntype Interface interface {\n\tEndpointSlice() EndpointSliceController\n}\n\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &version{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\ntype version struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\nfunc (v *version) EndpointSlice() EndpointSliceController {\n\treturn generic.NewController[*v1.EndpointSlice, *v1.EndpointSliceList](schema.GroupVersionKind{Group: \"discovery.k8s.io\", Version: \"v1\", Kind: \"EndpointSlice\"}, \"endpointslices\", true, v.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/extensions/factory.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage extensions\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"k8s.io/client-go/rest\"\n)\n\ntype Factory struct {\n\t*generic.Factory\n}\n\nfunc NewFactoryFromConfigOrDie(config *rest.Config) *Factory {\n\tf, err := NewFactoryFromConfig(config)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFactoryFromConfig(config *rest.Config) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, nil)\n}\n\nfunc NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, &FactoryOptions{\n\t\tNamespace: namespace,\n\t})\n}\n\ntype FactoryOptions = generic.FactoryOptions\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tf, err := generic.NewFactoryFromConfigWithOptions(config, opts)\n\treturn &Factory{\n\t\tFactory: f,\n\t}, err\n}\n\nfunc NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory {\n\tf, err := NewFactoryFromConfigWithOptions(config, opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc (c *Factory) Extensions() Interface {\n\treturn New(c.ControllerFactory())\n}\n\nfunc (c *Factory) WithAgent(userAgent string) Interface {\n\treturn New(controller.NewSharedControllerFactoryWithAgent(userAgent, c.ControllerFactory()))\n}\n"
  },
  {
    "path": "pkg/generated/controllers/extensions/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage extensions\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\tv1beta1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/extensions/v1beta1\"\n)\n\ntype Interface interface {\n\tV1beta1() v1beta1.Interface\n}\n\ntype group struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n// New returns a new Interface.\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &group{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\nfunc (g *group) V1beta1() v1beta1.Interface {\n\treturn v1beta1.New(g.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/extensions/v1beta1/ingress.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1beta1\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\t\"github.com/rancher/wrangler/v3/pkg/condition\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\tv1beta1 \"k8s.io/api/extensions/v1beta1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// IngressController interface for managing Ingress resources.\ntype IngressController interface {\n\tgeneric.ControllerInterface[*v1beta1.Ingress, *v1beta1.IngressList]\n}\n\n// IngressClient interface for managing Ingress resources in Kubernetes.\ntype IngressClient interface {\n\tgeneric.ClientInterface[*v1beta1.Ingress, *v1beta1.IngressList]\n}\n\n// IngressCache interface for retrieving Ingress resources in memory.\ntype IngressCache interface {\n\tgeneric.CacheInterface[*v1beta1.Ingress]\n}\n\n// IngressStatusHandler is executed for every added or modified Ingress. Should return the new status to be updated\ntype IngressStatusHandler func(obj *v1beta1.Ingress, status v1beta1.IngressStatus) (v1beta1.IngressStatus, error)\n\n// IngressGeneratingHandler is the top-level handler that is executed for every Ingress event. It extends IngressStatusHandler by a returning a slice of child objects to be passed to apply.Apply\ntype IngressGeneratingHandler func(obj *v1beta1.Ingress, status v1beta1.IngressStatus) ([]runtime.Object, v1beta1.IngressStatus, error)\n\n// RegisterIngressStatusHandler configures a IngressController to execute a IngressStatusHandler for every events observed.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterIngressStatusHandler(ctx context.Context, controller IngressController, condition condition.Cond, name string, handler IngressStatusHandler) {\n\tstatusHandler := &ingressStatusHandler{\n\t\tclient:    controller,\n\t\tcondition: condition,\n\t\thandler:   handler,\n\t}\n\tcontroller.AddGenericHandler(ctx, name, generic.FromObjectHandlerToHandler(statusHandler.sync))\n}\n\n// RegisterIngressGeneratingHandler configures a IngressController to execute a IngressGeneratingHandler for every events observed, passing the returned objects to the provided apply.Apply.\n// If a non-empty condition is provided, it will be updated in the status conditions for every handler execution\nfunc RegisterIngressGeneratingHandler(ctx context.Context, controller IngressController, apply apply.Apply,\n\tcondition condition.Cond, name string, handler IngressGeneratingHandler, opts *generic.GeneratingHandlerOptions) {\n\tstatusHandler := &ingressGeneratingHandler{\n\t\tIngressGeneratingHandler: handler,\n\t\tapply:                    apply,\n\t\tname:                     name,\n\t\tgvk:                      controller.GroupVersionKind(),\n\t}\n\tif opts != nil {\n\t\tstatusHandler.opts = *opts\n\t}\n\tcontroller.OnChange(ctx, name, statusHandler.Remove)\n\tRegisterIngressStatusHandler(ctx, controller, condition, name, statusHandler.Handle)\n}\n\ntype ingressStatusHandler struct {\n\tclient    IngressClient\n\tcondition condition.Cond\n\thandler   IngressStatusHandler\n}\n\n// sync is executed on every resource addition or modification. Executes the configured handlers and sends the updated status to the Kubernetes API\nfunc (a *ingressStatusHandler) sync(key string, obj *v1beta1.Ingress) (*v1beta1.Ingress, error) {\n\tif obj == nil {\n\t\treturn obj, nil\n\t}\n\n\torigStatus := obj.Status.DeepCopy()\n\tobj = obj.DeepCopy()\n\tnewStatus, err := a.handler(obj, obj.Status)\n\tif err != nil {\n\t\t// Revert to old status on error\n\t\tnewStatus = *origStatus.DeepCopy()\n\t}\n\n\tif a.condition != \"\" {\n\t\tif errors.IsConflict(err) {\n\t\t\ta.condition.SetError(&newStatus, \"\", nil)\n\t\t} else {\n\t\t\ta.condition.SetError(&newStatus, \"\", err)\n\t\t}\n\t}\n\tif !equality.Semantic.DeepEqual(origStatus, &newStatus) {\n\t\tif a.condition != \"\" {\n\t\t\t// Since status has changed, update the lastUpdatedTime\n\t\t\ta.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))\n\t\t}\n\n\t\tvar newErr error\n\t\tobj.Status = newStatus\n\t\tnewObj, newErr := a.client.UpdateStatus(obj)\n\t\tif err == nil {\n\t\t\terr = newErr\n\t\t}\n\t\tif newErr == nil {\n\t\t\tobj = newObj\n\t\t}\n\t}\n\treturn obj, err\n}\n\ntype ingressGeneratingHandler struct {\n\tIngressGeneratingHandler\n\tapply apply.Apply\n\topts  generic.GeneratingHandlerOptions\n\tgvk   schema.GroupVersionKind\n\tname  string\n\tseen  sync.Map\n}\n\n// Remove handles the observed deletion of a resource, cascade deleting every associated resource previously applied\nfunc (a *ingressGeneratingHandler) Remove(key string, obj *v1beta1.Ingress) (*v1beta1.Ingress, error) {\n\tif obj != nil {\n\t\treturn obj, nil\n\t}\n\n\tobj = &v1beta1.Ingress{}\n\tobj.Namespace, obj.Name = kv.RSplit(key, \"/\")\n\tobj.SetGroupVersionKind(a.gvk)\n\n\tif a.opts.UniqueApplyForResourceVersion {\n\t\ta.seen.Delete(key)\n\t}\n\n\treturn nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects()\n}\n\n// Handle executes the configured IngressGeneratingHandler and pass the resulting objects to apply.Apply, finally returning the new status of the resource\nfunc (a *ingressGeneratingHandler) Handle(obj *v1beta1.Ingress, status v1beta1.IngressStatus) (v1beta1.IngressStatus, error) {\n\tif !obj.DeletionTimestamp.IsZero() {\n\t\treturn status, nil\n\t}\n\n\tobjs, newStatus, err := a.IngressGeneratingHandler(obj, status)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\tif !a.isNewResourceVersion(obj) {\n\t\treturn newStatus, nil\n\t}\n\n\terr = generic.ConfigureApplyForObject(a.apply, obj, &a.opts).\n\t\tWithOwner(obj).\n\t\tWithSetID(a.name).\n\t\tApplyObjects(objs...)\n\tif err != nil {\n\t\treturn newStatus, err\n\t}\n\ta.storeResourceVersion(obj)\n\treturn newStatus, nil\n}\n\n// isNewResourceVersion detects if a specific resource version was already successfully processed.\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *ingressGeneratingHandler) isNewResourceVersion(obj *v1beta1.Ingress) bool {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn true\n\t}\n\n\t// Apply once per resource version\n\tkey := obj.Namespace + \"/\" + obj.Name\n\tprevious, ok := a.seen.Load(key)\n\treturn !ok || previous != obj.ResourceVersion\n}\n\n// storeResourceVersion keeps track of the latest resource version of an object for which Apply was executed\n// Only used if UniqueApplyForResourceVersion is set in generic.GeneratingHandlerOptions\nfunc (a *ingressGeneratingHandler) storeResourceVersion(obj *v1beta1.Ingress) {\n\tif !a.opts.UniqueApplyForResourceVersion {\n\t\treturn\n\t}\n\n\tkey := obj.Namespace + \"/\" + obj.Name\n\ta.seen.Store(key, obj.ResourceVersion)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/extensions/v1beta1/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1beta1\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\tv1beta1 \"k8s.io/api/extensions/v1beta1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc init() {\n\tschemes.Register(v1beta1.AddToScheme)\n}\n\ntype Interface interface {\n\tIngress() IngressController\n}\n\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &version{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\ntype version struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\nfunc (v *version) Ingress() IngressController {\n\treturn generic.NewController[*v1beta1.Ingress, *v1beta1.IngressList](schema.GroupVersionKind{Group: \"extensions\", Version: \"v1beta1\", Kind: \"Ingress\"}, \"ingresses\", true, v.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/networking.k8s.io/factory.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage networking\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"k8s.io/client-go/rest\"\n)\n\ntype Factory struct {\n\t*generic.Factory\n}\n\nfunc NewFactoryFromConfigOrDie(config *rest.Config) *Factory {\n\tf, err := NewFactoryFromConfig(config)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFactoryFromConfig(config *rest.Config) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, nil)\n}\n\nfunc NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, &FactoryOptions{\n\t\tNamespace: namespace,\n\t})\n}\n\ntype FactoryOptions = generic.FactoryOptions\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tf, err := generic.NewFactoryFromConfigWithOptions(config, opts)\n\treturn &Factory{\n\t\tFactory: f,\n\t}, err\n}\n\nfunc NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory {\n\tf, err := NewFactoryFromConfigWithOptions(config, opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc (c *Factory) Networking() Interface {\n\treturn New(c.ControllerFactory())\n}\n\nfunc (c *Factory) WithAgent(userAgent string) Interface {\n\treturn New(controller.NewSharedControllerFactoryWithAgent(userAgent, c.ControllerFactory()))\n}\n"
  },
  {
    "path": "pkg/generated/controllers/networking.k8s.io/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage networking\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\tv1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/networking.k8s.io/v1\"\n)\n\ntype Interface interface {\n\tV1() v1.Interface\n}\n\ntype group struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n// New returns a new Interface.\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &group{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\nfunc (g *group) V1() v1.Interface {\n\treturn v1.New(g.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/networking.k8s.io/v1/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\tv1 \"k8s.io/api/networking/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc init() {\n\tschemes.Register(v1.AddToScheme)\n}\n\ntype Interface interface {\n\tNetworkPolicy() NetworkPolicyController\n}\n\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &version{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\ntype version struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\nfunc (v *version) NetworkPolicy() NetworkPolicyController {\n\treturn generic.NewController[*v1.NetworkPolicy, *v1.NetworkPolicyList](schema.GroupVersionKind{Group: \"networking.k8s.io\", Version: \"v1\", Kind: \"NetworkPolicy\"}, \"networkpolicies\", true, v.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/networking.k8s.io/v1/networkpolicy.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/networking/v1\"\n)\n\n// NetworkPolicyController interface for managing NetworkPolicy resources.\ntype NetworkPolicyController interface {\n\tgeneric.ControllerInterface[*v1.NetworkPolicy, *v1.NetworkPolicyList]\n}\n\n// NetworkPolicyClient interface for managing NetworkPolicy resources in Kubernetes.\ntype NetworkPolicyClient interface {\n\tgeneric.ClientInterface[*v1.NetworkPolicy, *v1.NetworkPolicyList]\n}\n\n// NetworkPolicyCache interface for retrieving NetworkPolicy resources in memory.\ntype NetworkPolicyCache interface {\n\tgeneric.CacheInterface[*v1.NetworkPolicy]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/rbac/factory.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage rbac\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"k8s.io/client-go/rest\"\n)\n\ntype Factory struct {\n\t*generic.Factory\n}\n\nfunc NewFactoryFromConfigOrDie(config *rest.Config) *Factory {\n\tf, err := NewFactoryFromConfig(config)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFactoryFromConfig(config *rest.Config) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, nil)\n}\n\nfunc NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, &FactoryOptions{\n\t\tNamespace: namespace,\n\t})\n}\n\ntype FactoryOptions = generic.FactoryOptions\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tf, err := generic.NewFactoryFromConfigWithOptions(config, opts)\n\treturn &Factory{\n\t\tFactory: f,\n\t}, err\n}\n\nfunc NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory {\n\tf, err := NewFactoryFromConfigWithOptions(config, opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc (c *Factory) Rbac() Interface {\n\treturn New(c.ControllerFactory())\n}\n\nfunc (c *Factory) WithAgent(userAgent string) Interface {\n\treturn New(controller.NewSharedControllerFactoryWithAgent(userAgent, c.ControllerFactory()))\n}\n"
  },
  {
    "path": "pkg/generated/controllers/rbac/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage rbac\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\tv1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/rbac/v1\"\n)\n\ntype Interface interface {\n\tV1() v1.Interface\n}\n\ntype group struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n// New returns a new Interface.\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &group{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\nfunc (g *group) V1() v1.Interface {\n\treturn v1.New(g.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/rbac/v1/clusterrole.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/rbac/v1\"\n)\n\n// ClusterRoleController interface for managing ClusterRole resources.\ntype ClusterRoleController interface {\n\tgeneric.NonNamespacedControllerInterface[*v1.ClusterRole, *v1.ClusterRoleList]\n}\n\n// ClusterRoleClient interface for managing ClusterRole resources in Kubernetes.\ntype ClusterRoleClient interface {\n\tgeneric.NonNamespacedClientInterface[*v1.ClusterRole, *v1.ClusterRoleList]\n}\n\n// ClusterRoleCache interface for retrieving ClusterRole resources in memory.\ntype ClusterRoleCache interface {\n\tgeneric.NonNamespacedCacheInterface[*v1.ClusterRole]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/rbac/v1/clusterrolebinding.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/rbac/v1\"\n)\n\n// ClusterRoleBindingController interface for managing ClusterRoleBinding resources.\ntype ClusterRoleBindingController interface {\n\tgeneric.NonNamespacedControllerInterface[*v1.ClusterRoleBinding, *v1.ClusterRoleBindingList]\n}\n\n// ClusterRoleBindingClient interface for managing ClusterRoleBinding resources in Kubernetes.\ntype ClusterRoleBindingClient interface {\n\tgeneric.NonNamespacedClientInterface[*v1.ClusterRoleBinding, *v1.ClusterRoleBindingList]\n}\n\n// ClusterRoleBindingCache interface for retrieving ClusterRoleBinding resources in memory.\ntype ClusterRoleBindingCache interface {\n\tgeneric.NonNamespacedCacheInterface[*v1.ClusterRoleBinding]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/rbac/v1/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\tv1 \"k8s.io/api/rbac/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc init() {\n\tschemes.Register(v1.AddToScheme)\n}\n\ntype Interface interface {\n\tClusterRole() ClusterRoleController\n\tClusterRoleBinding() ClusterRoleBindingController\n\tRole() RoleController\n\tRoleBinding() RoleBindingController\n}\n\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &version{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\ntype version struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\nfunc (v *version) ClusterRole() ClusterRoleController {\n\treturn generic.NewNonNamespacedController[*v1.ClusterRole, *v1.ClusterRoleList](schema.GroupVersionKind{Group: \"rbac.authorization.k8s.io\", Version: \"v1\", Kind: \"ClusterRole\"}, \"clusterroles\", v.controllerFactory)\n}\n\nfunc (v *version) ClusterRoleBinding() ClusterRoleBindingController {\n\treturn generic.NewNonNamespacedController[*v1.ClusterRoleBinding, *v1.ClusterRoleBindingList](schema.GroupVersionKind{Group: \"rbac.authorization.k8s.io\", Version: \"v1\", Kind: \"ClusterRoleBinding\"}, \"clusterrolebindings\", v.controllerFactory)\n}\n\nfunc (v *version) Role() RoleController {\n\treturn generic.NewController[*v1.Role, *v1.RoleList](schema.GroupVersionKind{Group: \"rbac.authorization.k8s.io\", Version: \"v1\", Kind: \"Role\"}, \"roles\", true, v.controllerFactory)\n}\n\nfunc (v *version) RoleBinding() RoleBindingController {\n\treturn generic.NewController[*v1.RoleBinding, *v1.RoleBindingList](schema.GroupVersionKind{Group: \"rbac.authorization.k8s.io\", Version: \"v1\", Kind: \"RoleBinding\"}, \"rolebindings\", true, v.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/rbac/v1/role.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/rbac/v1\"\n)\n\n// RoleController interface for managing Role resources.\ntype RoleController interface {\n\tgeneric.ControllerInterface[*v1.Role, *v1.RoleList]\n}\n\n// RoleClient interface for managing Role resources in Kubernetes.\ntype RoleClient interface {\n\tgeneric.ClientInterface[*v1.Role, *v1.RoleList]\n}\n\n// RoleCache interface for retrieving Role resources in memory.\ntype RoleCache interface {\n\tgeneric.CacheInterface[*v1.Role]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/rbac/v1/rolebinding.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/rbac/v1\"\n)\n\n// RoleBindingController interface for managing RoleBinding resources.\ntype RoleBindingController interface {\n\tgeneric.ControllerInterface[*v1.RoleBinding, *v1.RoleBindingList]\n}\n\n// RoleBindingClient interface for managing RoleBinding resources in Kubernetes.\ntype RoleBindingClient interface {\n\tgeneric.ClientInterface[*v1.RoleBinding, *v1.RoleBindingList]\n}\n\n// RoleBindingCache interface for retrieving RoleBinding resources in memory.\ntype RoleBindingCache interface {\n\tgeneric.CacheInterface[*v1.RoleBinding]\n}\n"
  },
  {
    "path": "pkg/generated/controllers/storage/factory.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage storage\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"k8s.io/client-go/rest\"\n)\n\ntype Factory struct {\n\t*generic.Factory\n}\n\nfunc NewFactoryFromConfigOrDie(config *rest.Config) *Factory {\n\tf, err := NewFactoryFromConfig(config)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFactoryFromConfig(config *rest.Config) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, nil)\n}\n\nfunc NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {\n\treturn NewFactoryFromConfigWithOptions(config, &FactoryOptions{\n\t\tNamespace: namespace,\n\t})\n}\n\ntype FactoryOptions = generic.FactoryOptions\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tf, err := generic.NewFactoryFromConfigWithOptions(config, opts)\n\treturn &Factory{\n\t\tFactory: f,\n\t}, err\n}\n\nfunc NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory {\n\tf, err := NewFactoryFromConfigWithOptions(config, opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc (c *Factory) Storage() Interface {\n\treturn New(c.ControllerFactory())\n}\n\nfunc (c *Factory) WithAgent(userAgent string) Interface {\n\treturn New(controller.NewSharedControllerFactoryWithAgent(userAgent, c.ControllerFactory()))\n}\n"
  },
  {
    "path": "pkg/generated/controllers/storage/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage storage\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\tv1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/storage/v1\"\n)\n\ntype Interface interface {\n\tV1() v1.Interface\n}\n\ntype group struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\n// New returns a new Interface.\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &group{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\nfunc (g *group) V1() v1.Interface {\n\treturn v1.New(g.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/storage/v1/interface.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\tv1 \"k8s.io/api/storage/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc init() {\n\tschemes.Register(v1.AddToScheme)\n}\n\ntype Interface interface {\n\tStorageClass() StorageClassController\n}\n\nfunc New(controllerFactory controller.SharedControllerFactory) Interface {\n\treturn &version{\n\t\tcontrollerFactory: controllerFactory,\n\t}\n}\n\ntype version struct {\n\tcontrollerFactory controller.SharedControllerFactory\n}\n\nfunc (v *version) StorageClass() StorageClassController {\n\treturn generic.NewNonNamespacedController[*v1.StorageClass, *v1.StorageClassList](schema.GroupVersionKind{Group: \"storage.k8s.io\", Version: \"v1\", Kind: \"StorageClass\"}, \"storageclasses\", v.controllerFactory)\n}\n"
  },
  {
    "path": "pkg/generated/controllers/storage/v1/storageclass.go",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by main. DO NOT EDIT.\n\npackage v1\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\tv1 \"k8s.io/api/storage/v1\"\n)\n\n// StorageClassController interface for managing StorageClass resources.\ntype StorageClassController interface {\n\tgeneric.NonNamespacedControllerInterface[*v1.StorageClass, *v1.StorageClassList]\n}\n\n// StorageClassClient interface for managing StorageClass resources in Kubernetes.\ntype StorageClassClient interface {\n\tgeneric.NonNamespacedClientInterface[*v1.StorageClass, *v1.StorageClassList]\n}\n\n// StorageClassCache interface for retrieving StorageClass resources in memory.\ntype StorageClassCache interface {\n\tgeneric.NonNamespacedCacheInterface[*v1.StorageClass]\n}\n"
  },
  {
    "path": "pkg/generic/cache.go",
    "content": "package generic\n\nimport (\n\t\"fmt\"\n\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\tutilruntime \"k8s.io/apimachinery/pkg/util/runtime\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\n// CacheInterface is an interface for Object retrieval from memory.\ntype CacheInterface[T runtime.Object] interface {\n\t// Get returns the resources with the specified name in the given namespace from the cache.\n\tGet(namespace, name string) (T, error)\n\n\t// List will attempt to find resources in the given namespace from the Cache.\n\tList(namespace string, selector labels.Selector) ([]T, error)\n\n\t// AddIndexer adds  a new Indexer to the cache with the provided name.\n\t// If you call this after you already have data in the store, the results are undefined.\n\tAddIndexer(indexName string, indexer Indexer[T])\n\n\t// GetByIndex returns the stored objects whose set of indexed values\n\t// for the named index includes the given indexed value.\n\tGetByIndex(indexName, key string) ([]T, error)\n}\n\n// NonNamespacedCacheInterface is an interface for non namespaced Object retrieval from memory.\ntype NonNamespacedCacheInterface[T runtime.Object] interface {\n\t// Get returns the resources with the specified name from the cache.\n\tGet(name string) (T, error)\n\n\t// List will attempt to find resources from the Cache.\n\tList(selector labels.Selector) ([]T, error)\n\n\t// AddIndexer adds  a new Indexer to the cache with the provided name.\n\t// If you call this after you already have data in the store, the results are undefined.\n\tAddIndexer(indexName string, indexer Indexer[T])\n\n\t// GetByIndex returns the stored objects whose set of indexed values\n\t// for the named index includes the given indexed value.\n\tGetByIndex(indexName, key string) ([]T, error)\n}\n\nfunc NewCache[T runtime.Object](indexer cache.Indexer, resource schema.GroupResource) *Cache[T] {\n\treturn &Cache[T]{\n\t\tindexer:  indexer,\n\t\tresource: resource,\n\t}\n}\n\nfunc NewNonNamespacedCache[T runtime.Object](indexer cache.Indexer, resource schema.GroupResource) *NonNamespacedCache[T] {\n\treturn &NonNamespacedCache[T]{\n\t\tCacheInterface: &Cache[T]{\n\t\t\tindexer:  indexer,\n\t\t\tresource: resource,\n\t\t},\n\t}\n}\n\n// Cache is a object cache stored in memory for objects of type T.\ntype Cache[T runtime.Object] struct {\n\tindexer  cache.Indexer\n\tresource schema.GroupResource\n}\n\n// NonNamespacedCache is a Cache for objects of type T that are not namespaced.\ntype NonNamespacedCache[T runtime.Object] struct {\n\tCacheInterface[T]\n}\n\n// Get returns the resources with the specified name in the given namespace from the cache.\nfunc (c *Cache[T]) Get(namespace, name string) (T, error) {\n\tvar nilObj T\n\tkey := name\n\tif namespace != metav1.NamespaceAll {\n\t\tkey = namespace + \"/\" + key\n\t}\n\tobj, exists, err := c.indexer.GetByKey(key)\n\tif err != nil {\n\t\treturn nilObj, err\n\t}\n\tif !exists {\n\t\treturn nilObj, errors.NewNotFound(c.resource, name)\n\t}\n\tret, ok := obj.(T)\n\tif !ok {\n\t\treturn ret, fmt.Errorf(\"could not convert cache item to %T\", *new(T))\n\t}\n\treturn ret, nil\n}\n\n// List will attempt to find resources in the given namespace from the Cache.\nfunc (c *Cache[T]) List(namespace string, selector labels.Selector) (ret []T, err error) {\n\terr = cache.ListAllByNamespace(c.indexer, namespace, selector, func(m interface{}) {\n\t\tret = append(ret, m.(T))\n\t})\n\n\treturn ret, err\n}\n\n// AddIndexer adds  a new Indexer to the cache with the provided name.\n// If you call this after you already have data in the store, the results are undefined.\nfunc (c *Cache[T]) AddIndexer(indexName string, indexer Indexer[T]) {\n\tutilruntime.Must(c.indexer.AddIndexers(map[string]cache.IndexFunc{\n\t\tindexName: func(obj interface{}) (strings []string, e error) {\n\t\t\treturn indexer(obj.(T))\n\t\t},\n\t}))\n}\n\n// GetByIndex returns the stored objects whose set of indexed values\n// for the named index includes the given indexed value.\nfunc (c *Cache[T]) GetByIndex(indexName, key string) (result []T, err error) {\n\tobjs, err := c.indexer.ByIndex(indexName, key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresult = make([]T, 0, len(objs))\n\tfor _, obj := range objs {\n\t\tret, ok := obj.(T)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"could not convert cache item to %T\", *new(T))\n\t\t}\n\t\tresult = append(result, ret)\n\t}\n\treturn result, nil\n}\n\n// Get calls Cache.Get(...) with an empty namespace parameter.\nfunc (c *NonNamespacedCache[T]) Get(name string) (T, error) {\n\treturn c.CacheInterface.Get(metav1.NamespaceAll, name)\n}\n\n// Get calls Cache.List(...) with an empty namespace parameter.\nfunc (c *NonNamespacedCache[T]) List(selector labels.Selector) (ret []T, err error) {\n\treturn c.CacheInterface.List(metav1.NamespaceAll, selector)\n}\n"
  },
  {
    "path": "pkg/generic/cache_test.go",
    "content": "package generic\n\nimport (\n\t\"testing\"\n\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\nfunc TestCache(t *testing.T) {\n\tindexer := cache.NewIndexer(cache.DeletionHandlingMetaNamespaceKeyFunc, nil)\n\tindexer.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: \"test-01\"}})\n\n\t// test cache with correct type for indexer contents\n\tpodCache := NewCache[*v1.Pod](indexer, v1.SchemeGroupVersion.WithResource(\"pods\").GroupResource())\n\tif _, err := podCache.Get(metav1.NamespaceDefault, \"test-01\"); err != nil {\n\t\tt.Fatalf(\"failed to get pod: %v\", err)\n\t}\n\tif _, err := podCache.Get(metav1.NamespaceSystem, \"test-01\"); err == nil {\n\t\tt.Fatalf(\"unexpected success getting nonexistent pod\")\n\t}\n\tif _, err := podCache.Get(metav1.NamespaceDefault, \"test-02\"); err == nil {\n\t\tt.Fatalf(\"unexpected success getting nonexistent pod\")\n\t}\n\n\t// test cache with wrong type for indexer contents\n\tsecretCache := NewCache[*v1.Secret](indexer, v1.SchemeGroupVersion.WithResource(\"secrets\").GroupResource())\n\tif _, err := secretCache.Get(metav1.NamespaceDefault, \"test-01\"); err == nil {\n\t\tt.Fatalf(\"unexpected success getting secret from pod indexer\")\n\t}\n\tif _, err := secretCache.Get(metav1.NamespaceSystem, \"test-01\"); err == nil {\n\t\tt.Fatalf(\"unexpected success getting secret from pod indexer\")\n\t}\n\tif _, err := secretCache.Get(metav1.NamespaceDefault, \"test-02\"); err == nil {\n\t\tt.Fatalf(\"unexpected success getting secret from pod indexer\")\n\t}\n}\n\nfunc TestNonNamespacedCache(t *testing.T) {\n\tindexer := cache.NewIndexer(cache.DeletionHandlingMetaNamespaceKeyFunc, nil)\n\tindexer.Add(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: \"test-01\"}})\n\n\t// test cache with correct type for indexer contents\n\tnodeCache := NewNonNamespacedCache[*v1.Node](indexer, v1.SchemeGroupVersion.WithResource(\"nodes\").GroupResource())\n\tif _, err := nodeCache.Get(\"test-01\"); err != nil {\n\t\tt.Fatalf(\"failed to get node: %v\", err)\n\t}\n\tif _, err := nodeCache.Get(\"test-02\"); err == nil {\n\t\tt.Fatalf(\"unexpected success getting nonexistent node\")\n\t}\n\n\t// test cache with wrong type for indexer contents\n\tpvCache := NewNonNamespacedCache[*v1.PersistentVolume](indexer, v1.SchemeGroupVersion.WithResource(\"persistentvolumes\").GroupResource())\n\tif _, err := pvCache.Get(\"test-01\"); err == nil {\n\t\tt.Fatalf(\"unexpected success getting pv from node indexer\")\n\t}\n\tif _, err := pvCache.Get(\"test-02\"); err == nil {\n\t\tt.Fatalf(\"unexpected success getting pv from node indexer\")\n\t}\n}\n"
  },
  {
    "path": "pkg/generic/clientMocks_test.go",
    "content": "// Code generated by MockGen. DO NOT EDIT.\n// Source: ./embeddedClient.go\n//\n// Generated by this command:\n//\n//\tmockgen -package generic -destination ./clientMocks_test.go -source ./embeddedClient.go\n//\n\n// Package generic is a generated GoMock package.\npackage generic\n\nimport (\n\tcontext \"context\"\n\treflect \"reflect\"\n\n\tclient \"github.com/rancher/lasso/pkg/client\"\n\tgomock \"go.uber.org/mock/gomock\"\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n\ttypes \"k8s.io/apimachinery/pkg/types\"\n\twatch \"k8s.io/apimachinery/pkg/watch\"\n\trest \"k8s.io/client-go/rest\"\n)\n\n// MockembeddedClient is a mock of embeddedClient interface.\ntype MockembeddedClient struct {\n\tctrl     *gomock.Controller\n\trecorder *MockembeddedClientMockRecorder\n\tisgomock struct{}\n}\n\n// MockembeddedClientMockRecorder is the mock recorder for MockembeddedClient.\ntype MockembeddedClientMockRecorder struct {\n\tmock *MockembeddedClient\n}\n\n// NewMockembeddedClient creates a new mock instance.\nfunc NewMockembeddedClient(ctrl *gomock.Controller) *MockembeddedClient {\n\tmock := &MockembeddedClient{ctrl: ctrl}\n\tmock.recorder = &MockembeddedClientMockRecorder{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use.\nfunc (m *MockembeddedClient) EXPECT() *MockembeddedClientMockRecorder {\n\treturn m.recorder\n}\n\n// Create mocks base method.\nfunc (m *MockembeddedClient) Create(ctx context.Context, namespace string, obj, result runtime.Object, opts v1.CreateOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Create\", ctx, namespace, obj, result, opts)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// Create indicates an expected call of Create.\nfunc (mr *MockembeddedClientMockRecorder) Create(ctx, namespace, obj, result, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Create\", reflect.TypeOf((*MockembeddedClient)(nil).Create), ctx, namespace, obj, result, opts)\n}\n\n// Delete mocks base method.\nfunc (m *MockembeddedClient) Delete(ctx context.Context, namespace, name string, opts v1.DeleteOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Delete\", ctx, namespace, name, opts)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// Delete indicates an expected call of Delete.\nfunc (mr *MockembeddedClientMockRecorder) Delete(ctx, namespace, name, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Delete\", reflect.TypeOf((*MockembeddedClient)(nil).Delete), ctx, namespace, name, opts)\n}\n\n// DeleteCollection mocks base method.\nfunc (m *MockembeddedClient) DeleteCollection(ctx context.Context, namespace string, opts v1.DeleteOptions, listOpts v1.ListOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"DeleteCollection\", ctx, namespace, opts, listOpts)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// DeleteCollection indicates an expected call of DeleteCollection.\nfunc (mr *MockembeddedClientMockRecorder) DeleteCollection(ctx, namespace, opts, listOpts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"DeleteCollection\", reflect.TypeOf((*MockembeddedClient)(nil).DeleteCollection), ctx, namespace, opts, listOpts)\n}\n\n// Get mocks base method.\nfunc (m *MockembeddedClient) Get(ctx context.Context, namespace, name string, result runtime.Object, options v1.GetOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Get\", ctx, namespace, name, result, options)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// Get indicates an expected call of Get.\nfunc (mr *MockembeddedClientMockRecorder) Get(ctx, namespace, name, result, options any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Get\", reflect.TypeOf((*MockembeddedClient)(nil).Get), ctx, namespace, name, result, options)\n}\n\n// List mocks base method.\nfunc (m *MockembeddedClient) List(ctx context.Context, namespace string, result runtime.Object, opts v1.ListOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"List\", ctx, namespace, result, opts)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// List indicates an expected call of List.\nfunc (mr *MockembeddedClientMockRecorder) List(ctx, namespace, result, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"List\", reflect.TypeOf((*MockembeddedClient)(nil).List), ctx, namespace, result, opts)\n}\n\n// Patch mocks base method.\nfunc (m *MockembeddedClient) Patch(ctx context.Context, namespace, name string, pt types.PatchType, data []byte, result runtime.Object, opts v1.PatchOptions, subresources ...string) error {\n\tm.ctrl.T.Helper()\n\tvarargs := []any{ctx, namespace, name, pt, data, result, opts}\n\tfor _, a := range subresources {\n\t\tvarargs = append(varargs, a)\n\t}\n\tret := m.ctrl.Call(m, \"Patch\", varargs...)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// Patch indicates an expected call of Patch.\nfunc (mr *MockembeddedClientMockRecorder) Patch(ctx, namespace, name, pt, data, result, opts any, subresources ...any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\tvarargs := append([]any{ctx, namespace, name, pt, data, result, opts}, subresources...)\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Patch\", reflect.TypeOf((*MockembeddedClient)(nil).Patch), varargs...)\n}\n\n// Update mocks base method.\nfunc (m *MockembeddedClient) Update(ctx context.Context, namespace string, obj, result runtime.Object, opts v1.UpdateOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Update\", ctx, namespace, obj, result, opts)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// Update indicates an expected call of Update.\nfunc (mr *MockembeddedClientMockRecorder) Update(ctx, namespace, obj, result, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Update\", reflect.TypeOf((*MockembeddedClient)(nil).Update), ctx, namespace, obj, result, opts)\n}\n\n// UpdateStatus mocks base method.\nfunc (m *MockembeddedClient) UpdateStatus(ctx context.Context, namespace string, obj, result runtime.Object, opts v1.UpdateOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"UpdateStatus\", ctx, namespace, obj, result, opts)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// UpdateStatus indicates an expected call of UpdateStatus.\nfunc (mr *MockembeddedClientMockRecorder) UpdateStatus(ctx, namespace, obj, result, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"UpdateStatus\", reflect.TypeOf((*MockembeddedClient)(nil).UpdateStatus), ctx, namespace, obj, result, opts)\n}\n\n// Watch mocks base method.\nfunc (m *MockembeddedClient) Watch(ctx context.Context, namespace string, opts v1.ListOptions) (watch.Interface, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Watch\", ctx, namespace, opts)\n\tret0, _ := ret[0].(watch.Interface)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Watch indicates an expected call of Watch.\nfunc (mr *MockembeddedClientMockRecorder) Watch(ctx, namespace, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Watch\", reflect.TypeOf((*MockembeddedClient)(nil).Watch), ctx, namespace, opts)\n}\n\n// WithImpersonation mocks base method.\nfunc (m *MockembeddedClient) WithImpersonation(impersonate rest.ImpersonationConfig) (*client.Client, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"WithImpersonation\", impersonate)\n\tret0, _ := ret[0].(*client.Client)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// WithImpersonation indicates an expected call of WithImpersonation.\nfunc (mr *MockembeddedClientMockRecorder) WithImpersonation(impersonate any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"WithImpersonation\", reflect.TypeOf((*MockembeddedClient)(nil).WithImpersonation), impersonate)\n}\n"
  },
  {
    "path": "pkg/generic/controller.go",
    "content": "// Package generic provides generic types and implementations for Controllers, Clients, and Caches.\npackage generic\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"time\"\n\n\t\"github.com/rancher/lasso/pkg/controller\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"k8s.io/apimachinery/pkg/watch\"\n\t\"k8s.io/client-go/rest\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\n// ErrSkip notifies the caller to skip this error.\nvar ErrSkip = controller.ErrIgnore\n\n// ControllerMeta holds meta information shared by all controllers.\ntype ControllerMeta interface {\n\t// Informer returns the SharedIndexInformer used by this controller.\n\tInformer() cache.SharedIndexInformer\n\n\t// GroupVersionKind returns the GVK used to create this Controller.\n\tGroupVersionKind() schema.GroupVersionKind\n\n\t// AddGenericHandler adds a generic handler that runs when a resource changes.\n\tAddGenericHandler(ctx context.Context, name string, handler Handler)\n\n\t// AddGenericHandler adds a generic handler that runs when a resource is removed.\n\tAddGenericRemoveHandler(ctx context.Context, name string, handler Handler)\n\n\t// Updater returns a update function that will attempt to perform an update for a specific resource type.\n\tUpdater() Updater\n}\n\n// RuntimeMetaObject is an interface for a K8s Object to be used with a specific controller.\ntype RuntimeMetaObject interface {\n\tcomparable\n\truntime.Object\n\tmetav1.Object\n}\n\n// ControllerInterface interface for managing K8s Objects.\ntype ControllerInterface[T RuntimeMetaObject, TList runtime.Object] interface {\n\tControllerMeta\n\tClientInterface[T, TList]\n\n\t// OnChange runs the given object handler when the controller detects a resource was changed.\n\tOnChange(ctx context.Context, name string, sync ObjectHandler[T])\n\n\t// OnRemove runs the given object handler when the controller detects a resource was changed.\n\tOnRemove(ctx context.Context, name string, sync ObjectHandler[T])\n\n\t// Enqueue adds the resource with the given name in the provided namespace to the worker queue of the controller.\n\tEnqueue(namespace, name string)\n\n\t// EnqueueAfter runs Enqueue after the provided duration.\n\tEnqueueAfter(namespace, name string, duration time.Duration)\n\n\t// Cache returns a cache for the resource type T.\n\tCache() CacheInterface[T]\n}\n\n// NonNamespacedControllerInterface interface for managing non namespaced K8s Objects.\ntype NonNamespacedControllerInterface[T RuntimeMetaObject, TList runtime.Object] interface {\n\tControllerMeta\n\tNonNamespacedClientInterface[T, TList]\n\n\t// OnChange runs the given object handler when the controller detects a resource was changed.\n\tOnChange(ctx context.Context, name string, sync ObjectHandler[T])\n\n\t// OnRemove runs the given object handler when the controller detects a resource was changed.\n\tOnRemove(ctx context.Context, name string, sync ObjectHandler[T])\n\n\t// Enqueue adds the resource with the given name to the worker queue of the controller.\n\tEnqueue(name string)\n\n\t// EnqueueAfter runs Enqueue after the provided duration.\n\tEnqueueAfter(name string, duration time.Duration)\n\n\t// Cache returns a cache for the resource type T.\n\tCache() NonNamespacedCacheInterface[T]\n}\n\n// ClientInterface is an interface to performs CRUD like operations on an Objects.\ntype ClientInterface[T RuntimeMetaObject, TList runtime.Object] interface {\n\t// Create creates a new object and return the newly created Object or an error.\n\tCreate(T) (T, error)\n\n\t// Update updates the object and return the newly updated Object or an error.\n\tUpdate(T) (T, error)\n\n\t// UpdateStatus updates the Status field of a the object and return the newly updated Object or an error.\n\t// Will always return an error if the object does not have a status field.\n\tUpdateStatus(T) (T, error)\n\n\t// Delete deletes the Object in the given name and namespace.\n\tDelete(namespace, name string, options *metav1.DeleteOptions) error\n\n\t// Get will attempt to retrieve the resource with the given name in the given namespace.\n\tGet(namespace, name string, options metav1.GetOptions) (T, error)\n\n\t// List will attempt to find resources in the given namespace.\n\tList(namespace string, opts metav1.ListOptions) (TList, error)\n\n\t// Watch will start watching resources in the given namespace.\n\tWatch(namespace string, opts metav1.ListOptions) (watch.Interface, error)\n\n\t// Patch will patch the resource with the matching name in the matching namespace.\n\tPatch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (result T, err error)\n\n\t// WithImpersonation returns a new copy of the client that uses impersonation.\n\tWithImpersonation(impersonate rest.ImpersonationConfig) (ClientInterface[T, TList], error)\n\n\t// DeleteCollection deletes all resources matching the ListOptions in the\n\t// provided namespace.\n\tDeleteCollection(namespace string, deleteOpts metav1.DeleteOptions, listOpts metav1.ListOptions) error\n}\n\n// NonNamespacedClientInterface is an interface to performs CRUD like operations on nonNamespaced Objects.\ntype NonNamespacedClientInterface[T RuntimeMetaObject, TList runtime.Object] interface {\n\t// Create creates a new object and return the newly created Object or an error.\n\tCreate(T) (T, error)\n\n\t// Update updates the object and return the newly updated Object or an error.\n\tUpdate(T) (T, error)\n\n\t// UpdateStatus updates the Status field of a the object and return the newly updated Object or an error.\n\t// Will always return an error if the object does not have a status field.\n\tUpdateStatus(T) (T, error)\n\n\t// Delete deletes the Object in the given name.\n\tDelete(name string, options *metav1.DeleteOptions) error\n\n\t// Get will attempt to retrieve the resource with the specified name.\n\tGet(name string, options metav1.GetOptions) (T, error)\n\n\t// List will attempt to find multiple resources.\n\tList(opts metav1.ListOptions) (TList, error)\n\n\t// Watch will start watching resources.\n\tWatch(opts metav1.ListOptions) (watch.Interface, error)\n\n\t// Patch will patch the resource with the matching name.\n\tPatch(name string, pt types.PatchType, data []byte, subresources ...string) (result T, err error)\n\n\t// WithImpersonation returns a new copy of the client that uses impersonation.\n\tWithImpersonation(impersonate rest.ImpersonationConfig) (NonNamespacedClientInterface[T, TList], error)\n}\n\n// ObjectHandler performs operations on the given runtime.Object and returns the new runtime.Object or an error\ntype Handler func(key string, obj runtime.Object) (runtime.Object, error)\n\n// ObjectHandler performs operations on the given object and returns the new object or an error\ntype ObjectHandler[T runtime.Object] func(string, T) (T, error)\n\n// Indexer computes a set of indexed values for the provided object.\ntype Indexer[T runtime.Object] func(obj T) ([]string, error)\n\n// FromObjectHandlerToHandler converts an ObjecHandler to a Handler.\nfunc FromObjectHandlerToHandler[T RuntimeMetaObject](sync ObjectHandler[T]) Handler {\n\treturn func(key string, obj runtime.Object) (runtime.Object, error) {\n\t\tvar nilObj, retObj T\n\t\tvar err error\n\t\tif obj == nil {\n\t\t\tretObj, err = sync(key, nilObj)\n\t\t} else {\n\t\t\tretObj, err = sync(key, obj.(T))\n\t\t}\n\t\tif retObj == nilObj {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn retObj, err\n\t}\n}\n\n// Controller is used to manage objects of type T.\ntype Controller[T RuntimeMetaObject, TList runtime.Object] struct {\n\tcontroller controller.SharedController\n\tembeddedClient\n\tgvk           schema.GroupVersionKind\n\tgroupResource schema.GroupResource\n\tobjType       reflect.Type\n\tobjListType   reflect.Type\n}\n\n// NonNamespacedController is a Controller for non namespaced resources. This controller provides similar function definitions as Controller except the namespace parameter is omitted.\ntype NonNamespacedController[T RuntimeMetaObject, TList runtime.Object] struct {\n\t*Controller[T, TList]\n}\n\n// NewController creates a new controller for the given Object type and ObjectList type.\nfunc NewController[T RuntimeMetaObject, TList runtime.Object](gvk schema.GroupVersionKind, resource string, namespaced bool, controller controller.SharedControllerFactory) *Controller[T, TList] {\n\tsharedCtrl := controller.ForResourceKind(gvk.GroupVersion().WithResource(resource), gvk.Kind, namespaced)\n\tvar obj T\n\tobjPtrType := reflect.TypeOf(obj)\n\tif objPtrType.Kind() != reflect.Pointer {\n\t\tpanic(fmt.Sprintf(\"Controller requires Object T to be a pointer not %v\", objPtrType))\n\t}\n\tvar objList TList\n\tobjListPtrType := reflect.TypeOf(objList)\n\tif objListPtrType.Kind() != reflect.Pointer {\n\t\tpanic(fmt.Sprintf(\"Controller requires Object TList to be a pointer not %v\", objListPtrType))\n\t}\n\treturn &Controller[T, TList]{\n\t\tcontroller:     sharedCtrl,\n\t\tembeddedClient: sharedCtrl.Client(),\n\t\tgvk:            gvk,\n\t\tgroupResource: schema.GroupResource{\n\t\t\tGroup:    gvk.Group,\n\t\t\tResource: resource,\n\t\t},\n\t\tobjType:     objPtrType.Elem(),\n\t\tobjListType: objListPtrType.Elem(),\n\t}\n}\n\n// Updater creates a new Updater for the Object type T.\nfunc (c *Controller[T, TList]) Updater() Updater {\n\tvar nilObj T\n\treturn func(obj runtime.Object) (runtime.Object, error) {\n\t\tnewObj, err := c.Update(obj.(T))\n\t\tif newObj == nilObj {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn newObj, err\n\t}\n}\n\n// AddGenericHandler runs the given handler when the controller detects an object was changed.\nfunc (c *Controller[T, TList]) AddGenericHandler(ctx context.Context, name string, handler Handler) {\n\tc.controller.RegisterHandler(ctx, name, controller.SharedControllerHandlerFunc(handler))\n}\n\n// AddGenericRemoveHandler runs the given handler when the controller detects an object was removed.\nfunc (c *Controller[T, TList]) AddGenericRemoveHandler(ctx context.Context, name string, handler Handler) {\n\tc.AddGenericHandler(ctx, name, NewRemoveHandler(name, c.Updater(), handler))\n}\n\n// OnChange runs the given object handler when the controller detects a resource was changed.\nfunc (c *Controller[T, TList]) OnChange(ctx context.Context, name string, sync ObjectHandler[T]) {\n\tc.AddGenericHandler(ctx, name, FromObjectHandlerToHandler(sync))\n}\n\n// OnRemove runs the given object handler when the controller detects a resource was changed.\nfunc (c *Controller[T, TList]) OnRemove(ctx context.Context, name string, sync ObjectHandler[T]) {\n\tc.AddGenericHandler(ctx, name, NewRemoveHandler(name, c.Updater(), FromObjectHandlerToHandler(sync)))\n}\n\n// Enqueue adds the resource with the given name in the provided namespace to the worker queue of the controller.\nfunc (c *Controller[T, TList]) Enqueue(namespace, name string) {\n\tc.controller.Enqueue(namespace, name)\n}\n\n// EnqueueAfter runs Enqueue after the provided duration.\nfunc (c *Controller[T, TList]) EnqueueAfter(namespace, name string, duration time.Duration) {\n\tc.controller.EnqueueAfter(namespace, name, duration)\n}\n\n// Informer returns the SharedIndexInformer used by this controller.\nfunc (c *Controller[T, TList]) Informer() cache.SharedIndexInformer {\n\treturn c.controller.Informer()\n}\n\n// GroupVersionKind returns the GVK used to create this Controller.\nfunc (c *Controller[T, TList]) GroupVersionKind() schema.GroupVersionKind {\n\treturn c.gvk\n}\n\n// Cache returns a cache for the objects T.\nfunc (c *Controller[T, TList]) Cache() CacheInterface[T] {\n\treturn NewCache[T](c.Informer().GetIndexer(), c.groupResource)\n}\n\n// Create creates a new object and return the newly created Object or an error.\nfunc (c *Controller[T, TList]) Create(obj T) (T, error) {\n\tresult := reflect.New(c.objType).Interface().(T)\n\treturn result, c.embeddedClient.Create(context.TODO(), obj.GetNamespace(), obj, result, metav1.CreateOptions{})\n}\n\n// Update updates the object and return the newly updated Object or an error.\nfunc (c *Controller[T, TList]) Update(obj T) (T, error) {\n\tresult := reflect.New(c.objType).Interface().(T)\n\treturn result, c.embeddedClient.Update(context.TODO(), obj.GetNamespace(), obj, result, metav1.UpdateOptions{})\n}\n\n// UpdateStatus updates the Status field of a the object and return the newly updated Object or an error.\n// Will always return an error if the object does not have a status field.\nfunc (c *Controller[T, TList]) UpdateStatus(obj T) (T, error) {\n\tresult := reflect.New(c.objType).Interface().(T)\n\treturn result, c.embeddedClient.UpdateStatus(context.TODO(), obj.GetNamespace(), obj, result, metav1.UpdateOptions{})\n}\n\n// Delete deletes the Object in the given name and Namespace.\nfunc (c *Controller[T, TList]) Delete(namespace, name string, options *metav1.DeleteOptions) error {\n\tif options == nil {\n\t\toptions = &metav1.DeleteOptions{}\n\t}\n\treturn c.embeddedClient.Delete(context.TODO(), namespace, name, *options)\n}\n\n// Get gets returns the given resource with the given name in the provided namespace.\nfunc (c *Controller[T, TList]) Get(namespace, name string, options metav1.GetOptions) (T, error) {\n\tresult := reflect.New(c.objType).Interface().(T)\n\treturn result, c.embeddedClient.Get(context.TODO(), namespace, name, result, options)\n}\n\n// List will attempt to find resources in the given namespace.\nfunc (c *Controller[T, TList]) List(namespace string, opts metav1.ListOptions) (TList, error) {\n\tresult := reflect.New(c.objListType).Interface().(TList)\n\treturn result, c.embeddedClient.List(context.TODO(), namespace, result, opts)\n}\n\n// Watch will start watching resources in the given namespace.\nfunc (c *Controller[T, TList]) Watch(namespace string, opts metav1.ListOptions) (watch.Interface, error) {\n\treturn c.embeddedClient.Watch(context.TODO(), namespace, opts)\n}\n\n// Patch will patch the resource with the matching name in the matching namespace.\nfunc (c *Controller[T, TList]) Patch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (T, error) {\n\tresult := reflect.New(c.objType).Interface().(T)\n\treturn result, c.embeddedClient.Patch(context.TODO(), namespace, name, pt, data, result, metav1.PatchOptions{}, subresources...)\n}\n\n// DeleteCollection will delete the resources in the given namespace matching\n// the listOpts.\nfunc (c *Controller[T, TList]) DeleteCollection(namespace string, deleteOpts metav1.DeleteOptions, listOpts metav1.ListOptions) error {\n\treturn c.embeddedClient.DeleteCollection(context.TODO(), namespace, deleteOpts, listOpts)\n}\n\n// WithImpersonation returns a new copy of the client that uses impersonation.\nfunc (c *Controller[T, TList]) WithImpersonation(impersonate rest.ImpersonationConfig) (ClientInterface[T, TList], error) {\n\tnewClient, err := c.embeddedClient.WithImpersonation(impersonate)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to make new client: %w\", err)\n\t}\n\n\t// return a new controller with a new embeddedClient\n\treturn &Controller[T, TList]{\n\t\tcontroller:     c.controller,\n\t\tembeddedClient: newClient,\n\t\tobjType:        c.objType,\n\t\tobjListType:    c.objListType,\n\t\tgvk:            c.gvk,\n\t\tgroupResource:  c.groupResource,\n\t}, nil\n}\n\n// NewNonNamespacedController returns a Controller controller that is not namespaced.\n// NonNamespacedController redefines specific functions to no longer accept the namespace parameter.\nfunc NewNonNamespacedController[T RuntimeMetaObject, TList runtime.Object](gvk schema.GroupVersionKind, resource string,\n\tcontroller controller.SharedControllerFactory,\n) *NonNamespacedController[T, TList] {\n\tctrl := NewController[T, TList](gvk, resource, false, controller)\n\treturn &NonNamespacedController[T, TList]{\n\t\tController: ctrl,\n\t}\n}\n\n// Enqueue calls Controller.Enqueue(...) with an empty namespace parameter.\nfunc (c *NonNamespacedController[T, TList]) Enqueue(name string) {\n\tc.Controller.Enqueue(metav1.NamespaceAll, name)\n}\n\n// EnqueueAfter calls Controller.EnqueueAfter(...) with an empty namespace parameter.\nfunc (c *NonNamespacedController[T, TList]) EnqueueAfter(name string, duration time.Duration) {\n\tc.Controller.EnqueueAfter(metav1.NamespaceAll, name, duration)\n}\n\n// Delete calls Controller.Delete(...) with an empty namespace parameter.\nfunc (c *NonNamespacedController[T, TList]) Delete(name string, options *metav1.DeleteOptions) error {\n\treturn c.Controller.Delete(metav1.NamespaceAll, name, options)\n}\n\n// Get calls Controller.Get(...) with an empty namespace parameter.\nfunc (c *NonNamespacedController[T, TList]) Get(name string, options metav1.GetOptions) (T, error) {\n\treturn c.Controller.Get(metav1.NamespaceAll, name, options)\n}\n\n// List calls Controller.List(...) with an empty namespace parameter.\nfunc (c *NonNamespacedController[T, TList]) List(opts metav1.ListOptions) (TList, error) {\n\treturn c.Controller.List(metav1.NamespaceAll, opts)\n}\n\n// Watch calls Controller.Watch(...) with an empty namespace parameter.\nfunc (c *NonNamespacedController[T, TList]) Watch(opts metav1.ListOptions) (watch.Interface, error) {\n\treturn c.Controller.Watch(metav1.NamespaceAll, opts)\n}\n\n// Patch calls the Controller.Patch(...) with an empty namespace parameter.\nfunc (c *NonNamespacedController[T, TList]) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (T, error) {\n\treturn c.Controller.Patch(metav1.NamespaceAll, name, pt, data, subresources...)\n}\n\n// WithImpersonation returns a new copy of the client that uses impersonation.\nfunc (c *NonNamespacedController[T, TList]) WithImpersonation(impersonate rest.ImpersonationConfig) (NonNamespacedClientInterface[T, TList], error) {\n\tnewClient, err := c.Controller.WithImpersonation(impersonate)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to make new client: %w\", err)\n\t}\n\t// get the underlying controller so we can wrap it in a NonNamespacedController\n\tnewCtrl, ok := newClient.(*Controller[T, TList])\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"failed to make new client from: %T\", newCtrl)\n\t}\n\treturn &NonNamespacedController[T, TList]{newCtrl}, nil\n}\n\n// Cache calls ControllerInterface.Cache(...) and wraps the result in a new NonNamespacedCache.\nfunc (c *NonNamespacedController[T, TList]) Cache() NonNamespacedCacheInterface[T] {\n\treturn NewNonNamespacedCache[T](c.Informer().GetIndexer(), c.groupResource)\n}\n\n// DeleteCollection will delete the resources matching the listOpts.\nfunc (c *NonNamespacedController[T, TList]) DeleteCollection(deleteOpts metav1.DeleteOptions, listOpts metav1.ListOptions) error {\n\treturn c.Controller.DeleteCollection(metav1.NamespaceAll, deleteOpts, listOpts)\n}\n"
  },
  {
    "path": "pkg/generic/controllerFactoryMocks_test.go",
    "content": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/rancher/lasso/pkg/controller (interfaces: SharedControllerFactory,SharedController)\n//\n// Generated by this command:\n//\n//\tmockgen --build_flags=--mod=mod -package generic -destination ./controllerFactoryMocks_test.go github.com/rancher/lasso/pkg/controller SharedControllerFactory,SharedController\n//\n\n// Package generic is a generated GoMock package.\npackage generic\n\nimport (\n\tcontext \"context\"\n\treflect \"reflect\"\n\ttime \"time\"\n\n\tcache \"github.com/rancher/lasso/pkg/cache\"\n\tclient \"github.com/rancher/lasso/pkg/client\"\n\tcontroller \"github.com/rancher/lasso/pkg/controller\"\n\tgomock \"go.uber.org/mock/gomock\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n\tschema \"k8s.io/apimachinery/pkg/runtime/schema\"\n\tcache0 \"k8s.io/client-go/tools/cache\"\n)\n\n// MockSharedControllerFactory is a mock of SharedControllerFactory interface.\ntype MockSharedControllerFactory struct {\n\tctrl     *gomock.Controller\n\trecorder *MockSharedControllerFactoryMockRecorder\n\tisgomock struct{}\n}\n\n// MockSharedControllerFactoryMockRecorder is the mock recorder for MockSharedControllerFactory.\ntype MockSharedControllerFactoryMockRecorder struct {\n\tmock *MockSharedControllerFactory\n}\n\n// NewMockSharedControllerFactory creates a new mock instance.\nfunc NewMockSharedControllerFactory(ctrl *gomock.Controller) *MockSharedControllerFactory {\n\tmock := &MockSharedControllerFactory{ctrl: ctrl}\n\tmock.recorder = &MockSharedControllerFactoryMockRecorder{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use.\nfunc (m *MockSharedControllerFactory) EXPECT() *MockSharedControllerFactoryMockRecorder {\n\treturn m.recorder\n}\n\n// ForKind mocks base method.\nfunc (m *MockSharedControllerFactory) ForKind(gvk schema.GroupVersionKind) (controller.SharedController, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"ForKind\", gvk)\n\tret0, _ := ret[0].(controller.SharedController)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// ForKind indicates an expected call of ForKind.\nfunc (mr *MockSharedControllerFactoryMockRecorder) ForKind(gvk any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"ForKind\", reflect.TypeOf((*MockSharedControllerFactory)(nil).ForKind), gvk)\n}\n\n// ForObject mocks base method.\nfunc (m *MockSharedControllerFactory) ForObject(obj runtime.Object) (controller.SharedController, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"ForObject\", obj)\n\tret0, _ := ret[0].(controller.SharedController)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// ForObject indicates an expected call of ForObject.\nfunc (mr *MockSharedControllerFactoryMockRecorder) ForObject(obj any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"ForObject\", reflect.TypeOf((*MockSharedControllerFactory)(nil).ForObject), obj)\n}\n\n// ForResource mocks base method.\nfunc (m *MockSharedControllerFactory) ForResource(gvr schema.GroupVersionResource, namespaced bool) controller.SharedController {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"ForResource\", gvr, namespaced)\n\tret0, _ := ret[0].(controller.SharedController)\n\treturn ret0\n}\n\n// ForResource indicates an expected call of ForResource.\nfunc (mr *MockSharedControllerFactoryMockRecorder) ForResource(gvr, namespaced any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"ForResource\", reflect.TypeOf((*MockSharedControllerFactory)(nil).ForResource), gvr, namespaced)\n}\n\n// ForResourceKind mocks base method.\nfunc (m *MockSharedControllerFactory) ForResourceKind(gvr schema.GroupVersionResource, kind string, namespaced bool) controller.SharedController {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"ForResourceKind\", gvr, kind, namespaced)\n\tret0, _ := ret[0].(controller.SharedController)\n\treturn ret0\n}\n\n// ForResourceKind indicates an expected call of ForResourceKind.\nfunc (mr *MockSharedControllerFactoryMockRecorder) ForResourceKind(gvr, kind, namespaced any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"ForResourceKind\", reflect.TypeOf((*MockSharedControllerFactory)(nil).ForResourceKind), gvr, kind, namespaced)\n}\n\n// SharedCacheFactory mocks base method.\nfunc (m *MockSharedControllerFactory) SharedCacheFactory() cache.SharedCacheFactory {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"SharedCacheFactory\")\n\tret0, _ := ret[0].(cache.SharedCacheFactory)\n\treturn ret0\n}\n\n// SharedCacheFactory indicates an expected call of SharedCacheFactory.\nfunc (mr *MockSharedControllerFactoryMockRecorder) SharedCacheFactory() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SharedCacheFactory\", reflect.TypeOf((*MockSharedControllerFactory)(nil).SharedCacheFactory))\n}\n\n// Start mocks base method.\nfunc (m *MockSharedControllerFactory) Start(ctx context.Context, workers int) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Start\", ctx, workers)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// Start indicates an expected call of Start.\nfunc (mr *MockSharedControllerFactoryMockRecorder) Start(ctx, workers any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Start\", reflect.TypeOf((*MockSharedControllerFactory)(nil).Start), ctx, workers)\n}\n\n// MockSharedController is a mock of SharedController interface.\ntype MockSharedController struct {\n\tctrl     *gomock.Controller\n\trecorder *MockSharedControllerMockRecorder\n\tisgomock struct{}\n}\n\n// MockSharedControllerMockRecorder is the mock recorder for MockSharedController.\ntype MockSharedControllerMockRecorder struct {\n\tmock *MockSharedController\n}\n\n// NewMockSharedController creates a new mock instance.\nfunc NewMockSharedController(ctrl *gomock.Controller) *MockSharedController {\n\tmock := &MockSharedController{ctrl: ctrl}\n\tmock.recorder = &MockSharedControllerMockRecorder{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use.\nfunc (m *MockSharedController) EXPECT() *MockSharedControllerMockRecorder {\n\treturn m.recorder\n}\n\n// Client mocks base method.\nfunc (m *MockSharedController) Client() *client.Client {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Client\")\n\tret0, _ := ret[0].(*client.Client)\n\treturn ret0\n}\n\n// Client indicates an expected call of Client.\nfunc (mr *MockSharedControllerMockRecorder) Client() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Client\", reflect.TypeOf((*MockSharedController)(nil).Client))\n}\n\n// Enqueue mocks base method.\nfunc (m *MockSharedController) Enqueue(namespace, name string) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"Enqueue\", namespace, name)\n}\n\n// Enqueue indicates an expected call of Enqueue.\nfunc (mr *MockSharedControllerMockRecorder) Enqueue(namespace, name any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Enqueue\", reflect.TypeOf((*MockSharedController)(nil).Enqueue), namespace, name)\n}\n\n// EnqueueAfter mocks base method.\nfunc (m *MockSharedController) EnqueueAfter(namespace, name string, delay time.Duration) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"EnqueueAfter\", namespace, name, delay)\n}\n\n// EnqueueAfter indicates an expected call of EnqueueAfter.\nfunc (mr *MockSharedControllerMockRecorder) EnqueueAfter(namespace, name, delay any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"EnqueueAfter\", reflect.TypeOf((*MockSharedController)(nil).EnqueueAfter), namespace, name, delay)\n}\n\n// EnqueueKey mocks base method.\nfunc (m *MockSharedController) EnqueueKey(key string) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"EnqueueKey\", key)\n}\n\n// EnqueueKey indicates an expected call of EnqueueKey.\nfunc (mr *MockSharedControllerMockRecorder) EnqueueKey(key any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"EnqueueKey\", reflect.TypeOf((*MockSharedController)(nil).EnqueueKey), key)\n}\n\n// Informer mocks base method.\nfunc (m *MockSharedController) Informer() cache0.SharedIndexInformer {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Informer\")\n\tret0, _ := ret[0].(cache0.SharedIndexInformer)\n\treturn ret0\n}\n\n// Informer indicates an expected call of Informer.\nfunc (mr *MockSharedControllerMockRecorder) Informer() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Informer\", reflect.TypeOf((*MockSharedController)(nil).Informer))\n}\n\n// RegisterHandler mocks base method.\nfunc (m *MockSharedController) RegisterHandler(ctx context.Context, name string, handler controller.SharedControllerHandler) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"RegisterHandler\", ctx, name, handler)\n}\n\n// RegisterHandler indicates an expected call of RegisterHandler.\nfunc (mr *MockSharedControllerMockRecorder) RegisterHandler(ctx, name, handler any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"RegisterHandler\", reflect.TypeOf((*MockSharedController)(nil).RegisterHandler), ctx, name, handler)\n}\n\n// Start mocks base method.\nfunc (m *MockSharedController) Start(ctx context.Context, workers int) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Start\", ctx, workers)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// Start indicates an expected call of Start.\nfunc (mr *MockSharedControllerMockRecorder) Start(ctx, workers any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Start\", reflect.TypeOf((*MockSharedController)(nil).Start), ctx, workers)\n}\n"
  },
  {
    "path": "pkg/generic/controller_test.go",
    "content": "// Mocks for this test are generated with the following command.\n//go:generate sh -c \"rm -f *Mocks_test.go\"\n//go:generate mockgen --build_flags=--mod=mod -package generic -destination ./controllerFactoryMocks_test.go github.com/rancher/lasso/pkg/controller SharedControllerFactory,SharedController\n//go:generate mockgen -package generic -destination ./clientMocks_test.go -source ./embeddedClient.go\n\npackage generic\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/mock/gomock\"\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"k8s.io/apimachinery/pkg/watch\"\n)\n\nconst (\n\tglobalTestPodName   = \"High-Noon-Harry\"\n\tglobalTestNamespace = \"rodeo\"\n\tglobalTestNodeName  = \"cowboy-server\"\n)\n\nvar (\n\t// Interface implementation complile time check\n\t_ ControllerInterface[*v1.Pod, *v1.PodList]              = &Controller[*v1.Pod, *v1.PodList]{}\n\t_ NonNamespacedControllerInterface[*v1.Pod, *v1.PodList] = &NonNamespacedController[*v1.Pod, *v1.PodList]{}\n\t_ ClientInterface[*v1.Pod, *v1.PodList]                  = &Controller[*v1.Pod, *v1.PodList]{}\n\t_ NonNamespacedClientInterface[*v1.Pod, *v1.PodList]     = &NonNamespacedController[*v1.Pod, *v1.PodList]{}\n\t_ CacheInterface[*v1.Pod]                                = &Cache[*v1.Pod]{}\n\t_ NonNamespacedCacheInterface[*v1.Pod]                   = &NonNamespacedCache[*v1.Pod]{}\n)\n\nvar errExpected = fmt.Errorf(\"test-error\")\n\nfunc TestController_Get(parentT *testing.T) {\n\tparentT.Parallel()\n\ttestNamespace := globalTestNamespace\n\tvar testController *Controller[*v1.Pod, *v1.PodList]\n\tvar testNonNamespaceController *NonNamespacedController[*v1.Pod, *v1.PodList]\n\ttest := func(t *testing.T) {\n\t\ttestOptions := metav1.GetOptions{\n\t\t\tResourceVersion: \"3\",\n\t\t}\n\t\tctrl := gomock.NewController(t)\n\t\tmockClient := NewMockembeddedClient(ctrl)\n\t\tpod := &v1.Pod{}\n\t\tmockClient.EXPECT().Get(context.TODO(), testNamespace, globalTestPodName, gomock.AssignableToTypeOf(pod), testOptions).DoAndReturn(\n\t\t\tfunc(ctx context.Context, namespace string, name string, result runtime.Object, options metav1.GetOptions) error {\n\t\t\t\tresultPod, ok := result.(*v1.Pod)\n\t\t\t\trequire.True(t, ok, \"Created result object was the incorrect type.\")\n\t\t\t\tresultPod.Spec.NodeName = globalTestNodeName\n\t\t\t\treturn nil\n\t\t\t})\n\t\tvar newPod *v1.Pod\n\t\tvar err error\n\t\tif testNamespace == metav1.NamespaceAll {\n\t\t\ttestNonNamespaceController = NewTestNonNamespacedController(ctrl, mockClient)\n\t\t\tnewPod, err = testNonNamespaceController.Get(globalTestPodName, testOptions)\n\t\t} else {\n\t\t\ttestController = NewTestController(ctrl, mockClient)\n\t\t\tnewPod, err = testController.Get(testNamespace, globalTestPodName, testOptions)\n\t\t}\n\t\trequire.NoError(t, err, \"Error when calling get.\")\n\t\trequire.Equal(t, globalTestNodeName, newPod.Spec.NodeName, \"Get call did not correctly persist pod changes from the embeddedClient.\")\n\n\t\tmockClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errExpected)\n\t\tif testNamespace == metav1.NamespaceAll {\n\t\t\t_, err = testNonNamespaceController.Get(globalTestPodName, testOptions)\n\t\t} else {\n\t\t\t_, err = testController.Get(testNamespace, globalTestPodName, testOptions)\n\t\t}\n\t\trequire.Error(t, err, \"Error from client.Get() was not propagated\")\n\t}\n\tparentT.Run(\"Namespaced\", test)\n\ttestNamespace = metav1.NamespaceAll\n\tparentT.Run(\"NonNamespaced\", test)\n}\n\nfunc TestController_List(parentT *testing.T) {\n\tparentT.Parallel()\n\ttestNamespace := globalTestNamespace\n\tvar testController *Controller[*v1.Pod, *v1.PodList]\n\tvar testNonNamespaceController *NonNamespacedController[*v1.Pod, *v1.PodList]\n\ttest := func(t *testing.T) {\n\t\ttestOptions := metav1.ListOptions{\n\t\t\tResourceVersion: \"3\",\n\t\t}\n\t\tctrl := gomock.NewController(t)\n\t\tmockClient := NewMockembeddedClient(ctrl)\n\t\tpod := &v1.PodList{}\n\t\tmockClient.EXPECT().List(context.TODO(), testNamespace, gomock.AssignableToTypeOf(pod), testOptions).DoAndReturn(\n\t\t\tfunc(ctx context.Context, namespace string, result runtime.Object, options metav1.ListOptions) error {\n\t\t\t\tpods, ok := result.(*v1.PodList)\n\t\t\t\trequire.True(t, ok, \"Created result object was the incorrect type.\")\n\t\t\t\tpods.Items = []v1.Pod{{}}\n\t\t\t\tpods.Items[0].Spec.NodeName = globalTestNodeName\n\t\t\t\treturn nil\n\t\t\t})\n\t\tvar newPods *v1.PodList\n\t\tvar err error\n\t\tif testNamespace == metav1.NamespaceAll {\n\t\t\ttestNonNamespaceController = NewTestNonNamespacedController(ctrl, mockClient)\n\t\t\tnewPods, err = testNonNamespaceController.List(testOptions)\n\t\t} else {\n\t\t\ttestController = NewTestController(ctrl, mockClient)\n\t\t\tnewPods, err = testController.List(testNamespace, testOptions)\n\t\t}\n\t\trequire.NoError(t, err, \"Error when calling list.\")\n\t\trequire.Equal(t, globalTestNodeName, newPods.Items[0].Spec.NodeName, \"List call did not correctly persist pod changes from the embeddedClient\")\n\n\t\tmockClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errExpected)\n\t\tif testNamespace == metav1.NamespaceAll {\n\t\t\t_, err = testNonNamespaceController.List(testOptions)\n\t\t} else {\n\t\t\t_, err = testController.List(testNamespace, testOptions)\n\t\t}\n\t\trequire.Error(t, err, \"Error from client.List(...) was not propagated\")\n\t}\n\tparentT.Run(\"Namespaced\", test)\n\ttestNamespace = metav1.NamespaceAll\n\tparentT.Run(\"NonNamespaced\", test)\n}\nfunc TestController_Watch(parentT *testing.T) {\n\tparentT.Parallel()\n\ttestNamespace := globalTestNamespace\n\tvar testController *Controller[*v1.Pod, *v1.PodList]\n\tvar testNonNamespaceController *NonNamespacedController[*v1.Pod, *v1.PodList]\n\ttest := func(t *testing.T) {\n\t\ttestOptions := metav1.ListOptions{\n\t\t\tResourceVersion: \"3\",\n\t\t}\n\n\t\tctrl := gomock.NewController(t)\n\t\tmockClient := NewMockembeddedClient(ctrl)\n\t\temptyWatch := watch.NewEmptyWatch()\n\t\tmockClient.EXPECT().Watch(context.TODO(), testNamespace, testOptions).Return(emptyWatch, nil)\n\n\t\tvar watchInterface watch.Interface\n\t\tvar err error\n\t\tif testNamespace == metav1.NamespaceAll {\n\t\t\ttestNonNamespaceController = NewTestNonNamespacedController(ctrl, mockClient)\n\t\t\twatchInterface, err = testNonNamespaceController.Watch(testOptions)\n\t\t} else {\n\t\t\ttestController = NewTestController(ctrl, mockClient)\n\t\t\twatchInterface, err = testController.Watch(testNamespace, testOptions)\n\t\t}\n\t\trequire.NoError(t, err, \"Error when calling watch.\")\n\t\trequire.Equal(t, emptyWatch, watchInterface, \"Watch call did not send the watch interface from the request\")\n\n\t\tmockClient.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errExpected)\n\t\tif testNamespace == metav1.NamespaceAll {\n\t\t\t_, err = testNonNamespaceController.Watch(testOptions)\n\t\t} else {\n\t\t\t_, err = testController.Watch(testNamespace, testOptions)\n\t\t}\n\t\trequire.Error(t, err, \"Error from client.Watch(...) was not propagated\")\n\t}\n\tparentT.Run(\"Namespaced\", test)\n\ttestNamespace = metav1.NamespaceAll\n\tparentT.Run(\"NonNamespaced\", test)\n\n}\n\nfunc TestController_Patch(parentT *testing.T) {\n\tparentT.Parallel()\n\ttestNamespace := globalTestNamespace\n\tvar testController *Controller[*v1.Pod, *v1.PodList]\n\tvar testNonNamespaceController *NonNamespacedController[*v1.Pod, *v1.PodList]\n\ttest := func(t *testing.T) {\n\t\ttestOptions := metav1.PatchOptions{}\n\t\ttestPT := types.JSONPatchType\n\t\ttestData := []byte(globalTestNodeName)\n\t\tsubResources := []string{\"sub\", \"resources\"}\n\t\tctrl := gomock.NewController(t)\n\t\tmockClient := NewMockembeddedClient(ctrl)\n\t\tpod := &v1.Pod{}\n\t\tmockClient.EXPECT().Patch(context.TODO(), testNamespace, globalTestPodName, testPT, testData, gomock.AssignableToTypeOf(pod), gomock.AssignableToTypeOf(testOptions), subResources).DoAndReturn(\n\t\t\tfunc(ctx context.Context, namespace string, name string, pt types.PatchType, data []byte, result runtime.Object, opts metav1.PatchOptions, subresources ...string) error {\n\t\t\t\tresultPod, ok := result.(*v1.Pod)\n\t\t\t\trequire.True(t, ok, \"Created result object was the incorrect type.\")\n\t\t\t\tresultPod.Spec.NodeName = globalTestNodeName\n\t\t\t\trequire.Equal(t, testOptions, opts, \"Patch received unexpected patch options.\")\n\t\t\t\treturn nil\n\t\t\t})\n\t\tvar newPod *v1.Pod\n\t\tvar err error\n\t\tif testNamespace == metav1.NamespaceAll {\n\t\t\ttestNonNamespaceController = NewTestNonNamespacedController(ctrl, mockClient)\n\t\t\tnewPod, err = testNonNamespaceController.Patch(globalTestPodName, testPT, testData, subResources...)\n\t\t} else {\n\t\t\ttestController = NewTestController(ctrl, mockClient)\n\t\t\tnewPod, err = testController.Patch(testNamespace, globalTestPodName, testPT, testData, subResources...)\n\t\t}\n\t\trequire.NoError(t, err, \"Error when calling patch.\")\n\t\trequire.Equal(t, globalTestNodeName, newPod.Spec.NodeName, \"Patch call did not correctly persist pod changes from the embeddedClient\")\n\n\t\tmockClient.EXPECT().Patch(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errExpected)\n\t\tif testNamespace == metav1.NamespaceAll {\n\t\t\t_, err = testNonNamespaceController.Patch(globalTestPodName, testPT, testData, subResources...)\n\t\t} else {\n\t\t\t_, err = testController.Patch(testNamespace, globalTestPodName, testPT, testData, subResources...)\n\t\t}\n\t\trequire.Error(t, err, \"Error from client.Patch(...) was not propagated\")\n\t}\n\tparentT.Run(\"Namespaced\", test)\n\ttestNamespace = metav1.NamespaceAll\n\tparentT.Run(\"NonNamespaced\", test)\n\n}\n\nfunc TestController_Update(t *testing.T) {\n\tt.Parallel()\n\ttestOptions := metav1.UpdateOptions{}\n\tctrl := gomock.NewController(t)\n\tmockClient := NewMockembeddedClient(ctrl)\n\tpod := &v1.Pod{}\n\tmockClient.EXPECT().Update(context.TODO(), globalTestNamespace, gomock.AssignableToTypeOf(pod), gomock.AssignableToTypeOf(pod), gomock.AssignableToTypeOf(testOptions)).DoAndReturn(\n\t\tfunc(ctx context.Context, namespace string, obj runtime.Object, result runtime.Object, opts metav1.UpdateOptions) error {\n\t\t\tupdatePod, ok := obj.(*v1.Pod)\n\t\t\trequire.True(t, ok, \"Obj to update is the incorrect type.\")\n\t\t\trequire.Equal(t, updatePod, pod, \"Incorrect obj to update was sent to the client.\")\n\n\t\t\tresultPod, ok := result.(*v1.Pod)\n\t\t\trequire.True(t, ok, \"Created result object was the incorrect type.\")\n\t\t\tresultPod.Spec.NodeName = globalTestNodeName\n\t\t\trequire.Equal(t, testOptions, opts, \"Update received unexpected update options.\")\n\t\t\treturn nil\n\t\t})\n\ttestController := NewTestController(ctrl, mockClient)\n\tpod.Namespace = globalTestNamespace\n\tnewPod, err := testController.Update(pod)\n\trequire.NoError(t, err, \"Error when calling update.\")\n\trequire.Equal(t, globalTestNodeName, newPod.Spec.NodeName, \"Update call did not correctly persist pod changes from the embeddedClient\")\n\n\tmockClient.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errExpected)\n\t_, err = testController.Update(pod)\n\trequire.Error(t, err, \"Error from client.Update(...) was not propagated\")\n}\n\nfunc TestController_UpdateStatus(t *testing.T) {\n\tt.Parallel()\n\ttestOptions := metav1.UpdateOptions{}\n\tctrl := gomock.NewController(t)\n\tmockClient := NewMockembeddedClient(ctrl)\n\tpod := &v1.Pod{}\n\tmockClient.EXPECT().UpdateStatus(context.TODO(), globalTestNamespace, gomock.AssignableToTypeOf(pod), gomock.AssignableToTypeOf(pod), gomock.AssignableToTypeOf(testOptions)).DoAndReturn(\n\t\tfunc(ctx context.Context, namespace string, obj runtime.Object, result runtime.Object, opts metav1.UpdateOptions) error {\n\t\t\tupdatePod, ok := obj.(*v1.Pod)\n\t\t\trequire.True(t, ok, \"obj to updateStatus is the incorrect type\")\n\t\t\trequire.Equal(t, updatePod, pod, \"incorrect obj to update was sent to the client\")\n\t\t\tresultPod, ok := result.(*v1.Pod)\n\t\t\trequire.True(t, ok, \"Created result object was the incorrect type.\")\n\t\t\tresultPod.Status.Reason = globalTestNodeName\n\t\t\trequire.Equal(t, testOptions, opts, \"UpdateStatus received unexpected update options.\")\n\t\t\treturn nil\n\t\t})\n\ttestController := NewTestController(ctrl, mockClient)\n\tpod.Namespace = globalTestNamespace\n\tnewPod, err := testController.UpdateStatus(pod)\n\trequire.NoError(t, err, \"Error when calling UpdateStatus(...).\")\n\trequire.Equal(t, globalTestNodeName, newPod.Status.Reason, \"UpdateStatus call did not correctly persist pod changes from the embeddedClient\")\n\n\tmockClient.EXPECT().UpdateStatus(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errExpected)\n\t_, err = testController.UpdateStatus(pod)\n\trequire.Error(t, err, \"Error from client.UpdateStatus(...) was not propagated\")\n}\n\nfunc TestController_Create(t *testing.T) {\n\tt.Parallel()\n\ttestOptions := metav1.CreateOptions{}\n\tctrl := gomock.NewController(t)\n\tmockClient := NewMockembeddedClient(ctrl)\n\tpod := &v1.Pod{}\n\tmockClient.EXPECT().Create(context.TODO(), globalTestNamespace, gomock.AssignableToTypeOf(pod), gomock.AssignableToTypeOf(pod), gomock.AssignableToTypeOf(testOptions)).DoAndReturn(\n\t\tfunc(ctx context.Context, namespace string, obj runtime.Object, result runtime.Object, opts metav1.CreateOptions) error {\n\t\t\tcreatePod, ok := obj.(*v1.Pod)\n\t\t\trequire.True(t, ok, \"obj to create is the incorrect type\")\n\t\t\trequire.Equal(t, createPod, pod)\n\t\t\tresultPod, ok := result.(*v1.Pod)\n\t\t\trequire.True(t, ok, \"Created result object was the incorrect type.\")\n\t\t\tresultPod.Spec.NodeName = globalTestNodeName\n\t\t\trequire.Equal(t, testOptions, opts, \"Create received unexpected create options.\")\n\t\t\treturn nil\n\t\t})\n\ttestController := NewTestController(ctrl, mockClient)\n\tpod.Namespace = globalTestNamespace\n\tnewPod, err := testController.Create(pod)\n\trequire.NoError(t, err, \"Error when calling create.\")\n\trequire.Equal(t, globalTestNodeName, newPod.Spec.NodeName, \"Create call did not correctly persist pod changes from the embeddedClient\")\n\n\tmockClient.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errExpected)\n\t_, err = testController.Create(pod)\n\trequire.Error(t, err, \"Error from client.Create(...) was not propagated\")\n}\n\nfunc TestController_Delete(parentT *testing.T) {\n\tparentT.Parallel()\n\ttestNamespace := globalTestNamespace\n\tvar testController *Controller[*v1.Pod, *v1.PodList]\n\tvar testNonNamespaceController *NonNamespacedController[*v1.Pod, *v1.PodList]\n\ttest := func(t *testing.T) {\n\t\ttestOptions := metav1.DeleteOptions{}\n\t\tctrl := gomock.NewController(t)\n\t\tmockClient := NewMockembeddedClient(ctrl)\n\t\tmockClient.EXPECT().Delete(context.TODO(), testNamespace, globalTestPodName, testOptions).Return(nil)\n\t\tvar err error\n\t\tif testNamespace == metav1.NamespaceAll {\n\t\t\ttestNonNamespaceController = NewTestNonNamespacedController(ctrl, mockClient)\n\t\t\terr = testNonNamespaceController.Delete(globalTestPodName, &testOptions)\n\t\t} else {\n\t\t\ttestController = NewTestController(ctrl, mockClient)\n\t\t\terr = testController.Delete(testNamespace, globalTestPodName, &testOptions)\n\t\t}\n\t\trequire.NoError(t, err, \"Error when calling delete.\")\n\n\t\tmockClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errExpected)\n\t\tif testNamespace == metav1.NamespaceAll {\n\t\t\terr = testNonNamespaceController.Delete(globalTestPodName, &testOptions)\n\t\t} else {\n\t\t\terr = testController.Delete(testNamespace, globalTestPodName, &testOptions)\n\t\t}\n\t\trequire.Error(t, err, \"Error from client.Delete(...) was not propagated\")\n\t}\n\n\tparentT.Run(\"Namespaced\", test)\n\ttestNamespace = metav1.NamespaceAll\n\tparentT.Run(\"NonNamespaced\", test)\n}\n\nfunc NewTestController(ctrl *gomock.Controller, testClient embeddedClient) *Controller[*v1.Pod, *v1.PodList] {\n\t// create mock that allows the new function to run without panic\n\tmockFactory := NewMockSharedControllerFactory(ctrl)\n\tmockController := NewMockSharedController(ctrl)\n\tmockController.EXPECT().Client().Return(nil)\n\tmockFactory.EXPECT().ForResourceKind(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockController)\n\tnewController := NewController[*v1.Pod, *v1.PodList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"Pod\"}, \"pods\", true, mockFactory)\n\t// override the nil controller client with the test client\n\tnewController.embeddedClient = testClient\n\treturn newController\n}\n\nfunc NewTestNonNamespacedController(ctrl *gomock.Controller, testClient embeddedClient) *NonNamespacedController[*v1.Pod, *v1.PodList] {\n\t// create mock that allows the new function to run without panic\n\tmockFactory := NewMockSharedControllerFactory(ctrl)\n\tmockController := NewMockSharedController(ctrl)\n\tmockController.EXPECT().Client().Return(nil)\n\tmockFactory.EXPECT().ForResourceKind(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockController)\n\tnewController := NewNonNamespacedController[*v1.Pod, *v1.PodList](schema.GroupVersionKind{Group: \"\", Version: \"v1\", Kind: \"Pod\"}, \"pods\", mockFactory)\n\t// override the nil controller client with the test client\n\tnewController.embeddedClient = testClient\n\treturn newController\n}\n"
  },
  {
    "path": "pkg/generic/embeddedClient.go",
    "content": "package generic\n\nimport (\n\t\"context\"\n\n\t\"github.com/rancher/lasso/pkg/client\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"k8s.io/apimachinery/pkg/watch\"\n\t\"k8s.io/client-go/rest\"\n)\n\n// embeddedClient is the interface for the lasso Client used by the controller.\ntype embeddedClient interface {\n\tCreate(ctx context.Context, namespace string, obj runtime.Object, result runtime.Object, opts metav1.CreateOptions) (err error)\n\tDelete(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error\n\tGet(ctx context.Context, namespace string, name string, result runtime.Object, options metav1.GetOptions) (err error)\n\tList(ctx context.Context, namespace string, result runtime.Object, opts metav1.ListOptions) (err error)\n\tPatch(ctx context.Context, namespace string, name string, pt types.PatchType, data []byte, result runtime.Object, opts metav1.PatchOptions, subresources ...string) (err error)\n\tUpdate(ctx context.Context, namespace string, obj runtime.Object, result runtime.Object, opts metav1.UpdateOptions) (err error)\n\tUpdateStatus(ctx context.Context, namespace string, obj runtime.Object, result runtime.Object, opts metav1.UpdateOptions) (err error)\n\tWatch(ctx context.Context, namespace string, opts metav1.ListOptions) (watch.Interface, error)\n\tWithImpersonation(impersonate rest.ImpersonationConfig) (*client.Client, error)\n\tDeleteCollection(ctx context.Context, namespace string, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error\n}\n"
  },
  {
    "path": "pkg/generic/factory.go",
    "content": "package generic\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/lasso/pkg/log\"\n\t\"github.com/sirupsen/logrus\"\n\n\t\"github.com/rancher/lasso/pkg/cache\"\n\t\"github.com/rancher/lasso/pkg/client\"\n\t\"github.com/rancher/lasso/pkg/controller\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\tutilruntime \"k8s.io/apimachinery/pkg/util/runtime\"\n\t\"k8s.io/client-go/rest\"\n)\n\nfunc init() {\n\tlog.Infof = logrus.Infof\n\tlog.Errorf = logrus.Errorf\n}\n\ntype Factory struct {\n\tlock              sync.Mutex\n\tcacheFactory      cache.SharedCacheFactory\n\tcontrollerFactory controller.SharedControllerFactory\n\tthreadiness       map[schema.GroupVersionKind]int\n\tconfig            *rest.Config\n\topts              FactoryOptions\n}\n\ntype FactoryOptions struct {\n\tNamespace               string\n\tResync                  time.Duration\n\tSharedCacheFactory      cache.SharedCacheFactory\n\tSharedControllerFactory controller.SharedControllerFactory\n\tHealthCallback          func(bool)\n}\n\nfunc NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {\n\tif opts == nil {\n\t\topts = &FactoryOptions{}\n\t}\n\n\tf := &Factory{\n\t\tconfig:            config,\n\t\tthreadiness:       map[schema.GroupVersionKind]int{},\n\t\tcacheFactory:      opts.SharedCacheFactory,\n\t\tcontrollerFactory: opts.SharedControllerFactory,\n\t\topts:              *opts,\n\t}\n\n\tif f.cacheFactory == nil && f.controllerFactory != nil {\n\t\tf.cacheFactory = f.controllerFactory.SharedCacheFactory()\n\t}\n\n\treturn f, nil\n}\n\nfunc (c *Factory) SetThreadiness(gvk schema.GroupVersionKind, threadiness int) {\n\tc.threadiness[gvk] = threadiness\n}\n\nfunc (c *Factory) ControllerFactory() controller.SharedControllerFactory {\n\terr := c.setControllerFactoryWithLock()\n\tutilruntime.Must(err)\n\treturn c.controllerFactory\n}\n\nfunc (c *Factory) setControllerFactoryWithLock() error {\n\tc.lock.Lock()\n\tdefer c.lock.Unlock()\n\n\tif c.controllerFactory != nil {\n\t\treturn nil\n\t}\n\n\tcacheFactory := c.cacheFactory\n\tif cacheFactory == nil {\n\t\tclient, err := client.NewSharedClientFactory(c.config, &client.SharedClientFactoryOptions{\n\t\t\tScheme: schemes.All,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tcacheFactory = cache.NewSharedCachedFactory(client, &cache.SharedCacheFactoryOptions{\n\t\t\tDefaultNamespace: c.opts.Namespace,\n\t\t\tDefaultResync:    c.opts.Resync,\n\t\t\tHealthCallback:   c.opts.HealthCallback,\n\t\t})\n\t}\n\n\tc.cacheFactory = cacheFactory\n\tc.controllerFactory = controller.NewSharedControllerFactory(cacheFactory, &controller.SharedControllerFactoryOptions{\n\t\tKindWorkers: c.threadiness,\n\t})\n\n\treturn nil\n}\n\nfunc (c *Factory) Sync(ctx context.Context) error {\n\tif c.cacheFactory != nil {\n\t\tif err := c.cacheFactory.Start(ctx); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsyncStatus := c.cacheFactory.WaitForCacheSync(ctx)\n\t\tfor informerType, synced := range syncStatus {\n\t\t\tif !synced {\n\t\t\t\treturn fmt.Errorf(\"cache sync failed for informer %s\", informerType.Kind)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c *Factory) Start(ctx context.Context, defaultThreadiness int) error {\n\tif err := c.Sync(ctx); err != nil {\n\t\treturn err\n\t}\n\n\tif c.controllerFactory != nil {\n\t\treturn c.controllerFactory.Start(ctx, defaultThreadiness)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/generic/fake/README.md",
    "content": "# Generic Controller, Client, and Cache Mocks\n\nThis package leverages https://github.com/golang/mock for using generic mocks in tests.\n[gomock](https://pkg.go.dev/github.com/golang/mock/gomock) holds more specific information on different ways to use gomock in test.\n\n## Usage\nThis package has four entry points for creating a mock controller, client, or cache interface.<br>\n Note: A mock controller will implement both a generic.ControllerInterface and a generic.ClientInterface.\n- `NewMockControllerInterface[T runtime.Object, TList runtime.Object](*gomock.Controller)`\n- `NewMockNonNamespacedControllerInterface[T runtime.Object, TList runtime.Object](*gomock.Controller)`\n- `NewCacheInterface[T runtime.Object](*gomock.Controller)`\n- `NewNonNamespaceCacheInterface[T runtime.Object](*gomock.Controller)`\n\n## Examples\n\nExample use of generic/fake with a generated Deployment Controller.\n``` golang\n// Generated controller interface to mock.\ntype DeploymentController interface {\n\tgeneric.ControllerInterface[*v1.Deployment, *v1.DeploymentList]\n}\n```\n``` golang\n// Example Test Function \nimport (\n\t\"testing\"\n    \n\t\"github.com/golang/mock/gomock\"\n\twranglerv1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/rbac/v1\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic/fake\"\n\tv1 \"k8s.io/api/apps/v1\"\n)\n\nfunc TestController(t *testing.T){\n    // Create gomock controller. This is used by the gomock library.\n\tgomockCtrl := gomock.NewController(t)\n\n    // Create a new Generic Controller Mock with type apps1.Deployment.\n\tdeployMock := fake.NewMockControllerInterface[*v1.Deployment, *v1.DeploymentList](ctrl)\n\n    // Define expected calls to our mock controller using gomock.\n    deployMock.EXPECT().Enqueue(\"test-namespace\", \"test-name\").AnyTimes()\n\n    // Start Test Code.\n    // .\n    // . \n    // .\n\n    // Test calls Enqueue with expected parameters nothing happens.\n    deployMock.Enqueue(\"test-namespace\", \"test-name\")\n\n    // Test calls Enqueue with unexpected parameters.\n    // gomock will fail the test because it did not expect the call.\n    deployMock.Enqueue(\"unexpected-namespace\", \"unexpected-name\")\n}\n```\n\n### NonNamespacedController\n```golang\nctrl := gomock.NewController(t)\n\nmock := fake.NewMockNonNamespacedControllerInterface[*v3.RoleTemplate, *v3.RoleTemplateList](ctrl)\n\nmock.EXPECT().List(gomock.Any()).Return(nil, nil)\n```\n\n## Fake Generation\nThis package was generated with `mockgen` (see [`generate.go`](./generate.go)), just run:\n```shell\ngo generate ./...\n```\nto recreate them.\n\n#### Caveats\n\nDue to incomplete support for generics, some modifications over the original file need to be applied for the generation\nto succeeed.\n#### `controller.go`\n1. Comment out the `comparable` in RuntimeMetaObject"
  },
  {
    "path": "pkg/generic/fake/cache.go",
    "content": "// Code generated by MockGen. DO NOT EDIT.\n// Source: ../cache.go\n//\n// Generated by this command:\n//\n//\tmockgen -package fake -destination ./cache.go -source ../cache.go\n//\n\n// Package fake is a generated GoMock package.\npackage fake\n\nimport (\n\treflect \"reflect\"\n\n\tgeneric \"github.com/rancher/wrangler/v3/pkg/generic\"\n\tgomock \"go.uber.org/mock/gomock\"\n\tlabels \"k8s.io/apimachinery/pkg/labels\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n)\n\n// MockCacheInterface is a mock of CacheInterface interface.\ntype MockCacheInterface[T runtime.Object] struct {\n\tctrl     *gomock.Controller\n\trecorder *MockCacheInterfaceMockRecorder[T]\n\tisgomock struct{}\n}\n\n// MockCacheInterfaceMockRecorder is the mock recorder for MockCacheInterface.\ntype MockCacheInterfaceMockRecorder[T runtime.Object] struct {\n\tmock *MockCacheInterface[T]\n}\n\n// NewMockCacheInterface creates a new mock instance.\nfunc NewMockCacheInterface[T runtime.Object](ctrl *gomock.Controller) *MockCacheInterface[T] {\n\tmock := &MockCacheInterface[T]{ctrl: ctrl}\n\tmock.recorder = &MockCacheInterfaceMockRecorder[T]{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use.\nfunc (m *MockCacheInterface[T]) EXPECT() *MockCacheInterfaceMockRecorder[T] {\n\treturn m.recorder\n}\n\n// AddIndexer mocks base method.\nfunc (m *MockCacheInterface[T]) AddIndexer(indexName string, indexer generic.Indexer[T]) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"AddIndexer\", indexName, indexer)\n}\n\n// AddIndexer indicates an expected call of AddIndexer.\nfunc (mr *MockCacheInterfaceMockRecorder[T]) AddIndexer(indexName, indexer any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"AddIndexer\", reflect.TypeOf((*MockCacheInterface[T])(nil).AddIndexer), indexName, indexer)\n}\n\n// Get mocks base method.\nfunc (m *MockCacheInterface[T]) Get(namespace, name string) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Get\", namespace, name)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Get indicates an expected call of Get.\nfunc (mr *MockCacheInterfaceMockRecorder[T]) Get(namespace, name any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Get\", reflect.TypeOf((*MockCacheInterface[T])(nil).Get), namespace, name)\n}\n\n// GetByIndex mocks base method.\nfunc (m *MockCacheInterface[T]) GetByIndex(indexName, key string) ([]T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetByIndex\", indexName, key)\n\tret0, _ := ret[0].([]T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// GetByIndex indicates an expected call of GetByIndex.\nfunc (mr *MockCacheInterfaceMockRecorder[T]) GetByIndex(indexName, key any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetByIndex\", reflect.TypeOf((*MockCacheInterface[T])(nil).GetByIndex), indexName, key)\n}\n\n// List mocks base method.\nfunc (m *MockCacheInterface[T]) List(namespace string, selector labels.Selector) ([]T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"List\", namespace, selector)\n\tret0, _ := ret[0].([]T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// List indicates an expected call of List.\nfunc (mr *MockCacheInterfaceMockRecorder[T]) List(namespace, selector any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"List\", reflect.TypeOf((*MockCacheInterface[T])(nil).List), namespace, selector)\n}\n\n// MockNonNamespacedCacheInterface is a mock of NonNamespacedCacheInterface interface.\ntype MockNonNamespacedCacheInterface[T runtime.Object] struct {\n\tctrl     *gomock.Controller\n\trecorder *MockNonNamespacedCacheInterfaceMockRecorder[T]\n\tisgomock struct{}\n}\n\n// MockNonNamespacedCacheInterfaceMockRecorder is the mock recorder for MockNonNamespacedCacheInterface.\ntype MockNonNamespacedCacheInterfaceMockRecorder[T runtime.Object] struct {\n\tmock *MockNonNamespacedCacheInterface[T]\n}\n\n// NewMockNonNamespacedCacheInterface creates a new mock instance.\nfunc NewMockNonNamespacedCacheInterface[T runtime.Object](ctrl *gomock.Controller) *MockNonNamespacedCacheInterface[T] {\n\tmock := &MockNonNamespacedCacheInterface[T]{ctrl: ctrl}\n\tmock.recorder = &MockNonNamespacedCacheInterfaceMockRecorder[T]{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use.\nfunc (m *MockNonNamespacedCacheInterface[T]) EXPECT() *MockNonNamespacedCacheInterfaceMockRecorder[T] {\n\treturn m.recorder\n}\n\n// AddIndexer mocks base method.\nfunc (m *MockNonNamespacedCacheInterface[T]) AddIndexer(indexName string, indexer generic.Indexer[T]) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"AddIndexer\", indexName, indexer)\n}\n\n// AddIndexer indicates an expected call of AddIndexer.\nfunc (mr *MockNonNamespacedCacheInterfaceMockRecorder[T]) AddIndexer(indexName, indexer any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"AddIndexer\", reflect.TypeOf((*MockNonNamespacedCacheInterface[T])(nil).AddIndexer), indexName, indexer)\n}\n\n// Get mocks base method.\nfunc (m *MockNonNamespacedCacheInterface[T]) Get(name string) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Get\", name)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Get indicates an expected call of Get.\nfunc (mr *MockNonNamespacedCacheInterfaceMockRecorder[T]) Get(name any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Get\", reflect.TypeOf((*MockNonNamespacedCacheInterface[T])(nil).Get), name)\n}\n\n// GetByIndex mocks base method.\nfunc (m *MockNonNamespacedCacheInterface[T]) GetByIndex(indexName, key string) ([]T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetByIndex\", indexName, key)\n\tret0, _ := ret[0].([]T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// GetByIndex indicates an expected call of GetByIndex.\nfunc (mr *MockNonNamespacedCacheInterfaceMockRecorder[T]) GetByIndex(indexName, key any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetByIndex\", reflect.TypeOf((*MockNonNamespacedCacheInterface[T])(nil).GetByIndex), indexName, key)\n}\n\n// List mocks base method.\nfunc (m *MockNonNamespacedCacheInterface[T]) List(selector labels.Selector) ([]T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"List\", selector)\n\tret0, _ := ret[0].([]T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// List indicates an expected call of List.\nfunc (mr *MockNonNamespacedCacheInterfaceMockRecorder[T]) List(selector any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"List\", reflect.TypeOf((*MockNonNamespacedCacheInterface[T])(nil).List), selector)\n}\n"
  },
  {
    "path": "pkg/generic/fake/controller.go",
    "content": "// Code generated by MockGen. DO NOT EDIT.\n// Source: ../controller.go\n//\n// Generated by this command:\n//\n//\tmockgen -package fake -destination ./controller.go -source ../controller.go\n//\n\n// Package fake is a generated GoMock package.\npackage fake\n\nimport (\n\tcontext \"context\"\n\treflect \"reflect\"\n\ttime \"time\"\n\n\tgeneric \"github.com/rancher/wrangler/v3/pkg/generic\"\n\tgomock \"go.uber.org/mock/gomock\"\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n\tschema \"k8s.io/apimachinery/pkg/runtime/schema\"\n\ttypes \"k8s.io/apimachinery/pkg/types\"\n\twatch \"k8s.io/apimachinery/pkg/watch\"\n\trest \"k8s.io/client-go/rest\"\n\tcache \"k8s.io/client-go/tools/cache\"\n)\n\n// MockControllerMeta is a mock of ControllerMeta interface.\ntype MockControllerMeta struct {\n\tctrl     *gomock.Controller\n\trecorder *MockControllerMetaMockRecorder\n\tisgomock struct{}\n}\n\n// MockControllerMetaMockRecorder is the mock recorder for MockControllerMeta.\ntype MockControllerMetaMockRecorder struct {\n\tmock *MockControllerMeta\n}\n\n// NewMockControllerMeta creates a new mock instance.\nfunc NewMockControllerMeta(ctrl *gomock.Controller) *MockControllerMeta {\n\tmock := &MockControllerMeta{ctrl: ctrl}\n\tmock.recorder = &MockControllerMetaMockRecorder{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use.\nfunc (m *MockControllerMeta) EXPECT() *MockControllerMetaMockRecorder {\n\treturn m.recorder\n}\n\n// AddGenericHandler mocks base method.\nfunc (m *MockControllerMeta) AddGenericHandler(ctx context.Context, name string, handler generic.Handler) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"AddGenericHandler\", ctx, name, handler)\n}\n\n// AddGenericHandler indicates an expected call of AddGenericHandler.\nfunc (mr *MockControllerMetaMockRecorder) AddGenericHandler(ctx, name, handler any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"AddGenericHandler\", reflect.TypeOf((*MockControllerMeta)(nil).AddGenericHandler), ctx, name, handler)\n}\n\n// AddGenericRemoveHandler mocks base method.\nfunc (m *MockControllerMeta) AddGenericRemoveHandler(ctx context.Context, name string, handler generic.Handler) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"AddGenericRemoveHandler\", ctx, name, handler)\n}\n\n// AddGenericRemoveHandler indicates an expected call of AddGenericRemoveHandler.\nfunc (mr *MockControllerMetaMockRecorder) AddGenericRemoveHandler(ctx, name, handler any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"AddGenericRemoveHandler\", reflect.TypeOf((*MockControllerMeta)(nil).AddGenericRemoveHandler), ctx, name, handler)\n}\n\n// GroupVersionKind mocks base method.\nfunc (m *MockControllerMeta) GroupVersionKind() schema.GroupVersionKind {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GroupVersionKind\")\n\tret0, _ := ret[0].(schema.GroupVersionKind)\n\treturn ret0\n}\n\n// GroupVersionKind indicates an expected call of GroupVersionKind.\nfunc (mr *MockControllerMetaMockRecorder) GroupVersionKind() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GroupVersionKind\", reflect.TypeOf((*MockControllerMeta)(nil).GroupVersionKind))\n}\n\n// Informer mocks base method.\nfunc (m *MockControllerMeta) Informer() cache.SharedIndexInformer {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Informer\")\n\tret0, _ := ret[0].(cache.SharedIndexInformer)\n\treturn ret0\n}\n\n// Informer indicates an expected call of Informer.\nfunc (mr *MockControllerMetaMockRecorder) Informer() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Informer\", reflect.TypeOf((*MockControllerMeta)(nil).Informer))\n}\n\n// Updater mocks base method.\nfunc (m *MockControllerMeta) Updater() generic.Updater {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Updater\")\n\tret0, _ := ret[0].(generic.Updater)\n\treturn ret0\n}\n\n// Updater indicates an expected call of Updater.\nfunc (mr *MockControllerMetaMockRecorder) Updater() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Updater\", reflect.TypeOf((*MockControllerMeta)(nil).Updater))\n}\n\n// MockRuntimeMetaObject is a mock of RuntimeMetaObject interface.\ntype MockRuntimeMetaObject struct {\n\tctrl     *gomock.Controller\n\trecorder *MockRuntimeMetaObjectMockRecorder\n\tisgomock struct{}\n}\n\n// MockRuntimeMetaObjectMockRecorder is the mock recorder for MockRuntimeMetaObject.\ntype MockRuntimeMetaObjectMockRecorder struct {\n\tmock *MockRuntimeMetaObject\n}\n\n// NewMockRuntimeMetaObject creates a new mock instance.\nfunc NewMockRuntimeMetaObject(ctrl *gomock.Controller) *MockRuntimeMetaObject {\n\tmock := &MockRuntimeMetaObject{ctrl: ctrl}\n\tmock.recorder = &MockRuntimeMetaObjectMockRecorder{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use.\nfunc (m *MockRuntimeMetaObject) EXPECT() *MockRuntimeMetaObjectMockRecorder {\n\treturn m.recorder\n}\n\n// DeepCopyObject mocks base method.\nfunc (m *MockRuntimeMetaObject) DeepCopyObject() runtime.Object {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"DeepCopyObject\")\n\tret0, _ := ret[0].(runtime.Object)\n\treturn ret0\n}\n\n// DeepCopyObject indicates an expected call of DeepCopyObject.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) DeepCopyObject() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"DeepCopyObject\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).DeepCopyObject))\n}\n\n// GetAnnotations mocks base method.\nfunc (m *MockRuntimeMetaObject) GetAnnotations() map[string]string {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetAnnotations\")\n\tret0, _ := ret[0].(map[string]string)\n\treturn ret0\n}\n\n// GetAnnotations indicates an expected call of GetAnnotations.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetAnnotations() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetAnnotations\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetAnnotations))\n}\n\n// GetCreationTimestamp mocks base method.\nfunc (m *MockRuntimeMetaObject) GetCreationTimestamp() v1.Time {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetCreationTimestamp\")\n\tret0, _ := ret[0].(v1.Time)\n\treturn ret0\n}\n\n// GetCreationTimestamp indicates an expected call of GetCreationTimestamp.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetCreationTimestamp() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetCreationTimestamp\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetCreationTimestamp))\n}\n\n// GetDeletionGracePeriodSeconds mocks base method.\nfunc (m *MockRuntimeMetaObject) GetDeletionGracePeriodSeconds() *int64 {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetDeletionGracePeriodSeconds\")\n\tret0, _ := ret[0].(*int64)\n\treturn ret0\n}\n\n// GetDeletionGracePeriodSeconds indicates an expected call of GetDeletionGracePeriodSeconds.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetDeletionGracePeriodSeconds() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetDeletionGracePeriodSeconds\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetDeletionGracePeriodSeconds))\n}\n\n// GetDeletionTimestamp mocks base method.\nfunc (m *MockRuntimeMetaObject) GetDeletionTimestamp() *v1.Time {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetDeletionTimestamp\")\n\tret0, _ := ret[0].(*v1.Time)\n\treturn ret0\n}\n\n// GetDeletionTimestamp indicates an expected call of GetDeletionTimestamp.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetDeletionTimestamp() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetDeletionTimestamp\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetDeletionTimestamp))\n}\n\n// GetFinalizers mocks base method.\nfunc (m *MockRuntimeMetaObject) GetFinalizers() []string {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetFinalizers\")\n\tret0, _ := ret[0].([]string)\n\treturn ret0\n}\n\n// GetFinalizers indicates an expected call of GetFinalizers.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetFinalizers() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetFinalizers\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetFinalizers))\n}\n\n// GetGenerateName mocks base method.\nfunc (m *MockRuntimeMetaObject) GetGenerateName() string {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetGenerateName\")\n\tret0, _ := ret[0].(string)\n\treturn ret0\n}\n\n// GetGenerateName indicates an expected call of GetGenerateName.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetGenerateName() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetGenerateName\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetGenerateName))\n}\n\n// GetGeneration mocks base method.\nfunc (m *MockRuntimeMetaObject) GetGeneration() int64 {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetGeneration\")\n\tret0, _ := ret[0].(int64)\n\treturn ret0\n}\n\n// GetGeneration indicates an expected call of GetGeneration.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetGeneration() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetGeneration\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetGeneration))\n}\n\n// GetLabels mocks base method.\nfunc (m *MockRuntimeMetaObject) GetLabels() map[string]string {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetLabels\")\n\tret0, _ := ret[0].(map[string]string)\n\treturn ret0\n}\n\n// GetLabels indicates an expected call of GetLabels.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetLabels() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetLabels\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetLabels))\n}\n\n// GetManagedFields mocks base method.\nfunc (m *MockRuntimeMetaObject) GetManagedFields() []v1.ManagedFieldsEntry {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetManagedFields\")\n\tret0, _ := ret[0].([]v1.ManagedFieldsEntry)\n\treturn ret0\n}\n\n// GetManagedFields indicates an expected call of GetManagedFields.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetManagedFields() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetManagedFields\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetManagedFields))\n}\n\n// GetName mocks base method.\nfunc (m *MockRuntimeMetaObject) GetName() string {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetName\")\n\tret0, _ := ret[0].(string)\n\treturn ret0\n}\n\n// GetName indicates an expected call of GetName.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetName() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetName\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetName))\n}\n\n// GetNamespace mocks base method.\nfunc (m *MockRuntimeMetaObject) GetNamespace() string {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetNamespace\")\n\tret0, _ := ret[0].(string)\n\treturn ret0\n}\n\n// GetNamespace indicates an expected call of GetNamespace.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetNamespace() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetNamespace\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetNamespace))\n}\n\n// GetObjectKind mocks base method.\nfunc (m *MockRuntimeMetaObject) GetObjectKind() schema.ObjectKind {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetObjectKind\")\n\tret0, _ := ret[0].(schema.ObjectKind)\n\treturn ret0\n}\n\n// GetObjectKind indicates an expected call of GetObjectKind.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetObjectKind() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetObjectKind\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetObjectKind))\n}\n\n// GetOwnerReferences mocks base method.\nfunc (m *MockRuntimeMetaObject) GetOwnerReferences() []v1.OwnerReference {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetOwnerReferences\")\n\tret0, _ := ret[0].([]v1.OwnerReference)\n\treturn ret0\n}\n\n// GetOwnerReferences indicates an expected call of GetOwnerReferences.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetOwnerReferences() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetOwnerReferences\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetOwnerReferences))\n}\n\n// GetResourceVersion mocks base method.\nfunc (m *MockRuntimeMetaObject) GetResourceVersion() string {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetResourceVersion\")\n\tret0, _ := ret[0].(string)\n\treturn ret0\n}\n\n// GetResourceVersion indicates an expected call of GetResourceVersion.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetResourceVersion() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetResourceVersion\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetResourceVersion))\n}\n\n// GetSelfLink mocks base method.\nfunc (m *MockRuntimeMetaObject) GetSelfLink() string {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetSelfLink\")\n\tret0, _ := ret[0].(string)\n\treturn ret0\n}\n\n// GetSelfLink indicates an expected call of GetSelfLink.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetSelfLink() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetSelfLink\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetSelfLink))\n}\n\n// GetUID mocks base method.\nfunc (m *MockRuntimeMetaObject) GetUID() types.UID {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetUID\")\n\tret0, _ := ret[0].(types.UID)\n\treturn ret0\n}\n\n// GetUID indicates an expected call of GetUID.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) GetUID() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetUID\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).GetUID))\n}\n\n// SetAnnotations mocks base method.\nfunc (m *MockRuntimeMetaObject) SetAnnotations(annotations map[string]string) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetAnnotations\", annotations)\n}\n\n// SetAnnotations indicates an expected call of SetAnnotations.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetAnnotations(annotations any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetAnnotations\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetAnnotations), annotations)\n}\n\n// SetCreationTimestamp mocks base method.\nfunc (m *MockRuntimeMetaObject) SetCreationTimestamp(timestamp v1.Time) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetCreationTimestamp\", timestamp)\n}\n\n// SetCreationTimestamp indicates an expected call of SetCreationTimestamp.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetCreationTimestamp(timestamp any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetCreationTimestamp\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetCreationTimestamp), timestamp)\n}\n\n// SetDeletionGracePeriodSeconds mocks base method.\nfunc (m *MockRuntimeMetaObject) SetDeletionGracePeriodSeconds(arg0 *int64) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetDeletionGracePeriodSeconds\", arg0)\n}\n\n// SetDeletionGracePeriodSeconds indicates an expected call of SetDeletionGracePeriodSeconds.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetDeletionGracePeriodSeconds(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetDeletionGracePeriodSeconds\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetDeletionGracePeriodSeconds), arg0)\n}\n\n// SetDeletionTimestamp mocks base method.\nfunc (m *MockRuntimeMetaObject) SetDeletionTimestamp(timestamp *v1.Time) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetDeletionTimestamp\", timestamp)\n}\n\n// SetDeletionTimestamp indicates an expected call of SetDeletionTimestamp.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetDeletionTimestamp(timestamp any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetDeletionTimestamp\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetDeletionTimestamp), timestamp)\n}\n\n// SetFinalizers mocks base method.\nfunc (m *MockRuntimeMetaObject) SetFinalizers(finalizers []string) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetFinalizers\", finalizers)\n}\n\n// SetFinalizers indicates an expected call of SetFinalizers.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetFinalizers(finalizers any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetFinalizers\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetFinalizers), finalizers)\n}\n\n// SetGenerateName mocks base method.\nfunc (m *MockRuntimeMetaObject) SetGenerateName(name string) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetGenerateName\", name)\n}\n\n// SetGenerateName indicates an expected call of SetGenerateName.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetGenerateName(name any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetGenerateName\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetGenerateName), name)\n}\n\n// SetGeneration mocks base method.\nfunc (m *MockRuntimeMetaObject) SetGeneration(generation int64) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetGeneration\", generation)\n}\n\n// SetGeneration indicates an expected call of SetGeneration.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetGeneration(generation any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetGeneration\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetGeneration), generation)\n}\n\n// SetLabels mocks base method.\nfunc (m *MockRuntimeMetaObject) SetLabels(labels map[string]string) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetLabels\", labels)\n}\n\n// SetLabels indicates an expected call of SetLabels.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetLabels(labels any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetLabels\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetLabels), labels)\n}\n\n// SetManagedFields mocks base method.\nfunc (m *MockRuntimeMetaObject) SetManagedFields(managedFields []v1.ManagedFieldsEntry) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetManagedFields\", managedFields)\n}\n\n// SetManagedFields indicates an expected call of SetManagedFields.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetManagedFields(managedFields any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetManagedFields\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetManagedFields), managedFields)\n}\n\n// SetName mocks base method.\nfunc (m *MockRuntimeMetaObject) SetName(name string) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetName\", name)\n}\n\n// SetName indicates an expected call of SetName.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetName(name any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetName\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetName), name)\n}\n\n// SetNamespace mocks base method.\nfunc (m *MockRuntimeMetaObject) SetNamespace(namespace string) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetNamespace\", namespace)\n}\n\n// SetNamespace indicates an expected call of SetNamespace.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetNamespace(namespace any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetNamespace\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetNamespace), namespace)\n}\n\n// SetOwnerReferences mocks base method.\nfunc (m *MockRuntimeMetaObject) SetOwnerReferences(arg0 []v1.OwnerReference) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetOwnerReferences\", arg0)\n}\n\n// SetOwnerReferences indicates an expected call of SetOwnerReferences.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetOwnerReferences(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetOwnerReferences\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetOwnerReferences), arg0)\n}\n\n// SetResourceVersion mocks base method.\nfunc (m *MockRuntimeMetaObject) SetResourceVersion(version string) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetResourceVersion\", version)\n}\n\n// SetResourceVersion indicates an expected call of SetResourceVersion.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetResourceVersion(version any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetResourceVersion\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetResourceVersion), version)\n}\n\n// SetSelfLink mocks base method.\nfunc (m *MockRuntimeMetaObject) SetSelfLink(selfLink string) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetSelfLink\", selfLink)\n}\n\n// SetSelfLink indicates an expected call of SetSelfLink.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetSelfLink(selfLink any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetSelfLink\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetSelfLink), selfLink)\n}\n\n// SetUID mocks base method.\nfunc (m *MockRuntimeMetaObject) SetUID(uid types.UID) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"SetUID\", uid)\n}\n\n// SetUID indicates an expected call of SetUID.\nfunc (mr *MockRuntimeMetaObjectMockRecorder) SetUID(uid any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"SetUID\", reflect.TypeOf((*MockRuntimeMetaObject)(nil).SetUID), uid)\n}\n\n// MockControllerInterface is a mock of ControllerInterface interface.\ntype MockControllerInterface[T generic.RuntimeMetaObject, TList runtime.Object] struct {\n\tctrl     *gomock.Controller\n\trecorder *MockControllerInterfaceMockRecorder[T, TList]\n\tisgomock struct{}\n}\n\n// MockControllerInterfaceMockRecorder is the mock recorder for MockControllerInterface.\ntype MockControllerInterfaceMockRecorder[T generic.RuntimeMetaObject, TList runtime.Object] struct {\n\tmock *MockControllerInterface[T, TList]\n}\n\n// NewMockControllerInterface creates a new mock instance.\nfunc NewMockControllerInterface[T generic.RuntimeMetaObject, TList runtime.Object](ctrl *gomock.Controller) *MockControllerInterface[T, TList] {\n\tmock := &MockControllerInterface[T, TList]{ctrl: ctrl}\n\tmock.recorder = &MockControllerInterfaceMockRecorder[T, TList]{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use.\nfunc (m *MockControllerInterface[T, TList]) EXPECT() *MockControllerInterfaceMockRecorder[T, TList] {\n\treturn m.recorder\n}\n\n// AddGenericHandler mocks base method.\nfunc (m *MockControllerInterface[T, TList]) AddGenericHandler(ctx context.Context, name string, handler generic.Handler) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"AddGenericHandler\", ctx, name, handler)\n}\n\n// AddGenericHandler indicates an expected call of AddGenericHandler.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) AddGenericHandler(ctx, name, handler any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"AddGenericHandler\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).AddGenericHandler), ctx, name, handler)\n}\n\n// AddGenericRemoveHandler mocks base method.\nfunc (m *MockControllerInterface[T, TList]) AddGenericRemoveHandler(ctx context.Context, name string, handler generic.Handler) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"AddGenericRemoveHandler\", ctx, name, handler)\n}\n\n// AddGenericRemoveHandler indicates an expected call of AddGenericRemoveHandler.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) AddGenericRemoveHandler(ctx, name, handler any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"AddGenericRemoveHandler\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).AddGenericRemoveHandler), ctx, name, handler)\n}\n\n// Cache mocks base method.\nfunc (m *MockControllerInterface[T, TList]) Cache() generic.CacheInterface[T] {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Cache\")\n\tret0, _ := ret[0].(generic.CacheInterface[T])\n\treturn ret0\n}\n\n// Cache indicates an expected call of Cache.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) Cache() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Cache\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).Cache))\n}\n\n// Create mocks base method.\nfunc (m *MockControllerInterface[T, TList]) Create(arg0 T) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Create\", arg0)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Create indicates an expected call of Create.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) Create(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Create\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).Create), arg0)\n}\n\n// Delete mocks base method.\nfunc (m *MockControllerInterface[T, TList]) Delete(namespace, name string, options *v1.DeleteOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Delete\", namespace, name, options)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// Delete indicates an expected call of Delete.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) Delete(namespace, name, options any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Delete\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).Delete), namespace, name, options)\n}\n\n// DeleteCollection mocks base method.\nfunc (m *MockControllerInterface[T, TList]) DeleteCollection(namespace string, deleteOpts v1.DeleteOptions, listOpts v1.ListOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"DeleteCollection\", namespace, deleteOpts, listOpts)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// DeleteCollection indicates an expected call of DeleteCollection.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) DeleteCollection(namespace, deleteOpts, listOpts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"DeleteCollection\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).DeleteCollection), namespace, deleteOpts, listOpts)\n}\n\n// Enqueue mocks base method.\nfunc (m *MockControllerInterface[T, TList]) Enqueue(namespace, name string) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"Enqueue\", namespace, name)\n}\n\n// Enqueue indicates an expected call of Enqueue.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) Enqueue(namespace, name any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Enqueue\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).Enqueue), namespace, name)\n}\n\n// EnqueueAfter mocks base method.\nfunc (m *MockControllerInterface[T, TList]) EnqueueAfter(namespace, name string, duration time.Duration) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"EnqueueAfter\", namespace, name, duration)\n}\n\n// EnqueueAfter indicates an expected call of EnqueueAfter.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) EnqueueAfter(namespace, name, duration any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"EnqueueAfter\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).EnqueueAfter), namespace, name, duration)\n}\n\n// Get mocks base method.\nfunc (m *MockControllerInterface[T, TList]) Get(namespace, name string, options v1.GetOptions) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Get\", namespace, name, options)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Get indicates an expected call of Get.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) Get(namespace, name, options any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Get\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).Get), namespace, name, options)\n}\n\n// GroupVersionKind mocks base method.\nfunc (m *MockControllerInterface[T, TList]) GroupVersionKind() schema.GroupVersionKind {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GroupVersionKind\")\n\tret0, _ := ret[0].(schema.GroupVersionKind)\n\treturn ret0\n}\n\n// GroupVersionKind indicates an expected call of GroupVersionKind.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) GroupVersionKind() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GroupVersionKind\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).GroupVersionKind))\n}\n\n// Informer mocks base method.\nfunc (m *MockControllerInterface[T, TList]) Informer() cache.SharedIndexInformer {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Informer\")\n\tret0, _ := ret[0].(cache.SharedIndexInformer)\n\treturn ret0\n}\n\n// Informer indicates an expected call of Informer.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) Informer() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Informer\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).Informer))\n}\n\n// List mocks base method.\nfunc (m *MockControllerInterface[T, TList]) List(namespace string, opts v1.ListOptions) (TList, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"List\", namespace, opts)\n\tret0, _ := ret[0].(TList)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// List indicates an expected call of List.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) List(namespace, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"List\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).List), namespace, opts)\n}\n\n// OnChange mocks base method.\nfunc (m *MockControllerInterface[T, TList]) OnChange(ctx context.Context, name string, sync generic.ObjectHandler[T]) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"OnChange\", ctx, name, sync)\n}\n\n// OnChange indicates an expected call of OnChange.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) OnChange(ctx, name, sync any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"OnChange\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).OnChange), ctx, name, sync)\n}\n\n// OnRemove mocks base method.\nfunc (m *MockControllerInterface[T, TList]) OnRemove(ctx context.Context, name string, sync generic.ObjectHandler[T]) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"OnRemove\", ctx, name, sync)\n}\n\n// OnRemove indicates an expected call of OnRemove.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) OnRemove(ctx, name, sync any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"OnRemove\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).OnRemove), ctx, name, sync)\n}\n\n// Patch mocks base method.\nfunc (m *MockControllerInterface[T, TList]) Patch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (T, error) {\n\tm.ctrl.T.Helper()\n\tvarargs := []any{namespace, name, pt, data}\n\tfor _, a := range subresources {\n\t\tvarargs = append(varargs, a)\n\t}\n\tret := m.ctrl.Call(m, \"Patch\", varargs...)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Patch indicates an expected call of Patch.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) Patch(namespace, name, pt, data any, subresources ...any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\tvarargs := append([]any{namespace, name, pt, data}, subresources...)\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Patch\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).Patch), varargs...)\n}\n\n// Update mocks base method.\nfunc (m *MockControllerInterface[T, TList]) Update(arg0 T) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Update\", arg0)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Update indicates an expected call of Update.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) Update(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Update\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).Update), arg0)\n}\n\n// UpdateStatus mocks base method.\nfunc (m *MockControllerInterface[T, TList]) UpdateStatus(arg0 T) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"UpdateStatus\", arg0)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// UpdateStatus indicates an expected call of UpdateStatus.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) UpdateStatus(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"UpdateStatus\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).UpdateStatus), arg0)\n}\n\n// Updater mocks base method.\nfunc (m *MockControllerInterface[T, TList]) Updater() generic.Updater {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Updater\")\n\tret0, _ := ret[0].(generic.Updater)\n\treturn ret0\n}\n\n// Updater indicates an expected call of Updater.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) Updater() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Updater\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).Updater))\n}\n\n// Watch mocks base method.\nfunc (m *MockControllerInterface[T, TList]) Watch(namespace string, opts v1.ListOptions) (watch.Interface, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Watch\", namespace, opts)\n\tret0, _ := ret[0].(watch.Interface)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Watch indicates an expected call of Watch.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) Watch(namespace, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Watch\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).Watch), namespace, opts)\n}\n\n// WithImpersonation mocks base method.\nfunc (m *MockControllerInterface[T, TList]) WithImpersonation(impersonate rest.ImpersonationConfig) (generic.ClientInterface[T, TList], error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"WithImpersonation\", impersonate)\n\tret0, _ := ret[0].(generic.ClientInterface[T, TList])\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// WithImpersonation indicates an expected call of WithImpersonation.\nfunc (mr *MockControllerInterfaceMockRecorder[T, TList]) WithImpersonation(impersonate any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"WithImpersonation\", reflect.TypeOf((*MockControllerInterface[T, TList])(nil).WithImpersonation), impersonate)\n}\n\n// MockNonNamespacedControllerInterface is a mock of NonNamespacedControllerInterface interface.\ntype MockNonNamespacedControllerInterface[T generic.RuntimeMetaObject, TList runtime.Object] struct {\n\tctrl     *gomock.Controller\n\trecorder *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]\n\tisgomock struct{}\n}\n\n// MockNonNamespacedControllerInterfaceMockRecorder is the mock recorder for MockNonNamespacedControllerInterface.\ntype MockNonNamespacedControllerInterfaceMockRecorder[T generic.RuntimeMetaObject, TList runtime.Object] struct {\n\tmock *MockNonNamespacedControllerInterface[T, TList]\n}\n\n// NewMockNonNamespacedControllerInterface creates a new mock instance.\nfunc NewMockNonNamespacedControllerInterface[T generic.RuntimeMetaObject, TList runtime.Object](ctrl *gomock.Controller) *MockNonNamespacedControllerInterface[T, TList] {\n\tmock := &MockNonNamespacedControllerInterface[T, TList]{ctrl: ctrl}\n\tmock.recorder = &MockNonNamespacedControllerInterfaceMockRecorder[T, TList]{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) EXPECT() *MockNonNamespacedControllerInterfaceMockRecorder[T, TList] {\n\treturn m.recorder\n}\n\n// AddGenericHandler mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) AddGenericHandler(ctx context.Context, name string, handler generic.Handler) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"AddGenericHandler\", ctx, name, handler)\n}\n\n// AddGenericHandler indicates an expected call of AddGenericHandler.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) AddGenericHandler(ctx, name, handler any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"AddGenericHandler\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).AddGenericHandler), ctx, name, handler)\n}\n\n// AddGenericRemoveHandler mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) AddGenericRemoveHandler(ctx context.Context, name string, handler generic.Handler) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"AddGenericRemoveHandler\", ctx, name, handler)\n}\n\n// AddGenericRemoveHandler indicates an expected call of AddGenericRemoveHandler.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) AddGenericRemoveHandler(ctx, name, handler any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"AddGenericRemoveHandler\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).AddGenericRemoveHandler), ctx, name, handler)\n}\n\n// Cache mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) Cache() generic.NonNamespacedCacheInterface[T] {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Cache\")\n\tret0, _ := ret[0].(generic.NonNamespacedCacheInterface[T])\n\treturn ret0\n}\n\n// Cache indicates an expected call of Cache.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) Cache() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Cache\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).Cache))\n}\n\n// Create mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) Create(arg0 T) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Create\", arg0)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Create indicates an expected call of Create.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) Create(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Create\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).Create), arg0)\n}\n\n// Delete mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) Delete(name string, options *v1.DeleteOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Delete\", name, options)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// Delete indicates an expected call of Delete.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) Delete(name, options any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Delete\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).Delete), name, options)\n}\n\n// Enqueue mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) Enqueue(name string) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"Enqueue\", name)\n}\n\n// Enqueue indicates an expected call of Enqueue.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) Enqueue(name any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Enqueue\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).Enqueue), name)\n}\n\n// EnqueueAfter mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) EnqueueAfter(name string, duration time.Duration) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"EnqueueAfter\", name, duration)\n}\n\n// EnqueueAfter indicates an expected call of EnqueueAfter.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) EnqueueAfter(name, duration any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"EnqueueAfter\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).EnqueueAfter), name, duration)\n}\n\n// Get mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) Get(name string, options v1.GetOptions) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Get\", name, options)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Get indicates an expected call of Get.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) Get(name, options any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Get\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).Get), name, options)\n}\n\n// GroupVersionKind mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) GroupVersionKind() schema.GroupVersionKind {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GroupVersionKind\")\n\tret0, _ := ret[0].(schema.GroupVersionKind)\n\treturn ret0\n}\n\n// GroupVersionKind indicates an expected call of GroupVersionKind.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) GroupVersionKind() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GroupVersionKind\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).GroupVersionKind))\n}\n\n// Informer mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) Informer() cache.SharedIndexInformer {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Informer\")\n\tret0, _ := ret[0].(cache.SharedIndexInformer)\n\treturn ret0\n}\n\n// Informer indicates an expected call of Informer.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) Informer() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Informer\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).Informer))\n}\n\n// List mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) List(opts v1.ListOptions) (TList, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"List\", opts)\n\tret0, _ := ret[0].(TList)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// List indicates an expected call of List.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) List(opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"List\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).List), opts)\n}\n\n// OnChange mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) OnChange(ctx context.Context, name string, sync generic.ObjectHandler[T]) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"OnChange\", ctx, name, sync)\n}\n\n// OnChange indicates an expected call of OnChange.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) OnChange(ctx, name, sync any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"OnChange\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).OnChange), ctx, name, sync)\n}\n\n// OnRemove mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) OnRemove(ctx context.Context, name string, sync generic.ObjectHandler[T]) {\n\tm.ctrl.T.Helper()\n\tm.ctrl.Call(m, \"OnRemove\", ctx, name, sync)\n}\n\n// OnRemove indicates an expected call of OnRemove.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) OnRemove(ctx, name, sync any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"OnRemove\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).OnRemove), ctx, name, sync)\n}\n\n// Patch mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (T, error) {\n\tm.ctrl.T.Helper()\n\tvarargs := []any{name, pt, data}\n\tfor _, a := range subresources {\n\t\tvarargs = append(varargs, a)\n\t}\n\tret := m.ctrl.Call(m, \"Patch\", varargs...)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Patch indicates an expected call of Patch.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) Patch(name, pt, data any, subresources ...any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\tvarargs := append([]any{name, pt, data}, subresources...)\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Patch\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).Patch), varargs...)\n}\n\n// Update mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) Update(arg0 T) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Update\", arg0)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Update indicates an expected call of Update.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) Update(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Update\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).Update), arg0)\n}\n\n// UpdateStatus mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) UpdateStatus(arg0 T) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"UpdateStatus\", arg0)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// UpdateStatus indicates an expected call of UpdateStatus.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) UpdateStatus(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"UpdateStatus\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).UpdateStatus), arg0)\n}\n\n// Updater mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) Updater() generic.Updater {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Updater\")\n\tret0, _ := ret[0].(generic.Updater)\n\treturn ret0\n}\n\n// Updater indicates an expected call of Updater.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) Updater() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Updater\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).Updater))\n}\n\n// Watch mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) Watch(opts v1.ListOptions) (watch.Interface, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Watch\", opts)\n\tret0, _ := ret[0].(watch.Interface)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Watch indicates an expected call of Watch.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) Watch(opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Watch\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).Watch), opts)\n}\n\n// WithImpersonation mocks base method.\nfunc (m *MockNonNamespacedControllerInterface[T, TList]) WithImpersonation(impersonate rest.ImpersonationConfig) (generic.NonNamespacedClientInterface[T, TList], error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"WithImpersonation\", impersonate)\n\tret0, _ := ret[0].(generic.NonNamespacedClientInterface[T, TList])\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// WithImpersonation indicates an expected call of WithImpersonation.\nfunc (mr *MockNonNamespacedControllerInterfaceMockRecorder[T, TList]) WithImpersonation(impersonate any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"WithImpersonation\", reflect.TypeOf((*MockNonNamespacedControllerInterface[T, TList])(nil).WithImpersonation), impersonate)\n}\n\n// MockClientInterface is a mock of ClientInterface interface.\ntype MockClientInterface[T generic.RuntimeMetaObject, TList runtime.Object] struct {\n\tctrl     *gomock.Controller\n\trecorder *MockClientInterfaceMockRecorder[T, TList]\n\tisgomock struct{}\n}\n\n// MockClientInterfaceMockRecorder is the mock recorder for MockClientInterface.\ntype MockClientInterfaceMockRecorder[T generic.RuntimeMetaObject, TList runtime.Object] struct {\n\tmock *MockClientInterface[T, TList]\n}\n\n// NewMockClientInterface creates a new mock instance.\nfunc NewMockClientInterface[T generic.RuntimeMetaObject, TList runtime.Object](ctrl *gomock.Controller) *MockClientInterface[T, TList] {\n\tmock := &MockClientInterface[T, TList]{ctrl: ctrl}\n\tmock.recorder = &MockClientInterfaceMockRecorder[T, TList]{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use.\nfunc (m *MockClientInterface[T, TList]) EXPECT() *MockClientInterfaceMockRecorder[T, TList] {\n\treturn m.recorder\n}\n\n// Create mocks base method.\nfunc (m *MockClientInterface[T, TList]) Create(arg0 T) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Create\", arg0)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Create indicates an expected call of Create.\nfunc (mr *MockClientInterfaceMockRecorder[T, TList]) Create(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Create\", reflect.TypeOf((*MockClientInterface[T, TList])(nil).Create), arg0)\n}\n\n// Delete mocks base method.\nfunc (m *MockClientInterface[T, TList]) Delete(namespace, name string, options *v1.DeleteOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Delete\", namespace, name, options)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// Delete indicates an expected call of Delete.\nfunc (mr *MockClientInterfaceMockRecorder[T, TList]) Delete(namespace, name, options any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Delete\", reflect.TypeOf((*MockClientInterface[T, TList])(nil).Delete), namespace, name, options)\n}\n\n// DeleteCollection mocks base method.\nfunc (m *MockClientInterface[T, TList]) DeleteCollection(namespace string, deleteOpts v1.DeleteOptions, listOpts v1.ListOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"DeleteCollection\", namespace, deleteOpts, listOpts)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// DeleteCollection indicates an expected call of DeleteCollection.\nfunc (mr *MockClientInterfaceMockRecorder[T, TList]) DeleteCollection(namespace, deleteOpts, listOpts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"DeleteCollection\", reflect.TypeOf((*MockClientInterface[T, TList])(nil).DeleteCollection), namespace, deleteOpts, listOpts)\n}\n\n// Get mocks base method.\nfunc (m *MockClientInterface[T, TList]) Get(namespace, name string, options v1.GetOptions) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Get\", namespace, name, options)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Get indicates an expected call of Get.\nfunc (mr *MockClientInterfaceMockRecorder[T, TList]) Get(namespace, name, options any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Get\", reflect.TypeOf((*MockClientInterface[T, TList])(nil).Get), namespace, name, options)\n}\n\n// List mocks base method.\nfunc (m *MockClientInterface[T, TList]) List(namespace string, opts v1.ListOptions) (TList, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"List\", namespace, opts)\n\tret0, _ := ret[0].(TList)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// List indicates an expected call of List.\nfunc (mr *MockClientInterfaceMockRecorder[T, TList]) List(namespace, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"List\", reflect.TypeOf((*MockClientInterface[T, TList])(nil).List), namespace, opts)\n}\n\n// Patch mocks base method.\nfunc (m *MockClientInterface[T, TList]) Patch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (T, error) {\n\tm.ctrl.T.Helper()\n\tvarargs := []any{namespace, name, pt, data}\n\tfor _, a := range subresources {\n\t\tvarargs = append(varargs, a)\n\t}\n\tret := m.ctrl.Call(m, \"Patch\", varargs...)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Patch indicates an expected call of Patch.\nfunc (mr *MockClientInterfaceMockRecorder[T, TList]) Patch(namespace, name, pt, data any, subresources ...any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\tvarargs := append([]any{namespace, name, pt, data}, subresources...)\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Patch\", reflect.TypeOf((*MockClientInterface[T, TList])(nil).Patch), varargs...)\n}\n\n// Update mocks base method.\nfunc (m *MockClientInterface[T, TList]) Update(arg0 T) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Update\", arg0)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Update indicates an expected call of Update.\nfunc (mr *MockClientInterfaceMockRecorder[T, TList]) Update(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Update\", reflect.TypeOf((*MockClientInterface[T, TList])(nil).Update), arg0)\n}\n\n// UpdateStatus mocks base method.\nfunc (m *MockClientInterface[T, TList]) UpdateStatus(arg0 T) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"UpdateStatus\", arg0)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// UpdateStatus indicates an expected call of UpdateStatus.\nfunc (mr *MockClientInterfaceMockRecorder[T, TList]) UpdateStatus(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"UpdateStatus\", reflect.TypeOf((*MockClientInterface[T, TList])(nil).UpdateStatus), arg0)\n}\n\n// Watch mocks base method.\nfunc (m *MockClientInterface[T, TList]) Watch(namespace string, opts v1.ListOptions) (watch.Interface, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Watch\", namespace, opts)\n\tret0, _ := ret[0].(watch.Interface)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Watch indicates an expected call of Watch.\nfunc (mr *MockClientInterfaceMockRecorder[T, TList]) Watch(namespace, opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Watch\", reflect.TypeOf((*MockClientInterface[T, TList])(nil).Watch), namespace, opts)\n}\n\n// WithImpersonation mocks base method.\nfunc (m *MockClientInterface[T, TList]) WithImpersonation(impersonate rest.ImpersonationConfig) (generic.ClientInterface[T, TList], error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"WithImpersonation\", impersonate)\n\tret0, _ := ret[0].(generic.ClientInterface[T, TList])\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// WithImpersonation indicates an expected call of WithImpersonation.\nfunc (mr *MockClientInterfaceMockRecorder[T, TList]) WithImpersonation(impersonate any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"WithImpersonation\", reflect.TypeOf((*MockClientInterface[T, TList])(nil).WithImpersonation), impersonate)\n}\n\n// MockNonNamespacedClientInterface is a mock of NonNamespacedClientInterface interface.\ntype MockNonNamespacedClientInterface[T generic.RuntimeMetaObject, TList runtime.Object] struct {\n\tctrl     *gomock.Controller\n\trecorder *MockNonNamespacedClientInterfaceMockRecorder[T, TList]\n\tisgomock struct{}\n}\n\n// MockNonNamespacedClientInterfaceMockRecorder is the mock recorder for MockNonNamespacedClientInterface.\ntype MockNonNamespacedClientInterfaceMockRecorder[T generic.RuntimeMetaObject, TList runtime.Object] struct {\n\tmock *MockNonNamespacedClientInterface[T, TList]\n}\n\n// NewMockNonNamespacedClientInterface creates a new mock instance.\nfunc NewMockNonNamespacedClientInterface[T generic.RuntimeMetaObject, TList runtime.Object](ctrl *gomock.Controller) *MockNonNamespacedClientInterface[T, TList] {\n\tmock := &MockNonNamespacedClientInterface[T, TList]{ctrl: ctrl}\n\tmock.recorder = &MockNonNamespacedClientInterfaceMockRecorder[T, TList]{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use.\nfunc (m *MockNonNamespacedClientInterface[T, TList]) EXPECT() *MockNonNamespacedClientInterfaceMockRecorder[T, TList] {\n\treturn m.recorder\n}\n\n// Create mocks base method.\nfunc (m *MockNonNamespacedClientInterface[T, TList]) Create(arg0 T) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Create\", arg0)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Create indicates an expected call of Create.\nfunc (mr *MockNonNamespacedClientInterfaceMockRecorder[T, TList]) Create(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Create\", reflect.TypeOf((*MockNonNamespacedClientInterface[T, TList])(nil).Create), arg0)\n}\n\n// Delete mocks base method.\nfunc (m *MockNonNamespacedClientInterface[T, TList]) Delete(name string, options *v1.DeleteOptions) error {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Delete\", name, options)\n\tret0, _ := ret[0].(error)\n\treturn ret0\n}\n\n// Delete indicates an expected call of Delete.\nfunc (mr *MockNonNamespacedClientInterfaceMockRecorder[T, TList]) Delete(name, options any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Delete\", reflect.TypeOf((*MockNonNamespacedClientInterface[T, TList])(nil).Delete), name, options)\n}\n\n// Get mocks base method.\nfunc (m *MockNonNamespacedClientInterface[T, TList]) Get(name string, options v1.GetOptions) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Get\", name, options)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Get indicates an expected call of Get.\nfunc (mr *MockNonNamespacedClientInterfaceMockRecorder[T, TList]) Get(name, options any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Get\", reflect.TypeOf((*MockNonNamespacedClientInterface[T, TList])(nil).Get), name, options)\n}\n\n// List mocks base method.\nfunc (m *MockNonNamespacedClientInterface[T, TList]) List(opts v1.ListOptions) (TList, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"List\", opts)\n\tret0, _ := ret[0].(TList)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// List indicates an expected call of List.\nfunc (mr *MockNonNamespacedClientInterfaceMockRecorder[T, TList]) List(opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"List\", reflect.TypeOf((*MockNonNamespacedClientInterface[T, TList])(nil).List), opts)\n}\n\n// Patch mocks base method.\nfunc (m *MockNonNamespacedClientInterface[T, TList]) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (T, error) {\n\tm.ctrl.T.Helper()\n\tvarargs := []any{name, pt, data}\n\tfor _, a := range subresources {\n\t\tvarargs = append(varargs, a)\n\t}\n\tret := m.ctrl.Call(m, \"Patch\", varargs...)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Patch indicates an expected call of Patch.\nfunc (mr *MockNonNamespacedClientInterfaceMockRecorder[T, TList]) Patch(name, pt, data any, subresources ...any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\tvarargs := append([]any{name, pt, data}, subresources...)\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Patch\", reflect.TypeOf((*MockNonNamespacedClientInterface[T, TList])(nil).Patch), varargs...)\n}\n\n// Update mocks base method.\nfunc (m *MockNonNamespacedClientInterface[T, TList]) Update(arg0 T) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Update\", arg0)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Update indicates an expected call of Update.\nfunc (mr *MockNonNamespacedClientInterfaceMockRecorder[T, TList]) Update(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Update\", reflect.TypeOf((*MockNonNamespacedClientInterface[T, TList])(nil).Update), arg0)\n}\n\n// UpdateStatus mocks base method.\nfunc (m *MockNonNamespacedClientInterface[T, TList]) UpdateStatus(arg0 T) (T, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"UpdateStatus\", arg0)\n\tret0, _ := ret[0].(T)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// UpdateStatus indicates an expected call of UpdateStatus.\nfunc (mr *MockNonNamespacedClientInterfaceMockRecorder[T, TList]) UpdateStatus(arg0 any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"UpdateStatus\", reflect.TypeOf((*MockNonNamespacedClientInterface[T, TList])(nil).UpdateStatus), arg0)\n}\n\n// Watch mocks base method.\nfunc (m *MockNonNamespacedClientInterface[T, TList]) Watch(opts v1.ListOptions) (watch.Interface, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"Watch\", opts)\n\tret0, _ := ret[0].(watch.Interface)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// Watch indicates an expected call of Watch.\nfunc (mr *MockNonNamespacedClientInterfaceMockRecorder[T, TList]) Watch(opts any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"Watch\", reflect.TypeOf((*MockNonNamespacedClientInterface[T, TList])(nil).Watch), opts)\n}\n\n// WithImpersonation mocks base method.\nfunc (m *MockNonNamespacedClientInterface[T, TList]) WithImpersonation(impersonate rest.ImpersonationConfig) (generic.NonNamespacedClientInterface[T, TList], error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"WithImpersonation\", impersonate)\n\tret0, _ := ret[0].(generic.NonNamespacedClientInterface[T, TList])\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// WithImpersonation indicates an expected call of WithImpersonation.\nfunc (mr *MockNonNamespacedClientInterfaceMockRecorder[T, TList]) WithImpersonation(impersonate any) *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"WithImpersonation\", reflect.TypeOf((*MockNonNamespacedClientInterface[T, TList])(nil).WithImpersonation), impersonate)\n}\n"
  },
  {
    "path": "pkg/generic/fake/fake_test.go",
    "content": "package fake_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/generic/fake\"\n\tv1 \"k8s.io/api/core/v1\"\n)\n\n// TestInterfaceImplementation is a simple test to verify the fake package is kept up to date.\n// if this compiles it is valid.\nfunc TestInterfaceImplementation(t *testing.T) {\n\tvar (\n\t\t_ generic.ControllerInterface[*v1.Secret, *v1.SecretList]              = fake.NewMockControllerInterface[*v1.Secret, *v1.SecretList](nil)\n\t\t_ generic.NonNamespacedControllerInterface[*v1.Secret, *v1.SecretList] = fake.NewMockNonNamespacedControllerInterface[*v1.Secret, *v1.SecretList](nil)\n\t\t_ generic.ClientInterface[*v1.Secret, *v1.SecretList]                  = fake.NewMockClientInterface[*v1.Secret, *v1.SecretList](nil)\n\t\t_ generic.NonNamespacedClientInterface[*v1.Secret, *v1.SecretList]     = fake.NewMockNonNamespacedClientInterface[*v1.Secret, *v1.SecretList](nil)\n\t\t_ generic.CacheInterface[*v1.Secret]                                   = fake.NewMockCacheInterface[*v1.Secret](nil)\n\t\t_ generic.NonNamespacedCacheInterface[*v1.Secret]                      = fake.NewMockNonNamespacedCacheInterface[*v1.Secret](nil)\n\t)\n}\n"
  },
  {
    "path": "pkg/generic/fake/generate.go",
    "content": "package fake\n\n//go:generate bash -c \"tmp=$(mktemp); cp ../controller.go $DOLLAR{tmp} && sed -e 's#^\\\\t*comparable$DOLLAR#// comparable#' $DOLLAR{tmp} > ../controller.go && mockgen -package fake -destination ./controller.go -source ../controller.go; mv $DOLLAR{tmp} ../controller.go \"\n//go:generate mockgen -package fake -destination ./cache.go -source ../cache.go\n"
  },
  {
    "path": "pkg/generic/generating.go",
    "content": "package generic\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\ntype GeneratingHandlerOptions struct {\n\tAllowCrossNamespace bool\n\tAllowClusterScoped  bool\n\tNoOwnerReference    bool\n\tDynamicLookup       bool\n\t// UniqueApplyForResourceVersion will skip calling apply if the resource version didn't change from the previous execution\n\tUniqueApplyForResourceVersion bool\n}\n\nfunc ConfigureApplyForObject(apply apply.Apply, obj metav1.Object, opts *GeneratingHandlerOptions) apply.Apply {\n\tif opts == nil {\n\t\topts = &GeneratingHandlerOptions{}\n\t}\n\n\tif opts.DynamicLookup {\n\t\tapply = apply.WithDynamicLookup()\n\t}\n\n\tif opts.NoOwnerReference {\n\t\tapply = apply.WithSetOwnerReference(true, false)\n\t}\n\n\tif opts.AllowCrossNamespace && !opts.AllowClusterScoped {\n\t\tapply = apply.\n\t\t\tWithDefaultNamespace(obj.GetNamespace()).\n\t\t\tWithListerNamespace(obj.GetNamespace())\n\t}\n\n\tif !opts.AllowClusterScoped {\n\t\tapply = apply.WithRestrictClusterScoped()\n\t}\n\n\treturn apply\n}\n"
  },
  {
    "path": "pkg/generic/generating_test.go",
    "content": "package generic_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"go.uber.org/mock/gomock\"\n\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/apply\"\n\tfakeapply \"github.com/rancher/wrangler/v3/pkg/apply/fake\"\n\tv1 \"github.com/rancher/wrangler/v3/pkg/generated/controllers/core/v1\"\n\tfake2 \"github.com/rancher/wrangler/v3/pkg/generic/fake\"\n)\n\nfunc TestUniqueApplyForResourceVersion(t *testing.T) {\n\tconst numOfHandlerCalls = 3\n\ttype args struct {\n\t\topts *generic.GeneratingHandlerOptions\n\t}\n\ttests := []struct {\n\t\tname               string\n\t\targs               args\n\t\texpectedApplyCount int\n\t}{\n\t\t{\n\t\t\tname: \"disabled\",\n\t\t\targs: args{\n\t\t\t\topts: &generic.GeneratingHandlerOptions{UniqueApplyForResourceVersion: false},\n\t\t\t},\n\t\t\texpectedApplyCount: numOfHandlerCalls,\n\t\t},\n\t\t{\n\t\t\tname: \"enabled\",\n\t\t\targs: args{\n\t\t\t\topts: &generic.GeneratingHandlerOptions{UniqueApplyForResourceVersion: true},\n\t\t\t},\n\t\t\texpectedApplyCount: 1,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tctrl := gomock.NewController(t)\n\t\t\tapplySpy := &fakeapply.FakeApply{}\n\n\t\t\th := setupTestHandler(ctrl, applySpy, tt.args.opts)\n\t\t\tif h == nil {\n\t\t\t\tt.Fatal(\"error setting up test handler\")\n\t\t\t}\n\n\t\t\tservice := &corev1.Service{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{Name: \"testsvc\", Namespace: \"testns\", ResourceVersion: \"unchanged\"},\n\t\t\t\tSpec:       corev1.ServiceSpec{Ports: []corev1.ServicePort{{Name: \"http\", Protocol: \"tcp\", Port: 80}}},\n\t\t\t\tStatus: corev1.ServiceStatus{\n\t\t\t\t\tConditions: []metav1.Condition{},\n\t\t\t\t},\n\t\t\t}\n\t\t\tkey := service.Namespace + \"/\" + service.Name\n\t\t\tfor i := 0; i < numOfHandlerCalls; i++ {\n\t\t\t\tif _, err := h(key, service); err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif got, want := applySpy.Count, tt.expectedApplyCount; got != want {\n\t\t\t\tt.Errorf(\"Apply calls count = %v, want %v\", got, want)\n\t\t\t}\n\n\t\t\tservice.ResourceVersion = \"changed\"\n\t\t\tif _, err := h(key, service); err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tif got, want := applySpy.Count, tt.expectedApplyCount+1; got != want {\n\t\t\t\tt.Errorf(\"Apply calls count = %v, want %v\", got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc setupTestHandler(ctrl *gomock.Controller, apply apply.Apply, opts *generic.GeneratingHandlerOptions) (handler generic.Handler) {\n\tconst handlerName = \"test\"\n\tcontroller := fake2.NewMockControllerInterface[*corev1.Service, *corev1.ServiceList](ctrl)\n\tcontroller.EXPECT().GroupVersionKind()\n\tcontroller.EXPECT().OnChange(gomock.Any(), gomock.Any(), gomock.Any())\n\tcontroller.EXPECT().\n\t\tAddGenericHandler(gomock.Any(), gomock.Eq(handlerName), gomock.Any()).\n\t\tDo(func(_ context.Context, _ string, h generic.Handler) {\n\t\t\thandler = h\n\t\t}).Times(1)\n\n\tv1.RegisterServiceGeneratingHandler(context.Background(), controller, apply, \"\", handlerName,\n\t\tfunc(svc *corev1.Service, status corev1.ServiceStatus) (objs []runtime.Object, newstatus corev1.ServiceStatus, err error) {\n\t\t\treturn []runtime.Object{serviceToEndpoint(svc)}, status, nil\n\t\t}, opts)\n\treturn\n}\n\nfunc serviceToEndpoint(svc *corev1.Service) *corev1.Endpoints {\n\tvar ports []corev1.EndpointPort\n\tfor _, port := range svc.Spec.Ports {\n\t\tports = append(ports, corev1.EndpointPort{\n\t\t\tName: port.Name, Port: port.Port, Protocol: port.Protocol, AppProtocol: port.AppProtocol,\n\t\t})\n\t}\n\treturn &corev1.Endpoints{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:      svc.Name,\n\t\t\tNamespace: svc.Namespace,\n\t\t},\n\t\tSubsets: []corev1.EndpointSubset{\n\t\t\t{Ports: ports},\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "pkg/generic/remove.go",
    "content": "package generic\n\nimport (\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n)\n\nvar (\n\tfinalizerKey = \"wrangler.cattle.io/\"\n)\n\ntype Updater func(runtime.Object) (runtime.Object, error)\n\ntype objectLifecycleAdapter struct {\n\tname    string\n\thandler Handler\n\tupdater Updater\n}\n\nfunc NewRemoveHandler(name string, updater Updater, handler Handler) Handler {\n\to := objectLifecycleAdapter{\n\t\tname:    name,\n\t\thandler: handler,\n\t\tupdater: updater,\n\t}\n\treturn o.sync\n}\n\nfunc (o *objectLifecycleAdapter) sync(key string, obj runtime.Object) (runtime.Object, error) {\n\tif obj == nil {\n\t\treturn nil, nil\n\t}\n\n\tmetadata, err := meta.Accessor(obj)\n\tif err != nil {\n\t\treturn obj, err\n\t}\n\n\tif metadata.GetDeletionTimestamp() == nil {\n\t\treturn o.addFinalizer(obj)\n\t}\n\n\tif !o.hasFinalizer(obj) {\n\t\treturn obj, nil\n\t}\n\n\tnewObj, err := o.handler(key, obj)\n\tif err != nil {\n\t\treturn newObj, err\n\t}\n\n\tif newObj != nil {\n\t\tobj = newObj\n\t}\n\n\treturn o.removeFinalizer(obj)\n}\n\nfunc (o *objectLifecycleAdapter) constructFinalizerKey() string {\n\treturn finalizerKey + o.name\n}\n\nfunc (o *objectLifecycleAdapter) hasFinalizer(obj runtime.Object) bool {\n\tmetadata, err := meta.Accessor(obj)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tfinalizerKey := o.constructFinalizerKey()\n\tfinalizers := metadata.GetFinalizers()\n\tfor _, finalizer := range finalizers {\n\t\tif finalizer == finalizerKey {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (o *objectLifecycleAdapter) removeFinalizer(obj runtime.Object) (runtime.Object, error) {\n\tif !o.hasFinalizer(obj) {\n\t\treturn obj, nil\n\t}\n\n\tobj = obj.DeepCopyObject()\n\tmetadata, err := meta.Accessor(obj)\n\tif err != nil {\n\t\treturn obj, err\n\t}\n\n\tfinalizerKey := o.constructFinalizerKey()\n\tfinalizers := metadata.GetFinalizers()\n\n\tvar newFinalizers []string\n\tfor k, v := range finalizers {\n\t\tif v != finalizerKey {\n\t\t\tcontinue\n\t\t}\n\t\tnewFinalizers = append(finalizers[:k], finalizers[k+1:]...)\n\t}\n\n\tmetadata.SetFinalizers(newFinalizers)\n\treturn o.updater(obj)\n}\n\nfunc (o *objectLifecycleAdapter) addFinalizer(obj runtime.Object) (runtime.Object, error) {\n\tif o.hasFinalizer(obj) {\n\t\treturn obj, nil\n\t}\n\n\tobj = obj.DeepCopyObject()\n\tmetadata, err := meta.Accessor(obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmetadata.SetFinalizers(append(metadata.GetFinalizers(), o.constructFinalizerKey()))\n\treturn o.updater(obj)\n}\n"
  },
  {
    "path": "pkg/generic/remove_test.go",
    "content": "package generic\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n)\n\nfunc Test_objectLifecycleAdapter_sync(t *testing.T) {\n\tconst handlerName = \"test\"\n\ttype args struct {\n\t\tkey string\n\t\tobj runtime.Object\n\t}\n\ttests := []struct {\n\t\tname             string\n\t\targs             args\n\t\twantErr          bool\n\t\twantFunc         func(runtime.Object) error\n\t\twantHandlerCount int\n\t\twantUpdaterCount int\n\t}{\n\t\t{\n\t\t\tname: \"nil object\",\n\t\t\targs: args{\n\t\t\t\t\"objectkey\", nil,\n\t\t\t},\n\t\t\twantFunc: func(obj runtime.Object) error {\n\t\t\t\tif obj != nil {\n\t\t\t\t\treturn fmt.Errorf(\"obj should be nil\")\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"new object\",\n\t\t\targs: args{\n\t\t\t\t\"test/service\", &corev1.Service{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{Namespace: \"test\", Name: \"service\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantUpdaterCount: 1, // update to add finalizer\n\t\t},\n\t\t{\n\t\t\tname: \"existing object with finalizer\",\n\t\t\targs: args{\n\t\t\t\t\"test/service\", &corev1.Service{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tNamespace: \"test\", Name: \"service\",\n\t\t\t\t\t\tFinalizers: []string{\"finalizer\", finalizerKey + handlerName},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantUpdaterCount: 0, // finalizer already set, no need to update\n\t\t},\n\t\t{\n\t\t\tname: \"deleted object with finalizer\",\n\t\t\targs: args{\n\t\t\t\tkey: \"test/service\", obj: &corev1.Service{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tNamespace: \"test\", Name: \"service\",\n\t\t\t\t\t\tFinalizers:        []string{\"finalizer\", finalizerKey + handlerName},\n\t\t\t\t\t\tDeletionTimestamp: &metav1.Time{Time: time.Now()},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantHandlerCount: 1,\n\t\t\twantUpdaterCount: 1, // update removing finalizer\n\t\t},\n\t\t{\n\t\t\tname: \"deleted object without finalizer\",\n\t\t\targs: args{\n\t\t\t\tkey: \"test/service\", obj: &corev1.Service{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tNamespace: \"test\", Name: \"service\",\n\t\t\t\t\t\tDeletionTimestamp: &metav1.Time{Time: time.Now()},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantHandlerCount: 0,\n\t\t\twantUpdaterCount: 0,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar handlerCount, updaterCount int\n\t\t\tupdater := func(obj runtime.Object) (runtime.Object, error) {\n\t\t\t\tupdaterCount++\n\t\t\t\treturn obj, nil\n\t\t\t}\n\t\t\thandler := func(key string, obj runtime.Object) (runtime.Object, error) {\n\t\t\t\thandlerCount++\n\t\t\t\treturn obj, nil\n\t\t\t}\n\n\t\t\tsync := NewRemoveHandler(\"test\", updater, handler)\n\t\t\tgot, err := sync(tt.args.key, tt.args.obj)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"sync() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif tt.wantFunc != nil {\n\t\t\t\tif err := tt.wantFunc(got); err != nil {\n\t\t\t\t\tt.Errorf(\"sync() unexpected value = %v, err: %v\", got, err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif handlerCount != tt.wantHandlerCount {\n\t\t\t\tt.Errorf(\"handlerCount = %v, want %v\", handlerCount, tt.wantHandlerCount)\n\t\t\t}\n\t\t\tif updaterCount != tt.wantUpdaterCount {\n\t\t\t\tt.Errorf(\"updaterCount = %v, want %v\", updaterCount, tt.wantUpdaterCount)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/genericcondition/condition.go",
    "content": "package genericcondition\n\nimport v1 \"k8s.io/api/core/v1\"\n\ntype GenericCondition struct {\n\t// Type of cluster condition.\n\tType string `json:\"type\"`\n\t// Status of the condition, one of True, False, Unknown.\n\tStatus v1.ConditionStatus `json:\"status\"`\n\t// The last time this condition was updated.\n\tLastUpdateTime string `json:\"lastUpdateTime,omitempty\"`\n\t// Last time the condition transitioned from one status to another.\n\tLastTransitionTime string `json:\"lastTransitionTime,omitempty\"`\n\t// The reason for the condition's last transition.\n\tReason string `json:\"reason,omitempty\"`\n\t// Human-readable message indicating details about last transition\n\tMessage string `json:\"message,omitempty\"`\n}\n"
  },
  {
    "path": "pkg/gvk/detect.go",
    "content": "package gvk\n\nimport (\n\t\"encoding/json\"\n\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc Detect(obj []byte) (schema.GroupVersionKind, bool, error) {\n\tpartial := v1.PartialObjectMetadata{}\n\tif err := json.Unmarshal(obj, &partial); err != nil {\n\t\treturn schema.GroupVersionKind{}, false, err\n\t}\n\n\tresult := partial.GetObjectKind().GroupVersionKind()\n\tok := result.Kind != \"\" && result.Version != \"\"\n\treturn result, ok, nil\n}\n"
  },
  {
    "path": "pkg/gvk/get.go",
    "content": "package gvk\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/schemes\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc Get(obj runtime.Object) (schema.GroupVersionKind, error) {\n\tgvk := obj.GetObjectKind().GroupVersionKind()\n\tif gvk.Kind != \"\" {\n\t\treturn gvk, nil\n\t}\n\n\tgvks, _, err := schemes.All.ObjectKinds(obj)\n\tif err != nil {\n\t\treturn schema.GroupVersionKind{}, fmt.Errorf(\"failed to find gvk for %T, you may need to import the wrangler generated controller package: %w\", obj, err)\n\t}\n\n\tif len(gvks) == 0 {\n\t\treturn schema.GroupVersionKind{}, fmt.Errorf(\"failed to find gvk for %T\", obj)\n\t}\n\n\treturn gvks[0], nil\n}\n\nfunc Set(objs ...runtime.Object) error {\n\tfor _, obj := range objs {\n\t\tif err := setObject(obj); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc setObject(obj runtime.Object) error {\n\tgvk := obj.GetObjectKind().GroupVersionKind()\n\tif gvk.Kind != \"\" {\n\t\treturn nil\n\t}\n\n\tgvks, _, err := schemes.All.ObjectKinds(obj)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(gvks) == 0 {\n\t\treturn nil\n\t}\n\n\tkind := obj.GetObjectKind()\n\tkind.SetGroupVersionKind(gvks[0])\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/k8scheck/wait.go",
    "content": "package k8scheck\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/sirupsen/logrus\"\n\t\"k8s.io/client-go/kubernetes\"\n\t\"k8s.io/client-go/rest\"\n)\n\nfunc Wait(ctx context.Context, config rest.Config) error {\n\tclient, err := kubernetes.NewForConfig(&config)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor {\n\t\t_, err := client.Discovery().ServerVersion()\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t\tlogrus.Infof(\"Waiting for server to become available: %v\", err)\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn fmt.Errorf(\"startup canceled\")\n\t\tcase <-time.After(2 * time.Second):\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/kstatus/kstatus.go",
    "content": "package kstatus\n\nimport \"github.com/rancher/wrangler/v3/pkg/condition\"\n\n// Conditions read by the kstatus package\n\nconst (\n\tReconciling = condition.Cond(\"Reconciling\")\n\tStalled     = condition.Cond(\"Stalled\")\n)\n\nfunc SetError(obj interface{}, message string) {\n\tReconciling.False(obj)\n\tReconciling.Message(obj, \"\")\n\tReconciling.Reason(obj, \"\")\n\tStalled.True(obj)\n\tStalled.Reason(obj, string(Stalled))\n\tStalled.Message(obj, message)\n}\n\nfunc SetTransitioning(obj interface{}, message string) {\n\tReconciling.True(obj)\n\tReconciling.Message(obj, message)\n\tReconciling.Reason(obj, string(Reconciling))\n\tStalled.False(obj)\n\tStalled.Reason(obj, \"\")\n\tStalled.Message(obj, \"\")\n}\n\nfunc SetActive(obj interface{}) {\n\tReconciling.False(obj)\n\tReconciling.Message(obj, \"\")\n\tReconciling.Reason(obj, \"\")\n\tStalled.False(obj)\n\tStalled.Reason(obj, \"\")\n\tStalled.Message(obj, \"\")\n}\n"
  },
  {
    "path": "pkg/kubeconfig/loader.go",
    "content": "package kubeconfig\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"k8s.io/client-go/tools/clientcmd\"\n)\n\nfunc GetNonInteractiveClientConfig(kubeConfig string) clientcmd.ClientConfig {\n\treturn GetClientConfig(kubeConfig, nil)\n}\n\nfunc GetNonInteractiveClientConfigWithContext(kubeConfig, currentContext string) clientcmd.ClientConfig {\n\treturn GetClientConfigWithContext(kubeConfig, currentContext, nil)\n}\n\nfunc GetInteractiveClientConfig(kubeConfig string) clientcmd.ClientConfig {\n\treturn GetClientConfig(kubeConfig, os.Stdin)\n}\n\nfunc GetClientConfigWithContext(kubeConfig, currentContext string, reader io.Reader) clientcmd.ClientConfig {\n\tloadingRules := GetLoadingRules(kubeConfig)\n\toverrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults, CurrentContext: currentContext}\n\treturn clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, reader)\n}\n\nfunc GetClientConfig(kubeConfig string, reader io.Reader) clientcmd.ClientConfig {\n\treturn GetClientConfigWithContext(kubeConfig, \"\", reader)\n}\n\nfunc GetLoadingRules(kubeConfig string) *clientcmd.ClientConfigLoadingRules {\n\tloadingRules := clientcmd.NewDefaultClientConfigLoadingRules()\n\tloadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig\n\tif kubeConfig != \"\" {\n\t\tloadingRules.ExplicitPath = kubeConfig\n\t}\n\n\tvar otherFiles []string\n\thomeDir, err := os.UserHomeDir()\n\tif err == nil {\n\t\totherFiles = append(otherFiles, filepath.Join(homeDir, \".kube\", \"k3s.yaml\"))\n\t}\n\totherFiles = append(otherFiles, \"/etc/rancher/k3s/k3s.yaml\")\n\tloadingRules.Precedence = append(loadingRules.Precedence, canRead(otherFiles)...)\n\n\treturn loadingRules\n}\n\nfunc canRead(files []string) (result []string) {\n\tfor _, f := range files {\n\t\t_, err := os.ReadFile(f)\n\t\tif err == nil {\n\t\t\tresult = append(result, f)\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "pkg/kv/split.go",
    "content": "package kv\n\nimport \"strings\"\n\n// Like split but if there is only one item return \"\", item\nfunc RSplit(s, sep string) (string, string) {\n\tparts := strings.SplitN(s, sep, 2)\n\tif len(parts) == 1 {\n\t\treturn \"\", strings.TrimSpace(parts[0])\n\t}\n\treturn strings.TrimSpace(parts[0]), strings.TrimSpace(safeIndex(parts, 1))\n}\n\nfunc Split(s, sep string) (string, string) {\n\tparts := strings.SplitN(s, sep, 2)\n\treturn strings.TrimSpace(parts[0]), strings.TrimSpace(safeIndex(parts, 1))\n}\n\nfunc SplitLast(s, sep string) (string, string) {\n\tidx := strings.LastIndex(s, sep)\n\tif idx > -1 {\n\t\treturn strings.TrimSpace(s[:idx]), strings.TrimSpace(s[idx+1:])\n\t}\n\treturn s, \"\"\n}\n\nfunc SplitMap(s, sep string) map[string]string {\n\treturn SplitMapFromSlice(strings.Split(s, sep))\n}\n\nfunc SplitMapFromSlice(parts []string) map[string]string {\n\tresult := map[string]string{}\n\tfor _, part := range parts {\n\t\tk, v := Split(part, \"=\")\n\t\tresult[k] = v\n\t}\n\treturn result\n}\n\nfunc safeIndex(parts []string, idx int) string {\n\tif len(parts) <= idx {\n\t\treturn \"\"\n\t}\n\treturn parts[idx]\n}\n"
  },
  {
    "path": "pkg/leader/leader.go",
    "content": "package leader\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/sirupsen/logrus\"\n\t\"k8s.io/client-go/kubernetes\"\n\t\"k8s.io/client-go/tools/leaderelection\"\n\t\"k8s.io/client-go/tools/leaderelection/resourcelock\"\n)\n\ntype Callback func(cb context.Context)\n\nconst devModeEnvKey = \"CATTLE_DEV_MODE\"\nconst leaseDurationEnvKey = \"CATTLE_ELECTION_LEASE_DURATION\"\nconst renewDeadlineEnvKey = \"CATTLE_ELECTION_RENEW_DEADLINE\"\nconst retryPeriodEnvKey = \"CATTLE_ELECTION_RETRY_PERIOD\"\n\nconst defaultLeaseDuration = 45 * time.Second\nconst defaultRenewDeadline = 30 * time.Second\nconst defaultRetryPeriod = 2 * time.Second\n\nconst developmentLeaseDuration = 45 * time.Hour\nconst developmentRenewDeadline = 30 * time.Hour\n\nfunc RunOrDie(ctx context.Context, namespace, name string, client kubernetes.Interface, cb Callback) {\n\tif namespace == \"\" {\n\t\tnamespace = \"kube-system\"\n\t}\n\n\terr := run(ctx, namespace, name, client, cb)\n\tif err != nil {\n\t\tlogrus.Fatalf(\"Failed to start leader election for %s\", name)\n\t}\n\tpanic(\"Failed to start leader election for \" + name)\n}\n\nfunc run(ctx context.Context, namespace, name string, client kubernetes.Interface, cb Callback) error {\n\tid, err := os.Hostname()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trl, err := resourcelock.New(resourcelock.LeasesResourceLock,\n\t\tnamespace,\n\t\tname,\n\t\tclient.CoreV1(),\n\t\tclient.CoordinationV1(),\n\t\tresourcelock.ResourceLockConfig{\n\t\t\tIdentity: id,\n\t\t})\n\tif err != nil {\n\t\tlogrus.Fatalf(\"error creating leader lock for %s: %v\", name, err)\n\t}\n\n\tcbs := leaderelection.LeaderCallbacks{\n\t\tOnStartedLeading: func(ctx context.Context) {\n\t\t\tgo cb(ctx)\n\t\t},\n\t\tOnStoppedLeading: func() {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\t// The context has been canceled or is otherwise complete.\n\t\t\t\t// This is a request to terminate. Exit 0.\n\t\t\t\t// Exiting cleanly is useful when the context is canceled\n\t\t\t\t// so that Kubernetes doesn't record it exiting in error\n\t\t\t\t// when the exit was requested. For example, the wrangler-cli\n\t\t\t\t// package sets up a context that cancels when SIGTERM is\n\t\t\t\t// sent in. If a node is shut down this is the type of signal\n\t\t\t\t// sent. In that case you want the 0 exit code to mark it as\n\t\t\t\t// complete so that everything comes back up correctly after\n\t\t\t\t// a restart.\n\t\t\t\t// The pattern found here can be found inside the kube-scheduler.\n\t\t\t\tlogrus.Info(\"requested to terminate, exiting\")\n\t\t\t\tos.Exit(0)\n\t\t\tdefault:\n\t\t\t\tlogrus.Fatalf(\"leaderelection lost for %s\", name)\n\t\t\t}\n\t\t},\n\t}\n\n\tconfig, err := computeConfig(rl, cbs)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tleaderelection.RunOrDie(ctx, *config)\n\tpanic(\"unreachable\")\n}\n\nfunc computeConfig(rl resourcelock.Interface, cbs leaderelection.LeaderCallbacks) (*leaderelection.LeaderElectionConfig, error) {\n\tleaseDuration := defaultLeaseDuration\n\trenewDeadline := defaultRenewDeadline\n\tretryPeriod := defaultRetryPeriod\n\tvar err error\n\tif d := os.Getenv(devModeEnvKey); d != \"\" {\n\t\tleaseDuration = developmentLeaseDuration\n\t\trenewDeadline = developmentRenewDeadline\n\t}\n\tif d := os.Getenv(leaseDurationEnvKey); d != \"\" {\n\t\tleaseDuration, err = time.ParseDuration(d)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s value [%s] is not a valid duration: %w\", leaseDurationEnvKey, d, err)\n\t\t}\n\t}\n\tif d := os.Getenv(renewDeadlineEnvKey); d != \"\" {\n\t\trenewDeadline, err = time.ParseDuration(d)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s value [%s] is not a valid duration: %w\", renewDeadlineEnvKey, d, err)\n\t\t}\n\t}\n\tif d := os.Getenv(retryPeriodEnvKey); d != \"\" {\n\t\tretryPeriod, err = time.ParseDuration(d)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s value [%s] is not a valid duration: %w\", retryPeriodEnvKey, d, err)\n\t\t}\n\t}\n\n\treturn &leaderelection.LeaderElectionConfig{\n\t\tLock:            rl,\n\t\tLeaseDuration:   leaseDuration,\n\t\tRenewDeadline:   renewDeadline,\n\t\tRetryPeriod:     retryPeriod,\n\t\tCallbacks:       cbs,\n\t\tReleaseOnCancel: true,\n\t}, nil\n}\n"
  },
  {
    "path": "pkg/leader/leader_test.go",
    "content": "package leader\n\nimport (\n\t\"os\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"k8s.io/client-go/tools/leaderelection\"\n\t\"k8s.io/client-go/tools/leaderelection/resourcelock\"\n)\n\nfunc Test_computeConfig(t *testing.T) {\n\ttype args struct {\n\t\trl  resourcelock.Interface\n\t\tcbs leaderelection.LeaderCallbacks\n\t}\n\ttype env struct {\n\t\tkey   string\n\t\tvalue string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\tenvs    []env\n\t\twant    *leaderelection.LeaderElectionConfig\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"all defaults\",\n\t\t\targs: args{\n\t\t\t\trl:  nil,\n\t\t\t\tcbs: leaderelection.LeaderCallbacks{},\n\t\t\t},\n\t\t\tenvs: []env{},\n\t\t\twant: &leaderelection.LeaderElectionConfig{\n\t\t\t\tLock:            nil,\n\t\t\t\tLeaseDuration:   defaultLeaseDuration,\n\t\t\t\tRenewDeadline:   defaultRenewDeadline,\n\t\t\t\tRetryPeriod:     defaultRetryPeriod,\n\t\t\t\tCallbacks:       leaderelection.LeaderCallbacks{},\n\t\t\t\tReleaseOnCancel: true,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"dev mode\",\n\t\t\targs: args{\n\t\t\t\trl:  nil,\n\t\t\t\tcbs: leaderelection.LeaderCallbacks{},\n\t\t\t},\n\t\t\tenvs: []env{\n\t\t\t\t{key: devModeEnvKey, value: \"true\"},\n\t\t\t},\n\t\t\twant: &leaderelection.LeaderElectionConfig{\n\t\t\t\tLock:            nil,\n\t\t\t\tLeaseDuration:   developmentLeaseDuration,\n\t\t\t\tRenewDeadline:   developmentRenewDeadline,\n\t\t\t\tRetryPeriod:     defaultRetryPeriod,\n\t\t\t\tCallbacks:       leaderelection.LeaderCallbacks{},\n\t\t\t\tReleaseOnCancel: true,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"all overridden\",\n\t\t\targs: args{\n\t\t\t\trl:  nil,\n\t\t\t\tcbs: leaderelection.LeaderCallbacks{},\n\t\t\t},\n\t\t\tenvs: []env{\n\t\t\t\t{key: devModeEnvKey, value: \"true\"},\n\t\t\t\t{key: leaseDurationEnvKey, value: \"1s\"},\n\t\t\t\t{key: renewDeadlineEnvKey, value: \"2s\"},\n\t\t\t\t{key: retryPeriodEnvKey, value: \"3m\"},\n\t\t\t},\n\t\t\twant: &leaderelection.LeaderElectionConfig{\n\t\t\t\tLock:            nil,\n\t\t\t\tLeaseDuration:   time.Second,\n\t\t\t\tRenewDeadline:   2 * time.Second,\n\t\t\t\tRetryPeriod:     3 * time.Minute,\n\t\t\t\tCallbacks:       leaderelection.LeaderCallbacks{},\n\t\t\t\tReleaseOnCancel: true,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"unparseable lease duration\",\n\t\t\targs: args{\n\t\t\t\trl:  nil,\n\t\t\t\tcbs: leaderelection.LeaderCallbacks{},\n\t\t\t},\n\t\t\tenvs: []env{\n\t\t\t\t{key: leaseDurationEnvKey, value: \"bomb\"},\n\t\t\t\t{key: renewDeadlineEnvKey, value: \"2s\"},\n\t\t\t\t{key: retryPeriodEnvKey, value: \"3m\"},\n\t\t\t},\n\t\t\twant:    nil,\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"unparseable renew deadline\",\n\t\t\targs: args{\n\t\t\t\trl:  nil,\n\t\t\t\tcbs: leaderelection.LeaderCallbacks{},\n\t\t\t},\n\t\t\tenvs: []env{\n\t\t\t\t{key: leaseDurationEnvKey, value: \"1s\"},\n\t\t\t\t{key: renewDeadlineEnvKey, value: \"bomb\"},\n\t\t\t\t{key: retryPeriodEnvKey, value: \"3m\"},\n\t\t\t},\n\t\t\twant:    nil,\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"unparseable retry period\",\n\t\t\targs: args{\n\t\t\t\trl:  nil,\n\t\t\t\tcbs: leaderelection.LeaderCallbacks{},\n\t\t\t},\n\t\t\tenvs: []env{\n\t\t\t\t{key: leaseDurationEnvKey, value: \"1s\"},\n\t\t\t\t{key: renewDeadlineEnvKey, value: \"2s\"},\n\t\t\t\t{key: retryPeriodEnvKey, value: \"bomb\"},\n\t\t\t},\n\t\t\twant:    nil,\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tfor _, e := range []string{leaseDurationEnvKey, renewDeadlineEnvKey, retryPeriodEnvKey} {\n\t\t\t\terr := os.Unsetenv(e)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"could not Unsetenv: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor _, e := range tt.envs {\n\t\t\t\terr := os.Setenv(e.key, e.value)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"could not SetEnv: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tgot, err := computeConfig(tt.args.rl, tt.args.cbs)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"computeConfig() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"computeConfig() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/leader/manager.go",
    "content": "package leader\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/sirupsen/logrus\"\n\t\"k8s.io/client-go/kubernetes\"\n)\n\ntype Manager struct {\n\tsync.Mutex\n\tleaderChan    chan struct{}\n\tleaderStarted bool\n\tleaderCTX     context.Context\n\tnamespace     string\n\tname          string\n\tk8s           kubernetes.Interface\n}\n\nfunc NewManager(namespace, name string, k8s kubernetes.Interface) *Manager {\n\treturn &Manager{\n\t\tleaderChan: make(chan struct{}),\n\t\tnamespace:  namespace,\n\t\tname:       name,\n\t\tk8s:        k8s,\n\t}\n}\n\nfunc (m *Manager) Start(ctx context.Context) {\n\tm.Lock()\n\tdefer m.Unlock()\n\n\tif m.leaderStarted {\n\t\treturn\n\t}\n\n\tm.leaderStarted = true\n\tgo RunOrDie(ctx, m.namespace, m.name, m.k8s, func(ctx context.Context) {\n\t\tm.leaderCTX = ctx\n\t\tclose(m.leaderChan)\n\t})\n}\n\n// OnLeaderOrDie this function will be called when leadership is acquired or die if failed\nfunc (m *Manager) OnLeaderOrDie(name string, f func(ctx context.Context) error) {\n\tgo func() {\n\t\t<-m.leaderChan\n\t\tif err := f(m.leaderCTX); err != nil {\n\t\t\tlogrus.Fatalf(\"%s leader func failed be executed: %v\", name, err)\n\t\t} else {\n\t\t\tlogrus.Infof(\"%s leader func executed successfully\", name)\n\t\t}\n\t}()\n}\n\n// OnLeader this function will be called when leadership is acquired.\nfunc (m *Manager) OnLeader(f func(ctx context.Context) error) {\n\tgo func() {\n\t\t<-m.leaderChan\n\t\tfor {\n\t\t\tif err := f(m.leaderCTX); err != nil {\n\t\t\t\tlogrus.Errorf(\"failed to call leader func: %v\", err)\n\t\t\t\ttime.Sleep(5 * time.Second)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}()\n}\n"
  },
  {
    "path": "pkg/merr/error.go",
    "content": "package merr\n\nimport \"bytes\"\n\ntype Errors []error\n\nfunc (e Errors) Err() error {\n\treturn NewErrors(e...)\n}\n\nfunc (e Errors) Error() string {\n\tbuf := bytes.NewBuffer(nil)\n\tfor _, err := range e {\n\t\tif buf.Len() > 0 {\n\t\t\tbuf.WriteString(\", \")\n\t\t}\n\t\tbuf.WriteString(err.Error())\n\t}\n\n\treturn buf.String()\n}\n\nfunc NewErrors(inErrors ...error) error {\n\tvar errors []error\n\tfor _, err := range inErrors {\n\t\tif err != nil {\n\t\t\terrors = append(errors, err)\n\t\t}\n\t}\n\n\tif len(errors) == 0 {\n\t\treturn nil\n\t} else if len(errors) == 1 {\n\t\treturn errors[0]\n\t}\n\treturn Errors(errors)\n}\n"
  },
  {
    "path": "pkg/name/name.go",
    "content": "package name\n\nimport (\n\t\"crypto/md5\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"strings\"\n)\n\n// GuessPluralName attempts to pluralize a noun.\nfunc GuessPluralName(name string) string {\n\tif name == \"\" {\n\t\treturn name\n\t}\n\n\tif strings.EqualFold(name, \"Endpoints\") {\n\t\treturn name\n\t}\n\n\tif suffix(name, \"s\") || suffix(name, \"ch\") || suffix(name, \"x\") || suffix(name, \"sh\") {\n\t\treturn name + \"es\"\n\t}\n\n\tif suffix(name, \"f\") || suffix(name, \"fe\") {\n\t\treturn name + \"ves\"\n\t}\n\n\tif suffix(name, \"y\") && len(name) > 2 && !strings.ContainsAny(name[len(name)-2:len(name)-1], \"[aeiou]\") {\n\t\treturn name[0:len(name)-1] + \"ies\"\n\t}\n\n\treturn name + \"s\"\n}\n\nfunc suffix(str, end string) bool {\n\treturn strings.HasSuffix(str, end)\n}\n\n// Limit the length of a string to count characters. If the string's length is\n// greater or equal to count, it will be truncated and a hash will be appended\n// to the end.\n// Warning: runtime error for count <= 5: https://go.dev/play/p/UAbpZIOvIYo\nfunc Limit(s string, count int) string {\n\tif len(s) < count {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"%s-%s\", s[:count-6], Hex(s, 5))\n}\n\n// Hex gets the checksum of s, encodes it to hexadecimal and returns the first n characters of that hexadecimal.\n// Warning: runtime error for  n > 32 or n < 0.\nfunc Hex(s string, n int) string {\n\th := md5.Sum([]byte(s))\n\td := hex.EncodeToString(h[:])\n\treturn d[:n]\n}\n\n// SafeConcatName concatenates the given strings and ensures the returned name is under 64 characters\n// by cutting the string off at 57 characters and setting the last 6 with an encoded version of the concatenated string.\nfunc SafeConcatName(name ...string) string {\n\tfullPath := strings.Join(name, \"-\")\n\tif len(fullPath) < 64 {\n\t\treturn fullPath\n\t}\n\tdigest := sha256.Sum256([]byte(fullPath))\n\t// since we cut the string in the middle, the last char may not be compatible with what is expected in k8s\n\t// we are checking and if necessary removing the last char\n\tc := fullPath[56]\n\tif 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {\n\t\treturn fullPath[0:57] + \"-\" + hex.EncodeToString(digest[0:])[0:5]\n\t}\n\n\treturn fullPath[0:56] + \"-\" + hex.EncodeToString(digest[0:])[0:6]\n}\n"
  },
  {
    "path": "pkg/name/name_test.go",
    "content": "package name\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nconst (\n\tstring32 = \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\tstring63 = \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\tstring64 = \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n)\n\n// TODO: Improve GuessPluralNames. The rules used do not accurately pluralize nouns.\n// For now this unit test covers the existing functionality to ensure backwards compatibility.\nfunc TestGuessPluralName(t *testing.T) {\n\tt.Parallel()\n\ttests := []struct {\n\t\tname  string\n\t\tnouns map[string]string\n\t}{\n\t\t{\n\t\t\tname:  \"Empty string\",\n\t\t\tnouns: map[string]string{\"\": \"\"},\n\t\t},\n\t\t{\n\t\t\tname:  \"Special cases\",\n\t\t\tnouns: map[string]string{\"Endpoints\": \"Endpoints\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Strings ending in s ch x and sh\",\n\t\t\tnouns: map[string]string{\n\t\t\t\t\"iris\":  \"irises\",\n\t\t\t\t\"leech\": \"leeches\",\n\t\t\t\t\"tax\":   \"taxes\",\n\t\t\t\t\"fish\":  \"fishes\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Strings ending in f and fe\",\n\t\t\tnouns: map[string]string{\n\t\t\t\t\"elf\":  \"elfves\",\n\t\t\t\t\"safe\": \"safeves\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Strings ending in y\",\n\t\t\tnouns: map[string]string{\n\t\t\t\t\"candy\":    \"candies\",\n\t\t\t\t\"birthday\": \"birthdays\",\n\t\t\t\t\"turkey\":   \"turkeys\",\n\t\t\t\t\"toy\":      \"toys\",\n\t\t\t\t\"guy\":      \"guys\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Non-special strings\",\n\t\t\tnouns: map[string]string{\n\t\t\t\t\"friend\": \"friends\",\n\t\t\t\t\"cat\":    \"cats\",\n\t\t\t\t\"dog\":    \"dogs\",\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tfor k, v := range tt.nouns {\n\t\t\t\tgot := GuessPluralName(k)\n\t\t\t\tassert.Equal(t, v, got, \"GuessPluralName(%v) = %v, want %v\", k, got, v)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLimit(t *testing.T) {\n\tt.Parallel()\n\ttests := []struct {\n\t\tname      string\n\t\ts         string\n\t\tcount     int\n\t\twant      string\n\t\twantPanic bool\n\t}{\n\t\t{\n\t\t\tname:  \"length of string is less than count\",\n\t\t\ts:     \"aaaaaa\",\n\t\t\tcount: 7,\n\t\t\twant:  \"aaaaaa\",\n\t\t},\n\t\t{\n\t\t\tname:  \"length of string is equal to count\",\n\t\t\ts:     \"aaaaaaa\",\n\t\t\tcount: 7,\n\t\t\twant:  \"a-5d793\",\n\t\t},\n\t\t{\n\t\t\tname:  \"length of string is greater than count\",\n\t\t\ts:     \"aaaaaaaaaaaaaaaaaa\",\n\t\t\tcount: 8,\n\t\t\twant:  \"aa-2c60c\",\n\t\t},\n\t\t{\n\t\t\tname:  \"only hash exists when length of string and count are 6\",\n\t\t\ts:     \"aaaaaa\",\n\t\t\tcount: 6,\n\t\t\twant:  \"-0b4e7\",\n\t\t},\n\t\t{\n\t\t\tname:  \"empty string\",\n\t\t\ts:     \"\",\n\t\t\tcount: 7,\n\t\t\twant:  \"\",\n\t\t},\n\t\t{\n\t\t\tname:      \"panic when count <= 5\",\n\t\t\ts:         \"aaaaaaa\",\n\t\t\tcount:     5,\n\t\t\twantPanic: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tif tt.wantPanic {\n\t\t\t\tassert.Panics(t, func() { Limit(tt.s, tt.count) })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif got := Limit(tt.s, tt.count); got != tt.want {\n\t\t\t\tt.Errorf(\"Limit() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHex(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname      string\n\t\ts         string\n\t\tn         int\n\t\twant      string\n\t\twantPanic bool\n\t}{\n\t\t{\n\t\t\tname: \"basic test\",\n\t\t\ts:    \"aaaaaa\",\n\t\t\tn:    4,\n\t\t\twant: \"0b4e\",\n\t\t},\n\t\t{\n\t\t\tname: \"full checksum\",\n\t\t\ts:    \"aaaaaa\",\n\t\t\tn:    32,\n\t\t\twant: \"0b4e7a0e5fe84ad35fb5f95b9ceeac79\",\n\t\t},\n\t\t{\n\t\t\tname:      \"get more characters than full checksum\",\n\t\t\ts:         \"aaaaaa\",\n\t\t\tn:         33,\n\t\t\twantPanic: true,\n\t\t},\n\t\t{\n\t\t\tname:      \"get negative characters\",\n\t\t\ts:         \"aaaaaa\",\n\t\t\tn:         -1,\n\t\t\twantPanic: true,\n\t\t},\n\t\t{\n\t\t\tname: \"get 0 characters\",\n\t\t\ts:    \"aaaaaa\",\n\t\t\tn:    0,\n\t\t\twant: \"\",\n\t\t},\n\t\t{\n\t\t\tname: \"empty string\",\n\t\t\ts:    \"\",\n\t\t\tn:    4,\n\t\t\twant: \"d41d\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tif tt.wantPanic {\n\t\t\t\tassert.Panics(t, func() { Hex(tt.s, tt.n) })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif got := Hex(tt.s, tt.n); got != tt.want {\n\t\t\t\tt.Errorf(\"Hex() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSafeConcatName(t *testing.T) {\n\tt.Parallel()\n\ttests := []struct {\n\t\tname   string\n\t\tinput  []string\n\t\toutput string\n\t}{\n\t\t{\n\t\t\tname:   \"empty input\",\n\t\t\toutput: \"\",\n\t\t},\n\t\t{\n\t\t\tname:   \"single string\",\n\t\t\tinput:  []string{string63},\n\t\t\toutput: string63,\n\t\t},\n\t\t{\n\t\t\tname:   \"single long string\",\n\t\t\tinput:  []string{string64},\n\t\t\toutput: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-ffe05\",\n\t\t},\n\t\t{\n\t\t\tname:   \"concatenate strings\",\n\t\t\tinput:  []string{\"first\", \"second\", \"third\"},\n\t\t\toutput: \"first-second-third\",\n\t\t},\n\t\t{\n\t\t\tname:   \"concatenate past 64 characters\",\n\t\t\tinput:  []string{string32, string32},\n\t\t\toutput: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-aaaaaaaaaaaaaaaaaaaaaaaa-da5ed\",\n\t\t},\n\t\t{\n\t\t\tname:   \"last character after truncation is not alphanumeric\",\n\t\t\tinput:  []string{\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-aaaaaaa\"},\n\t\t\toutput: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-768c62\",\n\t\t},\n\t\t{\n\t\t\tname:   \"last characters after truncation aren't alphanumeric\",\n\t\t\tinput:  []string{\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--aaaaaaa\"},\n\t\t\toutput: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--9e8cfe\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tif got := SafeConcatName(tt.input...); got != tt.output {\n\t\t\t\tt.Errorf(\"SafeConcatName() = %v, want %v\", got, tt.output)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/needacert/needacert.go",
    "content": "package needacert\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/moby/locker\"\n\tadmissionregcontrollers \"github.com/rancher/wrangler/v3/pkg/generated/controllers/admissionregistration.k8s.io/v1\"\n\tapiextcontrollers \"github.com/rancher/wrangler/v3/pkg/generated/controllers/apiextensions.k8s.io/v1\"\n\tcorecontrollers \"github.com/rancher/wrangler/v3/pkg/generated/controllers/core/v1\"\n\t\"github.com/rancher/wrangler/v3/pkg/gvk\"\n\t\"github.com/rancher/wrangler/v3/pkg/relatedresource\"\n\t\"github.com/rancher/wrangler/v3/pkg/slice\"\n\t\"github.com/sirupsen/logrus\"\n\tadminregv1 \"k8s.io/api/admissionregistration/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tapiextv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\tapierror \"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/util/sets\"\n\t\"k8s.io/client-go/util/cert\"\n)\n\nvar (\n\tSecretAnnotation = \"need-a-cert.cattle.io/secret-name\"\n\tDNSAnnotation    = \"need-a-cert.cattle.io/dns-name\"\n)\n\nconst (\n\tbyServiceIndex = \"byService\"\n\tbySecretIndex  = \"bySecret\"\n)\n\nfunc Register(ctx context.Context,\n\tsecrets corecontrollers.SecretController,\n\tservice corecontrollers.ServiceController,\n\tmutatingController admissionregcontrollers.MutatingWebhookConfigurationController,\n\tvalidatingController admissionregcontrollers.ValidatingWebhookConfigurationController,\n\tcrdController apiextcontrollers.CustomResourceDefinitionController) {\n\th := handler{\n\t\tsecretsCache:       secrets.Cache(),\n\t\tsecrets:            secrets,\n\t\tservices:           service,\n\t\tserviceCache:       service.Cache(),\n\t\tmutatingWebHooks:   mutatingController,\n\t\tvalidatingWebHooks: validatingController,\n\t\tcrds:               crdController,\n\t}\n\n\tmutatingController.Cache().AddIndexer(byServiceIndex, mutatingWebhookServices)\n\tvalidatingController.Cache().AddIndexer(byServiceIndex, validatingWebhookServices)\n\tcrdController.Cache().AddIndexer(byServiceIndex, crdWebhookServices)\n\n\tservice.Cache().AddIndexer(bySecretIndex, serviceSecret)\n\n\tmutatingController.OnChange(ctx, \"need-a-cert\", h.OnMutationWebhookChange)\n\tvalidatingController.OnChange(ctx, \"need-a-cert\", h.OnValidatingWebhookChange)\n\tcrdController.OnChange(ctx, \"need-a-cert\", h.OnCRDChange)\n\tservice.OnChange(ctx, \"need-a-cert\", h.OnService)\n\n\trelatedresource.Watch(ctx, \"resolve-service-from-secret\", h.resolveServiceFromSecret, service, secrets)\n\n}\n\nfunc (h *handler) resolveServiceFromSecret(namespace, name string, obj runtime.Object) ([]relatedresource.Key, error) {\n\tif _, ok := obj.(*corev1.Secret); !ok {\n\t\treturn nil, nil\n\t}\n\n\tservices, err := h.serviceCache.GetByIndex(bySecretIndex, namespace+\"/\"+name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar result []relatedresource.Key\n\tfor _, service := range services {\n\t\tresult = append(result, relatedresource.NewKey(service.GetNamespace(), service.GetName()))\n\t}\n\treturn result, nil\n}\n\nfunc serviceSecret(obj *corev1.Service) ([]string, error) {\n\tsecretName := obj.Annotations[SecretAnnotation]\n\tif secretName == \"\" {\n\t\treturn nil, nil\n\t}\n\treturn []string{\n\t\tobj.Namespace + \"/\" + secretName,\n\t}, nil\n}\n\ntype handler struct {\n\tlocker             locker.Locker\n\tsecretsCache       corecontrollers.SecretCache\n\tsecrets            corecontrollers.SecretClient\n\tserviceCache       corecontrollers.ServiceCache\n\tservices           corecontrollers.ServiceController\n\tmutatingWebHooks   admissionregcontrollers.MutatingWebhookConfigurationController\n\tvalidatingWebHooks admissionregcontrollers.ValidatingWebhookConfigurationController\n\tcrds               apiextcontrollers.CustomResourceDefinitionController\n}\n\nfunc validatingWebhookServices(obj *adminregv1.ValidatingWebhookConfiguration) (result []string, _ error) {\n\tfor _, webhook := range obj.Webhooks {\n\t\tif webhook.ClientConfig.Service != nil {\n\t\t\tresult = append(result, webhook.ClientConfig.Service.Namespace+\"/\"+webhook.ClientConfig.Service.Name)\n\t\t}\n\t}\n\treturn\n}\n\nfunc crdWebhookServices(obj *apiextv1.CustomResourceDefinition) (result []string, _ error) {\n\tif obj.Spec.Conversion != nil &&\n\t\tobj.Spec.Conversion.Webhook != nil &&\n\t\tobj.Spec.Conversion.Webhook.ClientConfig != nil &&\n\t\tobj.Spec.Conversion.Webhook.ClientConfig.Service != nil {\n\t\treturn []string{\n\t\t\tfmt.Sprintf(\"%s/%s\",\n\t\t\t\tobj.Spec.Conversion.Webhook.ClientConfig.Service.Namespace,\n\t\t\t\tobj.Spec.Conversion.Webhook.ClientConfig.Service.Name),\n\t\t}, nil\n\t}\n\treturn nil, nil\n}\n\nfunc mutatingWebhookServices(obj *adminregv1.MutatingWebhookConfiguration) (result []string, _ error) {\n\tfor _, webhook := range obj.Webhooks {\n\t\tif webhook.ClientConfig.Service != nil {\n\t\t\tresult = append(result, webhook.ClientConfig.Service.Namespace+\"/\"+webhook.ClientConfig.Service.Name)\n\t\t}\n\t}\n\treturn\n}\n\nfunc (h *handler) OnMutationWebhookChange(key string, webhook *adminregv1.MutatingWebhookConfiguration) (*adminregv1.MutatingWebhookConfiguration, error) {\n\tif webhook == nil {\n\t\treturn nil, nil\n\t}\n\tneedUpdate := false\n\tfor i, webhookConfig := range webhook.Webhooks {\n\t\tif webhookConfig.ClientConfig.Service == nil || webhookConfig.ClientConfig.Service.Name == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tservice, err := h.serviceCache.Get(webhookConfig.ClientConfig.Service.Namespace, webhookConfig.ClientConfig.Service.Name)\n\t\tif apierror.IsNotFound(err) {\n\t\t\t// OnService will be called when the service is created, which will eventually update the webhook, so no\n\t\t\t// need to enqueue anything if we don't find the service\n\t\t\treturn webhook, nil\n\t\t} else if err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tsecret, err := h.generateSecret(service)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t} else if secret == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif !bytes.Equal(webhookConfig.ClientConfig.CABundle, secret.Data[corev1.TLSCertKey]) {\n\t\t\twebhook = webhook.DeepCopy()\n\t\t\twebhook.Webhooks[i].ClientConfig.CABundle = secret.Data[corev1.TLSCertKey]\n\t\t\tneedUpdate = true\n\t\t}\n\t}\n\n\tif needUpdate {\n\t\tlogrus.Debugf(\"Updating MutatingWebhookConfiguration %s/%s\", webhook.GetNamespace(), webhook.GetName())\n\t\twebhook, err := h.mutatingWebHooks.Update(webhook)\n\t\tif err != nil {\n\t\t\treturn webhook, err\n\t\t}\n\t}\n\n\treturn webhook, nil\n}\n\nfunc (h *handler) OnValidatingWebhookChange(key string, webhook *adminregv1.ValidatingWebhookConfiguration) (*adminregv1.ValidatingWebhookConfiguration, error) {\n\tif webhook == nil {\n\t\treturn nil, nil\n\t}\n\tneedUpdate := false\n\tfor i, webhookConfig := range webhook.Webhooks {\n\t\tif webhookConfig.ClientConfig.Service == nil || webhookConfig.ClientConfig.Service.Name == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tservice, err := h.serviceCache.Get(webhookConfig.ClientConfig.Service.Namespace, webhookConfig.ClientConfig.Service.Name)\n\t\tif apierror.IsNotFound(err) {\n\t\t\t// OnService will be called when the service is created, which will eventually update the webhook, so no\n\t\t\t// need to enqueue anything if we don't find the service\n\t\t\treturn webhook, nil\n\t\t} else if err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tsecret, err := h.generateSecret(service)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t} else if secret == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif !bytes.Equal(webhookConfig.ClientConfig.CABundle, secret.Data[corev1.TLSCertKey]) {\n\t\t\twebhook = webhook.DeepCopy()\n\t\t\twebhook.Webhooks[i].ClientConfig.CABundle = secret.Data[corev1.TLSCertKey]\n\t\t\tneedUpdate = true\n\t\t}\n\t}\n\n\tif needUpdate {\n\t\tlogrus.Debugf(\"Updating ValidatingWebhookConfiguration %s/%s\", webhook.GetNamespace(), webhook.GetName())\n\t\twebhook, err := h.validatingWebHooks.Update(webhook)\n\t\tif err != nil {\n\t\t\treturn webhook, err\n\t\t}\n\t}\n\n\treturn webhook, nil\n}\n\nfunc (h *handler) OnService(key string, service *corev1.Service) (*corev1.Service, error) {\n\tif service == nil {\n\t\treturn service, nil\n\t}\n\n\t_, err := h.generateSecret(service)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tindexKey := service.Namespace + \"/\" + service.Name\n\tmutating, err := h.mutatingWebHooks.Cache().GetByIndex(byServiceIndex, indexKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, mutating := range mutating {\n\t\th.mutatingWebHooks.Enqueue(mutating.Name)\n\t}\n\n\tvalidating, err := h.validatingWebHooks.Cache().GetByIndex(byServiceIndex, indexKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, validating := range validating {\n\t\th.validatingWebHooks.Enqueue(validating.Name)\n\t}\n\n\tcrd, err := h.crds.Cache().GetByIndex(byServiceIndex, indexKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, crd := range crd {\n\t\th.crds.Enqueue(crd.Name)\n\t}\n\n\treturn nil, err\n}\n\nfunc (h *handler) OnCRDChange(key string, crd *apiextv1.CustomResourceDefinition) (*apiextv1.CustomResourceDefinition, error) {\n\tif crd == nil || crd.Spec.Conversion == nil || crd.Spec.Conversion.Webhook == nil ||\n\t\tcrd.Spec.Conversion.Webhook.ClientConfig == nil ||\n\t\tcrd.Spec.Conversion.Webhook.ClientConfig.Service == nil ||\n\t\tcrd.Spec.Conversion.Webhook.ClientConfig.Service.Name == \"\" {\n\t\treturn crd, nil\n\t}\n\n\tservice, err := h.serviceCache.Get(crd.Spec.Conversion.Webhook.ClientConfig.Service.Namespace,\n\t\tcrd.Spec.Conversion.Webhook.ClientConfig.Service.Name)\n\n\tif apierror.IsNotFound(err) {\n\t\t// OnService will be called when the service is created, which will eventually update the CRD, so no\n\t\t// need to enqueue anything if we don't find the service\n\t\treturn crd, nil\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\tsecret, err := h.generateSecret(service)\n\tif err != nil || secret == nil {\n\t\treturn crd, nil\n\t}\n\n\tif !bytes.Equal(crd.Spec.Conversion.Webhook.ClientConfig.CABundle, secret.Data[corev1.TLSCertKey]) {\n\t\tcrd := crd.DeepCopy()\n\t\tcrd.Spec.Conversion.Webhook.ClientConfig.CABundle = secret.Data[corev1.TLSCertKey]\n\t\treturn h.crds.Update(crd)\n\t}\n\n\treturn crd, nil\n}\n\nfunc (h *handler) generateSecret(service *corev1.Service) (*corev1.Secret, error) {\n\tsecretName := service.Annotations[SecretAnnotation]\n\tif secretName == \"\" {\n\t\treturn nil, nil\n\t}\n\n\tlockKey := service.Namespace + \"/\" + service.Name\n\th.locker.Lock(lockKey)\n\tdefer h.locker.Unlock(lockKey)\n\n\tdnsNameSet := sets.NewString(service.Name+\".\"+service.Namespace,\n\t\tservice.Name+\".\"+service.Namespace+\".svc\",\n\t\tservice.Name+\".\"+service.Namespace+\".svc.cluster.local\")\n\tfor k, v := range service.Annotations {\n\t\tif !strings.HasPrefix(k, DNSAnnotation) {\n\t\t\tcontinue\n\t\t}\n\t\tdnsNameSet.Insert(v)\n\t}\n\n\t// this is sorted\n\tdnsNames := dnsNameSet.List()\n\tsecret, err := h.secretsCache.Get(service.Namespace, secretName)\n\tif apierror.IsNotFound(err) {\n\t\tnewSecret, err := h.createSecret(service, service.Namespace, secretName, dnsNames)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsecret, err = h.secrets.Create(newSecret)\n\t\tif apierror.IsAlreadyExists(err) {\n\t\t\tsecret, err = h.secrets.Get(service.Namespace, secretName, metav1.GetOptions{})\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t} else if err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\tcert, parseErr := parseCert(secret)\n\tif parseErr != nil {\n\t\treturn nil, parseErr\n\t}\n\n\tupdated, updateErr := h.updateSecret(service, secret, dnsNames, cert)\n\tif updateErr != nil {\n\t\treturn nil, updateErr\n\t} else if updated != nil {\n\t\tsecret, err = h.secrets.Update(updated)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif err := h.scheduleNextCertCheck(service, secret); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to schedule next cert check: %w\", err)\n\t}\n\n\treturn secret, nil\n}\n\nfunc (h *handler) updateSecret(owner runtime.Object, secret *corev1.Secret, dnsNames []string, cert *x509.Certificate) (*corev1.Secret, error) {\n\tlogrus.Debugf(\"checking cert %s for %s/%s\", cert.Subject.CommonName, secret.Namespace, secret.Name)\n\tif time.Now().Add(24*60*time.Hour).After(cert.NotAfter) ||\n\t\tlen(cert.DNSNames) == 0 ||\n\t\t!slice.StringsEqual(cert.DNSNames[1:], dnsNames) {\n\t\tlogrus.Debugf(\"regenerating cert %s for %s/%s\", cert.Subject.CommonName, secret.Namespace, secret.Name)\n\t\tnewSecret, err := h.createSecret(owner, secret.Namespace, secret.Name, dnsNames)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsecret = secret.DeepCopy()\n\t\tsecret.Data = newSecret.Data\n\t\treturn secret, nil\n\t}\n\tlogrus.Debugf(\"cert %s for %s/%s is valid until %s and covers %v\", cert.Subject.CommonName, secret.Namespace, secret.Name, cert.NotAfter, cert.DNSNames)\n\n\treturn nil, nil\n}\n\nfunc (h *handler) scheduleNextCertCheck(obj metav1.Object, secret *corev1.Secret) error {\n\tcert, err := parseCert(secret)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot parse certificate: %w\", err)\n\t}\n\n\trenewBefore := 24 * 60 * time.Hour\n\tnextCheck := time.Until(cert.NotAfter.Add(-renewBefore))\n\n\tif nextCheck < time.Minute {\n\t\tnextCheck = time.Minute\n\t}\n\n\tlogrus.Debugf(\"Next cert check for %s/%s scheduled in %v (expires %v)\",\n\t\tobj.GetNamespace(), obj.GetName(), nextCheck.Round(time.Second), cert.NotAfter)\n\n\th.services.EnqueueAfter(obj.GetNamespace(), obj.GetName(), nextCheck)\n\treturn nil\n}\n\nfunc (h *handler) createSecret(owner runtime.Object, ns, name string, dnsNames []string) (*corev1.Secret, error) {\n\tcert, key, err := cert.GenerateSelfSignedCertKey(ns+\"-\"+name, nil, dnsNames)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmeta, err := meta.Accessor(owner)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgvk, err := gvk.Get(owner)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tref := metav1.OwnerReference{\n\t\tName: meta.GetName(),\n\t\tUID:  meta.GetUID(),\n\t}\n\tref.APIVersion, ref.Kind = gvk.ToAPIVersionAndKind()\n\n\treturn &corev1.Secret{\n\t\tTypeMeta: metav1.TypeMeta{},\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:            name,\n\t\t\tNamespace:       ns,\n\t\t\tOwnerReferences: []metav1.OwnerReference{ref},\n\t\t},\n\t\tData: map[string][]byte{\n\t\t\tcorev1.TLSCertKey:       cert,\n\t\t\tcorev1.TLSPrivateKeyKey: key,\n\t\t},\n\t\tType: corev1.SecretTypeTLS,\n\t}, nil\n}\n\nfunc parseCert(secret *corev1.Secret) (*x509.Certificate, error) {\n\tif secret == nil || secret.Data == nil {\n\t\treturn nil, fmt.Errorf(\"secret or secret.Data is nil\")\n\t}\n\n\ttlsPair, err := tls.X509KeyPair(secret.Data[corev1.TLSCertKey], secret.Data[corev1.TLSPrivateKeyKey])\n\tif err != nil || len(tlsPair.Certificate) == 0 {\n\t\treturn nil, fmt.Errorf(\"failed to load TLS keypair: %w\", err)\n\t}\n\n\tcert, err := x509.ParseCertificate(tlsPair.Certificate[0])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse X509 certificate: %w\", err)\n\t}\n\n\treturn cert, nil\n}\n"
  },
  {
    "path": "pkg/needacert/needacert_test.go",
    "content": "package needacert\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/generic/fake\"\n\t\"go.uber.org/mock/gomock\"\n\n\t\"github.com/stretchr/testify/assert\"\n\tadminregv1 \"k8s.io/api/admissionregistration/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tapiextv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\tapierror \"k8s.io/apimachinery/pkg/api/errors\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/client-go/util/cert\"\n)\n\nfunc TestCreateSecret(t *testing.T) {\n\th := &handler{}\n\tservice := &corev1.Service{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:      \"svc\",\n\t\t\tNamespace: \"ns\",\n\t\t},\n\t}\n\tdnsNames := []string{\"svc.ns\", \"svc.ns.svc\"}\n\tsecret, err := h.createSecret(service, \"ns\", \"mysecret\", dnsNames)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"mysecret\", secret.Name)\n\tassert.Equal(t, \"ns\", secret.Namespace)\n\tassert.Equal(t, corev1.SecretTypeTLS, secret.Type)\n\tassert.NotEmpty(t, secret.Data[corev1.TLSCertKey])\n\tassert.NotEmpty(t, secret.Data[corev1.TLSPrivateKeyKey])\n}\n\nfunc TestUpdateSecret_ExpiredCert_ManyParallel(t *testing.T) {\n\tconst runs = 50\n\tfor i := 0; i < runs; i++ {\n\t\tt.Run(fmt.Sprintf(\"run-%d\", i), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\th := &handler{}\n\t\t\tservice := &corev1.Service{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"svc\",\n\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t},\n\t\t\t}\n\t\t\tdnsNames := []string{\"svc.ns\", \"svc.ns.svc\"}\n\n\t\t\tcertPEM, keyPEM, err := cert.GenerateSelfSignedCertKeyWithOptions(cert.SelfSignedCertKeyOptions{\n\t\t\t\tHost:         \"ns-mysecret\",\n\t\t\t\tAlternateDNS: dnsNames,\n\t\t\t\tMaxAge:       1 * time.Second,\n\t\t\t})\n\t\t\tassert.NoError(t, err)\n\n\t\t\texistingCert, err := cert.ParseCertsPEM(certPEM)\n\t\t\tassert.NoError(t, err)\n\n\t\t\tsecret := &corev1.Secret{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"mysecret\",\n\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t},\n\t\t\t\tType: corev1.SecretTypeTLS,\n\t\t\t\tData: map[string][]byte{\n\t\t\t\t\tcorev1.TLSCertKey:       certPEM,\n\t\t\t\t\tcorev1.TLSPrivateKeyKey: keyPEM,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\ttime.Sleep(2 * time.Second)\n\n\t\t\tupdated, err := h.updateSecret(service, secret, dnsNames, existingCert[0])\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.NotNil(t, updated)\n\t\t})\n\t}\n}\n\nfunc TestGenerateSecret_NoAnnotation(t *testing.T) {\n\th := &handler{}\n\tservice := &corev1.Service{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:        \"svc\",\n\t\t\tNamespace:   \"ns\",\n\t\t\tAnnotations: map[string]string{},\n\t\t},\n\t}\n\tsecret, err := h.generateSecret(service)\n\tassert.NoError(t, err)\n\tassert.Nil(t, secret)\n}\n\nfunc TestHandler_OnMutationWebhookChange(t *testing.T) {\n\tctrl := gomock.NewController(t)\n\tdefer ctrl.Finish()\n\n\tmockServiceCache := fake.NewMockCacheInterface[*corev1.Service](ctrl)\n\tmockServices := fake.NewMockControllerInterface[*corev1.Service, *corev1.ServiceList](ctrl)\n\tmockSecretsCache := fake.NewMockCacheInterface[*corev1.Secret](ctrl)\n\tmockSecrets := fake.NewMockControllerInterface[*corev1.Secret, *corev1.SecretList](ctrl)\n\tmockMutatingWebHooks := fake.NewMockNonNamespacedControllerInterface[*adminregv1.MutatingWebhookConfiguration, *adminregv1.MutatingWebhookConfigurationList](ctrl)\n\n\tmockService := &corev1.Service{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:      \"svc\",\n\t\t\tNamespace: \"ns\",\n\t\t\tAnnotations: map[string]string{\n\t\t\t\tSecretAnnotation: \"mysecret\",\n\t\t\t},\n\t\t},\n\t}\n\tcertPEM, keyPEM, _ := cert.GenerateSelfSignedCertKey(\"ns-mysecret\", nil, []string{\"svc.ns\", \"svc.ns.svc\"})\n\tmockSecret := &corev1.Secret{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:      \"mysecret\",\n\t\t\tNamespace: \"ns\",\n\t\t},\n\t\tType: corev1.SecretTypeTLS,\n\t\tData: map[string][]byte{\n\t\t\tcorev1.TLSCertKey:       certPEM,\n\t\t\tcorev1.TLSPrivateKeyKey: keyPEM,\n\t\t},\n\t}\n\n\tmockServices.EXPECT().\n\t\tEnqueueAfter(\"ns\", \"svc\", gomock.Any()).\n\t\tAnyTimes()\n\tmockServiceCache.EXPECT().\n\t\tGet(\"ns\", \"svc\").\n\t\tReturn(mockService, nil).\n\t\tTimes(2)\n\n\tmockSecretsCache.EXPECT().\n\t\tGet(\"ns\", \"mysecret\").\n\t\tReturn(mockSecret, nil).\n\t\tTimes(2)\n\n\tmockSecrets.EXPECT().\n\t\tUpdate(gomock.Any()).\n\t\tDoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {\n\t\t\treturn secret, nil\n\t\t}).Times(2)\n\n\tmockMutatingWebHooks.EXPECT().\n\t\tUpdate(gomock.Any()).\n\t\tDoAndReturn(func(webhook *adminregv1.MutatingWebhookConfiguration) (*adminregv1.MutatingWebhookConfiguration, error) {\n\t\t\treturn webhook, nil\n\t\t}).Times(1)\n\n\th := &handler{\n\t\tservices:         mockServices,\n\t\tserviceCache:     mockServiceCache,\n\t\tsecretsCache:     mockSecretsCache,\n\t\tsecrets:          mockSecrets,\n\t\tmutatingWebHooks: mockMutatingWebHooks,\n\t}\n\n\twebhook := &adminregv1.MutatingWebhookConfiguration{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName: \"webhook\",\n\t\t},\n\t\tWebhooks: []adminregv1.MutatingWebhook{\n\t\t\t{\n\t\t\t\tName: \"wh\",\n\t\t\t\tClientConfig: adminregv1.WebhookClientConfig{\n\t\t\t\t\tService: &adminregv1.ServiceReference{\n\t\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t\t\tName:      \"svc\",\n\t\t\t\t\t},\n\t\t\t\t\tCABundle: []byte{},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"wh2\",\n\t\t\t\tClientConfig: adminregv1.WebhookClientConfig{\n\t\t\t\t\tService: &adminregv1.ServiceReference{\n\t\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t\t\tName:      \"svc\",\n\t\t\t\t\t},\n\t\t\t\t\tCABundle: []byte{},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tupdated, err := h.OnMutationWebhookChange(\"key\", webhook)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, updated)\n\tassert.NotEmpty(t, updated.Webhooks[0].ClientConfig.CABundle)\n\tassert.NotEmpty(t, updated.Webhooks[1].ClientConfig.CABundle)\n\tassert.True(t, bytes.HasPrefix(updated.Webhooks[0].ClientConfig.CABundle, []byte(\"-----BEGIN CERTIFICATE-----\")))\n\tassert.True(t, bytes.HasPrefix(updated.Webhooks[1].ClientConfig.CABundle, []byte(\"-----BEGIN CERTIFICATE-----\")))\n}\n\nfunc TestHandler_OnValidatingWebhookChange_Parallel(t *testing.T) {\n\tctrl := gomock.NewController(t)\n\tdefer ctrl.Finish()\n\n\tconst runs = 10\n\tfor i := 0; i < runs; i++ {\n\t\tt.Run(fmt.Sprintf(\"run-%d\", i), func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tmockServiceCache := fake.NewMockCacheInterface[*corev1.Service](ctrl)\n\t\t\tmockServices := fake.NewMockControllerInterface[*corev1.Service, *corev1.ServiceList](ctrl)\n\t\t\tmockSecretsCache := fake.NewMockCacheInterface[*corev1.Secret](ctrl)\n\t\t\tmockSecrets := fake.NewMockControllerInterface[*corev1.Secret, *corev1.SecretList](ctrl)\n\t\t\tmockValidatingWebHooks := fake.NewMockNonNamespacedControllerInterface[*adminregv1.ValidatingWebhookConfiguration, *adminregv1.ValidatingWebhookConfigurationList](ctrl)\n\n\t\t\tmockService := &corev1.Service{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"svc\",\n\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\tSecretAnnotation: \"mysecret\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t\tcertPEM, keyPEM, _ := cert.GenerateSelfSignedCertKey(\"ns-mysecret\", nil, []string{\"svc.ns\", \"svc.ns.svc\"})\n\t\t\tmockSecret := &corev1.Secret{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"mysecret\",\n\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t},\n\t\t\t\tType: corev1.SecretTypeTLS,\n\t\t\t\tData: map[string][]byte{\n\t\t\t\t\tcorev1.TLSCertKey:       certPEM,\n\t\t\t\t\tcorev1.TLSPrivateKeyKey: keyPEM,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tmockServices.EXPECT().\n\t\t\t\tEnqueueAfter(\"ns\", \"svc\", gomock.Any()).\n\t\t\t\tAnyTimes()\n\t\t\tmockServiceCache.EXPECT().\n\t\t\t\tGet(\"ns\", \"svc\").\n\t\t\t\tReturn(mockService, nil).\n\t\t\t\tTimes(2)\n\n\t\t\tmockSecretsCache.EXPECT().\n\t\t\t\tGet(\"ns\", \"mysecret\").\n\t\t\t\tReturn(mockSecret, nil).\n\t\t\t\tTimes(2)\n\n\t\t\tmockSecrets.EXPECT().\n\t\t\t\tUpdate(gomock.Any()).\n\t\t\t\tDoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {\n\t\t\t\t\treturn secret, nil\n\t\t\t\t}).\n\t\t\t\tTimes(2)\n\n\t\t\tmockValidatingWebHooks.EXPECT().\n\t\t\t\tUpdate(gomock.Any()).\n\t\t\t\tDoAndReturn(func(webhook *adminregv1.ValidatingWebhookConfiguration) (*adminregv1.ValidatingWebhookConfiguration, error) {\n\t\t\t\t\treturn webhook, nil\n\t\t\t\t}).Times(1)\n\n\t\t\th := &handler{\n\t\t\t\tservices:           mockServices,\n\t\t\t\tserviceCache:       mockServiceCache,\n\t\t\t\tsecretsCache:       mockSecretsCache,\n\t\t\t\tsecrets:            mockSecrets,\n\t\t\t\tvalidatingWebHooks: mockValidatingWebHooks,\n\t\t\t}\n\n\t\t\twebhook := &adminregv1.ValidatingWebhookConfiguration{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName: \"webhook\",\n\t\t\t\t},\n\t\t\t\tWebhooks: []adminregv1.ValidatingWebhook{\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"wh\",\n\t\t\t\t\t\tClientConfig: adminregv1.WebhookClientConfig{\n\t\t\t\t\t\t\tService: &adminregv1.ServiceReference{\n\t\t\t\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t\t\t\t\tName:      \"svc\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCABundle: []byte{},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"wh2\",\n\t\t\t\t\t\tClientConfig: adminregv1.WebhookClientConfig{\n\t\t\t\t\t\t\tService: &adminregv1.ServiceReference{\n\t\t\t\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t\t\t\t\tName:      \"svc\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCABundle: []byte{},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tupdated, err := h.OnValidatingWebhookChange(\"key\", webhook)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.NotNil(t, updated)\n\t\t\tassert.NotEmpty(t, updated.Webhooks[0].ClientConfig.CABundle)\n\t\t\tassert.NotEmpty(t, updated.Webhooks[1].ClientConfig.CABundle)\n\t\t\tassert.True(t, bytes.HasPrefix(updated.Webhooks[0].ClientConfig.CABundle, []byte(\"-----BEGIN CERTIFICATE-----\")))\n\t\t\tassert.True(t, bytes.HasPrefix(updated.Webhooks[1].ClientConfig.CABundle, []byte(\"-----BEGIN CERTIFICATE-----\")))\n\t\t})\n\t}\n}\n\nfunc TestHandler_OnMutationWebhookChange_Parallel(t *testing.T) {\n\tctrl := gomock.NewController(t)\n\tdefer ctrl.Finish()\n\n\tconst runs = 10\n\tfor i := 0; i < runs; i++ {\n\t\tt.Run(fmt.Sprintf(\"run-%d\", i), func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tmockServices := fake.NewMockControllerInterface[*corev1.Service, *corev1.ServiceList](ctrl)\n\t\t\tmockServiceCache := fake.NewMockCacheInterface[*corev1.Service](ctrl)\n\t\t\tmockSecretsCache := fake.NewMockCacheInterface[*corev1.Secret](ctrl)\n\t\t\tmockSecrets := fake.NewMockControllerInterface[*corev1.Secret, *corev1.SecretList](ctrl)\n\t\t\tmockMutatingWebHooks := fake.NewMockNonNamespacedControllerInterface[*adminregv1.MutatingWebhookConfiguration, *adminregv1.MutatingWebhookConfigurationList](ctrl)\n\n\t\t\tmockService := &corev1.Service{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"svc\",\n\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\tSecretAnnotation: \"mysecret\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t\tcertPEM, keyPEM, _ := cert.GenerateSelfSignedCertKey(\"ns-mysecret\", nil, []string{\"svc.ns\", \"svc.ns.svc\"})\n\t\t\tmockSecret := &corev1.Secret{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"mysecret\",\n\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t},\n\t\t\t\tType: corev1.SecretTypeTLS,\n\t\t\t\tData: map[string][]byte{\n\t\t\t\t\tcorev1.TLSCertKey:       certPEM,\n\t\t\t\t\tcorev1.TLSPrivateKeyKey: keyPEM,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tmockServices.EXPECT().\n\t\t\t\tEnqueueAfter(\"ns\", \"svc\", gomock.Any()).\n\t\t\t\tAnyTimes()\n\t\t\tmockServiceCache.EXPECT().\n\t\t\t\tGet(\"ns\", \"svc\").\n\t\t\t\tReturn(mockService, nil)\n\n\t\t\tmockSecretsCache.EXPECT().\n\t\t\t\tGet(\"ns\", \"mysecret\").\n\t\t\t\tReturn(mockSecret, nil)\n\n\t\t\tmockSecrets.EXPECT().\n\t\t\t\tUpdate(gomock.Any()).\n\t\t\t\tDoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {\n\t\t\t\t\treturn secret, nil\n\t\t\t\t})\n\n\t\t\tmockMutatingWebHooks.EXPECT().\n\t\t\t\tUpdate(gomock.Any()).\n\t\t\t\tDoAndReturn(func(webhook *adminregv1.MutatingWebhookConfiguration) (*adminregv1.MutatingWebhookConfiguration, error) {\n\t\t\t\t\treturn webhook, nil\n\t\t\t\t})\n\n\t\t\th := &handler{\n\t\t\t\tservices:         mockServices,\n\t\t\t\tserviceCache:     mockServiceCache,\n\t\t\t\tsecretsCache:     mockSecretsCache,\n\t\t\t\tsecrets:          mockSecrets,\n\t\t\t\tmutatingWebHooks: mockMutatingWebHooks,\n\t\t\t}\n\n\t\t\twebhook := &adminregv1.MutatingWebhookConfiguration{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName: \"webhook\",\n\t\t\t\t},\n\t\t\t\tWebhooks: []adminregv1.MutatingWebhook{\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"wh\",\n\t\t\t\t\t\tClientConfig: adminregv1.WebhookClientConfig{\n\t\t\t\t\t\t\tService: &adminregv1.ServiceReference{\n\t\t\t\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t\t\t\t\tName:      \"svc\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCABundle: []byte{},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tupdated, err := h.OnMutationWebhookChange(\"key\", webhook)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.NotNil(t, updated)\n\t\t\tassert.NotEmpty(t, updated.Webhooks[0].ClientConfig.CABundle)\n\t\t\tassert.True(t, bytes.HasPrefix(updated.Webhooks[0].ClientConfig.CABundle, []byte(\"-----BEGIN CERTIFICATE-----\")))\n\t\t})\n\t}\n}\n\nfunc TestHandler_OnService_Parallel(t *testing.T) {\n\tctrl := gomock.NewController(t)\n\tdefer ctrl.Finish()\n\n\tconst runs = 10\n\tfor i := 0; i < runs; i++ {\n\t\tt.Run(fmt.Sprintf(\"run-%d\", i), func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tmockServices := fake.NewMockControllerInterface[*corev1.Service, *corev1.ServiceList](ctrl)\n\t\t\tmockServiceCache := fake.NewMockCacheInterface[*corev1.Service](ctrl)\n\t\t\tmockSecretsCache := fake.NewMockCacheInterface[*corev1.Secret](ctrl)\n\t\t\tmockSecrets := fake.NewMockControllerInterface[*corev1.Secret, *corev1.SecretList](ctrl)\n\t\t\tmockMutatingWebHooks := fake.NewMockNonNamespacedControllerInterface[*adminregv1.MutatingWebhookConfiguration, *adminregv1.MutatingWebhookConfigurationList](ctrl)\n\t\t\tmockValidatingWebHooks := fake.NewMockNonNamespacedControllerInterface[*adminregv1.ValidatingWebhookConfiguration, *adminregv1.ValidatingWebhookConfigurationList](ctrl)\n\t\t\tmockCRDs := fake.NewMockNonNamespacedControllerInterface[*apiextv1.CustomResourceDefinition, *apiextv1.CustomResourceDefinitionList](ctrl)\n\n\t\t\tmockMutatingCache := fake.NewMockNonNamespacedCacheInterface[*adminregv1.MutatingWebhookConfiguration](ctrl)\n\t\t\tmockValidatingCache := fake.NewMockNonNamespacedCacheInterface[*adminregv1.ValidatingWebhookConfiguration](ctrl)\n\t\t\tmockCRDsCache := fake.NewMockNonNamespacedCacheInterface[*apiextv1.CustomResourceDefinition](ctrl)\n\n\t\t\tmockMutatingWebHooks.EXPECT().Cache().Return(mockMutatingCache).AnyTimes()\n\t\t\tmockValidatingWebHooks.EXPECT().Cache().Return(mockValidatingCache).AnyTimes()\n\t\t\tmockCRDs.EXPECT().Cache().Return(mockCRDsCache).AnyTimes()\n\n\t\t\tmockMutatingCache.EXPECT().GetByIndex(gomock.Any(), gomock.Any()).Return([]*adminregv1.MutatingWebhookConfiguration{}, nil).AnyTimes()\n\t\t\tmockValidatingCache.EXPECT().GetByIndex(gomock.Any(), gomock.Any()).Return([]*adminregv1.ValidatingWebhookConfiguration{}, nil).AnyTimes()\n\t\t\tmockCRDsCache.EXPECT().GetByIndex(gomock.Any(), gomock.Any()).Return([]*apiextv1.CustomResourceDefinition{}, nil).AnyTimes()\n\n\t\t\tmockMutatingWebHooks.EXPECT().Enqueue(gomock.Any()).AnyTimes()\n\t\t\tmockValidatingWebHooks.EXPECT().Enqueue(gomock.Any()).AnyTimes()\n\t\t\tmockCRDs.EXPECT().Enqueue(gomock.Any()).AnyTimes()\n\n\t\t\tservice := &corev1.Service{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"svc\",\n\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\tSecretAnnotation: \"mysecret\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tcertPEM, keyPEM, _ := cert.GenerateSelfSignedCertKey(\"ns-mysecret\", nil, []string{\"svc.ns\", \"svc.ns.svc\"})\n\t\t\tmockSecret := &corev1.Secret{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"mysecret\",\n\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t},\n\t\t\t\tType: corev1.SecretTypeTLS,\n\t\t\t\tData: map[string][]byte{\n\t\t\t\t\tcorev1.TLSCertKey:       certPEM,\n\t\t\t\t\tcorev1.TLSPrivateKeyKey: keyPEM,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tmockServices.EXPECT().\n\t\t\t\tEnqueueAfter(\"ns\", \"svc\", gomock.Any()).\n\t\t\t\tAnyTimes()\n\t\t\tmockSecretsCache.EXPECT().\n\t\t\t\tGet(\"ns\", \"mysecret\").\n\t\t\t\tReturn(mockSecret, nil).AnyTimes()\n\t\t\tmockSecrets.EXPECT().\n\t\t\t\tUpdate(gomock.Any()).\n\t\t\t\tDoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {\n\t\t\t\t\treturn secret, nil\n\t\t\t\t}).AnyTimes()\n\n\t\t\th := &handler{\n\t\t\t\tservices:           mockServices,\n\t\t\t\tserviceCache:       mockServiceCache,\n\t\t\t\tsecretsCache:       mockSecretsCache,\n\t\t\t\tsecrets:            mockSecrets,\n\t\t\t\tmutatingWebHooks:   mockMutatingWebHooks,\n\t\t\t\tvalidatingWebHooks: mockValidatingWebHooks,\n\t\t\t\tcrds:               mockCRDs,\n\t\t\t}\n\n\t\t\t_, err := h.OnService(\"ns/svc\", service)\n\t\t\tassert.NoError(t, err)\n\t\t})\n\t}\n}\n\nfunc TestHandler_OnCRDChange_Parallel(t *testing.T) {\n\tctrl := gomock.NewController(t)\n\tdefer ctrl.Finish()\n\n\tconst runs = 10\n\tfor i := 0; i < runs; i++ {\n\t\tt.Run(fmt.Sprintf(\"run-%d\", i), func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tmockServices := fake.NewMockControllerInterface[*corev1.Service, *corev1.ServiceList](ctrl)\n\t\t\tmockServiceCache := fake.NewMockCacheInterface[*corev1.Service](ctrl)\n\t\t\tmockSecretsCache := fake.NewMockCacheInterface[*corev1.Secret](ctrl)\n\t\t\tmockSecrets := fake.NewMockControllerInterface[*corev1.Secret, *corev1.SecretList](ctrl)\n\t\t\tmockCRDs := fake.NewMockNonNamespacedControllerInterface[*apiextv1.CustomResourceDefinition, *apiextv1.CustomResourceDefinitionList](ctrl)\n\n\t\t\tmockCRDsCache := fake.NewMockNonNamespacedCacheInterface[*apiextv1.CustomResourceDefinition](ctrl)\n\t\t\tmockCRDs.EXPECT().Cache().Return(mockCRDsCache).AnyTimes()\n\t\t\tmockCRDs.EXPECT().Enqueue(gomock.Any()).AnyTimes()\n\n\t\t\tservice := &corev1.Service{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"svc\",\n\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\tSecretAnnotation: \"mysecret\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tcertPEM, keyPEM, _ := cert.GenerateSelfSignedCertKey(\"ns-mysecret\", nil, []string{\"svc.ns\", \"svc.ns.svc\"})\n\t\t\tmockSecret := &corev1.Secret{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"mysecret\",\n\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t},\n\t\t\t\tType: corev1.SecretTypeTLS,\n\t\t\t\tData: map[string][]byte{\n\t\t\t\t\tcorev1.TLSCertKey:       certPEM,\n\t\t\t\t\tcorev1.TLSPrivateKeyKey: keyPEM,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tmockServices.EXPECT().\n\t\t\t\tEnqueueAfter(\"ns\", \"svc\", gomock.Any()).\n\t\t\t\tAnyTimes()\n\t\t\tmockServiceCache.EXPECT().\n\t\t\t\tGet(\"ns\", \"svc\").\n\t\t\t\tReturn(service, nil).AnyTimes()\n\t\t\tmockSecretsCache.EXPECT().\n\t\t\t\tGet(\"ns\", \"mysecret\").\n\t\t\t\tReturn(mockSecret, nil).AnyTimes()\n\t\t\tmockSecrets.EXPECT().\n\t\t\t\tUpdate(gomock.Any()).\n\t\t\t\tDoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {\n\t\t\t\t\treturn secret, nil\n\t\t\t\t}).AnyTimes()\n\t\t\tmockSecrets.EXPECT().\n\t\t\t\tCreate(gomock.Any()).\n\t\t\t\tDoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {\n\t\t\t\t\treturn secret, nil\n\t\t\t\t}).AnyTimes()\n\t\t\tmockCRDs.EXPECT().\n\t\t\t\tUpdate(gomock.Any()).\n\t\t\t\tDoAndReturn(func(crd *apiextv1.CustomResourceDefinition) (*apiextv1.CustomResourceDefinition, error) {\n\t\t\t\t\treturn crd, nil\n\t\t\t\t}).AnyTimes()\n\n\t\t\th := &handler{\n\t\t\t\tservices:     mockServices,\n\t\t\t\tserviceCache: mockServiceCache,\n\t\t\t\tsecretsCache: mockSecretsCache,\n\t\t\t\tsecrets:      mockSecrets,\n\t\t\t\tcrds:         mockCRDs,\n\t\t\t}\n\n\t\t\tcrd := &apiextv1.CustomResourceDefinition{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName: \"crd\",\n\t\t\t\t},\n\t\t\t\tSpec: apiextv1.CustomResourceDefinitionSpec{\n\t\t\t\t\tConversion: &apiextv1.CustomResourceConversion{\n\t\t\t\t\t\tStrategy: apiextv1.WebhookConverter,\n\t\t\t\t\t\tWebhook: &apiextv1.WebhookConversion{\n\t\t\t\t\t\t\tClientConfig: &apiextv1.WebhookClientConfig{\n\t\t\t\t\t\t\t\tService: &apiextv1.ServiceReference{\n\t\t\t\t\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t\t\t\t\t\tName:      \"svc\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tCABundle: []byte{},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tupdated, err := h.OnCRDChange(\"key\", crd)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.NotNil(t, updated)\n\t\t\tassert.NotEmpty(t, updated.Spec.Conversion.Webhook.ClientConfig.CABundle)\n\t\t\tassert.True(t, bytes.HasPrefix(updated.Spec.Conversion.Webhook.ClientConfig.CABundle, []byte(\"-----BEGIN CERTIFICATE-----\")))\n\t\t})\n\t}\n}\n\nfunc TestHandler_GenerateSecret_Race(t *testing.T) {\n\tctrl := gomock.NewController(t)\n\tdefer ctrl.Finish()\n\n\tmockServices := fake.NewMockControllerInterface[*corev1.Service, *corev1.ServiceList](ctrl)\n\tmockSecretsCache := fake.NewMockCacheInterface[*corev1.Secret](ctrl)\n\tmockSecrets := fake.NewMockControllerInterface[*corev1.Secret, *corev1.SecretList](ctrl)\n\n\tservice := &corev1.Service{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:      \"svc\",\n\t\t\tNamespace: \"ns\",\n\t\t\tAnnotations: map[string]string{\n\t\t\t\tSecretAnnotation: \"mysecret\",\n\t\t\t},\n\t\t},\n\t}\n\n\tcertPEM, keyPEM, _ := cert.GenerateSelfSignedCertKey(\"ns-mysecret\", nil, []string{\"svc.ns\", \"svc.ns.svc\"})\n\tmockSecret := &corev1.Secret{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:      \"mysecret\",\n\t\t\tNamespace: \"ns\",\n\t\t},\n\t\tType: corev1.SecretTypeTLS,\n\t\tData: map[string][]byte{\n\t\t\tcorev1.TLSCertKey:       certPEM,\n\t\t\tcorev1.TLSPrivateKeyKey: keyPEM,\n\t\t},\n\t}\n\n\tmockServices.EXPECT().\n\t\tEnqueueAfter(\"ns\", \"svc\", gomock.Any()).\n\t\tAnyTimes()\n\tmockSecretsCache.EXPECT().\n\t\tGet(\"ns\", \"mysecret\").\n\t\tReturn(mockSecret, nil).AnyTimes()\n\tmockSecrets.EXPECT().\n\t\tUpdate(gomock.Any()).\n\t\tDoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {\n\t\t\treturn secret, nil\n\t\t}).AnyTimes()\n\n\th := &handler{\n\t\tservices:     mockServices,\n\t\tsecretsCache: mockSecretsCache,\n\t\tsecrets:      mockSecrets,\n\t}\n\n\tconst concurrency = 10\n\tdone := make(chan struct{})\n\tfor i := 0; i < concurrency; i++ {\n\t\tgo func() {\n\t\t\t_, err := h.generateSecret(service)\n\t\t\tassert.NoError(t, err)\n\t\t\tdone <- struct{}{}\n\t\t}()\n\t}\n\tfor i := 0; i < concurrency; i++ {\n\t\t<-done\n\t}\n}\n\nfunc TestHandler_GenerateSecret_Race_MultiService(t *testing.T) {\n\tctrl := gomock.NewController(t)\n\tdefer ctrl.Finish()\n\n\tmockServices := fake.NewMockControllerInterface[*corev1.Service, *corev1.ServiceList](ctrl)\n\tmockSecretsCache := fake.NewMockCacheInterface[*corev1.Secret](ctrl)\n\tmockSecrets := fake.NewMockControllerInterface[*corev1.Secret, *corev1.SecretList](ctrl)\n\n\tconst concurrency = 10\n\tdone := make(chan struct{})\n\n\tfor i := 0; i < concurrency; i++ {\n\t\tserviceName := fmt.Sprintf(\"svc-%d\", i)\n\t\tsecretName := fmt.Sprintf(\"secret-%d\", i)\n\t\tservice := &corev1.Service{\n\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\tName:      serviceName,\n\t\t\t\tNamespace: \"ns\",\n\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\tSecretAnnotation: secretName,\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t\tcertPEM, keyPEM, _ := cert.GenerateSelfSignedCertKey(\"ns-\"+secretName, nil, []string{serviceName + \".ns\", serviceName + \".ns.svc\"})\n\t\tmockSecret := &corev1.Secret{\n\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\tName:      secretName,\n\t\t\t\tNamespace: \"ns\",\n\t\t\t},\n\t\t\tType: corev1.SecretTypeTLS,\n\t\t\tData: map[string][]byte{\n\t\t\t\tcorev1.TLSCertKey:       certPEM,\n\t\t\t\tcorev1.TLSPrivateKeyKey: keyPEM,\n\t\t\t},\n\t\t}\n\n\t\tmockServices.EXPECT().\n\t\t\tEnqueueAfter(\"ns\", gomock.Any(), gomock.Any()).\n\t\t\tAnyTimes()\n\t\tmockSecretsCache.EXPECT().\n\t\t\tGet(\"ns\", secretName).\n\t\t\tReturn(mockSecret, nil).AnyTimes()\n\t\tmockSecrets.EXPECT().\n\t\t\tUpdate(gomock.Any()).\n\t\t\tDoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {\n\t\t\t\treturn secret, nil\n\t\t\t}).AnyTimes()\n\n\t\tgo func(svc *corev1.Service) {\n\t\t\th := &handler{\n\t\t\t\tservices:     mockServices,\n\t\t\t\tsecretsCache: mockSecretsCache,\n\t\t\t\tsecrets:      mockSecrets,\n\t\t\t}\n\t\t\t_, err := h.generateSecret(svc)\n\t\t\tassert.NoError(t, err)\n\t\t\tdone <- struct{}{}\n\t\t}(service)\n\t}\n\n\tfor i := 0; i < concurrency; i++ {\n\t\t<-done\n\t}\n}\n\nfunc TestHandler_Race_Stress(t *testing.T) {\n\tctrl := gomock.NewController(t)\n\tdefer ctrl.Finish()\n\n\tmockServices := fake.NewMockControllerInterface[*corev1.Service, *corev1.ServiceList](ctrl)\n\tmockServiceCache := fake.NewMockCacheInterface[*corev1.Service](ctrl)\n\tmockSecretsCache := fake.NewMockCacheInterface[*corev1.Secret](ctrl)\n\tmockSecrets := fake.NewMockControllerInterface[*corev1.Secret, *corev1.SecretList](ctrl)\n\tmockMutatingWebHooks := fake.NewMockNonNamespacedControllerInterface[*adminregv1.MutatingWebhookConfiguration, *adminregv1.MutatingWebhookConfigurationList](ctrl)\n\tmockValidatingWebHooks := fake.NewMockNonNamespacedControllerInterface[*adminregv1.ValidatingWebhookConfiguration, *adminregv1.ValidatingWebhookConfigurationList](ctrl)\n\tmockCRDs := fake.NewMockNonNamespacedControllerInterface[*apiextv1.CustomResourceDefinition, *apiextv1.CustomResourceDefinitionList](ctrl)\n\n\tmockMutatingCache := fake.NewMockNonNamespacedCacheInterface[*adminregv1.MutatingWebhookConfiguration](ctrl)\n\tmockValidatingCache := fake.NewMockNonNamespacedCacheInterface[*adminregv1.ValidatingWebhookConfiguration](ctrl)\n\tmockCRDsCache := fake.NewMockNonNamespacedCacheInterface[*apiextv1.CustomResourceDefinition](ctrl)\n\n\tmockMutatingWebHooks.EXPECT().Cache().Return(mockMutatingCache).AnyTimes()\n\tmockValidatingWebHooks.EXPECT().Cache().Return(mockValidatingCache).AnyTimes()\n\tmockCRDs.EXPECT().Cache().Return(mockCRDsCache).AnyTimes()\n\n\tmockMutatingCache.EXPECT().GetByIndex(gomock.Any(), gomock.Any()).Return([]*adminregv1.MutatingWebhookConfiguration{}, nil).AnyTimes()\n\tmockValidatingCache.EXPECT().GetByIndex(gomock.Any(), gomock.Any()).Return([]*adminregv1.ValidatingWebhookConfiguration{}, nil).AnyTimes()\n\tmockCRDsCache.EXPECT().GetByIndex(gomock.Any(), gomock.Any()).Return([]*apiextv1.CustomResourceDefinition{}, nil).AnyTimes()\n\n\tmockMutatingWebHooks.EXPECT().Enqueue(gomock.Any()).AnyTimes()\n\tmockValidatingWebHooks.EXPECT().Enqueue(gomock.Any()).AnyTimes()\n\tmockCRDs.EXPECT().Enqueue(gomock.Any()).AnyTimes()\n\n\tmockSecrets.EXPECT().Update(gomock.Any()).DoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {\n\t\treturn secret, nil\n\t}).AnyTimes()\n\tmockSecrets.EXPECT().Create(gomock.Any()).DoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {\n\t\treturn secret, nil\n\t}).AnyTimes()\n\tmockCRDs.EXPECT().Update(gomock.Any()).DoAndReturn(func(crd *apiextv1.CustomResourceDefinition) (*apiextv1.CustomResourceDefinition, error) {\n\t\treturn crd, nil\n\t}).AnyTimes()\n\n\th := &handler{\n\t\tservices:           mockServices,\n\t\tserviceCache:       mockServiceCache,\n\t\tsecretsCache:       mockSecretsCache,\n\t\tsecrets:            mockSecrets,\n\t\tmutatingWebHooks:   mockMutatingWebHooks,\n\t\tvalidatingWebHooks: mockValidatingWebHooks,\n\t\tcrds:               mockCRDs,\n\t}\n\n\tconst concurrency = 10\n\tdone := make(chan struct{})\n\tfor i := 0; i < concurrency; i++ {\n\t\tgo func(i int) {\n\t\t\tserviceName := fmt.Sprintf(\"svc-%d\", i%5)\n\t\t\tsecretName := fmt.Sprintf(\"secret-%d\", i%5)\n\t\t\tservice := &corev1.Service{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      serviceName,\n\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\tSecretAnnotation: secretName,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t\tmockServices.EXPECT().\n\t\t\t\tEnqueueAfter(\"ns\", gomock.Any(), gomock.Any()).\n\t\t\t\tAnyTimes()\n\t\t\tmockServiceCache.EXPECT().\n\t\t\t\tGet(\"ns\", serviceName).\n\t\t\t\tReturn(service, nil).AnyTimes()\n\t\t\tcertPEM, keyPEM, _ := cert.GenerateSelfSignedCertKey(\"ns-\"+secretName, nil, []string{serviceName + \".ns\", serviceName + \".ns.svc\"})\n\t\t\tmockSecret := &corev1.Secret{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      secretName,\n\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t},\n\t\t\t\tType: corev1.SecretTypeTLS,\n\t\t\t\tData: map[string][]byte{\n\t\t\t\t\tcorev1.TLSCertKey:       certPEM,\n\t\t\t\t\tcorev1.TLSPrivateKeyKey: keyPEM,\n\t\t\t\t},\n\t\t\t}\n\t\t\tmockSecretsCache.EXPECT().\n\t\t\t\tGet(\"ns\", secretName).\n\t\t\t\tReturn(mockSecret, nil).AnyTimes()\n\n\t\t\tswitch i % 3 {\n\t\t\tcase 0:\n\t\t\t\t_, err := h.generateSecret(service)\n\t\t\t\tassert.NoError(t, err)\n\t\t\tcase 1:\n\t\t\t\t_, err := h.OnService(\"ns/\"+serviceName, service)\n\t\t\t\tassert.NoError(t, err)\n\t\t\tcase 2:\n\t\t\t\tcrd := &apiextv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"crd-\" + serviceName,\n\t\t\t\t\t},\n\t\t\t\t\tSpec: apiextv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tConversion: &apiextv1.CustomResourceConversion{\n\t\t\t\t\t\t\tStrategy: apiextv1.WebhookConverter,\n\t\t\t\t\t\t\tWebhook: &apiextv1.WebhookConversion{\n\t\t\t\t\t\t\t\tClientConfig: &apiextv1.WebhookClientConfig{\n\t\t\t\t\t\t\t\t\tService: &apiextv1.ServiceReference{\n\t\t\t\t\t\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t\t\t\t\t\t\tName:      serviceName,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tCABundle: []byte{},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\t_, err := h.OnCRDChange(\"key\", crd)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t}\n\t\t\tdone <- struct{}{}\n\t\t}(i)\n\t}\n\tfor i := 0; i < concurrency; i++ {\n\t\t<-done\n\t}\n}\n\nfunc TestHandler_ParseCert_CorruptedData(t *testing.T) {\n\tsecret := &corev1.Secret{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:      \"badsecret\",\n\t\t\tNamespace: \"ns\",\n\t\t},\n\t\tType: corev1.SecretTypeTLS,\n\t\tData: map[string][]byte{\n\t\t\tcorev1.TLSCertKey:       []byte(\"-----BEGIN CERTIFICATE-----\\nMIIB fake cert\\n-----END CERTIFICATE-----\"),\n\t\t\tcorev1.TLSPrivateKeyKey: []byte(\"not-a-key\"),\n\t\t},\n\t}\n\n\tparsed, err := parseCert(secret)\n\tassert.Error(t, err, \"expected error when parsing corrupted TLS secret\")\n\tassert.Nil(t, parsed, \"no updated secret should be returned on corrupted data\")\n}\n\nfunc TestHandler_GenerateSecret_Race_SharedSecret(t *testing.T) {\n\tctrl := gomock.NewController(t)\n\tdefer ctrl.Finish()\n\n\tmockServices := fake.NewMockControllerInterface[*corev1.Service, *corev1.ServiceList](ctrl)\n\tmockSecretsCache := fake.NewMockCacheInterface[*corev1.Secret](ctrl)\n\tmockSecrets := fake.NewMockControllerInterface[*corev1.Secret, *corev1.SecretList](ctrl)\n\n\t// Both services point to the same secret name\n\tservice1 := &corev1.Service{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:      \"svc1\",\n\t\t\tNamespace: \"ns\",\n\t\t\tAnnotations: map[string]string{\n\t\t\t\tSecretAnnotation: \"shared-secret\",\n\t\t\t},\n\t\t},\n\t}\n\tservice2 := &corev1.Service{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:      \"svc2\",\n\t\t\tNamespace: \"ns\",\n\t\t\tAnnotations: map[string]string{\n\t\t\t\tSecretAnnotation: \"shared-secret\",\n\t\t\t},\n\t\t},\n\t}\n\n\t// Intentionally returning notfound from the cache each time so that\n\t// multiple goroutines will attempt to create the same secret concurrently.\n\tmockServices.EXPECT().\n\t\tEnqueueAfter(\"ns\", gomock.Any(), gomock.Any()).\n\t\tAnyTimes()\n\tmockSecretsCache.EXPECT().\n\t\tGet(\"ns\", \"shared-secret\").\n\t\tReturn(nil, apierror.NewNotFound(corev1.Resource(\"secrets\"), \"shared-secret\")).\n\t\tAnyTimes()\n\tmockSecrets.EXPECT().\n\t\tCreate(gomock.Any()).\n\t\tDoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {\n\t\t\treturn secret, nil\n\t\t}).AnyTimes()\n\tmockSecrets.EXPECT().\n\t\tUpdate(gomock.Any()).\n\t\tDoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {\n\t\t\treturn secret, nil\n\t\t}).AnyTimes()\n\n\th := &handler{\n\t\tservices:     mockServices,\n\t\tsecretsCache: mockSecretsCache,\n\t\tsecrets:      mockSecrets,\n\t}\n\n\tconst concurrency = 10\n\tdone := make(chan struct{})\n\tfor i := 0; i < concurrency; i++ {\n\t\tgo func(i int) {\n\t\t\tvar svc *corev1.Service\n\t\t\tif i%2 == 0 {\n\t\t\t\tsvc = service1\n\t\t\t} else {\n\t\t\t\tsvc = service2\n\t\t\t}\n\t\t\t_, err := h.generateSecret(svc)\n\t\t\tassert.NoError(t, err)\n\t\t\tdone <- struct{}{}\n\t\t}(i)\n\t}\n\n\tfor i := 0; i < concurrency; i++ {\n\t\t<-done\n\t}\n}\n\nfunc TestHandler_GenerateSecret_StaleCacheAlreadyExists(t *testing.T) {\n\tctrl := gomock.NewController(t)\n\tdefer ctrl.Finish()\n\n\tmockServices := fake.NewMockControllerInterface[*corev1.Service, *corev1.ServiceList](ctrl)\n\tmockSecretsCache := fake.NewMockCacheInterface[*corev1.Secret](ctrl)\n\tmockSecrets := fake.NewMockControllerInterface[*corev1.Secret, *corev1.SecretList](ctrl)\n\n\tservice := &corev1.Service{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:      \"svc\",\n\t\t\tNamespace: \"ns\",\n\t\t\tAnnotations: map[string]string{\n\t\t\t\tSecretAnnotation: \"mysecret\",\n\t\t\t},\n\t\t},\n\t}\n\n\t// Simulate cache always lags and reports NotFound\n\tmockServices.EXPECT().\n\t\tEnqueueAfter(\"ns\", \"svc\", gomock.Any()).\n\t\tAnyTimes()\n\tmockSecretsCache.EXPECT().\n\t\tGet(\"ns\", \"mysecret\").\n\t\tReturn(nil, apierror.NewNotFound(corev1.Resource(\"secrets\"), \"mysecret\")).\n\t\tAnyTimes()\n\n\tmockSecrets.EXPECT().\n\t\tCreate(gomock.Any()).\n\t\tDoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {\n\t\t\treturn nil, apierror.NewAlreadyExists(corev1.Resource(\"secrets\"), \"mysecret\")\n\t\t}).\n\t\tAnyTimes()\n\n\tcertPEM, keyPEM, err := cert.GenerateSelfSignedCertKey(\"mysecret\", nil, []string{\"svc.ns\"})\n\tassert.NoError(t, err)\n\n\texpectedSecret := &corev1.Secret{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:      \"mysecret\",\n\t\t\tNamespace: \"ns\",\n\t\t},\n\t\tType: corev1.SecretTypeTLS,\n\t\tData: map[string][]byte{\n\t\t\tcorev1.TLSCertKey:       certPEM,\n\t\t\tcorev1.TLSPrivateKeyKey: keyPEM,\n\t\t},\n\t}\n\tmockSecrets.EXPECT().\n\t\tGet(\"ns\", \"mysecret\", gomock.Any()).\n\t\tReturn(expectedSecret, nil).\n\t\tAnyTimes()\n\tmockSecrets.EXPECT().\n\t\tUpdate(gomock.Any()).\n\t\tReturn(expectedSecret, nil).\n\t\tAnyTimes()\n\n\th := &handler{\n\t\tservices:     mockServices,\n\t\tsecretsCache: mockSecretsCache,\n\t\tsecrets:      mockSecrets,\n\t}\n\n\tsecret, err := h.generateSecret(service)\n\n\tassert.NoError(t, err)\n\tassert.NotNil(t, secret)\n}\n\nfunc TestHandler_scheduleNextCertCheck(t *testing.T) {\n\ttype enqueueCall struct {\n\t\tns, name string\n\t\tdelay    time.Duration\n\t}\n\tvar calls []enqueueCall\n\n\tctrl := gomock.NewController(t)\n\tdefer ctrl.Finish()\n\n\tmockServices := fake.NewMockControllerInterface[*corev1.Service, *corev1.ServiceList](ctrl)\n\tmockServices.EXPECT().\n\t\tEnqueueAfter(gomock.Any(), gomock.Any(), gomock.Any()).\n\t\tDo(func(ns, name string, delay time.Duration) {\n\t\t\tcalls = append(calls, enqueueCall{ns, name, delay})\n\t\t}).\n\t\tTimes(2)\n\n\th := &handler{services: mockServices}\n\n\ttests := []struct {\n\t\tname      string\n\t\tmaxAge    time.Duration\n\t\twantDelay time.Duration\n\t\twantErr   bool\n\t}{\n\t\t{\n\t\t\tname:      \"cert expires in 90 days → schedule at 30 days\",\n\t\t\tmaxAge:    90 * 24 * time.Hour,\n\t\t\twantDelay: 30 * 24 * time.Hour,\n\t\t},\n\t\t{\n\t\t\tname:      \"cert expires in 30 days → clamp to 1 minute\",\n\t\t\tmaxAge:    30 * 24 * time.Hour,\n\t\t\twantDelay: time.Minute,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tcalls = nil\n\n\t\t\tcertPEM, keyPEM, err := cert.GenerateSelfSignedCertKeyWithOptions(cert.SelfSignedCertKeyOptions{\n\t\t\t\tHost:   \"ns-mysecret\",\n\t\t\t\tMaxAge: tt.maxAge,\n\t\t\t})\n\t\t\tassert.NoError(t, err)\n\n\t\t\tsecret := &corev1.Secret{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"mysecret\",\n\t\t\t\t\tNamespace: \"ns\",\n\t\t\t\t},\n\t\t\t\tType: corev1.SecretTypeTLS,\n\t\t\t\tData: map[string][]byte{\n\t\t\t\t\tcorev1.TLSCertKey:       certPEM,\n\t\t\t\t\tcorev1.TLSPrivateKeyKey: keyPEM,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tobj := &corev1.Service{}\n\t\t\terr = h.scheduleNextCertCheck(obj, secret)\n\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Fatalf(\"scheduleNextCertCheck() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\n\t\t\tif len(calls) != 1 {\n\t\t\t\tt.Fatalf(\"expected 1 EnqueueAfter call, got %d\", len(calls))\n\t\t\t}\n\n\t\t\tgot := calls[0].delay\n\t\t\ttolerance := 1*time.Hour + 10*time.Second\n\t\t\tif math.Abs(got.Seconds()-tt.wantDelay.Seconds()) > tolerance.Seconds() {\n\t\t\t\tt.Errorf(\"expected delay ~%v, got %v\", tt.wantDelay, got)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/objectset/objectset.go",
    "content": "package objectset\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/gvk\"\n\t\"github.com/rancher/wrangler/v3/pkg/stringset\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/merr\"\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\ntype ObjectKey struct {\n\tName      string\n\tNamespace string\n}\n\nfunc NewObjectKey(obj v1.Object) ObjectKey {\n\treturn ObjectKey{\n\t\tNamespace: obj.GetNamespace(),\n\t\tName:      obj.GetName(),\n\t}\n}\n\nfunc (o ObjectKey) String() string {\n\tif o.Namespace == \"\" {\n\t\treturn o.Name\n\t}\n\treturn fmt.Sprintf(\"%s/%s\", o.Namespace, o.Name)\n}\n\ntype ObjectKeyByGVK map[schema.GroupVersionKind][]ObjectKey\n\ntype ObjectByGVK map[schema.GroupVersionKind]map[ObjectKey]runtime.Object\n\nfunc (o ObjectByGVK) Add(obj runtime.Object) (schema.GroupVersionKind, error) {\n\tmetadata, err := meta.Accessor(obj)\n\tif err != nil {\n\t\treturn schema.GroupVersionKind{}, err\n\t}\n\n\tgvk, err := gvk.Get(obj)\n\tif err != nil {\n\t\treturn schema.GroupVersionKind{}, err\n\t}\n\n\tobjs := o[gvk]\n\tif objs == nil {\n\t\tobjs = ObjectByKey{}\n\t\to[gvk] = objs\n\t}\n\n\tobjs[ObjectKey{\n\t\tNamespace: metadata.GetNamespace(),\n\t\tName:      metadata.GetName(),\n\t}] = obj\n\n\treturn gvk, nil\n}\n\ntype ObjectSet struct {\n\terrs        []error\n\tobjects     ObjectByGVK\n\tobjectsByGK ObjectByGK\n\torder       []runtime.Object\n\tgvkOrder    []schema.GroupVersionKind\n\tgvkSeen     map[schema.GroupVersionKind]bool\n}\n\nfunc NewObjectSet(objs ...runtime.Object) *ObjectSet {\n\tos := &ObjectSet{\n\t\tobjects:     ObjectByGVK{},\n\t\tobjectsByGK: ObjectByGK{},\n\t\tgvkSeen:     map[schema.GroupVersionKind]bool{},\n\t}\n\tos.Add(objs...)\n\treturn os\n}\n\nfunc (o *ObjectSet) ObjectsByGVK() ObjectByGVK {\n\tif o == nil {\n\t\treturn nil\n\t}\n\treturn o.objects\n}\n\nfunc (o *ObjectSet) Contains(gk schema.GroupKind, key ObjectKey) bool {\n\t_, ok := o.objectsByGK[gk][key]\n\treturn ok\n}\n\nfunc (o *ObjectSet) All() []runtime.Object {\n\treturn o.order\n}\n\nfunc (o *ObjectSet) Add(objs ...runtime.Object) *ObjectSet {\n\tfor _, obj := range objs {\n\t\to.add(obj)\n\t}\n\treturn o\n}\n\nfunc (o *ObjectSet) add(obj runtime.Object) {\n\tif obj == nil || reflect.ValueOf(obj).IsNil() {\n\t\treturn\n\t}\n\n\tgvk, err := o.objects.Add(obj)\n\tif err != nil {\n\t\to.err(fmt.Errorf(\"failed to add %T: %w\", obj, err))\n\t\treturn\n\t}\n\n\t_, err = o.objectsByGK.Add(obj)\n\tif err != nil {\n\t\to.err(fmt.Errorf(\"failed to add %T: %w\", obj, err))\n\t\treturn\n\t}\n\n\to.order = append(o.order, obj)\n\tif !o.gvkSeen[gvk] {\n\t\to.gvkSeen[gvk] = true\n\t\to.gvkOrder = append(o.gvkOrder, gvk)\n\t}\n}\n\nfunc (o *ObjectSet) err(err error) error {\n\to.errs = append(o.errs, err)\n\treturn o.Err()\n}\n\nfunc (o *ObjectSet) AddErr(err error) {\n\to.errs = append(o.errs, err)\n}\n\nfunc (o *ObjectSet) Err() error {\n\treturn merr.NewErrors(o.errs...)\n}\n\nfunc (o *ObjectSet) Len() int {\n\treturn len(o.objects)\n}\n\nfunc (o *ObjectSet) GVKs() []schema.GroupVersionKind {\n\treturn o.GVKOrder()\n}\n\nfunc (o *ObjectSet) GVKOrder(known ...schema.GroupVersionKind) []schema.GroupVersionKind {\n\tvar rest []schema.GroupVersionKind\n\n\tfor _, gvk := range known {\n\t\tif o.gvkSeen[gvk] {\n\t\t\tcontinue\n\t\t}\n\t\trest = append(rest, gvk)\n\t}\n\n\tsort.Slice(rest, func(i, j int) bool {\n\t\treturn rest[i].String() < rest[j].String()\n\t})\n\n\treturn append(o.gvkOrder, rest...)\n}\n\n// Namespaces all distinct namespaces found on the objects in this set.\nfunc (o *ObjectSet) Namespaces() []string {\n\tnamespaces := stringset.Set{}\n\tfor _, objsByKey := range o.ObjectsByGVK() {\n\t\tfor objKey := range objsByKey {\n\t\t\tnamespaces.Add(objKey.Namespace)\n\t\t}\n\t}\n\treturn namespaces.Values()\n}\n\ntype ObjectByKey map[ObjectKey]runtime.Object\n\nfunc (o ObjectByKey) Namespaces() []string {\n\tnamespaces := stringset.Set{}\n\tfor objKey := range o {\n\t\tnamespaces.Add(objKey.Namespace)\n\t}\n\treturn namespaces.Values()\n}\n\ntype ObjectByGK map[schema.GroupKind]map[ObjectKey]runtime.Object\n\nfunc (o ObjectByGK) Add(obj runtime.Object) (schema.GroupKind, error) {\n\tmetadata, err := meta.Accessor(obj)\n\tif err != nil {\n\t\treturn schema.GroupKind{}, err\n\t}\n\n\tgvk, err := gvk.Get(obj)\n\tif err != nil {\n\t\treturn schema.GroupKind{}, err\n\t}\n\n\tgk := gvk.GroupKind()\n\n\tobjs := o[gk]\n\tif objs == nil {\n\t\tobjs = ObjectByKey{}\n\t\to[gk] = objs\n\t}\n\n\tobjs[ObjectKey{\n\t\tNamespace: metadata.GetNamespace(),\n\t\tName:      metadata.GetName(),\n\t}] = obj\n\n\treturn gk, nil\n}\n"
  },
  {
    "path": "pkg/objectset/objectset_test.go",
    "content": "package objectset\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc TestObjectSet_Namespaces(t *testing.T) {\n\ttype fields struct {\n\t\terrs        []error\n\t\tobjects     ObjectByGVK\n\t\tobjectsByGK ObjectByGK\n\t\torder       []runtime.Object\n\t\tgvkOrder    []schema.GroupVersionKind\n\t\tgvkSeen     map[schema.GroupVersionKind]bool\n\t}\n\ttests := []struct {\n\t\tname           string\n\t\tfields         fields\n\t\twantNamespaces []string\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\tfields: fields{\n\t\t\t\tobjects: map[schema.GroupVersionKind]map[ObjectKey]runtime.Object{},\n\t\t\t},\n\t\t\twantNamespaces: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"1 namespace\",\n\t\t\tfields: fields{\n\t\t\t\tobjects: map[schema.GroupVersionKind]map[ObjectKey]runtime.Object{\n\t\t\t\t\tschema.GroupVersionKind{}: {\n\t\t\t\t\t\tObjectKey{Namespace: \"ns1\", Name: \"a\"}: nil,\n\t\t\t\t\t\tObjectKey{Namespace: \"ns1\", Name: \"b\"}: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantNamespaces: []string{\"ns1\"},\n\t\t},\n\t\t{\n\t\t\tname: \"many namespace\",\n\t\t\tfields: fields{\n\t\t\t\tobjects: map[schema.GroupVersionKind]map[ObjectKey]runtime.Object{\n\t\t\t\t\tschema.GroupVersionKind{}: {\n\t\t\t\t\t\tObjectKey{Namespace: \"ns1\", Name: \"a\"}: nil,\n\t\t\t\t\t\tObjectKey{Namespace: \"ns2\", Name: \"b\"}: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantNamespaces: []string{\"ns1\", \"ns2\"},\n\t\t},\n\t\t{\n\t\t\tname: \"missing namespace\",\n\t\t\tfields: fields{\n\t\t\t\tobjects: map[schema.GroupVersionKind]map[ObjectKey]runtime.Object{\n\t\t\t\t\tschema.GroupVersionKind{}: {\n\t\t\t\t\t\tObjectKey{Namespace: \"ns1\", Name: \"a\"}: nil,\n\t\t\t\t\t\tObjectKey{Name: \"b\"}:                   nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantNamespaces: []string{\"\", \"ns1\"},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\to := &ObjectSet{\n\t\t\t\terrs:        tt.fields.errs,\n\t\t\t\tobjects:     tt.fields.objects,\n\t\t\t\tobjectsByGK: tt.fields.objectsByGK,\n\t\t\t\torder:       tt.fields.order,\n\t\t\t\tgvkOrder:    tt.fields.gvkOrder,\n\t\t\t\tgvkSeen:     tt.fields.gvkSeen,\n\t\t\t}\n\n\t\t\tgotNamespaces := o.Namespaces()\n\t\t\tassert.ElementsMatchf(t, tt.wantNamespaces, gotNamespaces, \"Namespaces() = %v, want %v\", gotNamespaces, tt.wantNamespaces)\n\t\t})\n\t}\n}\n\nfunc TestObjectByKey_Namespaces(t *testing.T) {\n\ttests := []struct {\n\t\tname           string\n\t\tobjects        ObjectByKey\n\t\twantNamespaces []string\n\t}{\n\t\t{\n\t\t\tname:           \"empty\",\n\t\t\tobjects:        ObjectByKey{},\n\t\t\twantNamespaces: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"1 namespace\",\n\t\t\tobjects: ObjectByKey{\n\t\t\t\tObjectKey{Namespace: \"ns1\", Name: \"a\"}: nil,\n\t\t\t\tObjectKey{Namespace: \"ns1\", Name: \"b\"}: nil,\n\t\t\t},\n\t\t\twantNamespaces: []string{\"ns1\"},\n\t\t},\n\t\t{\n\t\t\tname: \"many namespaces\",\n\t\t\tobjects: ObjectByKey{\n\t\t\t\tObjectKey{Namespace: \"ns1\", Name: \"a\"}: nil,\n\t\t\t\tObjectKey{Namespace: \"ns2\", Name: \"b\"}: nil,\n\t\t\t},\n\t\t\twantNamespaces: []string{\"ns1\", \"ns2\"},\n\t\t},\n\t\t{\n\t\t\tname: \"many namespaces with duplicates\",\n\t\t\tobjects: ObjectByKey{\n\t\t\t\tObjectKey{Namespace: \"ns1\", Name: \"a\"}: nil,\n\t\t\t\tObjectKey{Namespace: \"ns2\", Name: \"b\"}: nil,\n\t\t\t\tObjectKey{Namespace: \"ns1\", Name: \"c\"}: nil,\n\t\t\t},\n\t\t\twantNamespaces: []string{\"ns1\", \"ns2\"},\n\t\t},\n\t\t{\n\t\t\tname: \"missing namespace\",\n\t\t\tobjects: ObjectByKey{\n\t\t\t\tObjectKey{Namespace: \"ns1\", Name: \"a\"}: nil,\n\t\t\t\tObjectKey{Name: \"b\"}:                   nil,\n\t\t\t},\n\t\t\twantNamespaces: []string{\"\", \"ns1\"},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgotNamespaces := tt.objects.Namespaces()\n\t\t\tassert.ElementsMatchf(t, tt.wantNamespaces, gotNamespaces, \"Namespaces() = %v, want %v\", gotNamespaces, tt.wantNamespaces)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/patch/apply.go",
    "content": "package patch\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\n\tjsonpatch \"github.com/evanphx/json-patch\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"k8s.io/apimachinery/pkg/util/strategicpatch\"\n)\n\nfunc Apply(original, patch []byte) ([]byte, error) {\n\tstyle, metadata, err := GetPatchStyle(original, patch)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch style {\n\tcase types.JSONPatchType:\n\t\treturn applyJSONPatch(original, patch)\n\tcase types.MergePatchType:\n\t\treturn applyMergePatch(original, patch)\n\tcase types.StrategicMergePatchType:\n\t\treturn applyStrategicMergePatch(original, patch, metadata)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"invalid patch\")\n\t}\n}\n\nfunc applyStrategicMergePatch(original, patch []byte, lookup strategicpatch.LookupPatchMeta) ([]byte, error) {\n\toriginalMap := map[string]interface{}{}\n\tpatchMap := map[string]interface{}{}\n\tif err := json.Unmarshal(original, &originalMap); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := json.Unmarshal(patch, &patchMap); err != nil {\n\t\treturn nil, err\n\t}\n\tpatchedMap, err := strategicpatch.StrategicMergeMapPatchUsingLookupPatchMeta(originalMap, patchMap, lookup)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn json.Marshal(patchedMap)\n}\n\nfunc applyMergePatch(original, patch []byte) ([]byte, error) {\n\treturn jsonpatch.MergePatch(original, patch)\n}\n\nfunc applyJSONPatch(original, patch []byte) ([]byte, error) {\n\tjsonPatch, err := jsonpatch.DecodePatch(patch)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn jsonPatch.Apply(original)\n}\n"
  },
  {
    "path": "pkg/patch/style.go",
    "content": "package patch\n\nimport (\n\t\"sync\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/gvk\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"k8s.io/apimachinery/pkg/util/strategicpatch\"\n\t\"k8s.io/client-go/kubernetes/scheme\"\n)\n\nvar (\n\tpatchCache     = map[schema.GroupVersionKind]patchCacheEntry{}\n\tpatchCacheLock = sync.Mutex{}\n)\n\ntype patchCacheEntry struct {\n\tpatchType types.PatchType\n\tlookup    strategicpatch.LookupPatchMeta\n}\n\nfunc isJSONPatch(patch []byte) bool {\n\t// a JSON patch is a list\n\treturn len(patch) > 0 && patch[0] == '['\n}\n\nfunc GetPatchStyle(original, patch []byte) (types.PatchType, strategicpatch.LookupPatchMeta, error) {\n\tif isJSONPatch(patch) {\n\t\treturn types.JSONPatchType, nil, nil\n\t}\n\tgvk, ok, err := gvk.Detect(original)\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\tif !ok {\n\t\treturn types.MergePatchType, nil, nil\n\t}\n\treturn GetMergeStyle(gvk)\n}\n\nfunc GetMergeStyle(gvk schema.GroupVersionKind) (types.PatchType, strategicpatch.LookupPatchMeta, error) {\n\tvar (\n\t\tpatchType       types.PatchType\n\t\tlookupPatchMeta strategicpatch.LookupPatchMeta\n\t)\n\n\tpatchCacheLock.Lock()\n\tentry, ok := patchCache[gvk]\n\tpatchCacheLock.Unlock()\n\n\tif ok {\n\t\treturn entry.patchType, entry.lookup, nil\n\t}\n\n\tversionedObject, err := scheme.Scheme.New(gvk)\n\n\tif runtime.IsNotRegisteredError(err) || gvk.Kind == \"CustomResourceDefinition\" {\n\t\tpatchType = types.MergePatchType\n\t} else if err != nil {\n\t\treturn patchType, nil, err\n\t} else {\n\t\tpatchType = types.StrategicMergePatchType\n\t\tlookupPatchMeta, err = strategicpatch.NewPatchMetaFromStruct(versionedObject)\n\t\tif err != nil {\n\t\t\treturn patchType, nil, err\n\t\t}\n\t}\n\n\tpatchCacheLock.Lock()\n\tpatchCache[gvk] = patchCacheEntry{\n\t\tpatchType: patchType,\n\t\tlookup:    lookupPatchMeta,\n\t}\n\tpatchCacheLock.Unlock()\n\n\treturn patchType, lookupPatchMeta, nil\n}\n"
  },
  {
    "path": "pkg/randomtoken/token.go",
    "content": "package randomtoken\n\nimport (\n\t\"crypto/rand\"\n\t\"math/big\"\n)\n\nconst (\n\tcharacters  = \"bcdfghjklmnpqrstvwxz2456789\"\n\ttokenLength = 54\n)\n\nvar charsLength = big.NewInt(int64(len(characters)))\n\nfunc Generate() (string, error) {\n\ttoken := make([]byte, tokenLength)\n\tfor i := range token {\n\t\tr, err := rand.Int(rand.Reader, charsLength)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\ttoken[i] = characters[r.Int64()]\n\t}\n\treturn string(token), nil\n}\n"
  },
  {
    "path": "pkg/ratelimit/none.go",
    "content": "package ratelimit\n\nimport (\n\t\"context\"\n\n\t\"k8s.io/client-go/util/flowcontrol\"\n)\n\nvar (\n\tNone = flowcontrol.RateLimiter((*none)(nil))\n)\n\ntype none struct{}\n\nfunc (*none) TryAccept() bool              { return true }\nfunc (*none) Stop()                        {}\nfunc (*none) Accept()                      {}\nfunc (*none) QPS() float32                 { return 1 }\nfunc (*none) Wait(_ context.Context) error { return nil }\n"
  },
  {
    "path": "pkg/relatedresource/all.go",
    "content": "package relatedresource\n\nimport \"k8s.io/apimachinery/pkg/runtime\"\n\nconst (\n\tAllKey = \"_all_\"\n)\n\nfunc TriggerAllKey(namespace, name string, obj runtime.Object) ([]Key, error) {\n\tif name != AllKey {\n\t\treturn []Key{{\n\t\t\tName: AllKey,\n\t\t}}, nil\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "pkg/relatedresource/changeset.go",
    "content": "package relatedresource\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/sirupsen/logrus\"\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\ntype Key struct {\n\tNamespace string\n\tName      string\n}\n\nfunc NewKey(namespace, name string) Key {\n\treturn Key{\n\t\tNamespace: namespace,\n\t\tName:      name,\n\t}\n}\n\nfunc FromString(key string) Key {\n\treturn NewKey(kv.RSplit(key, \"/\"))\n}\n\ntype ControllerWrapper interface {\n\tInformer() cache.SharedIndexInformer\n\tAddGenericHandler(ctx context.Context, name string, handler generic.Handler)\n}\n\ntype ClusterScopedEnqueuer interface {\n\tEnqueue(name string)\n}\n\ntype Enqueuer interface {\n\tEnqueue(namespace, name string)\n}\n\ntype Resolver func(namespace, name string, obj runtime.Object) ([]Key, error)\n\nfunc WatchClusterScoped(ctx context.Context, name string, resolve Resolver, enq ClusterScopedEnqueuer, watching ...ControllerWrapper) {\n\tWatch(ctx, name, resolve, &wrapper{ClusterScopedEnqueuer: enq}, watching...)\n}\n\nfunc Watch(ctx context.Context, name string, resolve Resolver, enq Enqueuer, watching ...ControllerWrapper) {\n\tfor _, c := range watching {\n\t\twatch(ctx, name, enq, resolve, c)\n\t}\n}\n\nfunc watch(ctx context.Context, name string, enq Enqueuer, resolve Resolver, controller ControllerWrapper) {\n\trunResolve := func(ns, name string, obj runtime.Object) error {\n\t\tkeys, err := resolve(ns, name, obj)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, key := range keys {\n\t\t\tif key.Name != \"\" {\n\t\t\t\tenq.Enqueue(key.Namespace, key.Name)\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t}\n\n\taddResourceEventHandler(ctx, controller.Informer(), cache.ResourceEventHandlerFuncs{\n\t\tDeleteFunc: func(obj interface{}) {\n\t\t\tro, ok := obj.(runtime.Object)\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tmeta, err := meta.Accessor(ro)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tgo func() {\n\t\t\t\ttime.Sleep(time.Second)\n\t\t\t\trunResolve(meta.GetNamespace(), meta.GetName(), ro)\n\t\t\t}()\n\t\t},\n\t})\n\n\tcontroller.AddGenericHandler(ctx, name, func(key string, obj runtime.Object) (runtime.Object, error) {\n\t\tns, name := kv.RSplit(key, \"/\")\n\t\treturn obj, runResolve(ns, name, obj)\n\t})\n}\n\ntype wrapper struct {\n\tClusterScopedEnqueuer\n}\n\nfunc (w *wrapper) Enqueue(namespace, name string) {\n\tw.ClusterScopedEnqueuer.Enqueue(name)\n}\n\n// informerRegisterer is a subset of the cache.SharedIndexInformer, so it's easier to replace in tests\ntype informerRegisterer interface {\n\tAddEventHandler(funcs cache.ResourceEventHandler) (cache.ResourceEventHandlerRegistration, error)\n\tRemoveEventHandler(cache.ResourceEventHandlerRegistration) error\n}\n\nfunc addResourceEventHandler(ctx context.Context, informer informerRegisterer, handler cache.ResourceEventHandler) {\n\thandlerReg, err := informer.AddEventHandler(handler)\n\tif err != nil {\n\t\tlogrus.WithError(err).Error(\"failed to add ResourceEventHandler\")\n\t\treturn\n\t}\n\tgo func() {\n\t\t<-ctx.Done()\n\t\tif err := informer.RemoveEventHandler(handlerReg); err != nil {\n\t\t\tlogrus.WithError(err).Warn(\"failed to remove ResourceEventHandler\")\n\t\t}\n\t}()\n}\n"
  },
  {
    "path": "pkg/relatedresource/changeset_test.go",
    "content": "package relatedresource\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\nfunc Test_addResourceEventHandler(t *testing.T) {\n\tconst expectedCalls = 5\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tvar counter int\n\thandler := &cache.ResourceEventHandlerFuncs{\n\t\tAddFunc: func(obj interface{}) { counter++ },\n\t}\n\n\tinformer := &fakeInformer{}\n\taddResourceEventHandler(ctx, informer, handler)\n\n\tfor i := 0; i < expectedCalls; i++ {\n\t\tinformer.add(nil)\n\t}\n\n\tif want, got := expectedCalls, counter; want != got {\n\t\tt.Errorf(\"unexpected number of executions of handler func, want: %d, got: %d\", want, got)\n\t}\n\n\t// Close enqueuer context and wait for unregistering goroutine\n\tcancel()\n\tinformer.waitUntilDeleted(handler, 1*time.Second)\n\n\t// New informer calls should not trigger our handler\n\tinformer.add(nil)\n\tif got, want := counter, expectedCalls; got != want {\n\t\tt.Errorf(\"resource event handler is not correctly removed\")\n\t}\n}\n\ntype handlerRegistration struct{}\n\nfunc (h handlerRegistration) HasSynced() bool { return true }\n\n// fakeInformer implements a subset of cache.SharedIndexInformer, only those methods used by addResourceEventHandler\ntype fakeInformer struct {\n\thandlers   []cache.ResourceEventHandler\n\treg        []cache.ResourceEventHandlerRegistration\n\tdeleteChan []chan struct{}\n}\n\nfunc (informer *fakeInformer) AddEventHandler(handler cache.ResourceEventHandler) (cache.ResourceEventHandlerRegistration, error) {\n\thandlerReg := handlerRegistration{}\n\tinformer.handlers = append(informer.handlers, handler)\n\tinformer.reg = append(informer.reg, handlerReg)\n\tinformer.deleteChan = append(informer.deleteChan, make(chan struct{}))\n\treturn handlerReg, nil\n}\n\nfunc (informer *fakeInformer) RemoveEventHandler(handlerReg cache.ResourceEventHandlerRegistration) error {\n\tx := slicesIndex(informer.reg, handlerReg)\n\tif x < 0 {\n\t\treturn fmt.Errorf(\"handler not found\")\n\t}\n\n\tclose(informer.deleteChan[x])\n\tinformer.reg = deleteIndex(informer.reg, x)\n\tinformer.handlers = deleteIndex(informer.handlers, x)\n\tinformer.deleteChan = deleteIndex(informer.deleteChan, x)\n\treturn nil\n}\n\nfunc (informer *fakeInformer) add(obj runtime.Object) {\n\tfor _, handler := range informer.handlers {\n\t\thandler.OnAdd(obj, false)\n\t}\n}\n\nfunc (informer *fakeInformer) waitUntilDeleted(handler cache.ResourceEventHandler, timeout time.Duration) {\n\tx := slicesIndex(informer.handlers, handler)\n\tif x < 0 {\n\t\treturn\n\t}\n\n\tselect {\n\tcase <-informer.deleteChan[x]:\n\tcase <-time.After(timeout):\n\t}\n}\n\nfunc slicesIndex[S ~[]E, E comparable](s S, v E) int {\n\tfor i := range s {\n\t\tif v == s[i] {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc deleteIndex[S ~[]E, E any](s S, i int) S {\n\treturn append(s[:i], s[i+1:]...)\n}\n"
  },
  {
    "path": "pkg/relatedresource/owner.go",
    "content": "package relatedresource\n\nimport (\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n)\n\n// OwnerResolver Look for owner references that match the apiVersion and kind and resolve to the namespace and\n// name of the parent. The namespaced flag is whether the apiVersion/kind referenced is expected to be namespaced\nfunc OwnerResolver(namespaced bool, apiVersion, kind string) Resolver {\n\treturn func(namespace, name string, obj runtime.Object) ([]Key, error) {\n\t\tif obj == nil {\n\t\t\treturn nil, nil\n\t\t}\n\n\t\tmeta, err := meta.Accessor(obj)\n\t\tif err != nil {\n\t\t\t// ignore err\n\t\t\treturn nil, nil\n\t\t}\n\n\t\tvar result []Key\n\t\tfor _, owner := range meta.GetOwnerReferences() {\n\t\t\tif owner.Kind == kind && owner.APIVersion == apiVersion {\n\t\t\t\tns := \"\"\n\t\t\t\tif namespaced {\n\t\t\t\t\tns = meta.GetNamespace()\n\t\t\t\t}\n\t\t\t\tresult = append(result, Key{\n\t\t\t\t\tNamespace: ns,\n\t\t\t\t\tName:      owner.Name,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\treturn result, nil\n\t}\n}\n"
  },
  {
    "path": "pkg/resolvehome/main.go",
    "content": "package resolvehome\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n)\n\nvar (\n\thomes = []string{\"$HOME\", \"${HOME}\", \"~\"}\n)\n\nfunc Resolve(s string) (string, error) {\n\tfor _, home := range homes {\n\t\tif strings.Contains(s, home) {\n\t\t\thomeDir, err := os.UserHomeDir()\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", fmt.Errorf(\"determining current user: %w\", err)\n\t\t\t}\n\t\t\ts = strings.Replace(s, home, homeDir, -1)\n\t\t}\n\t}\n\n\treturn s, nil\n}\n"
  },
  {
    "path": "pkg/schemas/definition/definition.go",
    "content": "package definition\n\nimport (\n\t\"strings\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n)\n\nfunc IsMapType(fieldType string) bool {\n\treturn strings.HasPrefix(fieldType, \"map[\") && strings.HasSuffix(fieldType, \"]\")\n}\n\nfunc IsArrayType(fieldType string) bool {\n\treturn strings.HasPrefix(fieldType, \"array[\") && strings.HasSuffix(fieldType, \"]\")\n}\n\nfunc IsReferenceType(fieldType string) bool {\n\treturn strings.HasPrefix(fieldType, \"reference[\") && strings.HasSuffix(fieldType, \"]\")\n}\n\nfunc HasReferenceType(fieldType string) bool {\n\treturn strings.Contains(fieldType, \"reference[\")\n}\n\nfunc SubType(fieldType string) string {\n\ti := strings.Index(fieldType, \"[\")\n\tif i <= 0 || i >= len(fieldType)-1 {\n\t\treturn fieldType\n\t}\n\n\treturn fieldType[i+1 : len(fieldType)-1]\n}\n\nfunc GetType(data map[string]interface{}) string {\n\treturn convert.ToString(data[\"type\"])\n}\n"
  },
  {
    "path": "pkg/schemas/mapper.go",
    "content": "package schemas\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n\t\"github.com/rancher/wrangler/v3/pkg/merr\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemas/definition\"\n)\n\ntype Mapper interface {\n\tFromInternal(data data.Object)\n\tToInternal(data data.Object) error\n\tModifySchema(schema *Schema, schemas *Schemas) error\n}\n\ntype Mappers []Mapper\n\nfunc (m Mappers) FromInternal(data data.Object) {\n\tfor _, mapper := range m {\n\t\tmapper.FromInternal(data)\n\t}\n}\n\nfunc (m Mappers) ToInternal(data data.Object) error {\n\tvar errors []error\n\tfor i := len(m) - 1; i >= 0; i-- {\n\t\terrors = append(errors, m[i].ToInternal(data))\n\t}\n\treturn merr.NewErrors(errors...)\n}\n\nfunc (m Mappers) ModifySchema(schema *Schema, schemas *Schemas) error {\n\tfor _, mapper := range m {\n\t\tif err := mapper.ModifySchema(schema, schemas); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\ntype typeMapper struct {\n\tMappers         []Mapper\n\troot            bool\n\ttypeName        string\n\tsubSchemas      map[string]*Schema\n\tsubArraySchemas map[string]*Schema\n\tsubMapSchemas   map[string]*Schema\n}\n\nfunc (t *typeMapper) FromInternal(data data.Object) {\n\tfor fieldName, schema := range t.subSchemas {\n\t\tif schema.Mapper == nil {\n\t\t\tcontinue\n\t\t}\n\t\tschema.Mapper.FromInternal(data.Map(fieldName))\n\t}\n\n\tfor fieldName, schema := range t.subMapSchemas {\n\t\tif schema.Mapper == nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, fieldData := range data.Map(fieldName).Values() {\n\t\t\tschema.Mapper.FromInternal(fieldData)\n\t\t}\n\t}\n\n\tfor fieldName, schema := range t.subArraySchemas {\n\t\tif schema.Mapper == nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, fieldData := range data.Slice(fieldName) {\n\t\t\tschema.Mapper.FromInternal(fieldData)\n\t\t}\n\t}\n\n\tMappers(t.Mappers).FromInternal(data)\n}\n\nfunc addError(errors []error, err error) []error {\n\tif err == nil {\n\t\treturn errors\n\t}\n\treturn append(errors, err)\n}\n\nfunc (t *typeMapper) ToInternal(data data.Object) error {\n\tvar errors []error\n\terrors = addError(errors, Mappers(t.Mappers).ToInternal(data))\n\n\tfor fieldName, schema := range t.subArraySchemas {\n\t\tif schema.Mapper == nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, fieldData := range data.Slice(fieldName) {\n\t\t\terrors = addError(errors, schema.Mapper.ToInternal(fieldData))\n\t\t}\n\t}\n\n\tfor fieldName, schema := range t.subMapSchemas {\n\t\tif schema.Mapper == nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, fieldData := range data.Map(fieldName) {\n\t\t\terrors = addError(errors, schema.Mapper.ToInternal(convert.ToMapInterface(fieldData)))\n\t\t}\n\t}\n\n\tfor fieldName, schema := range t.subSchemas {\n\t\tif schema.Mapper == nil {\n\t\t\tcontinue\n\t\t}\n\t\terrors = addError(errors, schema.Mapper.ToInternal(data.Map(fieldName)))\n\t}\n\n\treturn merr.NewErrors(errors...)\n}\n\nfunc (t *typeMapper) ModifySchema(schema *Schema, schemas *Schemas) error {\n\tt.subSchemas = map[string]*Schema{}\n\tt.subArraySchemas = map[string]*Schema{}\n\tt.subMapSchemas = map[string]*Schema{}\n\tt.typeName = schema.ID\n\n\tmapperSchema := schema\n\tif schema.InternalSchema != nil {\n\t\tmapperSchema = schema.InternalSchema\n\t}\n\tfor name, field := range mapperSchema.ResourceFields {\n\t\tfieldType := field.Type\n\t\ttargetMap := t.subSchemas\n\t\tif definition.IsArrayType(fieldType) {\n\t\t\tfieldType = definition.SubType(fieldType)\n\t\t\ttargetMap = t.subArraySchemas\n\t\t} else if definition.IsMapType(fieldType) {\n\t\t\tfieldType = definition.SubType(fieldType)\n\t\t\ttargetMap = t.subMapSchemas\n\t\t}\n\n\t\tschema := schemas.doSchema(fieldType, false)\n\t\tif schema != nil {\n\t\t\ttargetMap[name] = schema\n\t\t}\n\t}\n\n\treturn Mappers(t.Mappers).ModifySchema(schema, schemas)\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/access.go",
    "content": "package mappers\n\nimport (\n\t\"strings\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\ntype Access struct {\n\tFields   map[string]string\n\tOptional bool\n}\n\nfunc (e Access) FromInternal(data data.Object) {\n}\n\nfunc (e Access) ToInternal(data data.Object) error {\n\treturn nil\n}\n\nfunc (e Access) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {\n\tfor name, access := range e.Fields {\n\t\tif err := ValidateField(name, schema); err != nil {\n\t\t\tif e.Optional {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\n\t\tfield := schema.ResourceFields[name]\n\t\tfield.Create = strings.Contains(access, \"c\")\n\t\tfield.Update = strings.Contains(access, \"u\")\n\t\tfield.WriteOnly = strings.Contains(access, \"o\")\n\n\t\tschema.ResourceFields[name] = field\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/alias.go",
    "content": "package mappers\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\ntype AliasField struct {\n\tField string\n\tNames []string\n}\n\nfunc NewAlias(field string, names ...string) types.Mapper {\n\treturn AliasField{\n\t\tField: field,\n\t\tNames: names,\n\t}\n}\n\nfunc (d AliasField) FromInternal(data data.Object) {\n}\n\nfunc (d AliasField) ToInternal(data data.Object) error {\n\tfor _, name := range d.Names {\n\t\tif v, ok := data[name]; ok {\n\t\t\tdelete(data, name)\n\t\t\tdata[d.Field] = v\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (d AliasField) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {\n\tfor _, name := range d.Names {\n\t\tschema.ResourceFields[name] = types.Field{}\n\t}\n\n\treturn ValidateField(d.Field, schema)\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/check.go",
    "content": "package mappers\n\nimport (\n\t\"fmt\"\n\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\nfunc ValidateField(field string, schema *types.Schema) error {\n\tif _, ok := schema.ResourceFields[field]; !ok {\n\t\treturn fmt.Errorf(\"field %s missing on schema %s\", field, schema.ID)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/condition.go",
    "content": "package mappers\n\nimport (\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\ntype Condition struct {\n\tField  string\n\tValue  interface{}\n\tMapper types.Mapper\n}\n\nfunc (m Condition) FromInternal(data map[string]interface{}) {\n\tif data[m.Field] == m.Value {\n\t\tm.Mapper.FromInternal(data)\n\t}\n}\n\nfunc (m Condition) ToInternal(data map[string]interface{}) error {\n\tif data[m.Field] == m.Value {\n\t\treturn m.Mapper.ToInternal(data)\n\t}\n\treturn nil\n}\n\nfunc (m Condition) ModifySchema(s *types.Schema, schemas *types.Schemas) error {\n\treturn m.Mapper.ModifySchema(s, schemas)\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/copy.go",
    "content": "package mappers\n\nimport (\n\t\"fmt\"\n\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\ntype Copy struct {\n\tFrom, To string\n}\n\nfunc (c Copy) FromInternal(data map[string]interface{}) {\n\tif data == nil {\n\t\treturn\n\t}\n\tv, ok := data[c.From]\n\tif ok {\n\t\tdata[c.To] = v\n\t}\n}\n\nfunc (c Copy) ToInternal(data map[string]interface{}) error {\n\tif data == nil {\n\t\treturn nil\n\t}\n\tt, tok := data[c.To]\n\t_, fok := data[c.From]\n\tif tok && !fok {\n\t\tdata[c.From] = t\n\t}\n\n\treturn nil\n}\n\nfunc (c Copy) ModifySchema(s *types.Schema, schemas *types.Schemas) error {\n\tf, ok := s.ResourceFields[c.From]\n\tif !ok {\n\t\treturn fmt.Errorf(\"field %s missing on schema %s\", c.From, s.ID)\n\t}\n\n\ts.ResourceFields[c.To] = f\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/default.go",
    "content": "package mappers\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\ntype DefaultMapper struct {\n\tField string\n}\n\nfunc (d DefaultMapper) FromInternal(data data.Object) {\n}\n\nfunc (d DefaultMapper) ToInternal(data data.Object) error {\n\treturn nil\n}\n\nfunc (d DefaultMapper) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {\n\tif d.Field != \"\" {\n\t\treturn ValidateField(d.Field, schema)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/drop.go",
    "content": "package mappers\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\ntype Drop struct {\n\tField    string\n\tOptional bool\n}\n\nfunc (d Drop) FromInternal(data data.Object) {\n\tdelete(data, d.Field)\n}\n\nfunc (d Drop) ToInternal(data data.Object) error {\n\treturn nil\n}\n\nfunc (d Drop) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {\n\tif _, ok := schema.ResourceFields[d.Field]; !ok {\n\t\tif !d.Optional {\n\t\t\treturn fmt.Errorf(\"can not drop missing field %s on %s\", d.Field, schema.ID)\n\t\t}\n\t}\n\n\tdelete(schema.ResourceFields, d.Field)\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/embed.go",
    "content": "package mappers\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\ntype Embed struct {\n\tField          string\n\tOptional       bool\n\tReadOnly       bool\n\tIgnore         []string\n\tignoreOverride bool\n\tembeddedFields []string\n\tEmptyValueOk   bool\n}\n\nfunc (e *Embed) FromInternal(data data.Object) {\n\tsub := data.Map(e.Field)\n\tfor _, fieldName := range e.embeddedFields {\n\t\tif v, ok := sub[fieldName]; ok {\n\t\t\tdata[fieldName] = v\n\t\t}\n\t}\n\tdelete(data, e.Field)\n}\n\nfunc (e *Embed) ToInternal(data data.Object) error {\n\tif data == nil {\n\t\treturn nil\n\t}\n\n\tsub := map[string]interface{}{}\n\tfor _, fieldName := range e.embeddedFields {\n\t\tif v, ok := data[fieldName]; ok {\n\t\t\tsub[fieldName] = v\n\t\t}\n\n\t\tdelete(data, fieldName)\n\t}\n\tif len(sub) == 0 {\n\t\tif e.EmptyValueOk {\n\t\t\tdata[e.Field] = nil\n\t\t}\n\t\treturn nil\n\t}\n\tdata[e.Field] = sub\n\treturn nil\n}\n\nfunc (e *Embed) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {\n\terr := ValidateField(e.Field, schema)\n\tif err != nil {\n\t\tif e.Optional {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\n\te.embeddedFields = []string{}\n\n\tembeddedSchemaID := schema.ResourceFields[e.Field].Type\n\tembeddedSchema := schemas.Schema(embeddedSchemaID)\n\tif embeddedSchema == nil {\n\t\tif e.Optional {\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"failed to find schema %s for embedding\", embeddedSchemaID)\n\t}\n\n\tdeleteField := true\nouter:\n\tfor name, field := range embeddedSchema.ResourceFields {\n\t\tfor _, ignore := range e.Ignore {\n\t\t\tif ignore == name {\n\t\t\t\tcontinue outer\n\t\t\t}\n\t\t}\n\n\t\tif name == e.Field {\n\t\t\tdeleteField = false\n\t\t} else {\n\t\t\tif !e.ignoreOverride {\n\t\t\t\tif _, ok := schema.ResourceFields[name]; ok {\n\t\t\t\t\treturn fmt.Errorf(\"embedding field %s on %s will overwrite the field %s\",\n\t\t\t\t\t\te.Field, schema.ID, name)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif e.ReadOnly {\n\t\t\tfield.Create = false\n\t\t\tfield.Update = false\n\t\t}\n\n\t\tschema.ResourceFields[name] = field\n\t\te.embeddedFields = append(e.embeddedFields, name)\n\t}\n\n\tif deleteField {\n\t\tdelete(schema.ResourceFields, e.Field)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/empty.go",
    "content": "package mappers\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\ntype EmptyMapper struct {\n}\n\nfunc (e *EmptyMapper) FromInternal(data data.Object) {\n}\n\nfunc (e *EmptyMapper) ToInternal(data data.Object) error {\n\treturn nil\n}\n\nfunc (e *EmptyMapper) ModifySchema(schema *schemas.Schema, schemas *schemas.Schemas) error {\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/enum.go",
    "content": "package mappers\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\ntype Enum struct {\n\tDefaultMapper\n\tvals map[string]string\n}\n\nfunc NewEnum(field string, vals ...string) schemas.Mapper {\n\tf := &Enum{\n\t\tDefaultMapper: DefaultMapper{\n\t\t\tField: field,\n\t\t},\n\t\tvals: map[string]string{},\n\t}\n\n\tfor _, v := range vals {\n\t\tk := v\n\t\tif strings.Contains(v, \"=\") {\n\t\t\tv, k = kv.Split(v, \"=\")\n\t\t}\n\t\tf.vals[normalize(v)] = k\n\t}\n\n\treturn f\n}\n\nfunc normalize(v string) string {\n\tv = strings.ReplaceAll(v, \"_\", \"\")\n\tv = strings.ReplaceAll(v, \"-\", \"\")\n\treturn strings.ToLower(v)\n}\n\nfunc (d *Enum) FromInternal(data data.Object) {\n}\n\nfunc (d *Enum) ToInternal(data data.Object) error {\n\tv, ok := data[d.Field]\n\tif ok {\n\t\tnewValue, ok := d.vals[normalize(convert.ToString(v))]\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"%s is not a valid value for field %s\", v, d.Field)\n\t\t}\n\t\tdata[d.Field] = newValue\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/exists.go",
    "content": "package mappers\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\ntype Exists struct {\n\tField   string\n\tMapper  types.Mapper\n\tenabled bool\n}\n\nfunc (m *Exists) FromInternal(data data.Object) {\n\tif m.enabled {\n\t\tm.Mapper.FromInternal(data)\n\t}\n}\n\nfunc (m *Exists) ToInternal(data data.Object) error {\n\tif m.enabled {\n\t\treturn m.Mapper.ToInternal(data)\n\t}\n\treturn nil\n}\n\nfunc (m *Exists) ModifySchema(s *types.Schema, schemas *types.Schemas) error {\n\tif _, ok := s.ResourceFields[m.Field]; ok {\n\t\tm.enabled = true\n\t\treturn m.Mapper.ModifySchema(s, schemas)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/json_keys.go",
    "content": "package mappers\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\ntype JSONKeys struct {\n}\n\nfunc (d JSONKeys) FromInternal(data data.Object) {\n}\n\nfunc (d JSONKeys) ToInternal(data data.Object) error {\n\tfor key, value := range data {\n\t\tnewKey := convert.ToJSONKey(key)\n\t\tif newKey != key {\n\t\t\tdata[newKey] = value\n\t\t\tdelete(data, key)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (d JSONKeys) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/metadata.go",
    "content": "package mappers\n\nimport (\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\nfunc NewMetadataMapper() types.Mapper {\n\treturn types.Mappers{\n\t\tMove{From: \"name\", To: \"id\", CodeName: \"ID\"},\n\t\tDrop{Field: \"namespace\"},\n\t\tDrop{Field: \"generateName\"},\n\t\tMove{From: \"uid\", To: \"uuid\", CodeName: \"UUID\"},\n\t\tDrop{Field: \"resourceVersion\"},\n\t\tDrop{Field: \"generation\"},\n\t\tMove{From: \"creationTimestamp\", To: \"created\"},\n\t\tMove{From: \"deletionTimestamp\", To: \"removed\"},\n\t\tDrop{Field: \"deletionGracePeriodSeconds\"},\n\t\tDrop{Field: \"initializers\"},\n\t\tDrop{Field: \"finalizers\"},\n\t\tDrop{Field: \"managedFields\"},\n\t\tDrop{Field: \"ownerReferences\"},\n\t\tDrop{Field: \"clusterName\"},\n\t\tDrop{Field: \"selfLink\"},\n\t\tAccess{\n\t\t\tFields: map[string]string{\n\t\t\t\t\"labels\":      \"cu\",\n\t\t\t\t\"annotations\": \"cu\",\n\t\t\t},\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/move.go",
    "content": "package mappers\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemas/definition\"\n)\n\ntype Move struct {\n\tOptional           bool\n\tFrom, To, CodeName string\n\tDestDefined        bool\n\tNoDeleteFromField  bool\n}\n\nfunc (m Move) FromInternal(d data.Object) {\n\tif v, ok := data.RemoveValue(d, strings.Split(m.From, \"/\")...); ok {\n\t\tdata.PutValue(d, v, strings.Split(m.To, \"/\")...)\n\t}\n}\n\nfunc (m Move) ToInternal(d data.Object) error {\n\tif v, ok := data.RemoveValue(d, strings.Split(m.To, \"/\")...); ok {\n\t\tdata.PutValue(d, v, strings.Split(m.From, \"/\")...)\n\t}\n\treturn nil\n}\n\nfunc (m Move) ModifySchema(s *types.Schema, schemas *types.Schemas) error {\n\tfromSchema, _, fromField, ok, err := getField(s, schemas, m.From)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !ok {\n\t\tif m.Optional {\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"failed to find field %s on schema %s\", m.From, s.ID)\n\t}\n\n\ttoSchema, toFieldName, _, _, err := getField(s, schemas, m.To)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, ok = toSchema.ResourceFields[toFieldName]\n\tif ok && !strings.Contains(m.To, \"/\") && !m.DestDefined {\n\t\treturn fmt.Errorf(\"field %s already exists on schema %s\", m.To, s.ID)\n\t}\n\n\tif !m.NoDeleteFromField {\n\t\tdelete(fromSchema.ResourceFields, m.From)\n\t}\n\n\tif !m.DestDefined {\n\t\tif m.CodeName == \"\" {\n\t\t\tfromField.CodeName = convert.Capitalize(toFieldName)\n\t\t} else {\n\t\t\tfromField.CodeName = m.CodeName\n\t\t}\n\t\ttoSchema.ResourceFields[toFieldName] = fromField\n\t}\n\n\treturn nil\n}\n\nfunc getField(schema *types.Schema, schemas *types.Schemas, target string) (*types.Schema, string, types.Field, bool, error) {\n\tparts := strings.Split(target, \"/\")\n\tfor i, part := range parts {\n\t\tif i == len(parts)-1 {\n\t\t\tcontinue\n\t\t}\n\n\t\tfieldType := schema.ResourceFields[part].Type\n\t\tif definition.IsArrayType(fieldType) {\n\t\t\tfieldType = definition.SubType(fieldType)\n\t\t}\n\t\tsubSchema := schemas.Schema(fieldType)\n\t\tif subSchema == nil {\n\t\t\treturn nil, \"\", types.Field{}, false, fmt.Errorf(\"failed to find field or schema for %s on %s\", part, schema.ID)\n\t\t}\n\n\t\tschema = subSchema\n\t}\n\n\tname := parts[len(parts)-1]\n\tf, ok := schema.ResourceFields[name]\n\treturn schema, name, f, ok, nil\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/set_value.go",
    "content": "package mappers\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\ntype SetValue struct {\n\tField         string\n\tInternalValue interface{}\n\tExternalValue interface{}\n}\n\nfunc (d SetValue) FromInternal(data data.Object) {\n\tif data != nil && d.ExternalValue != nil {\n\t\tdata[d.Field] = d.ExternalValue\n\t}\n}\n\nfunc (d SetValue) ToInternal(data data.Object) error {\n\tif data != nil && d.InternalValue != nil {\n\t\tdata[d.Field] = d.InternalValue\n\t}\n\treturn nil\n}\n\nfunc (d SetValue) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {\n\treturn ValidateField(d.Field, schema)\n}\n"
  },
  {
    "path": "pkg/schemas/mappers/slice_to_map.go",
    "content": "package mappers\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemas/definition\"\n)\n\ntype SliceToMap struct {\n\tField string\n\tKey   string\n}\n\nfunc (s SliceToMap) FromInternal(data data.Object) {\n\tdatas := data.Slice(s.Field)\n\tresult := map[string]interface{}{}\n\n\tfor _, item := range datas {\n\t\tname, _ := item[s.Key].(string)\n\t\tdelete(item, s.Key)\n\t\tresult[name] = item\n\t}\n\n\tif len(result) > 0 {\n\t\tdata[s.Field] = result\n\t}\n}\n\nfunc (s SliceToMap) ToInternal(data data.Object) error {\n\tdatas := data.Map(s.Field)\n\tvar result []interface{}\n\n\tfor name, item := range datas {\n\t\tmapItem, _ := item.(map[string]interface{})\n\t\tif mapItem != nil {\n\t\t\tmapItem[s.Key] = name\n\t\t\tresult = append(result, mapItem)\n\t\t}\n\t}\n\n\tif len(result) > 0 {\n\t\tdata[s.Field] = result\n\t} else if datas != nil {\n\t\tdata[s.Field] = result\n\t}\n\n\treturn nil\n}\n\nfunc (s SliceToMap) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {\n\terr := ValidateField(s.Field, schema)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsubSchema, subFieldName, _, _, err := getField(schema, schemas, fmt.Sprintf(\"%s/%s\", s.Field, s.Key))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfield := schema.ResourceFields[s.Field]\n\tif !definition.IsArrayType(field.Type) {\n\t\treturn fmt.Errorf(\"field %s on %s is not an array\", s.Field, schema.ID)\n\t}\n\n\tfield.Type = \"map[\" + definition.SubType(field.Type) + \"]\"\n\tschema.ResourceFields[s.Field] = field\n\n\tdelete(subSchema.ResourceFields, subFieldName)\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/schemas/openapi/generate.go",
    "content": "package openapi\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\n\ttypes \"github.com/rancher/wrangler/v3/pkg/schemas\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemas/definition\"\n\tv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n)\n\nfunc MustGenerate(obj interface{}) *v1.JSONSchemaProps {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tresult, err := ToOpenAPIFromStruct(obj)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn result\n}\n\nfunc ToOpenAPIFromStruct(obj interface{}) (*v1.JSONSchemaProps, error) {\n\tschemas := types.EmptySchemas()\n\tschema, err := schemas.Import(obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ToOpenAPI(schema.ID, schemas)\n}\n\nfunc ToOpenAPI(name string, schemas *types.Schemas) (*v1.JSONSchemaProps, error) {\n\tschema := schemas.Schema(name)\n\tif schema == nil {\n\t\treturn nil, fmt.Errorf(\"failed to find schema: %s\", name)\n\t}\n\n\tnewSchema := schema.DeepCopy()\n\tif newSchema.InternalSchema != nil {\n\t\tnewSchema = newSchema.InternalSchema.DeepCopy()\n\t}\n\tdelete(newSchema.ResourceFields, \"kind\")\n\tdelete(newSchema.ResourceFields, \"apiVersion\")\n\tdelete(newSchema.ResourceFields, \"metadata\")\n\n\treturn schemaToProps(newSchema, schemas, map[string]bool{})\n}\n\nfunc populateField(fieldJSP *v1.JSONSchemaProps, f *types.Field) error {\n\tfieldJSP.Description = f.Description\n\t// don't reset this to not nullable\n\tif f.Nullable {\n\t\tfieldJSP.Nullable = f.Nullable\n\t}\n\tfieldJSP.MinLength = f.MinLength\n\tfieldJSP.MaxLength = f.MaxLength\n\n\tif f.Type == \"string\" && len(f.Options) > 0 {\n\t\tfor _, opt := range append(f.Options, \"\") {\n\t\t\tbytes, err := json.Marshal(&opt)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfieldJSP.Enum = append(fieldJSP.Enum, v1.JSON{\n\t\t\t\tRaw: bytes,\n\t\t\t})\n\t\t}\n\t}\n\n\tif len(f.InvalidChars) > 0 {\n\t\tfieldJSP.Pattern = fmt.Sprintf(\"^[^%s]*$\", f.InvalidChars)\n\t}\n\n\tif len(f.ValidChars) > 0 {\n\t\tfieldJSP.Pattern = fmt.Sprintf(\"^[%s]*$\", f.ValidChars)\n\t}\n\n\tif f.Min != nil {\n\t\tfl := float64(*f.Min)\n\t\tfieldJSP.Minimum = &fl\n\t}\n\n\tif f.Max != nil {\n\t\tfl := float64(*f.Max)\n\t\tfieldJSP.Maximum = &fl\n\t}\n\n\tif f.Default != nil {\n\t\tbytes, err := json.Marshal(f.Default)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfieldJSP.Default = &v1.JSON{\n\t\t\tRaw: bytes,\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc typeToProps(typeName string, schemas *types.Schemas, inflight map[string]bool) (*v1.JSONSchemaProps, error) {\n\tt, subType, schema, err := typeAndSchema(typeName, schemas)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif schema != nil {\n\t\treturn schemaToProps(schema, schemas, inflight)\n\t}\n\n\tjsp := &v1.JSONSchemaProps{}\n\n\tswitch t {\n\tcase \"map\":\n\t\tadditionalProps, err := typeToProps(subType, schemas, inflight)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tjsp.Type = \"object\"\n\t\tjsp.Nullable = true\n\t\tif subType != \"json\" {\n\t\t\tjsp.AdditionalProperties = &v1.JSONSchemaPropsOrBool{\n\t\t\t\tAllows: true,\n\t\t\t\tSchema: additionalProps,\n\t\t\t}\n\t\t}\n\tcase \"array\":\n\t\titems, err := typeToProps(subType, schemas, inflight)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tjsp.Type = \"array\"\n\t\tjsp.Nullable = true\n\t\tjsp.Items = &v1.JSONSchemaPropsOrArray{\n\t\t\tSchema: items,\n\t\t}\n\tcase \"string\":\n\t\tjsp.Type = t\n\t\tjsp.Nullable = true\n\tcase \"intOrString\":\n\t\tjsp.XIntOrString = true\n\tdefault:\n\t\tjsp.Type = t\n\t}\n\n\tif jsp.Type == \"object\" && jsp.AdditionalProperties == nil {\n\t\tjsp.XPreserveUnknownFields = &[]bool{true}[0]\n\t}\n\n\treturn jsp, nil\n}\n\nfunc schemaToProps(schema *types.Schema, schemas *types.Schemas, inflight map[string]bool) (*v1.JSONSchemaProps, error) {\n\tjsp := &v1.JSONSchemaProps{\n\t\tDescription: schema.Description,\n\t\tType:        \"object\",\n\t}\n\n\tif inflight[schema.ID] {\n\t\treturn jsp, nil\n\t}\n\n\tinflight[schema.ID] = true\n\tdefer delete(inflight, schema.ID)\n\n\tjsp.Properties = map[string]v1.JSONSchemaProps{}\n\n\tfor name, f := range schema.ResourceFields {\n\t\tfieldJSP, err := typeToProps(f.Type, schemas, inflight)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err := populateField(fieldJSP, &f); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif f.Required {\n\t\t\tjsp.Required = append(jsp.Required, name)\n\t\t}\n\t\tjsp.Properties[name] = *fieldJSP\n\t}\n\n\tsort.Strings(jsp.Required)\n\tif len(jsp.Properties) == 0 && strings.HasSuffix(strings.ToLower(schema.ID), \"map\") {\n\t\tjsp.XPreserveUnknownFields = &[]bool{true}[0]\n\t}\n\treturn jsp, nil\n}\n\nfunc typeAndSchema(typeName string, schemas *types.Schemas) (string, string, *types.Schema, error) {\n\tif definition.IsReferenceType(typeName) {\n\t\treturn \"string\", \"\", nil, nil\n\t}\n\n\tif definition.IsArrayType(typeName) {\n\t\treturn \"array\", definition.SubType(typeName), nil, nil\n\t}\n\n\tif definition.IsMapType(typeName) {\n\t\treturn \"map\", definition.SubType(typeName), nil, nil\n\t}\n\n\tswitch typeName {\n\tcase \"intOrString\":\n\t\treturn \"intOrString\", \"\", nil, nil\n\tcase \"int\":\n\t\treturn \"integer\", \"\", nil, nil\n\tcase \"float\":\n\t\treturn \"number\", \"\", nil, nil\n\tcase \"string\":\n\t\treturn \"string\", \"\", nil, nil\n\tcase \"date\":\n\t\treturn \"string\", \"\", nil, nil\n\tcase \"enum\":\n\t\treturn \"string\", \"\", nil, nil\n\tcase \"base64\":\n\t\treturn \"string\", \"\", nil, nil\n\tcase \"password\":\n\t\treturn \"string\", \"\", nil, nil\n\tcase \"hostname\":\n\t\treturn \"string\", \"\", nil, nil\n\tcase \"boolean\":\n\t\treturn \"boolean\", \"\", nil, nil\n\tcase \"json\":\n\t\treturn \"object\", \"\", nil, nil\n\t}\n\n\tschema := schemas.Schema(typeName)\n\tif schema == nil {\n\t\treturn \"\", \"\", nil, fmt.Errorf(\"failed to find schema %s\", typeName)\n\t}\n\tif schema.InternalSchema != nil {\n\t\treturn \"\", \"\", schema.InternalSchema, nil\n\t}\n\treturn \"\", \"\", schema, nil\n}\n"
  },
  {
    "path": "pkg/schemas/reflection.go",
    "content": "package schemas\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n\t\"github.com/rancher/wrangler/v3/pkg/slice\"\n\t\"github.com/sirupsen/logrus\"\n)\n\nvar (\n\tskippedNames = map[string]bool{\n\t\t\"links\":   true,\n\t\t\"actions\": true,\n\t}\n)\n\nfunc (s *Schemas) TypeName(name string, obj interface{}) *Schemas {\n\ts.typeNames[reflect.TypeOf(obj)] = name\n\treturn s\n}\n\nfunc (s *Schemas) getTypeName(t reflect.Type) string {\n\tif name, ok := s.typeNames[t]; ok {\n\t\treturn name\n\t}\n\treturn convert.LowerTitle(t.Name())\n}\n\nfunc (s *Schemas) SchemaFor(t reflect.Type) *Schema {\n\tname := s.getTypeName(t)\n\treturn s.Schema(name)\n}\n\nfunc (s *Schemas) AddMapperForType(obj interface{}, mapper ...Mapper) *Schemas {\n\tif len(mapper) == 0 {\n\t\treturn s\n\t}\n\n\tt := reflect.TypeOf(obj)\n\ttypeName := s.getTypeName(t)\n\tif len(mapper) == 1 {\n\t\treturn s.AddMapper(typeName, mapper[0])\n\t}\n\treturn s.AddMapper(typeName, Mappers(mapper))\n}\n\nfunc (s *Schemas) MustImport(obj interface{}, externalOverrides ...interface{}) *Schemas {\n\tif reflect.ValueOf(obj).Kind() == reflect.Ptr {\n\t\tpanic(fmt.Errorf(\"obj cannot be a pointer\"))\n\t}\n\n\tif _, err := s.Import(obj, externalOverrides...); err != nil {\n\t\tpanic(err)\n\t}\n\treturn s\n}\n\nfunc (s *Schemas) MustImportAndCustomize(obj interface{}, f func(*Schema), externalOverrides ...interface{}) *Schemas {\n\treturn s.MustImport(obj, externalOverrides...).\n\t\tMustCustomizeType(obj, f)\n}\n\nfunc getType(obj interface{}) reflect.Type {\n\tif t, ok := obj.(reflect.Type); ok {\n\t\treturn t\n\t}\n\n\tt := reflect.TypeOf(obj)\n\tif t.Kind() == reflect.Ptr {\n\t\tt = t.Elem()\n\t}\n\treturn t\n}\n\nfunc (s *Schemas) Import(obj interface{}, externalOverrides ...interface{}) (*Schema, error) {\n\tvar types []reflect.Type\n\tfor _, override := range externalOverrides {\n\t\ttypes = append(types, getType(override))\n\t}\n\n\tt := getType(obj)\n\treturn s.importType(t, types...)\n}\n\nfunc (s *Schemas) newSchemaFromType(t reflect.Type, typeName string) (*Schema, error) {\n\tschema := &Schema{\n\t\tID:                  typeName,\n\t\tCodeName:            t.Name(),\n\t\tPkgName:             t.PkgPath(),\n\t\tResourceFields:      map[string]Field{},\n\t\tResourceActions:     map[string]Action{},\n\t\tCollectionActions:   map[string]Action{},\n\t\tAttributes:          map[string]interface{}{},\n\t\tResourcePermissions: ResourcePermissions{},\n\t}\n\n\ts.processingTypes[t] = schema\n\tdefer delete(s.processingTypes, t)\n\n\tif err := s.readFields(schema, t); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn schema, nil\n}\n\nfunc (s *Schemas) MustCustomizeType(obj interface{}, f func(*Schema)) *Schemas {\n\tname := s.getTypeName(reflect.TypeOf(obj))\n\tschema := s.Schema(name)\n\tif schema == nil {\n\t\tpanic(\"Failed to find schema \" + name)\n\t}\n\n\tf(schema)\n\n\treturn s\n}\n\nfunc (s *Schemas) assignMappers(schema *Schema) error {\n\tif schema.Mapper != nil {\n\t\treturn nil\n\t}\n\n\tmappers := s.mapper(schema.ID)\n\tif canList(schema) {\n\t\tif s.DefaultMapper != nil {\n\t\t\tmappers = append([]Mapper{s.DefaultMapper()}, mappers...)\n\t\t}\n\t\tif s.DefaultPostMapper != nil {\n\t\t\tmappers = append(mappers, s.DefaultPostMapper())\n\t\t}\n\t}\n\n\tif len(mappers) > 0 {\n\t\tschema.InternalSchema = schema.DeepCopy()\n\t}\n\n\tmapper := &typeMapper{\n\t\tMappers: mappers,\n\t\troot:    canList(schema),\n\t}\n\n\tif err := mapper.ModifySchema(schema, s); err != nil {\n\t\treturn err\n\t}\n\n\tschema.Mapper = mapper\n\treturn nil\n}\n\nfunc canList(schema *Schema) bool {\n\treturn slice.ContainsString(schema.CollectionMethods, \"GET\")\n}\n\nfunc (s *Schemas) importType(t reflect.Type, overrides ...reflect.Type) (*Schema, error) {\n\ttypeName := s.getTypeName(t)\n\n\texisting := s.Schema(typeName)\n\tif existing != nil {\n\t\treturn existing, nil\n\t}\n\n\tif s, ok := s.processingTypes[t]; ok {\n\t\tlogrus.Debugf(\"Returning half built schema %s for %v\", typeName, t)\n\t\treturn s, nil\n\t}\n\n\tlogrus.Tracef(\"Inspecting schema %s for %v\", typeName, t)\n\n\tschema, err := s.newSchemaFromType(t, typeName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, override := range overrides {\n\t\tif err := s.readFields(schema, override); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif err := s.assignMappers(schema); err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = s.AddSchema(*schema)\n\treturn s.Schema(schema.ID), err\n}\n\nfunc jsonName(f reflect.StructField) string {\n\treturn strings.SplitN(f.Tag.Get(\"json\"), \",\", 2)[0]\n}\n\nfunc k8sType(field reflect.StructField) bool {\n\treturn field.Type.Name() == \"TypeMeta\" &&\n\t\tstrings.HasSuffix(field.Type.PkgPath(), \"k8s.io/apimachinery/pkg/apis/meta/v1\")\n}\n\nfunc k8sObject(field reflect.StructField) bool {\n\treturn field.Type.Name() == \"ObjectMeta\" &&\n\t\tstrings.HasSuffix(field.Type.PkgPath(), \"k8s.io/apimachinery/pkg/apis/meta/v1\")\n}\n\nfunc (s *Schemas) readFields(schema *Schema, t reflect.Type) error {\n\thasType := false\n\thasMeta := false\n\n\tfor i := 0; i < t.NumField(); i++ {\n\t\tfield := t.Field(i)\n\n\t\tif field.PkgPath != \"\" {\n\t\t\t// unexported field\n\t\t\tcontinue\n\t\t}\n\n\t\tjsonName := jsonName(field)\n\t\tif jsonName == \"-\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tif field.Anonymous && jsonName == \"\" && k8sType(field) {\n\t\t\thasType = true\n\t\t}\n\n\t\tif field.Anonymous && jsonName == \"metadata\" && k8sObject(field) {\n\t\t\thasMeta = true\n\t\t}\n\n\t\tif field.Anonymous && jsonName == \"\" {\n\t\t\tt := field.Type\n\t\t\tif t.Kind() == reflect.Ptr {\n\t\t\t\tt = t.Elem()\n\t\t\t}\n\t\t\tif t.Kind() == reflect.Struct {\n\t\t\t\tif err := s.readFields(schema, t); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tfieldName := jsonName\n\t\tif fieldName == \"\" {\n\t\t\tfieldName = convert.LowerTitle(field.Name)\n\t\t\tif strings.HasSuffix(fieldName, \"ID\") {\n\t\t\t\tfieldName = strings.TrimSuffix(fieldName, \"ID\") + \"Id\"\n\t\t\t}\n\t\t}\n\n\t\tif skippedNames[fieldName] {\n\t\t\tlogrus.Debugf(\"Ignoring skip field %s.%s for %v\", schema.ID, fieldName, field)\n\t\t\tcontinue\n\t\t}\n\n\t\tlogrus.Tracef(\"Inspecting field %s.%s for %v\", schema.ID, fieldName, field)\n\n\t\tschemaField := Field{\n\t\t\tCodeName: field.Name,\n\t\t\tCreate:   true,\n\t\t\tUpdate:   true,\n\t\t}\n\n\t\tfieldType := field.Type\n\t\tif fieldType.Kind() == reflect.Ptr {\n\t\t\tschemaField.Nullable = true\n\t\t\tfieldType = fieldType.Elem()\n\t\t} else if fieldType.Kind() == reflect.Bool {\n\t\t\tschemaField.Nullable = false\n\t\t} else if fieldType.Kind() == reflect.Int ||\n\t\t\tfieldType.Kind() == reflect.Uint ||\n\t\t\tfieldType.Kind() == reflect.Uintptr ||\n\t\t\tfieldType.Kind() == reflect.Uint32 ||\n\t\t\tfieldType.Kind() == reflect.Int32 ||\n\t\t\tfieldType.Kind() == reflect.Uint64 ||\n\t\t\tfieldType.Kind() == reflect.Int64 ||\n\t\t\tfieldType.Kind() == reflect.Float32 ||\n\t\t\tfieldType.Kind() == reflect.Float64 {\n\t\t\tschemaField.Nullable = false\n\t\t}\n\n\t\tif err := applyTag(&field, &schemaField); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif schemaField.Type == \"\" {\n\t\t\tinferredType, err := s.determineSchemaType(fieldType)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed inspecting type %s, field %s: %v\", t, fieldName, err)\n\t\t\t}\n\t\t\tschemaField.Type = inferredType\n\t\t}\n\n\t\tif schemaField.Default != nil {\n\t\t\tswitch schemaField.Type {\n\t\t\tcase \"int\":\n\t\t\t\tn, err := convert.ToNumber(schemaField.Default)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tschemaField.Default = n\n\t\t\tcase \"float\":\n\t\t\t\tn, err := convert.ToFloat(schemaField.Default)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tschemaField.Default = n\n\t\t\tcase \"boolean\":\n\t\t\t\tschemaField.Default = convert.ToBool(schemaField.Default)\n\t\t\t}\n\t\t}\n\n\t\tif s.fieldMappers != nil {\n\t\t\tif err := s.processFieldsMappers(t, fieldName, schema, field); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tlogrus.Tracef(\"Setting field %s.%s: %#v\", schema.ID, fieldName, schemaField)\n\t\tschema.ResourceFields[fieldName] = schemaField\n\t}\n\n\tif hasType && hasMeta {\n\t\tdelete(schema.ResourceFields, \"kind\")\n\t\tdelete(schema.ResourceFields, \"apiVersion\")\n\t\tdelete(schema.ResourceFields, \"metadata\")\n\t\tschema.CollectionMethods = []string{\"GET\", \"POST\"}\n\t\tschema.ResourceMethods = []string{\"GET\", \"PUT\", \"DELETE\"}\n\t}\n\n\treturn nil\n}\n\nfunc (s *Schemas) processFieldsMappers(t reflect.Type, fieldName string, schema *Schema, field reflect.StructField) error {\n\tfor _, fieldMapper := range strings.Split(field.Tag.Get(\"mapper\"), \",\") {\n\t\tif fieldMapper == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar (\n\t\t\tname string\n\t\t\topts []string\n\t\t)\n\t\tparts := strings.SplitN(fieldMapper, \"=\", 2)\n\t\tname = parts[0]\n\t\tif len(parts) == 2 {\n\t\t\topts = append(opts, strings.Split(parts[1], \"|\")...)\n\t\t}\n\n\t\tfactory, ok := s.fieldMappers[name]\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"failed to find field mapper [%s] for type [%v]\", name, t)\n\t\t}\n\n\t\ts.AddMapper(schema.ID, factory(fieldName, opts...))\n\t}\n\n\treturn nil\n}\n\nfunc applyTag(structField *reflect.StructField, field *Field) error {\n\tt := structField.Tag.Get(\"wrangler\")\n\tfor _, part := range strings.Split(t, \",\") {\n\t\tif part == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar err error\n\t\tkey, value := getKeyValue(part)\n\n\t\tswitch key {\n\t\tcase \"type\":\n\t\t\tfield.Type = value\n\t\tcase \"codeName\":\n\t\t\tfield.CodeName = value\n\t\tcase \"default\":\n\t\t\tfield.Default = value\n\t\tcase \"nullable\":\n\t\t\tfield.Nullable = true\n\t\tcase \"notnullable\":\n\t\t\tfield.Nullable = false\n\t\tcase \"create\":\n\t\t\tfield.Create = true\n\t\tcase \"nocreate\":\n\t\t\tfield.Create = false\n\t\tcase \"writeOnly\":\n\t\t\tfield.WriteOnly = true\n\t\tcase \"required\":\n\t\t\tfield.Required = true\n\t\tcase \"update\":\n\t\t\tfield.Update = true\n\t\tcase \"noupdate\":\n\t\t\tfield.Update = false\n\t\tcase \"minLength\":\n\t\t\tfield.MinLength, err = toInt(value, structField)\n\t\tcase \"maxLength\":\n\t\t\tfield.MaxLength, err = toInt(value, structField)\n\t\tcase \"min\":\n\t\t\tfield.Min, err = toInt(value, structField)\n\t\tcase \"max\":\n\t\t\tfield.Max, err = toInt(value, structField)\n\t\tcase \"options\":\n\t\t\tfield.Options = split(value)\n\t\t\tif field.Type == \"\" {\n\t\t\t\tfield.Type = \"enum\"\n\t\t\t}\n\t\tcase \"validChars\":\n\t\t\tfield.ValidChars = value\n\t\tcase \"invalidChars\":\n\t\t\tfield.InvalidChars = value\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"invalid tag %s on field %s\", key, structField.Name)\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc toInt(value string, structField *reflect.StructField) (*int64, error) {\n\ti, err := strconv.ParseInt(value, 10, 64)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid number on field %s: %v\", structField.Name, err)\n\t}\n\treturn &i, nil\n}\n\nfunc split(input string) []string {\n\tvar result []string\n\tfor _, i := range strings.Split(input, \"|\") {\n\t\tfor _, part := range strings.Split(i, \" \") {\n\t\t\tpart = strings.TrimSpace(part)\n\t\t\tif len(part) > 0 {\n\t\t\t\tresult = append(result, part)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result\n}\n\nfunc getKeyValue(input string) (string, string) {\n\tvar (\n\t\tkey, value string\n\t)\n\tparts := strings.SplitN(input, \"=\", 2)\n\tkey = parts[0]\n\tif len(parts) > 1 {\n\t\tvalue = parts[1]\n\t}\n\n\treturn key, value\n}\n\nfunc deRef(p reflect.Type) reflect.Type {\n\tif p.Kind() == reflect.Ptr {\n\t\treturn p.Elem()\n\t}\n\treturn p\n}\n\nfunc (s *Schemas) determineSchemaType(t reflect.Type) (string, error) {\n\tswitch t.Kind() {\n\tcase reflect.Uint8:\n\t\treturn \"byte\", nil\n\tcase reflect.Bool:\n\t\treturn \"boolean\", nil\n\tcase reflect.Int, reflect.Int32, reflect.Uint, reflect.Uintptr, reflect.Uint32, reflect.Uint64, reflect.Int64:\n\t\treturn \"int\", nil\n\tcase reflect.Float32, reflect.Float64:\n\t\treturn \"float\", nil\n\tcase reflect.Interface:\n\t\treturn \"json\", nil\n\tcase reflect.Map:\n\t\tsubType, err := s.determineSchemaType(deRef(t.Elem()))\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn fmt.Sprintf(\"map[%s]\", subType), nil\n\tcase reflect.Slice:\n\t\tsubType, err := s.determineSchemaType(deRef(t.Elem()))\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif subType == \"byte\" {\n\t\t\treturn \"base64\", nil\n\t\t}\n\t\treturn fmt.Sprintf(\"array[%s]\", subType), nil\n\tcase reflect.String:\n\t\treturn \"string\", nil\n\tcase reflect.Struct:\n\t\tif t.Name() == \"Time\" {\n\t\t\treturn \"date\", nil\n\t\t}\n\t\tif t.Name() == \"IntOrString\" {\n\t\t\treturn \"intOrString\", nil\n\t\t}\n\t\tif t.Name() == \"Quantity\" {\n\t\t\treturn \"string\", nil\n\t\t}\n\t\tschema, err := s.importType(t)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif t.Name() == \"Duration\" && strings.Contains(schema.PkgName, \"k8s.io/apimachinery/pkg/apis/meta/v1\") {\n\t\t\treturn \"string\", nil\n\t\t}\n\t\treturn schema.ID, nil\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unknown type kind %s\", t.Kind())\n\t}\n\n}\n"
  },
  {
    "path": "pkg/schemas/schemas.go",
    "content": "package schemas\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n\t\"github.com/rancher/wrangler/v3/pkg/merr\"\n\t\"github.com/rancher/wrangler/v3/pkg/name\"\n)\n\ntype SchemasInitFunc func(*Schemas) *Schemas\n\ntype MapperFactory func() Mapper\n\ntype FieldMapperFactory func(fieldName string, args ...string) Mapper\n\ntype Schemas struct {\n\tsync.Mutex\n\tprocessingTypes   map[reflect.Type]*Schema\n\ttypeNames         map[reflect.Type]string\n\tschemasByID       map[string]*Schema\n\tmappers           map[string][]Mapper\n\tembedded          map[string]*Schema\n\tfieldMappers      map[string]FieldMapperFactory\n\tDefaultMapper     MapperFactory\n\tDefaultPostMapper MapperFactory\n\tschemas           []*Schema\n}\n\nfunc EmptySchemas() *Schemas {\n\ts, _ := NewSchemas()\n\treturn s\n}\n\nfunc NewSchemas(schemas ...*Schemas) (*Schemas, error) {\n\tvar (\n\t\terrs []error\n\t)\n\n\ts := &Schemas{\n\t\tprocessingTypes: map[reflect.Type]*Schema{},\n\t\ttypeNames:       map[reflect.Type]string{},\n\t\tschemasByID:     map[string]*Schema{},\n\t\tmappers:         map[string][]Mapper{},\n\t\tembedded:        map[string]*Schema{},\n\t}\n\n\tfor _, schemas := range schemas {\n\t\tif _, err := s.AddSchemas(schemas); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\n\treturn s, merr.NewErrors(errs...)\n}\n\nfunc (s *Schemas) Init(initFunc SchemasInitFunc) *Schemas {\n\treturn initFunc(s)\n}\n\nfunc (s *Schemas) MustAddSchemas(schema *Schemas) *Schemas {\n\ts, err := s.AddSchemas(schema)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn s\n}\n\nfunc (s *Schemas) AddSchemas(schema *Schemas) (*Schemas, error) {\n\tvar errs []error\n\tfor _, schema := range schema.Schemas() {\n\t\tif err := s.AddSchema(*schema); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\treturn s, merr.NewErrors(errs...)\n}\n\nfunc (s *Schemas) RemoveSchema(schema Schema) *Schemas {\n\ts.Lock()\n\tdefer s.Unlock()\n\treturn s.doRemoveSchema(schema)\n}\n\nfunc (s *Schemas) doRemoveSchema(schema Schema) *Schemas {\n\tdelete(s.schemasByID, schema.ID)\n\treturn s\n}\n\nfunc (s *Schemas) MustAddSchema(schema Schema) *Schemas {\n\terr := s.AddSchema(schema)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn s\n}\n\nfunc (s *Schemas) AddSchema(schema Schema) error {\n\ts.Lock()\n\tdefer s.Unlock()\n\treturn s.doAddSchema(schema)\n}\n\nfunc (s *Schemas) doAddSchema(schema Schema) error {\n\tif err := s.setupDefaults(&schema); err != nil {\n\t\treturn err\n\t}\n\n\texisting, ok := s.schemasByID[schema.ID]\n\tif ok {\n\t\t*existing = schema\n\t} else {\n\t\ts.schemasByID[schema.ID] = &schema\n\t\ts.schemas = append(s.schemas, &schema)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Schemas) setupDefaults(schema *Schema) (err error) {\n\tif schema.ID == \"\" {\n\t\treturn fmt.Errorf(\"ID is not set on schema: %v\", schema)\n\t}\n\tif schema.PluralName == \"\" {\n\t\tschema.PluralName = name.GuessPluralName(schema.ID)\n\t}\n\tif schema.CodeName == \"\" {\n\t\tschema.CodeName = convert.Capitalize(schema.ID)\n\t}\n\tif schema.CodeNamePlural == \"\" {\n\t\tschema.CodeNamePlural = name.GuessPluralName(schema.CodeName)\n\t}\n\tif err := s.assignMappers(schema); err != nil {\n\t\treturn err\n\t}\n\n\treturn\n}\n\nfunc (s *Schemas) AddFieldMapper(name string, factory FieldMapperFactory) *Schemas {\n\tif s.fieldMappers == nil {\n\t\ts.fieldMappers = map[string]FieldMapperFactory{}\n\t}\n\ts.fieldMappers[name] = factory\n\treturn s\n}\n\nfunc (s *Schemas) AddMapper(schemaID string, mapper Mapper) *Schemas {\n\ts.mappers[schemaID] = append(s.mappers[schemaID], mapper)\n\treturn s\n}\n\nfunc (s *Schemas) Schemas() []*Schema {\n\treturn s.schemas\n}\n\nfunc (s *Schemas) SchemasByID() map[string]*Schema {\n\treturn s.schemasByID\n}\n\nfunc (s *Schemas) mapper(schemaID string) []Mapper {\n\treturn s.mappers[schemaID]\n}\n\nfunc (s *Schemas) Schema(name string) *Schema {\n\treturn s.doSchema(name, true)\n}\n\nfunc (s *Schemas) doSchema(name string, lock bool) *Schema {\n\tif lock {\n\t\ts.Lock()\n\t}\n\tschema, ok := s.schemasByID[name]\n\tif lock {\n\t\ts.Unlock()\n\t}\n\tif ok {\n\t\treturn schema\n\t}\n\n\tfor _, check := range s.schemas {\n\t\tif strings.EqualFold(check.ID, name) || strings.EqualFold(check.PluralName, name) {\n\t\t\treturn check\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *Schema) MustCustomizeField(name string, f func(f Field) Field) *Schema {\n\tfield, ok := s.ResourceFields[name]\n\tif !ok {\n\t\tpanic(\"Failed to find field \" + name + \" on schema \" + s.ID)\n\t}\n\ts.ResourceFields[name] = f(field)\n\treturn s\n}\n"
  },
  {
    "path": "pkg/schemas/types.go",
    "content": "package schemas\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n)\n\ntype Schema struct {\n\tID                  string                 `json:\"-\"`\n\tDescription         string                 `json:\"description,omitempty\"`\n\tCodeName            string                 `json:\"-\"`\n\tCodeNamePlural      string                 `json:\"-\"`\n\tPkgName             string                 `json:\"-\"`\n\tPluralName          string                 `json:\"pluralName,omitempty\"`\n\tResourceMethods     []string               `json:\"resourceMethods,omitempty\"`\n\tResourceFields      map[string]Field       `json:\"resourceFields\"`\n\tResourceActions     map[string]Action      `json:\"resourceActions,omitempty\"`\n\tCollectionMethods   []string               `json:\"collectionMethods,omitempty\"`\n\tCollectionFields    map[string]Field       `json:\"collectionFields,omitempty\"`\n\tCollectionActions   map[string]Action      `json:\"collectionActions,omitempty\"`\n\tAttributes          map[string]interface{} `json:\"attributes,omitempty\"`\n\tResourcePermissions ResourcePermissions    `json:\"resourcePermissions,omitempty\"`\n\n\tInternalSchema *Schema `json:\"-\"`\n\tMapper         Mapper  `json:\"-\"`\n}\n\nfunc (s *Schema) DeepCopy() *Schema {\n\tr := *s\n\n\tif s.ResourceFields != nil {\n\t\tr.ResourceFields = map[string]Field{}\n\t\tfor k, v := range s.ResourceFields {\n\t\t\tr.ResourceFields[k] = v\n\t\t}\n\t}\n\n\tif s.ResourceActions != nil {\n\t\tr.ResourceActions = map[string]Action{}\n\t\tfor k, v := range s.ResourceActions {\n\t\t\tr.ResourceActions[k] = v\n\t\t}\n\t}\n\n\tif s.CollectionFields != nil {\n\t\tr.CollectionFields = map[string]Field{}\n\t\tfor k, v := range s.CollectionFields {\n\t\t\tr.CollectionFields[k] = v\n\t\t}\n\t}\n\n\tif s.CollectionActions != nil {\n\t\tr.CollectionActions = map[string]Action{}\n\t\tfor k, v := range s.CollectionActions {\n\t\t\tr.CollectionActions[k] = v\n\t\t}\n\t}\n\n\tif s.Attributes != nil {\n\t\tr.Attributes = map[string]interface{}{}\n\t\tfor k, v := range s.Attributes {\n\t\t\tr.Attributes[k] = v\n\t\t}\n\t}\n\n\tif s.ResourcePermissions != nil {\n\t\tr.ResourcePermissions = make(ResourcePermissions)\n\t\tfor res, perms := range s.ResourcePermissions {\n\t\t\tpermCopy := make(ResourceVerbs)\n\t\t\tfor verb, url := range perms {\n\t\t\t\tpermCopy[verb] = url\n\t\t\t}\n\t\t\tr.ResourcePermissions[res] = permCopy\n\t\t}\n\t}\n\n\tif s.InternalSchema != nil {\n\t\tr.InternalSchema = r.InternalSchema.DeepCopy()\n\t}\n\n\treturn &r\n}\n\nfunc SetHasObservedGeneration(s *Schema, value bool) {\n\tif s == nil {\n\t\treturn\n\t}\n\tif s.Attributes == nil {\n\t\ts.Attributes = map[string]interface{}{}\n\t}\n\ts.Attributes[\"hasObservedGeneration\"] = value\n}\n\nfunc HasObservedGeneration(s *Schema) bool {\n\tif s == nil || s.Attributes == nil {\n\t\treturn false\n\t}\n\treturn convert.ToBool(s.Attributes[\"hasObservedGeneration\"])\n}\n\ntype Field struct {\n\tType         string      `json:\"type,omitempty\"`\n\tDefault      interface{} `json:\"default,omitempty\"`\n\tNullable     bool        `json:\"nullable,omitempty\"`\n\tCreate       bool        `json:\"create\"`\n\tWriteOnly    bool        `json:\"writeOnly,omitempty\"`\n\tRequired     bool        `json:\"required,omitempty\"`\n\tUpdate       bool        `json:\"update\"`\n\tMinLength    *int64      `json:\"minLength,omitempty\"`\n\tMaxLength    *int64      `json:\"maxLength,omitempty\"`\n\tMin          *int64      `json:\"min,omitempty\"`\n\tMax          *int64      `json:\"max,omitempty\"`\n\tOptions      []string    `json:\"options,omitempty\"`\n\tValidChars   string      `json:\"validChars,omitempty\"`\n\tInvalidChars string      `json:\"invalidChars,omitempty\"`\n\tDescription  string      `json:\"description,omitempty\"`\n\tCodeName     string      `json:\"-\"`\n}\n\ntype Action struct {\n\tInput  string `json:\"input,omitempty\"`\n\tOutput string `json:\"output,omitempty\"`\n}\n\ntype ResourcePermissions map[string]ResourceVerbs\n\ntype ResourceVerbs map[string]string\n"
  },
  {
    "path": "pkg/schemas/validation/error.go",
    "content": "package validation\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nvar (\n\tUnauthorized     = ErrorCode{\"Unauthorized\", 401}\n\tPermissionDenied = ErrorCode{\"PermissionDenied\", 403}\n\tNotFound         = ErrorCode{\"NotFound\", 404}\n\tMethodNotAllowed = ErrorCode{\"MethodNotAllowed\", 405}\n\tConflict         = ErrorCode{\"Conflict\", 409}\n\n\tInvalidDateFormat  = ErrorCode{\"InvalidDateFormat\", 422}\n\tInvalidFormat      = ErrorCode{\"InvalidFormat\", 422}\n\tInvalidReference   = ErrorCode{\"InvalidReference\", 422}\n\tNotNullable        = ErrorCode{\"NotNullable\", 422}\n\tNotUnique          = ErrorCode{\"NotUnique\", 422}\n\tMinLimitExceeded   = ErrorCode{\"MinLimitExceeded\", 422}\n\tMaxLimitExceeded   = ErrorCode{\"MaxLimitExceeded\", 422}\n\tMinLengthExceeded  = ErrorCode{\"MinLengthExceeded\", 422}\n\tMaxLengthExceeded  = ErrorCode{\"MaxLengthExceeded\", 422}\n\tInvalidOption      = ErrorCode{\"InvalidOption\", 422}\n\tInvalidCharacters  = ErrorCode{\"InvalidCharacters\", 422}\n\tMissingRequired    = ErrorCode{\"MissingRequired\", 422}\n\tInvalidCSRFToken   = ErrorCode{\"InvalidCSRFToken\", 422}\n\tInvalidAction      = ErrorCode{\"InvalidAction\", 422}\n\tInvalidBodyContent = ErrorCode{\"InvalidBodyContent\", 422}\n\tInvalidType        = ErrorCode{\"InvalidType\", 422}\n\tActionNotAvailable = ErrorCode{\"ActionNotAvailable\", 404}\n\tInvalidState       = ErrorCode{\"InvalidState\", 422}\n\n\tServerError        = ErrorCode{\"ServerError\", 500}\n\tClusterUnavailable = ErrorCode{\"ClusterUnavailable\", 503}\n\n\tErrComplete = errors.New(\"request completed\")\n)\n\ntype ErrorCode struct {\n\tCode   string\n\tStatus int\n}\n\nfunc (e ErrorCode) Error() string {\n\treturn fmt.Sprintf(\"%s %d\", e.Code, e.Status)\n}\n"
  },
  {
    "path": "pkg/schemas/validation/validation.go",
    "content": "package validation\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n\t\"github.com/rancher/wrangler/v3/pkg/schemas\"\n\t\"k8s.io/apimachinery/pkg/util/validation\"\n)\n\nvar (\n\tErrComplexType = errors.New(\"complex type\")\n)\n\nfunc CheckFieldCriteria(fieldName string, field schemas.Field, value interface{}) error {\n\tnumVal, isNum := value.(int64)\n\tstrVal := \"\"\n\thasStrVal := false\n\n\tif value == nil && field.Default != nil {\n\t\tvalue = field.Default\n\t}\n\n\tif value != nil && value != \"\" {\n\t\thasStrVal = true\n\t\tstrVal = fmt.Sprint(value)\n\t}\n\n\tif (value == nil || value == \"\") && !field.Nullable {\n\t\tif field.Default == nil {\n\t\t\treturn NotNullable\n\t\t}\n\t}\n\n\tif isNum {\n\t\tif field.Min != nil && numVal < *field.Min {\n\t\t\treturn MinLimitExceeded\n\t\t}\n\t\tif field.Max != nil && numVal > *field.Max {\n\t\t\treturn MaxLimitExceeded\n\t\t}\n\t}\n\n\tif hasStrVal || value == \"\" {\n\t\tif field.MinLength != nil && int64(len(strVal)) < *field.MinLength {\n\t\t\treturn MinLengthExceeded\n\t\t}\n\t\tif field.MaxLength != nil && int64(len(strVal)) > *field.MaxLength {\n\t\t\treturn MaxLengthExceeded\n\t\t}\n\t}\n\n\tif len(field.Options) > 0 {\n\t\tif hasStrVal || !field.Nullable {\n\t\t\tfound := false\n\t\t\tfor _, option := range field.Options {\n\t\t\t\tif strVal == option {\n\t\t\t\t\tfound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !found {\n\t\t\t\treturn InvalidOption\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(field.ValidChars) > 0 && hasStrVal {\n\t\tfor _, c := range strVal {\n\t\t\tif !strings.ContainsRune(field.ValidChars, c) {\n\t\t\t\treturn InvalidCharacters\n\t\t\t}\n\n\t\t}\n\t}\n\n\tif len(field.InvalidChars) > 0 && hasStrVal {\n\t\tif strings.ContainsAny(strVal, field.InvalidChars) {\n\t\t\treturn InvalidCharacters\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc ConvertSimple(fieldType string, value interface{}) (interface{}, error) {\n\tif value == nil {\n\t\treturn value, nil\n\t}\n\n\tswitch fieldType {\n\tcase \"json\":\n\t\treturn value, nil\n\tcase \"date\":\n\t\tv := convert.ToString(value)\n\t\tif v == \"\" {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn v, nil\n\tcase \"boolean\":\n\t\treturn convert.ToBool(value), nil\n\tcase \"enum\":\n\t\treturn convert.ToString(value), nil\n\tcase \"int\":\n\t\treturn convert.ToNumber(value)\n\tcase \"float\":\n\t\treturn convert.ToFloat(value)\n\tcase \"password\":\n\t\treturn convert.ToString(value), nil\n\tcase \"string\":\n\t\treturn convert.ToString(value), nil\n\tcase \"dnsLabel\":\n\t\tstr := convert.ToString(value)\n\t\tif str == \"\" {\n\t\t\treturn \"\", nil\n\t\t}\n\t\tif errs := validation.IsDNS1123Label(str); len(errs) != 0 {\n\t\t\treturn nil, InvalidFormat\n\t\t}\n\t\treturn str, nil\n\tcase \"dnsLabelRestricted\":\n\t\tstr := convert.ToString(value)\n\t\tif str == \"\" {\n\t\t\treturn \"\", nil\n\t\t}\n\t\tif errs := validation.IsDNS1035Label(str); len(errs) != 0 {\n\t\t\treturn value, InvalidFormat\n\t\t}\n\t\treturn str, nil\n\tcase \"hostname\":\n\t\tstr := convert.ToString(value)\n\t\tif str == \"\" {\n\t\t\treturn \"\", nil\n\t\t}\n\t\tif errs := validation.IsDNS1123Subdomain(str); len(errs) != 0 {\n\t\t\treturn value, InvalidFormat\n\t\t}\n\t\treturn str, nil\n\tcase \"intOrString\":\n\t\tnum, err := convert.ToNumber(value)\n\t\tif err == nil {\n\t\t\treturn num, nil\n\t\t}\n\t\treturn convert.ToString(value), nil\n\tcase \"base64\":\n\t\treturn convert.ToString(value), nil\n\tcase \"reference\":\n\t\treturn convert.ToString(value), nil\n\t}\n\n\treturn nil, ErrComplexType\n}\n"
  },
  {
    "path": "pkg/schemes/all.go",
    "content": "package schemes\n\nimport (\n\t\"github.com/rancher/lasso/pkg/scheme\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n)\n\nvar (\n\tAll                = scheme.All\n\tlocalSchemeBuilder = runtime.NewSchemeBuilder()\n)\n\nfunc Register(addToScheme func(*runtime.Scheme) error) error {\n\tlocalSchemeBuilder = append(localSchemeBuilder, addToScheme)\n\treturn addToScheme(All)\n}\n\nfunc AddToScheme(scheme *runtime.Scheme) error {\n\treturn localSchemeBuilder.AddToScheme(scheme)\n}\n"
  },
  {
    "path": "pkg/seen/strings.go",
    "content": "package seen\n\nimport \"k8s.io/apimachinery/pkg/util/sets\"\n\ntype Seen interface {\n\tString(value string) bool\n}\n\nfunc New() Seen {\n\treturn &strings{\n\t\ts: sets.NewString(),\n\t}\n}\n\ntype strings struct {\n\ts sets.String\n}\n\nfunc (s strings) String(value string) bool {\n\tif s.s.Has(value) {\n\t\treturn true\n\t}\n\ts.s.Insert(value)\n\treturn false\n}\n"
  },
  {
    "path": "pkg/signals/signal.go",
    "content": "/*\nCopyright 2017 The Kubernetes Authors.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage signals\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"os/signal\"\n\n\t\"github.com/sirupsen/logrus\"\n)\n\nvar onlyOneSignalHandler = make(chan struct{})\nvar shutdownHandler chan os.Signal\n\n// SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned\n// which is closed on one of these signals. If a second signal is caught, the program\n// is terminated with exit code 1.\n// Only one of SetupSignalContext and SetupSignalHandler should be called, and only can\n// be called once.\nfunc SetupSignalHandler() <-chan struct{} {\n\treturn SetupSignalContext().Done()\n}\n\n// SetupSignalContext is same as SetupSignalHandler, but a context.Context is returned.\n// Only one of SetupSignalContext and SetupSignalHandler should be called, and only can\n// be called once.\nfunc SetupSignalContext() context.Context {\n\tclose(onlyOneSignalHandler) // panics when called twice\n\n\tshutdownHandler = make(chan os.Signal, 2)\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tsignal.Notify(shutdownHandler, shutdownSignals...)\n\tgo func() {\n\t\ts := <-shutdownHandler\n\t\tlogrus.Warnf(\"signal received: %q, canceling context...\", s)\n\t\tcancel()\n\t\ts = <-shutdownHandler\n\t\tlogrus.Warnf(\"second signal received: %q, exiting...\", s)\n\t\tos.Exit(1) // second signal. Exit directly.\n\t}()\n\n\treturn ctx\n}\n\n// RequestShutdown emulates a received event that is considered as shutdown signal (SIGTERM/SIGINT)\n// This returns whether a handler was notified\nfunc RequestShutdown() bool {\n\tif shutdownHandler != nil {\n\t\tselect {\n\t\tcase shutdownHandler <- shutdownSignals[0]:\n\t\t\treturn true\n\t\tdefault:\n\t\t}\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "pkg/signals/signal_posix.go",
    "content": "//go:build !windows\n// +build !windows\n\n/*\nCopyright 2017 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage signals\n\nimport (\n\t\"os\"\n\t\"syscall\"\n)\n\nvar shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM}\n"
  },
  {
    "path": "pkg/signals/signal_windows.go",
    "content": "/*\nCopyright 2017 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage signals\n\nimport (\n\t\"os\"\n)\n\nvar shutdownSignals = []os.Signal{os.Interrupt}\n"
  },
  {
    "path": "pkg/slice/contains.go",
    "content": "package slice\n\nfunc ContainsString(slice []string, item string) bool {\n\tfor _, j := range slice {\n\t\tif j == item {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc StringsEqual(left, right []string) bool {\n\tif len(left) != len(right) {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(left); i++ {\n\t\tif left[i] != right[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "pkg/start/all.go",
    "content": "package start\n\nimport (\n\t\"context\"\n\n\t\"golang.org/x/sync/errgroup\"\n)\n\ntype Starter interface {\n\tSync(ctx context.Context) error\n\tStart(ctx context.Context, threadiness int) error\n}\n\nfunc All(ctx context.Context, threadiness int, starters ...Starter) error {\n\tif err := Sync(ctx, starters...); err != nil {\n\t\treturn err\n\t}\n\treturn Start(ctx, threadiness, starters...)\n}\n\nfunc Sync(ctx context.Context, starters ...Starter) error {\n\teg, _ := errgroup.WithContext(ctx)\n\tfor _, starter := range starters {\n\t\tfunc(starter Starter) {\n\t\t\teg.Go(func() error {\n\t\t\t\treturn starter.Sync(ctx)\n\t\t\t})\n\t\t}(starter)\n\t}\n\treturn eg.Wait()\n}\n\nfunc Start(ctx context.Context, threadiness int, starters ...Starter) error {\n\tfor _, starter := range starters {\n\t\tif err := starter.Start(ctx, threadiness); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/stringset/stringset.go",
    "content": "package stringset\n\nvar empty struct{}\n\n// Set is an exceptionally simple `set` implementation for strings.\n// It is not threadsafe, but can be used in place of a simple `map[string]struct{}`\n// as long as you don't want to do too much with it.\ntype Set struct {\n\tm map[string]struct{}\n}\n\nfunc (s *Set) Add(ss ...string) {\n\tif s.m == nil {\n\t\ts.m = make(map[string]struct{}, len(ss))\n\t}\n\tfor _, k := range ss {\n\t\ts.m[k] = empty\n\t}\n}\n\nfunc (s *Set) Delete(ss ...string) {\n\tif s.m == nil {\n\t\treturn\n\t}\n\tfor _, k := range ss {\n\t\tdelete(s.m, k)\n\t}\n}\n\nfunc (s *Set) Has(ss string) bool {\n\tif s.m == nil {\n\t\treturn false\n\t}\n\t_, ok := s.m[ss]\n\treturn ok\n}\n\nfunc (s *Set) Len() int {\n\treturn len(s.m)\n}\n\nfunc (s *Set) Values() []string {\n\ti := 0\n\tkeys := make([]string, len(s.m))\n\tfor key := range s.m {\n\t\tkeys[i] = key\n\t\ti++\n\t}\n\n\treturn keys\n}\n"
  },
  {
    "path": "pkg/stringset/stringset_test.go",
    "content": "package stringset\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc stringPtr(s string) *string {\n\treturn &s\n}\n\nfunc Test_Set(t *testing.T) {\n\ttests := []struct {\n\t\tname          string\n\t\taddStrings    [][]string\n\t\tdeleteStrings [][]string\n\t\thasString     *string\n\t\tmissingString *string\n\t\tfinalStrings  []string\n\t\twantLen       int\n\t}{\n\t\t{\n\t\t\tname:          \"test 1\",\n\t\t\taddStrings:    [][]string{},\n\t\t\tdeleteStrings: [][]string{},\n\t\t\thasString:     nil,\n\t\t\tmissingString: stringPtr(\"bar\"),\n\t\t\tfinalStrings:  []string{},\n\t\t\twantLen:       0,\n\t\t},\n\t\t{\n\t\t\tname:          \"test 2\",\n\t\t\taddStrings:    [][]string{{\"foo\"}},\n\t\t\tdeleteStrings: [][]string{},\n\t\t\thasString:     stringPtr(\"foo\"),\n\t\t\tmissingString: stringPtr(\"bar\"),\n\t\t\tfinalStrings:  []string{\"foo\"},\n\t\t\twantLen:       1,\n\t\t},\n\t\t{\n\t\t\tname:          \"test 3\",\n\t\t\taddStrings:    [][]string{{\"foo\", \"bar\", \"baz\"}, {\"bar\", \"baz\"}, {\"bop\"}},\n\t\t\tdeleteStrings: [][]string{{\"foo\", \"baz\"}},\n\t\t\thasString:     stringPtr(\"bar\"),\n\t\t\tmissingString: stringPtr(\"foo\"),\n\t\t\tfinalStrings:  []string{\"bar\", \"bop\"},\n\t\t\twantLen:       2,\n\t\t},\n\t\t{\n\t\t\tname:          \"test 4\",\n\t\t\taddStrings:    [][]string{{\"foo\"}, {\"\"}, {\"bar\"}},\n\t\t\tdeleteStrings: [][]string{{\"bar\"}},\n\t\t\thasString:     stringPtr(\"\"),\n\t\t\tmissingString: stringPtr(\"bar\"),\n\t\t\tfinalStrings:  []string{\"foo\", \"\"},\n\t\t\twantLen:       2,\n\t\t},\n\t\t{\n\t\t\tname:          \"test 5\",\n\t\t\taddStrings:    [][]string{{\"foo\"}, {\"foo\", \"bar\"}, {\"foo\", \"bar\", \"baz\"}},\n\t\t\tdeleteStrings: [][]string{{\"foo\"}, {\"bar\", \"baz\"}},\n\t\t\thasString:     nil,\n\t\t\tmissingString: stringPtr(\"foo\"),\n\t\t\tfinalStrings:  []string{},\n\t\t\twantLen:       0,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tset := Set{}\n\t\t\tfor _, ss := range tt.addStrings {\n\t\t\t\tset.Add(ss...)\n\t\t\t}\n\t\t\tfor _, s := range tt.deleteStrings {\n\t\t\t\tset.Delete(s...)\n\t\t\t}\n\n\t\t\tif tt.hasString != nil {\n\t\t\t\thasString := set.Has(*tt.hasString)\n\t\t\t\tassert.True(t, hasString, \"HasString(%#v)\", tt.hasString)\n\t\t\t}\n\n\t\t\tif tt.missingString != nil {\n\t\t\t\tmissingString := set.Has(*tt.missingString)\n\t\t\t\tassert.False(t, missingString, \"HasString(%#v)\", tt.missingString)\n\t\t\t}\n\n\t\t\tgotStrings := set.Values()\n\t\t\tassert.ElementsMatchf(t, tt.finalStrings, gotStrings, \"Values() = %v, want %v\", gotStrings, tt.finalStrings)\n\n\t\t\tgotLen := set.Len()\n\t\t\tassert.Equal(t, tt.wantLen, gotLen, \"Len() = %v, want %v\", gotLen, tt.wantLen)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/summary/capi_cluster_test.go",
    "content": "package summary\n\nimport (\n\t\"testing\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestIsCAPICluster(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tobj      data.Object\n\t\texpected bool\n\t}{\n\t\t{\n\t\t\tname: \"CAPI v1beta2 Cluster\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"Cluster\",\n\t\t\t},\n\t\t\texpected: true,\n\t\t},\n\t\t{\n\t\t\tname: \"CAPI v1beta1 Cluster\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta1\",\n\t\t\t\t\"kind\":       \"Cluster\",\n\t\t\t},\n\t\t\texpected: true,\n\t\t},\n\t\t{\n\t\t\tname: \"CAPI v1beta2 Machine (not Cluster)\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"Machine\",\n\t\t\t},\n\t\t\texpected: false,\n\t\t},\n\t\t{\n\t\t\tname: \"CAPI v1beta2 MachineSet (not Cluster)\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"MachineSet\",\n\t\t\t},\n\t\t\texpected: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Rancher management Cluster (not CAPI)\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"management.cattle.io/v3\",\n\t\t\t\t\"kind\":       \"Cluster\",\n\t\t\t},\n\t\t\texpected: false,\n\t\t},\n\t\t{\n\t\t\tname: \"non-CAPI kind\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"apps/v1\",\n\t\t\t\t\"kind\":       \"Deployment\",\n\t\t\t},\n\t\t\texpected: false,\n\t\t},\n\t\t{\n\t\t\tname:     \"empty object\",\n\t\t\tobj:      data.Object{},\n\t\t\texpected: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tassert.Equal(t, tt.expected, isCAPICluster(tt.obj))\n\t\t})\n\t}\n}\n\n// makeClusterObj builds a minimal CAPI Cluster data.Object with the given\n// worker replica fields under status.workers. Use -1 to omit a field.\nfunc makeClusterObj(desiredReplicas, replicas, readyReplicas, availableReplicas, upToDateReplicas int64) data.Object {\n\tobj := data.Object{\n\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\"kind\":       \"Cluster\",\n\t\t\"status\": map[string]interface{}{\n\t\t\t\"workers\": map[string]interface{}{},\n\t\t},\n\t}\n\tworkers := obj[\"status\"].(map[string]interface{})[\"workers\"].(map[string]interface{})\n\tif desiredReplicas >= 0 {\n\t\tworkers[\"desiredReplicas\"] = desiredReplicas\n\t}\n\tif replicas >= 0 {\n\t\tworkers[\"replicas\"] = replicas\n\t}\n\tif readyReplicas >= 0 {\n\t\tworkers[\"readyReplicas\"] = readyReplicas\n\t}\n\tif availableReplicas >= 0 {\n\t\tworkers[\"availableReplicas\"] = availableReplicas\n\t}\n\tif upToDateReplicas >= 0 {\n\t\tworkers[\"upToDateReplicas\"] = upToDateReplicas\n\t}\n\treturn obj\n}\n\nfunc TestCheckCAPIClusterTransitioning(t *testing.T) {\n\ttests := []struct {\n\t\tname             string\n\t\tobj              data.Object\n\t\tconditions       []Condition\n\t\texpectedState    string\n\t\texpectedTransit  bool\n\t\texpectedError    bool\n\t\texpectedMessages []string\n\t}{\n\t\t// --- Priority 1: Deleting ---\n\t\t{\n\t\t\tname: \"Deleting=True takes absolute priority over everything\",\n\t\t\tobj:  makeClusterObj(1, 1, 1, 1, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"False\", \"NotAvailable\", \"* Deleting: ...\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"* MachineDeployment md: Scaling down from 1 to 0 replicas\"),\n\t\t\t\tNewCondition(\"Deleting\", \"True\", \"WaitingForWorkersDeletion\", \"* MachineDeployments: md\\n* MachineSets: ms\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"deleting\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"waiting for workers deletion\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Deleting=True with empty message\",\n\t\t\tobj:  makeClusterObj(1, 1, 1, 1, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"True\", \"Deleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"deleting\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"Deleting=False does not trigger removing\",\n\t\t\tobj:  makeClusterObj(2, 2, 2, 2, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"True\", \"Available\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\n\t\t// --- Priority 2: Paused ---\n\t\t{\n\t\t\tname: \"Paused=True takes priority over scaling and Available\",\n\t\t\tobj:  makeClusterObj(3, 2, 2, 2, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"False\", \"NotAvailable\", \"something\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"True\", \"ScalingUp\", \"scaling\"),\n\t\t\t\tNewCondition(\"Paused\", \"True\", \"Paused\", \"cluster is paused\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"paused\",\n\t\t\texpectedTransit: true,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"Paused=False is ignored\",\n\t\t\tobj:  makeClusterObj(2, 2, 2, 2, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"True\", \"Available\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\n\t\t// --- Priority 3: Rolling out ---\n\t\t{\n\t\t\tname: \"RollingOut=True takes priority over ScalingDown during rolling upgrade\",\n\t\t\tobj:  makeClusterObj(4, 5, 5, 5, 3),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"True\", \"RollingOut\", \"* MachineDeployment do-check-jiaqi-dow:\\n  * Rolling out 2 not up-to-date replicas\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"* MachineDeployment do-check-jiaqi-dow: Scaling down from 4 to 3 replicas\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"Available\", \"True\", \"Available\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"rollingout\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"rolling out 2 not up-to-date replicas\"},\n\t\t},\n\t\t{\n\t\t\tname: \"RollingOut=True takes priority over ScalingUp during rolling upgrade\",\n\t\t\tobj:  makeClusterObj(4, 4, 3, 3, 2),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"True\", \"RollingOut\", \"* MachineDeployment md:\\n  * Rolling out 2 not up-to-date replicas\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"Available\", \"False\", \"NotAvailable\", \"something\"),\n\t\t\t},\n\t\t\texpectedState:    \"rollingout\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"rolling out 2 not up-to-date replicas\"},\n\t\t},\n\n\t\t// --- Priority 4: ScalingDown ---\n\t\t{\n\t\t\tname: \"ScalingDown=True — message constructed from worker replicas\",\n\t\t\tobj:  makeClusterObj(2, 3, 3, 3, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"True\", \"Available\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"* MachineDeployment md: Scaling down from 2 to 1 replicas\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"updating\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling down from 3 to 2 machines\"},\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingDown detected by replica mismatch only (stale condition)\",\n\t\t\tobj:  makeClusterObj(2, 3, 2, 2, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"True\", \"Available\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"updating\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling down from 3 to 2 machines\"},\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingDown takes priority over ScalingUp when both detectable\",\n\t\t\tobj:  makeClusterObj(1, 3, 0, 0, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"scaling down\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"updating\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling down from 3 to 1 machines\"},\n\t\t},\n\n\t\t// --- Priority 5: ScalingUp ---\n\t\t{\n\t\t\tname: \"ScalingUp=True — scale-up scenario (2→3 workers)\",\n\t\t\tobj:  makeClusterObj(3, 2, 2, 2, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"False\", \"NotAvailable\", \"* WorkersAvailable: insufficient replicas\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"True\", \"ScalingUp\", \"* MachineDeployment md: Scaling up from 1 to 2 replicas\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"updating\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling up from 2 to 3 machines\"},\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingUp detected by replica mismatch (stale condition)\",\n\t\t\tobj:  makeClusterObj(3, 2, 2, 2, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"False\", \"NotAvailable\", \"something\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"updating\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling up from 2 to 3 machines\"},\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingUp during cluster creation (0→1 workers)\",\n\t\t\tobj:  makeClusterObj(1, -1, -1, -1, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"False\", \"NotAvailable\", \"* WorkersAvailable: waiting\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"True\", \"ScalingUp\", \"* MachineDeployment md: Scaling up from 0 to 1 replicas\"),\n\t\t\t\tNewCondition(\"ControlPlaneInitialized\", \"False\", \"NotInitialized\", \"Control plane not yet initialized\"),\n\t\t\t\tNewCondition(\"ControlPlaneAvailable\", \"False\", \"NotAvailable\", \"CP not available\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"updating\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: nil, // no readyReplicas field → no message\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingUp from zero readyReplicas during creation\",\n\t\t\tobj:  makeClusterObj(1, 0, 0, 0, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"False\", \"NotAvailable\", \"not available\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"True\", \"ScalingUp\", \"* MachineDeployment md: Scaling up from 0 to 1 replicas\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"updating\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling up from 0 to 1 machines\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Desired workers exceed ready workers — reports scalingup before Available=False\",\n\t\t\tobj:  makeClusterObj(3, 3, 2, 2, 3),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"False\", \"NotAvailable\", \"* MachineDeployment md: 2 available, 3 required\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"False\", \"NotRollingOut\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"updating\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling up from 2 to 3 machines\"},\n\t\t},\n\n\t\t// --- Priority 6: Available=False ---\n\t\t{\n\t\t\tname: \"Available=False without any scaling → updating\",\n\t\t\tobj:  makeClusterObj(2, 2, 2, 2, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"False\", \"NotAvailable\", \"* RemoteConnectionProbe: Remote connection not established yet\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"updating\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"establishing connection to control plane\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Available=False with empty message → updating with no messages\",\n\t\t\tobj:  makeClusterObj(1, 1, 1, 1, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"False\", \"NotAvailable\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"updating\",\n\t\t\texpectedTransit: true,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"Deleting=True takes priority over RollingOut=True on Cluster\",\n\t\t\tobj:  makeClusterObj(4, 5, 5, 5, 3),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"True\", \"WaitingForWorkersDeletion\", \"cleanup\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"True\", \"RollingOut\", \"* MachineDeployment md:\\n  * Rolling out 2 not up-to-date replicas\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"scaling down\"),\n\t\t\t},\n\t\t\texpectedState:    \"deleting\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"cleanup\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Paused=True takes priority over RollingOut=True on Cluster\",\n\t\t\tobj:  makeClusterObj(4, 5, 5, 5, 3),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"True\", \"Paused\", \"\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"True\", \"RollingOut\", \"* MachineDeployment md:\\n  * Rolling out 2 not up-to-date replicas\"),\n\t\t\t},\n\t\t\texpectedState:   \"paused\",\n\t\t\texpectedTransit: true,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"RollingOut=False does not trigger rollingout — falls through to ScalingDown\",\n\t\t\tobj:  makeClusterObj(3, 4, 4, 4, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"False\", \"NotRollingOut\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"scaling down\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"Available\", \"True\", \"Available\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"updating\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling down from 4 to 3 machines\"},\n\t\t},\n\t\t{\n\t\t\tname: \"RollingOut=True on Cluster with missing upToDateReplicas — no message\",\n\t\t\tobj:  makeClusterObj(4, 5, 5, 5, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"True\", \"RollingOut\", \"* MachineDeployment md:\\n  * Rolling out 2 not up-to-date replicas\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"scaling down\"),\n\t\t\t},\n\t\t\texpectedState:    \"rollingout\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: nil,\n\t\t},\n\n\t\t// --- Priority 7: Steady state / pass through ---\n\t\t{\n\t\t\tname: \"Steady state — all healthy → pass through\",\n\t\t\tobj:  makeClusterObj(2, 2, 2, 2, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"True\", \"Available\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"WorkersAvailable\", \"True\", \"Available\", \"\"),\n\t\t\t\tNewCondition(\"ControlPlaneAvailable\", \"True\", \"Available\", \"\"),\n\t\t\t\tNewCondition(\"WorkerMachinesReady\", \"True\", \"Ready\", \"\"),\n\t\t\t\tNewCondition(\"ControlPlaneMachinesReady\", \"True\", \"Ready\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\n\t\t// --- Edge cases ---\n\t\t{\n\t\t\tname:            \"No conditions → pass through\",\n\t\t\tobj:             makeClusterObj(1, 1, 1, 1, -1),\n\t\t\tconditions:      []Condition{},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"Missing worker replica fields → pass through (no panic)\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"Cluster\",\n\t\t\t},\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Available\", \"True\", \"Available\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingUp=True but no worker replica fields → updating, no message\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"Cluster\",\n\t\t\t},\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"True\", \"ScalingUp\", \"Scaling up\"),\n\t\t\t},\n\t\t\texpectedState:    \"updating\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingDown=True but no worker replica fields → updating, no message\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"Cluster\",\n\t\t\t},\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"Scaling down\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"updating\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"Deleting=True takes priority over active scale-down\",\n\t\t\tobj:  makeClusterObj(1, 1, 1, 1, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"True\", \"WaitingForWorkersDeletion\", \"cleanup in progress\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"Scaling down from 1 to 0\"),\n\t\t\t\tNewCondition(\"Available\", \"False\", \"NotAvailable\", \"not available\"),\n\t\t\t},\n\t\t\texpectedState:    \"deleting\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"cleanup in progress\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Paused=True takes priority over scale-up detected by replicas\",\n\t\t\tobj:  makeClusterObj(3, 1, 1, 1, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"True\", \"Paused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"paused\",\n\t\t\texpectedTransit: true,\n\t\t\texpectedError:   false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult := checkCAPIClusterTransitioning(tt.obj, tt.conditions, Summary{})\n\t\t\tassert.Equal(t, tt.expectedState, result.State, \"state mismatch\")\n\t\t\tassert.Equal(t, tt.expectedTransit, result.Transitioning, \"transitioning mismatch\")\n\t\t\tassert.Equal(t, tt.expectedError, result.Error, \"error mismatch\")\n\t\t\tif tt.expectedMessages != nil {\n\t\t\t\tassert.Equal(t, tt.expectedMessages, result.Message, \"messages mismatch\")\n\t\t\t} else {\n\t\t\t\tassert.Empty(t, result.Message, \"expected no messages\")\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestCheckTransitioning_CAPIClusterDispatch verifies that checkTransitioning\n// dispatches to checkCAPIClusterTransitioning for CAPI Clusters and not to\n// the generic path.\nfunc TestCheckTransitioning_CAPIClusterDispatch(t *testing.T) {\n\t// A CAPI Cluster with ScalingUp=True should get \"updating\" from the\n\t// CAPI Cluster path.\n\tcapiObj := makeClusterObj(2, 1, 1, 1, -1)\n\tconditions := []Condition{\n\t\tNewCondition(\"ScalingUp\", \"True\", \"ScalingUp\", \"* MachineDeployment md: Scaling up from 1 to 2 replicas\"),\n\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\tNewCondition(\"Available\", \"False\", \"NotAvailable\", \"something\"),\n\t}\n\tresult := checkTransitioning(capiObj, conditions, Summary{})\n\tassert.Equal(t, \"updating\", result.State)\n\tassert.True(t, result.Transitioning)\n\n\t// A CAPI Cluster with RollingOut=True during a rolling upgrade should\n\t// get state=\"rollingout\" instead of \"updating\" (from ScalingDown).\n\trollingClusterObj := makeClusterObj(4, 5, 5, 5, 3)\n\trollingConditions := []Condition{\n\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\tNewCondition(\"RollingOut\", \"True\", \"RollingOut\", \"* MachineDeployment md:\\n  * Rolling out 2 not up-to-date replicas\"),\n\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"* MachineDeployment md: Scaling down from 4 to 3 replicas\"),\n\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\tNewCondition(\"Available\", \"True\", \"Available\", \"\"),\n\t}\n\trollingResult := checkTransitioning(rollingClusterObj, rollingConditions, Summary{})\n\tassert.Equal(t, \"rollingout\", rollingResult.State, \"rolling upgrade should produce state=rollingout\")\n\tassert.True(t, rollingResult.Transitioning)\n\tassert.Equal(t, []string{\"rolling out 2 not up-to-date replicas\"}, rollingResult.Message)\n\n\t// A non-CAPI Cluster (e.g. management.cattle.io) should use the generic path.\n\tgenericObj := data.Object{\n\t\t\"apiVersion\": \"management.cattle.io/v3\",\n\t\t\"kind\":       \"Cluster\",\n\t}\n\tgenericConditions := []Condition{\n\t\tNewCondition(\"Available\", \"False\", \"MinimumReplicasUnavailable\", \"not available\"),\n\t}\n\tgenericResult := checkTransitioning(genericObj, genericConditions, Summary{})\n\tassert.Equal(t, \"updating\", genericResult.State)\n\tassert.True(t, genericResult.Transitioning)\n}\n"
  },
  {
    "path": "pkg/summary/capi_machine_test.go",
    "content": "package summary\n\nimport (\n\t\"testing\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestIsCAPIMachine(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tobj      data.Object\n\t\texpected bool\n\t}{\n\t\t{\n\t\t\tname: \"CAPI v1beta2 Machine\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"Machine\",\n\t\t\t},\n\t\t\texpected: true,\n\t\t},\n\t\t{\n\t\t\tname: \"CAPI v1beta1 Machine\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta1\",\n\t\t\t\t\"kind\":       \"Machine\",\n\t\t\t},\n\t\t\texpected: true,\n\t\t},\n\t\t{\n\t\t\tname: \"CAPI v1beta2 Cluster\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"Cluster\",\n\t\t\t},\n\t\t\texpected: false,\n\t\t},\n\t\t{\n\t\t\tname: \"CAPI v1beta2 MachineSet\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"MachineSet\",\n\t\t\t},\n\t\t\texpected: false,\n\t\t},\n\t\t{\n\t\t\tname: \"non-CAPI Machine kind\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"apps/v1\",\n\t\t\t\t\"kind\":       \"Deployment\",\n\t\t\t},\n\t\t\texpected: false,\n\t\t},\n\t\t{\n\t\t\tname:     \"empty object\",\n\t\t\tobj:      data.Object{},\n\t\t\texpected: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tassert.Equal(t, tt.expected, isCAPIMachine(tt.obj))\n\t\t})\n\t}\n}\n\nfunc TestParseMessage(t *testing.T) {\n\ttests := []struct {\n\t\tname           string\n\t\tmessage        string\n\t\texpectedDetail string\n\t\texpectedPrefix string\n\t}{\n\t\t{\n\t\t\tname:           \"empty message\",\n\t\t\tmessage:        \"\",\n\t\t\texpectedDetail: \"\",\n\t\t\texpectedPrefix: \"\",\n\t\t},\n\t\t{\n\t\t\tname:           \"single bullet BootstrapConfigReady\",\n\t\t\tmessage:        \"* BootstrapConfigReady: RKEBootstrap status.initialization.dataSecretCreated is false\",\n\t\t\texpectedDetail: \"RKEBootstrap status.initialization.dataSecretCreated is false\",\n\t\t\texpectedPrefix: \"BootstrapConfigReady\",\n\t\t},\n\t\t{\n\t\t\tname:           \"single bullet InfrastructureReady\",\n\t\t\tmessage:        \"* InfrastructureReady: creating server of kind (DigitaloceanMachine)\",\n\t\t\texpectedDetail: \"creating server of kind (DigitaloceanMachine)\",\n\t\t\texpectedPrefix: \"InfrastructureReady\",\n\t\t},\n\t\t{\n\t\t\tname:           \"single bullet NodeHealthy\",\n\t\t\tmessage:        \"* NodeHealthy: Waiting for Cluster control plane to be initialized\",\n\t\t\texpectedDetail: \"Waiting for Cluster control plane to be initialized\",\n\t\t\texpectedPrefix: \"NodeHealthy\",\n\t\t},\n\t\t{\n\t\t\tname:           \"multiple bullets - takes first only\",\n\t\t\tmessage:        \"* BootstrapConfigReady: bootstrap not ready\\n* InfrastructureReady: infra not ready\\n* NodeHealthy: waiting\",\n\t\t\texpectedDetail: \"bootstrap not ready\",\n\t\t\texpectedPrefix: \"BootstrapConfigReady\",\n\t\t},\n\t\t{\n\t\t\tname:           \"multiple bullets starting with InfrastructureReady\",\n\t\t\tmessage:        \"* InfrastructureReady: creating server...\\n* NodeHealthy: Waiting for CP init\",\n\t\t\texpectedDetail: \"creating server...\",\n\t\t\texpectedPrefix: \"InfrastructureReady\",\n\t\t},\n\t\t{\n\t\t\tname:           \"no bullet prefix\",\n\t\t\tmessage:        \"some plain message\",\n\t\t\texpectedDetail: \"some plain message\",\n\t\t\texpectedPrefix: \"\",\n\t\t},\n\t\t{\n\t\t\tname:           \"bullet without colon separator\",\n\t\t\tmessage:        \"* SomeCondition without colon\",\n\t\t\texpectedDetail: \"SomeCondition without colon\",\n\t\t\texpectedPrefix: \"\",\n\t\t},\n\t\t{\n\t\t\tname:           \"nested sub-bullet: NodeHealthy with empty inline detail\",\n\t\t\tmessage:        \"* NodeHealthy:\\n  * Node.AllConditions: Kubelet stopped posting node status.\",\n\t\t\texpectedDetail: \"Kubelet stopped posting node status.\",\n\t\t\texpectedPrefix: \"NodeHealthy\",\n\t\t},\n\t\t{\n\t\t\tname:           \"nested sub-bullet: multiple sub-bullets takes first\",\n\t\t\tmessage:        \"* NodeHealthy:\\n  * Node.AllConditions: first issue\\n  * Node.OtherCondition: second issue\",\n\t\t\texpectedDetail: \"first issue\",\n\t\t\texpectedPrefix: \"NodeHealthy\",\n\t\t},\n\t\t{\n\t\t\tname:           \"nested sub-bullet: sub-bullet without colon separator\",\n\t\t\tmessage:        \"* NodeHealthy:\\n  * some plain sub-bullet\",\n\t\t\texpectedDetail: \"some plain sub-bullet\",\n\t\t\texpectedPrefix: \"NodeHealthy\",\n\t\t},\n\t\t{\n\t\t\tname:           \"nested sub-bullet: InfrastructureReady with empty inline detail\",\n\t\t\tmessage:        \"* InfrastructureReady:\\n  * SubCondition: infra detail here\",\n\t\t\texpectedDetail: \"infra detail here\",\n\t\t\texpectedPrefix: \"InfrastructureReady\",\n\t\t},\n\t\t{\n\t\t\tname:           \"nested sub-bullet: no sub-bullets found (only non-bullet lines)\",\n\t\t\tmessage:        \"* NodeHealthy:\\n  some non-bullet line\",\n\t\t\texpectedDetail: \"\",\n\t\t\texpectedPrefix: \"NodeHealthy\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tdetail, prefix := parseMessage(tt.message)\n\t\t\tassert.Equal(t, tt.expectedDetail, detail)\n\t\t\tassert.Equal(t, tt.expectedPrefix, prefix)\n\t\t})\n\t}\n}\n\nfunc TestCheckCAPIMachineTransitioning(t *testing.T) {\n\ttests := []struct {\n\t\tname             string\n\t\tconditions       []Condition\n\t\texpectedState    string\n\t\texpectedTransit  bool\n\t\texpectedError    bool\n\t\texpectedMessages []string\n\t}{\n\t\t// --- Priority 1: Deleting ---\n\t\t{\n\t\t\tname: \"Deleting=True takes absolute priority\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Reconciled\", \"Unknown\", \"Waiting\", \"reconciling something\"),\n\t\t\t\tNewCondition(\"Deleting\", \"True\", \"Deleting\", \"machine is being deleted\"),\n\t\t\t\tNewCondition(\"Ready\", \"False\", \"NotReady\", \"* InfrastructureReady: infra not ready\"),\n\t\t\t},\n\t\t\texpectedState:    \"deleting\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"machine is being deleted\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Deleting=True with empty message\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"True\", \"Deleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"deleting\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"Deleting=True with drain message rewrite\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"True\", \"Draining\", \"Drain not completed yet (1/3 nodes drained)\"),\n\t\t\t},\n\t\t\texpectedState:    \"deleting\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Draining node\"},\n\t\t},\n\n\t\t// --- Priority 2: Paused ---\n\t\t{\n\t\t\tname: \"Paused=True takes priority over Reconciled and Ready\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Reconciled\", \"Unknown\", \"Waiting\", \"reconciling\"),\n\t\t\t\tNewCondition(\"Paused\", \"True\", \"Paused\", \"machine is paused\"),\n\t\t\t\tNewCondition(\"Ready\", \"False\", \"NotReady\", \"* InfrastructureReady: infra not ready\"),\n\t\t\t},\n\t\t\texpectedState:   \"paused\",\n\t\t\texpectedTransit: true,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"Paused=False is ignored (normal state)\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Ready\", \"True\", \"Ready\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\n\t\t// --- Priority 3: Reconciled not True ---\n\t\t{\n\t\t\tname: \"Reconciled=Unknown → reconciling with message\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"Unknown\", \"ReadyUnknown\", \"* NodeHealthy: waiting\"),\n\t\t\t\tNewCondition(\"Reconciled\", \"Unknown\", \"Waiting\", \"waiting for agent to check in and apply initial plan\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"reconciling\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"waiting for agent to check in and apply initial plan\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Reconciled=Unknown with empty message\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"Unknown\", \"ReadyUnknown\", \"* NodeHealthy: waiting\"),\n\t\t\t\tNewCondition(\"Reconciled\", \"Unknown\", \"Waiting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"reconciling\",\n\t\t\texpectedTransit: true,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"Reconciled=False → reconciling with error\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"True\", \"Ready\", \"\"),\n\t\t\t\tNewCondition(\"Reconciled\", \"False\", \"ReconcileError\", \"failed to reconcile machine\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"reconciling\",\n\t\t\texpectedTransit:  false,\n\t\t\texpectedError:    true,\n\t\t\texpectedMessages: []string{\"failed to reconcile machine\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Reconciled=False with empty message → reconciling with error, no messages\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"True\", \"Ready\", \"\"),\n\t\t\t\tNewCondition(\"Reconciled\", \"False\", \"ReconcileError\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"reconciling\",\n\t\t\texpectedTransit:  false,\n\t\t\texpectedError:    true,\n\t\t\texpectedMessages: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"Reconciled=Unknown takes priority over Ready=False\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"False\", \"NotReady\", \"* InfrastructureReady: infra not ready\"),\n\t\t\t\tNewCondition(\"Reconciled\", \"Unknown\", \"Waiting\", \"reconciling\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"reconciling\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"reconciling\"},\n\t\t},\n\n\t\t// --- Priority 4: Ready=False ---\n\t\t{\n\t\t\tname: \"Ready=False, first bullet BootstrapConfigReady → waitingforinfrastructure\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"False\", \"NotReady\",\n\t\t\t\t\t\"* BootstrapConfigReady: RKEBootstrap status.initialization.dataSecretCreated is false\\n\"+\n\t\t\t\t\t\t\"* InfrastructureReady: DigitaloceanMachine status.initialization.provisioned is false\\n\"+\n\t\t\t\t\t\t\"* NodeHealthy: Waiting for Cluster control plane to be initialized\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"waitingforinfrastructure\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"Ready=False, first bullet InfrastructureReady → waitingfornoderef\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"False\", \"NotReady\",\n\t\t\t\t\t\"* InfrastructureReady: creating server [fleet-default/prod-jiaqi-pa-qxwqc-9gdk2] of kind (DigitaloceanMachine) for machine prod-jiaqi-pa-qxwqc-9gdk2 in infrastructure provider\\n\"+\n\t\t\t\t\t\t\"* NodeHealthy: Waiting for Cluster control plane to be initialized\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"waitingfornoderef\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"creating server [fleet-default/prod-jiaqi-pa-qxwqc-9gdk2] of kind (DigitaloceanMachine) for machine prod-jiaqi-pa-qxwqc-9gdk2 in infrastructure provider\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Ready=False, unknown first bullet → pass through (no state set)\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"False\", \"NotReady\", \"* SomeOtherCondition: something happened\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"Ready=False, InfrastructureReady detail suppressed when ending with 'status.initialization.provisioned is false'\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"False\", \"NotReady\",\n\t\t\t\t\t\"* InfrastructureReady: DigitaloceanMachine status.initialization.provisioned is false\\n\"+\n\t\t\t\t\t\t\"* NodeHealthy: Waiting for Cluster control plane to be initialized\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"waitingfornoderef\",\n\t\t\texpectedTransit: true,\n\t\t\texpectedError:   false,\n\t\t\t// Detail is suppressed — no messages expected.\n\t\t},\n\t\t{\n\t\t\tname: \"Ready=False with empty message → pass through\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"False\", \"NotReady\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\n\t\t// --- Priority 5: Ready=Unknown ---\n\t\t{\n\t\t\tname: \"Ready=Unknown with NodeHealthy bullet → reconciling\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"Unknown\", \"ReadyUnknown\", \"* NodeHealthy: Waiting for Cluster control plane to be initialized\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"reconciling\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Waiting for Cluster control plane to be initialized\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Ready=Unknown, Reconciled=True → reconciling (rule 5, not rule 3)\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"Unknown\", \"ReadyUnknown\", \"* NodeHealthy: Waiting for Cluster control plane to be initialized\"),\n\t\t\t\tNewCondition(\"Reconciled\", \"True\", \"Reconciled\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"reconciling\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Waiting for Cluster control plane to be initialized\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Ready=Unknown with multiple bullets → reconciling with first bullet detail\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"Unknown\", \"ReadyUnknown\", \"* InfrastructureReady: some issue\\n* NodeHealthy: waiting\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"reconciling\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"some issue\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Ready=Unknown with nested sub-bullet (NodeHealthy empty detail) → reconciling with sub-bullet detail\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"Unknown\", \"ReadyUnknown\", \"* NodeHealthy:\\n  * Node.AllConditions: Kubelet stopped posting node status.\"),\n\t\t\t\tNewCondition(\"Reconciled\", \"True\", \"Reconciled\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"reconciling\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Kubelet stopped posting node status.\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Ready=Unknown with NodeHealthy detail suppressed when ending with 'to report spec.providerID'\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"Unknown\", \"ReadyUnknown\", \"* NodeHealthy: Waiting for Node controller to report spec.providerID\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"reconciling\",\n\t\t\texpectedTransit: true,\n\t\t\texpectedError:   false,\n\t\t\t// Detail is suppressed because it ends with \"to report spec.providerID\".\n\t\t},\n\t\t{\n\t\t\tname: \"Ready=Unknown with empty message → reconciling with no messages\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"Unknown\", \"ReadyUnknown\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"reconciling\",\n\t\t\texpectedTransit: true,\n\t\t\texpectedError:   false,\n\t\t},\n\n\t\t// --- Priority 6: Ready=True ---\n\t\t{\n\t\t\tname: \"Ready=True → pass through (no state set)\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"True\", \"Ready\", \"\"),\n\t\t\t\tNewCondition(\"Reconciled\", \"True\", \"Reconciled\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\n\t\t// --- Edge cases ---\n\t\t{\n\t\t\tname:            \"no conditions → pass through\",\n\t\t\tconditions:      []Condition{},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"no Ready or Reconciled conditions → pass through\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"Reconciled=True is skipped (not a problem state)\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Ready\", \"True\", \"Ready\", \"\"),\n\t\t\t\tNewCondition(\"Reconciled\", \"True\", \"Reconciled\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"Deleting=False does not trigger removing\",\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Ready\", \"True\", \"Ready\", \"\"),\n\t\t\t\tNewCondition(\"Reconciled\", \"True\", \"Reconciled\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult := checkCAPIMachineTransitioning(tt.conditions, Summary{})\n\t\t\tassert.Equal(t, tt.expectedState, result.State)\n\t\t\tassert.Equal(t, tt.expectedTransit, result.Transitioning)\n\t\t\tassert.Equal(t, tt.expectedError, result.Error)\n\t\t\tif tt.expectedMessages != nil {\n\t\t\t\tassert.Equal(t, tt.expectedMessages, result.Message)\n\t\t\t} else {\n\t\t\t\tassert.Empty(t, result.Message)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestCheckTransitioning_CAPIMachineDispatch verifies that checkTransitioning\n// dispatches to checkCAPIMachineTransitioning for CAPI Machines and to\n// checkGenericTransitioning for everything else.\nfunc TestCheckTransitioning_CAPIMachineDispatch(t *testing.T) {\n\t// A CAPI Machine with Reconciled=Unknown should get \"reconciling\" from\n\t// the CAPI path, not the generic TransitioningUnknown path.\n\tcapiObj := data.Object{\n\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\"kind\":       \"Machine\",\n\t}\n\tconditions := []Condition{\n\t\tNewCondition(\"Reconciled\", \"Unknown\", \"Waiting\", \"waiting for agent\"),\n\t\tNewCondition(\"Ready\", \"Unknown\", \"ReadyUnknown\", \"* NodeHealthy: waiting\"),\n\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t}\n\tresult := checkTransitioning(capiObj, conditions, Summary{})\n\tassert.Equal(t, \"reconciling\", result.State)\n\tassert.True(t, result.Transitioning)\n\n\t// A non-CAPI object with Reconciled=Unknown should use the generic path.\n\t// In TransitioningUnknown, Reconciled maps to \"reconciling\" for Unknown status.\n\tgenericObj := data.Object{\n\t\t\"apiVersion\": \"management.cattle.io/v3\",\n\t\t\"kind\":       \"Cluster\",\n\t}\n\tgenericConditions := []Condition{\n\t\tNewCondition(\"Reconciled\", \"Unknown\", \"Waiting\", \"waiting for something\"),\n\t}\n\tgenericResult := checkTransitioning(genericObj, genericConditions, Summary{})\n\tassert.Equal(t, \"reconciling\", genericResult.State)\n\tassert.True(t, genericResult.Transitioning)\n}\n\n// TestCheckTransitioning_NonCAPIMachineUnchanged verifies that the generic\n// path remains unchanged for non-CAPI objects.\nfunc TestCheckTransitioning_NonCAPIMachineUnchanged(t *testing.T) {\n\tobj := data.Object{\n\t\t\"apiVersion\": \"apps/v1\",\n\t\t\"kind\":       \"Deployment\",\n\t}\n\t// Available=False in TransitioningFalse maps to \"updating\"\n\tconditions := []Condition{\n\t\tNewCondition(\"Available\", \"False\", \"MinimumReplicasUnavailable\", \"Deployment does not have minimum availability\"),\n\t}\n\tresult := checkTransitioning(obj, conditions, Summary{})\n\tassert.Equal(t, \"updating\", result.State)\n\tassert.True(t, result.Transitioning)\n\tassert.False(t, result.Error)\n}\n"
  },
  {
    "path": "pkg/summary/capi_machineset_test.go",
    "content": "package summary\n\nimport (\n\t\"testing\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestIsCAPIMachineSet(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tobj      data.Object\n\t\texpected bool\n\t}{\n\t\t{\n\t\t\tname: \"CAPI v1beta2 MachineSet\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"MachineSet\",\n\t\t\t},\n\t\t\texpected: true,\n\t\t},\n\t\t{\n\t\t\tname: \"CAPI v1beta1 MachineSet\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta1\",\n\t\t\t\t\"kind\":       \"MachineSet\",\n\t\t\t},\n\t\t\texpected: true,\n\t\t},\n\t\t{\n\t\t\tname: \"CAPI v1beta2 Machine (not MachineSet)\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"Machine\",\n\t\t\t},\n\t\t\texpected: false,\n\t\t},\n\t\t{\n\t\t\tname: \"CAPI v1beta2 Cluster\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"Cluster\",\n\t\t\t},\n\t\t\texpected: false,\n\t\t},\n\t\t{\n\t\t\tname: \"non-CAPI MachineSet kind\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"apps/v1\",\n\t\t\t\t\"kind\":       \"ReplicaSet\",\n\t\t\t},\n\t\t\texpected: false,\n\t\t},\n\t\t{\n\t\t\tname:     \"empty object\",\n\t\t\tobj:      data.Object{},\n\t\t\texpected: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tassert.Equal(t, tt.expected, isCAPIMachineSet(tt.obj))\n\t\t})\n\t}\n}\n\n// makeMachineSetObj builds a minimal CAPI MachineSet data.Object with the\n// given replica fields. Use -1 to omit a field (simulating it not being set).\nfunc makeMachineSetObj(specReplicas, statusReplicas, readyReplicas int64) data.Object {\n\tobj := data.Object{\n\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\"kind\":       \"MachineSet\",\n\t\t\"spec\":       map[string]interface{}{},\n\t\t\"status\":     map[string]interface{}{},\n\t}\n\tif specReplicas >= 0 {\n\t\tobj[\"spec\"].(map[string]interface{})[\"replicas\"] = specReplicas\n\t}\n\tif statusReplicas >= 0 {\n\t\tobj[\"status\"].(map[string]interface{})[\"replicas\"] = statusReplicas\n\t}\n\tif readyReplicas >= 0 {\n\t\tobj[\"status\"].(map[string]interface{})[\"readyReplicas\"] = readyReplicas\n\t}\n\treturn obj\n}\n\n// makeMachineDeploymentObj builds a minimal CAPI MachineDeployment data.Object\n// with the given replica fields. Use -1 to omit a field.\nfunc makeMachineDeploymentObj(specReplicas, statusReplicas, readyReplicas, upToDateReplicas int64) data.Object {\n\tobj := data.Object{\n\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\"kind\":       \"MachineDeployment\",\n\t\t\"spec\":       map[string]interface{}{},\n\t\t\"status\":     map[string]interface{}{},\n\t}\n\tif specReplicas >= 0 {\n\t\tobj[\"spec\"].(map[string]interface{})[\"replicas\"] = specReplicas\n\t}\n\tif statusReplicas >= 0 {\n\t\tobj[\"status\"].(map[string]interface{})[\"replicas\"] = statusReplicas\n\t}\n\tif readyReplicas >= 0 {\n\t\tobj[\"status\"].(map[string]interface{})[\"readyReplicas\"] = readyReplicas\n\t}\n\tif upToDateReplicas >= 0 {\n\t\tobj[\"status\"].(map[string]interface{})[\"upToDateReplicas\"] = upToDateReplicas\n\t}\n\treturn obj\n}\n\nfunc TestCheckCAPIMachineSetAndDeploymentTransitioning(t *testing.T) {\n\ttests := []struct {\n\t\tname             string\n\t\tobj              data.Object\n\t\tconditions       []Condition\n\t\texpectedState    string\n\t\texpectedTransit  bool\n\t\texpectedError    bool\n\t\texpectedMessages []string\n\t}{\n\t\t// --- Priority 1: Deleting ---\n\t\t{\n\t\t\tname: \"Deleting=True takes absolute priority\",\n\t\t\tobj:  makeMachineSetObj(2, 2, 2),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"True\", \"Deleting\", \"machineset is being deleted\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"True\", \"ScalingUp\", \"Scaling up from 1 to 2 replicas\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"deleting\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"machineset is being deleted\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Deleting=True with empty message\",\n\t\t\tobj:  makeMachineSetObj(2, 2, 2),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"True\", \"Deleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"deleting\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"Deleting=False is ignored\",\n\t\t\tobj:  makeMachineSetObj(2, 2, 2),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"MachinesReady\", \"True\", \"Ready\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\n\t\t// --- Priority 2: Paused ---\n\t\t{\n\t\t\tname: \"Paused=True takes priority over scaling conditions\",\n\t\t\tobj:  makeMachineSetObj(2, 1, 1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Paused\", \"True\", \"Paused\", \"machineset is paused\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"True\", \"ScalingUp\", \"Scaling up from 1 to 2 replicas\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"paused\",\n\t\t\texpectedTransit: true,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"Paused=False is ignored\",\n\t\t\tobj:  makeMachineSetObj(2, 2, 2),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"MachinesReady\", \"True\", \"Ready\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\n\t\t// --- Priority 3: RollingOut ---\n\t\t// RollingOut only applies to MachineDeployment, not MachineSet.\n\t\t{\n\t\t\tname: \"RollingOut=True on MachineDeployment takes priority over ScalingDown\",\n\t\t\tobj:  makeMachineDeploymentObj(3, 4, 3, 2),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"True\", \"RollingOut\", \"Rolling out 2 not up-to-date replicas\\n* DigitaloceanMachine is not up-to-date\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"Scaling down from 4 to 3 replicas\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"rollingout\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"rolling out 2 not up-to-date replicas\"},\n\t\t},\n\t\t{\n\t\t\tname: \"RollingOut=True on MachineDeployment takes priority over ScalingUp by replicas\",\n\t\t\tobj:  makeMachineDeploymentObj(3, 3, 1, 1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"True\", \"RollingOut\", \"Rolling out 2 not up-to-date replicas\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"rollingout\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"rolling out 2 not up-to-date replicas\"},\n\t\t},\n\n\t\t// --- Priority 4: ScalingDown ---\n\t\t{\n\t\t\tname: \"ScalingDown=True — message always constructed from replicas\",\n\t\t\tobj:  makeMachineSetObj(1, 2, 2),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"Scaling down from 2 to 1 replicas\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingdown\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling down from 2 to 1 replicas, waiting for machines to be deleted\"},\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingDown=True with empty condition message — constructs from replicas\",\n\t\t\tobj:  makeMachineSetObj(1, 2, 2),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingdown\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling down from 2 to 1 replicas, waiting for machines to be deleted\"},\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingDown detected by replica mismatch only (stale condition)\",\n\t\t\tobj:  makeMachineSetObj(1, 2, 2),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingdown\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling down from 2 to 1 replicas, waiting for machines to be deleted\"},\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingDown detected by replica mismatch\",\n\t\t\tobj:  makeMachineSetObj(1, 2, 1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"MachinesReady\", \"True\", \"Ready\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingdown\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling down from 2 to 1 replicas, waiting for machines to be deleted\"},\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingDown takes priority over ScalingUp when both detectable\",\n\t\t\tobj:  makeMachineSetObj(1, 3, 0),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"Scaling down from 3 to 1 replicas\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingdown\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling down from 3 to 1 replicas, waiting for machines to be deleted\"},\n\t\t},\n\n\t\t// --- Priority 5: ScalingUp ---\n\t\t{\n\t\t\tname: \"ScalingUp=True — message always constructed from replicas\",\n\t\t\tobj:  makeMachineSetObj(2, 1, 1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"True\", \"ScalingUp\", \"Scaling up from 1 to 2 replicas\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingup\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling up from 1 to 2 replicas, waiting for machines to be ready\"},\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingUp=True with empty condition message — constructs from replicas\",\n\t\t\tobj:  makeMachineSetObj(2, 1, 1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"True\", \"ScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingup\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling up from 1 to 2 replicas, waiting for machines to be ready\"},\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingUp detected by readyReplicas mismatch only\",\n\t\t\tobj:  makeMachineSetObj(2, 1, 1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingup\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling up from 1 to 2 replicas, waiting for machines to be ready\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Spec replicas exceed ready replicas — reports scalingup even when all machines exist\",\n\t\t\tobj:  makeMachineSetObj(2, 2, 1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"MachinesReady\", \"True\", \"Ready\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingup\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling up from 1 to 2 replicas, waiting for machines to be ready\"},\n\t\t},\n\t\t{\n\t\t\tname: \"MachineDeployment with ready replicas below spec — reports scalingup before other conditions\",\n\t\t\tobj:  makeMachineDeploymentObj(3, 3, 2, 3),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"False\", \"NotRollingOut\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"Available\", \"False\", \"NotAvailable\", \"2 available replicas, at least 3 required\"),\n\t\t\t\tNewCondition(\"MachinesReady\", \"Unknown\", \"ReadyUnknown\", \"* Machine test-m:\\n  * NodeHealthy: Kubelet stopped posting node status\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingup\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling up from 2 to 3 replicas, waiting for machines to be ready\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Deleting=True takes priority over RollingOut=True\",\n\t\t\tobj:  makeMachineDeploymentObj(3, 4, 3, 2),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"True\", \"Deleting\", \"being deleted\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"True\", \"RollingOut\", \"Rolling out 2 not up-to-date replicas\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"Scaling down from 4 to 3 replicas\"),\n\t\t\t},\n\t\t\texpectedState:    \"deleting\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"being deleted\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Paused=True takes priority over RollingOut=True\",\n\t\t\tobj:  makeMachineDeploymentObj(3, 4, 3, 2),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"True\", \"Paused\", \"\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"True\", \"RollingOut\", \"Rolling out 2 not up-to-date replicas\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"Scaling down from 4 to 3 replicas\"),\n\t\t\t},\n\t\t\texpectedState:   \"paused\",\n\t\t\texpectedTransit: true,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"RollingOut=False does not trigger rollingout — falls through to ScalingDown\",\n\t\t\tobj:  makeMachineDeploymentObj(1, 2, 2, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"False\", \"NotRollingOut\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"Scaling down from 2 to 1 replicas\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingdown\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling down from 2 to 1 replicas, waiting for machines to be deleted\"},\n\t\t},\n\t\t{\n\t\t\tname: \"RollingOut=True on MachineDeployment with missing upToDateReplicas — no message\",\n\t\t\tobj:  makeMachineDeploymentObj(3, 4, 3, -1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"RollingOut\", \"True\", \"RollingOut\", \"Rolling out 2 not up-to-date replicas\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"Scaling down from 4 to 3 replicas\"),\n\t\t\t},\n\t\t\texpectedState:    \"rollingout\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: nil,\n\t\t},\n\n\t\t// --- Pass through (steady state) ---\n\t\t{\n\t\t\tname: \"Steady state — all replicas match → pass through\",\n\t\t\tobj:  makeMachineSetObj(2, 2, 2),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t\tNewCondition(\"MachinesReady\", \"True\", \"Ready\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\n\t\t// --- Edge cases ---\n\t\t{\n\t\t\tname:            \"No conditions → pass through\",\n\t\t\tobj:             makeMachineSetObj(1, 1, 1),\n\t\t\tconditions:      []Condition{},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"Missing replica fields → pass through (no panic)\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"MachineSet\",\n\t\t\t},\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"\",\n\t\t\texpectedTransit: false,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingUp=True but no replica fields → scalingup, no message\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"MachineSet\",\n\t\t\t},\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"True\", \"ScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingup\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingUp from zero readyReplicas\",\n\t\t\tobj:  makeMachineSetObj(2, 0, 0),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"True\", \"ScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingup\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling up from 0 to 2 replicas, waiting for machines to be ready\"},\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingUp with larger replica counts (3 → 5)\",\n\t\t\tobj:  makeMachineSetObj(5, 3, 3),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"True\", \"ScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingup\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"Scaling up from 3 to 5 replicas, waiting for machines to be ready\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Deleting=True takes priority over active scale-down\",\n\t\t\tobj:  makeMachineSetObj(1, 2, 2),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"True\", \"Deleting\", \"machineset cleanup in progress\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"Scaling down from 2 to 1 replicas\"),\n\t\t\t},\n\t\t\texpectedState:    \"deleting\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: []string{\"machineset cleanup in progress\"},\n\t\t},\n\t\t{\n\t\t\tname: \"Paused=True takes priority over scale-up detected by replicas\",\n\t\t\tobj:  makeMachineSetObj(3, 1, 1),\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"True\", \"Paused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:   \"paused\",\n\t\t\texpectedTransit: true,\n\t\t\texpectedError:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"ScalingDown=True but no replica fields → scalingdown, no message\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"MachineSet\",\n\t\t\t},\n\t\t\tconditions: []Condition{\n\t\t\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\t\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\t\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"\"),\n\t\t\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\t\t},\n\t\t\texpectedState:    \"scalingdown\",\n\t\t\texpectedTransit:  true,\n\t\t\texpectedError:    false,\n\t\t\texpectedMessages: nil,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult := checkCAPIMachineSetAndDeploymentTransitioning(tt.obj, tt.conditions, Summary{})\n\t\t\tassert.Equal(t, tt.expectedState, result.State, \"state mismatch\")\n\t\t\tassert.Equal(t, tt.expectedTransit, result.Transitioning, \"transitioning mismatch\")\n\t\t\tassert.Equal(t, tt.expectedError, result.Error, \"error mismatch\")\n\t\t\tif tt.expectedMessages != nil {\n\t\t\t\tassert.Equal(t, tt.expectedMessages, result.Message, \"messages mismatch\")\n\t\t\t} else {\n\t\t\t\tassert.Empty(t, result.Message, \"expected no messages\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsCAPIMachineDeployment(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tobj      data.Object\n\t\texpected bool\n\t}{\n\t\t{\n\t\t\tname: \"CAPI v1beta2 MachineDeployment\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"MachineDeployment\",\n\t\t\t},\n\t\t\texpected: true,\n\t\t},\n\t\t{\n\t\t\tname: \"CAPI v1beta1 MachineDeployment\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta1\",\n\t\t\t\t\"kind\":       \"MachineDeployment\",\n\t\t\t},\n\t\t\texpected: true,\n\t\t},\n\t\t{\n\t\t\tname: \"CAPI v1beta2 MachineSet (not MachineDeployment)\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"MachineSet\",\n\t\t\t},\n\t\t\texpected: false,\n\t\t},\n\t\t{\n\t\t\tname: \"CAPI v1beta2 Cluster\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"kind\":       \"Cluster\",\n\t\t\t},\n\t\t\texpected: false,\n\t\t},\n\t\t{\n\t\t\tname: \"non-CAPI Deployment kind\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"apiVersion\": \"apps/v1\",\n\t\t\t\t\"kind\":       \"Deployment\",\n\t\t\t},\n\t\t\texpected: false,\n\t\t},\n\t\t{\n\t\t\tname:     \"empty object\",\n\t\t\tobj:      data.Object{},\n\t\t\texpected: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tassert.Equal(t, tt.expected, isCAPIMachineDeployment(tt.obj))\n\t\t})\n\t}\n}\n\n// TestCheckTransitioning_CAPIMachineDeploymentDispatch verifies that\n// checkTransitioning dispatches CAPI MachineDeployment objects to\n// checkCAPIMachineSetAndDeploymentTransitioning and that the replica-field\n// paths behave correctly for deployments (not just MachineSets).\nfunc TestCheckTransitioning_CAPIMachineDeploymentDispatch(t *testing.T) {\n\t// A MachineDeployment scaling up: spec.replicas=3, status.replicas=1.\n\t// Even with ScalingUp=False (stale condition), the replica mismatch\n\t// (spec > status) should be detected via the CAPI handler.\n\tscalingObj := data.Object{\n\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\"kind\":       \"MachineDeployment\",\n\t\t\"spec\": map[string]interface{}{\n\t\t\t\"replicas\": int64(3),\n\t\t},\n\t\t\"status\": map[string]interface{}{\n\t\t\t\"replicas\":      int64(1),\n\t\t\t\"readyReplicas\": int64(1),\n\t\t},\n\t}\n\tconditions := []Condition{\n\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t}\n\tresult := checkTransitioning(scalingObj, conditions, Summary{})\n\tassert.Equal(t, \"scalingup\", result.State)\n\tassert.True(t, result.Transitioning)\n\tassert.Equal(t, []string{\"Scaling up from 1 to 3 replicas, waiting for machines to be ready\"}, result.Message)\n\n\t// A MachineDeployment in steady state: all replicas match.\n\tsteadyObj := data.Object{\n\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\"kind\":       \"MachineDeployment\",\n\t\t\"spec\": map[string]interface{}{\n\t\t\t\"replicas\": int64(2),\n\t\t},\n\t\t\"status\": map[string]interface{}{\n\t\t\t\"replicas\":      int64(2),\n\t\t\t\"readyReplicas\": int64(2),\n\t\t},\n\t}\n\tsteadyConditions := []Condition{\n\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\tNewCondition(\"ScalingDown\", \"False\", \"NotScalingDown\", \"\"),\n\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t\tNewCondition(\"MachinesReady\", \"True\", \"Ready\", \"\"),\n\t}\n\tsteadyResult := checkTransitioning(steadyObj, steadyConditions, Summary{})\n\tassert.Empty(t, steadyResult.State, \"steady-state MachineDeployment should pass through\")\n\tassert.False(t, steadyResult.Transitioning)\n\tassert.False(t, steadyResult.Error)\n\n\t// A MachineDeployment doing a rolling upgrade: RollingOut=True should\n\t// take priority over the ScalingDown condition and replica mismatch.\n\trollingObj := data.Object{\n\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\"kind\":       \"MachineDeployment\",\n\t\t\"spec\": map[string]interface{}{\n\t\t\t\"replicas\": int64(3),\n\t\t},\n\t\t\"status\": map[string]interface{}{\n\t\t\t\"replicas\":         int64(4),\n\t\t\t\"readyReplicas\":    int64(3),\n\t\t\t\"upToDateReplicas\": int64(2),\n\t\t},\n\t}\n\trollingConditions := []Condition{\n\t\tNewCondition(\"Deleting\", \"False\", \"NotDeleting\", \"\"),\n\t\tNewCondition(\"Paused\", \"False\", \"NotPaused\", \"\"),\n\t\tNewCondition(\"RollingOut\", \"True\", \"RollingOut\", \"Rolling out 2 not up-to-date replicas\"),\n\t\tNewCondition(\"ScalingDown\", \"True\", \"ScalingDown\", \"Scaling down from 4 to 3 replicas\"),\n\t\tNewCondition(\"ScalingUp\", \"False\", \"NotScalingUp\", \"\"),\n\t}\n\trollingResult := checkTransitioning(rollingObj, rollingConditions, Summary{})\n\tassert.Equal(t, \"rollingout\", rollingResult.State, \"rolling upgrade should produce state=rollingout\")\n\tassert.True(t, rollingResult.Transitioning)\n\tassert.Equal(t, []string{\"rolling out 2 not up-to-date replicas\"}, rollingResult.Message)\n}\n"
  },
  {
    "path": "pkg/summary/cattletypes.go",
    "content": "package summary\n\nimport (\n\t\"strings\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n)\n\nfunc checkCattleReady(obj data.Object, condition []Condition, summary Summary) Summary {\n\tif strings.Contains(obj.String(\"apiVersion\"), \"cattle.io/\") {\n\t\tfor _, condition := range condition {\n\t\t\tif condition.Type() == \"Ready\" && condition.Status() == \"False\" && condition.Message() != \"\" {\n\t\t\t\tsummary.Message = append(summary.Message, condition.Message())\n\t\t\t\tsummary.Error = true\n\t\t\t\treturn summary\n\t\t\t}\n\t\t}\n\t}\n\n\treturn summary\n}\n\nfunc checkCattleTypes(obj data.Object, condition []Condition, summary Summary) Summary {\n\treturn checkRelease(obj, condition, summary)\n}\n\nfunc checkRelease(obj data.Object, _ []Condition, summary Summary) Summary {\n\tif !isKind(obj, \"App\", \"catalog.cattle.io\") {\n\t\treturn summary\n\t}\n\tif obj.String(\"status\", \"summary\", \"state\") != \"deployed\" {\n\t\treturn summary\n\t}\n\tfor _, resources := range obj.Slice(\"spec\", \"resources\") {\n\t\tsummary.Relationships = append(summary.Relationships, Relationship{\n\t\t\tName:       resources.String(\"name\"),\n\t\t\tNamespace:  resources.String(\"namespace\"),\n\t\t\tKind:       resources.String(\"kind\"),\n\t\t\tAPIVersion: resources.String(\"apiVersion\"),\n\t\t\tType:       \"helmresource\",\n\t\t})\n\t}\n\treturn summary\n}\n"
  },
  {
    "path": "pkg/summary/cattletypes_test.go",
    "content": "package summary\n\nimport (\n\t\"testing\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCheckRelease(t *testing.T) {\n\ttype testCase struct {\n\t\tname     string\n\t\tinput    data.Object\n\t\texpected Summary\n\t}\n\n\tcases := []testCase{\n\t\t{\n\t\t\tname: \"namespaced resource for deployed app.catalog.cattle.io\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"apiVersion\": \"catalog.cattle.io/v1\",\n\t\t\t\t\"kind\":       \"App\",\n\n\t\t\t\t\"spec\": map[string]any{\n\t\t\t\t\t\"resources\": []any{\n\t\t\t\t\t\tmap[string]any{\n\t\t\t\t\t\t\t\"apiVersion\": \"rbac.authorization.k8s.io/v1\",\n\t\t\t\t\t\t\t\"kind\":       \"Role\",\n\t\t\t\t\t\t\t\"name\":       \"monitoring-dashboard-admin\",\n\t\t\t\t\t\t\t\"namespace\":  \"cattle-dashboards\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t\"status\": map[string]any{\n\t\t\t\t\t\"summary\": map[string]any{\n\t\t\t\t\t\t\"state\": \"deployed\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: Summary{\n\t\t\t\tRelationships: []Relationship{\n\t\t\t\t\t{\n\t\t\t\t\t\tName:       \"monitoring-dashboard-admin\",\n\t\t\t\t\t\tNamespace:  \"cattle-dashboards\",\n\t\t\t\t\t\tKind:       \"Role\",\n\t\t\t\t\t\tAPIVersion: \"rbac.authorization.k8s.io/v1\",\n\t\t\t\t\t\tType:       \"helmresource\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\n\t\t{\n\t\t\tname: \"non-namespaced resource for deployed app.catalog.cattle.io\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"apiVersion\": \"catalog.cattle.io/v1\",\n\t\t\t\t\"kind\":       \"App\",\n\n\t\t\t\t\"spec\": map[string]any{\n\t\t\t\t\t\"resources\": []any{\n\t\t\t\t\t\tmap[string]any{\n\t\t\t\t\t\t\t\"apiVersion\": \"rbac.authorization.k8s.io/v1\",\n\t\t\t\t\t\t\t\"kind\":       \"ClusterRole\",\n\t\t\t\t\t\t\t\"name\":       \"monitoring-admin\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t\"status\": map[string]any{\n\t\t\t\t\t\"summary\": map[string]any{\n\t\t\t\t\t\t\"state\": \"deployed\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: Summary{\n\t\t\t\tRelationships: []Relationship{\n\t\t\t\t\t{\n\t\t\t\t\t\tName:       \"monitoring-admin\",\n\t\t\t\t\t\tKind:       \"ClusterRole\",\n\t\t\t\t\t\tAPIVersion: \"rbac.authorization.k8s.io/v1\",\n\t\t\t\t\t\tType:       \"helmresource\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\n\t\t{\n\t\t\tname: \"resource for undeployed app.catalog.cattle.io\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"apiVersion\": \"catalog.cattle.io/v1\",\n\t\t\t\t\"kind\":       \"App\",\n\n\t\t\t\t\"spec\": map[string]any{\n\t\t\t\t\t\"resources\": []any{\n\t\t\t\t\t\tmap[string]any{\n\t\t\t\t\t\t\t\"apiVersion\": \"rbac.authorization.k8s.io/v1\",\n\t\t\t\t\t\t\t\"kind\":       \"Role\",\n\t\t\t\t\t\t\t\"name\":       \"monitoring-dashboard-admin\",\n\t\t\t\t\t\t\t\"namespace\":  \"cattle-dashboards\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t\"status\": map[string]any{\n\t\t\t\t\t\"summary\": map[string]any{\n\t\t\t\t\t\t\"state\": \"failed\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: Summary{},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(c.name, func(t *testing.T) {\n\t\t\tactual := checkRelease(c.input, nil, Summary{})\n\t\t\tassert.Equal(t, c.expected, actual)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/summary/client/interface.go",
    "content": "package client\n\nimport (\n\t\"context\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/summary\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/watch\"\n)\n\ntype Interface interface {\n\tResource(resource schema.GroupVersionResource) NamespaceableResourceInterface\n}\n\ntype ExtendedInterface interface {\n\tInterface\n\tResourceWithOptions(resource schema.GroupVersionResource, opts *Options) NamespaceableResourceInterface\n}\n\ntype ResourceInterface interface {\n\tList(ctx context.Context, opts metav1.ListOptions) (*summary.SummarizedObjectList, error)\n\tWatch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)\n}\n\ntype NamespaceableResourceInterface interface {\n\tNamespace(string) ResourceInterface\n\tResourceInterface\n}\n"
  },
  {
    "path": "pkg/summary/client/options.go",
    "content": "package client\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/schemas\"\n)\n\ntype Options struct {\n\tSchema *schemas.Schema\n}\n"
  },
  {
    "path": "pkg/summary/client/simple.go",
    "content": "package client\n\nimport (\n\t\"context\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/schemas\"\n\t\"github.com/rancher/wrangler/v3/pkg/summary\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/watch\"\n\t\"k8s.io/client-go/dynamic\"\n)\n\ntype summaryClient struct {\n\tclient dynamic.Interface\n}\n\nvar _ Interface = &summaryClient{}\n\nfunc NewForDynamicClient(client dynamic.Interface) Interface {\n\treturn &summaryClient{client: client}\n}\n\nfunc NewForExtendedDynamicClient(client dynamic.Interface) ExtendedInterface {\n\treturn &summaryClient{client: client}\n}\n\ntype summaryResourceClient struct {\n\tclient    dynamic.Interface\n\tnamespace string\n\tresource  schema.GroupVersionResource\n\toptions   Options\n}\n\nfunc (c *summaryClient) Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface {\n\treturn c.ResourceWithOptions(resource, nil)\n}\n\nfunc (c *summaryClient) ResourceWithOptions(resource schema.GroupVersionResource, opts *Options) NamespaceableResourceInterface {\n\tif opts == nil {\n\t\topts = &Options{}\n\t}\n\treturn &summaryResourceClient{\n\t\tclient:   c.client,\n\t\tresource: resource,\n\t\toptions:  *opts,\n\t}\n}\n\nfunc (c *summaryResourceClient) Namespace(ns string) ResourceInterface {\n\tret := *c\n\tret.namespace = ns\n\treturn &ret\n}\n\nfunc (c *summaryResourceClient) List(ctx context.Context, opts metav1.ListOptions) (*summary.SummarizedObjectList, error) {\n\tvar (\n\t\tu   *unstructured.UnstructuredList\n\t\terr error\n\t)\n\n\tif c.namespace == \"\" {\n\t\tu, err = c.client.Resource(c.resource).List(ctx, opts)\n\t} else {\n\t\tu, err = c.client.Resource(c.resource).Namespace(c.namespace).List(ctx, opts)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlist := &summary.SummarizedObjectList{\n\t\tTypeMeta: metav1.TypeMeta{\n\t\t\tKind:       u.GetKind(),\n\t\t\tAPIVersion: u.GetAPIVersion(),\n\t\t},\n\t\tListMeta: metav1.ListMeta{\n\t\t\tResourceVersion:    u.GetResourceVersion(),\n\t\t\tContinue:           u.GetContinue(),\n\t\t\tRemainingItemCount: u.GetRemainingItemCount(),\n\t\t},\n\t}\n\n\tfor _, obj := range u.Items {\n\t\tlist.Items = append(list.Items, *summary.SummarizedWithOptions(&obj, generateSummarizeOpts(c.options.Schema)))\n\t}\n\n\treturn list, nil\n}\n\nfunc (c *summaryResourceClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {\n\tvar (\n\t\tresp watch.Interface\n\t\terr  error\n\t)\n\n\teventChan := make(chan watch.Event)\n\n\tif c.namespace == \"\" {\n\t\tresp, err = c.client.Resource(c.resource).Watch(ctx, opts)\n\t} else {\n\t\tresp, err = c.client.Resource(c.resource).Namespace(c.namespace).Watch(ctx, opts)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgo func() {\n\t\tdefer close(eventChan)\n\t\tfor event := range resp.ResultChan() {\n\t\t\t// don't encode status objects\n\t\t\tif _, ok := event.Object.(*metav1.Status); !ok {\n\t\t\t\tevent.Object = summary.SummarizedWithOptions(event.Object, generateSummarizeOpts(c.options.Schema))\n\t\t\t}\n\t\t\teventChan <- event\n\t\t}\n\t}()\n\n\treturn &watcher{\n\t\tInterface: resp,\n\t\teventChan: eventChan,\n\t}, nil\n}\n\ntype watcher struct {\n\twatch.Interface\n\teventChan chan watch.Event\n}\n\nfunc (w watcher) ResultChan() <-chan watch.Event {\n\treturn w.eventChan\n}\n\nfunc generateSummarizeOpts(schema *schemas.Schema) *summary.SummarizeOptions {\n\thasObservedGeneration := false\n\tif schema != nil && schema.Attributes != nil {\n\t\tif v, ok := schema.Attributes[\"hasObservedGeneration\"]; ok {\n\t\t\tif b, ok := v.(bool); ok {\n\t\t\t\thasObservedGeneration = b\n\t\t\t}\n\t\t}\n\t}\n\tsummaryOpts := &summary.SummarizeOptions{\n\t\tHasObservedGeneration: hasObservedGeneration,\n\t}\n\n\treturn summaryOpts\n}\n"
  },
  {
    "path": "pkg/summary/condition.go",
    "content": "package summary\n\nimport (\n\t\"encoding/json\"\n\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n)\n\nfunc GetUnstructuredConditions(obj map[string]interface{}) []Condition {\n\treturn getConditions(obj)\n}\n\nfunc getRawConditions(obj data.Object) []data.Object {\n\tresult := make([]data.Object, 0)\n\tconditions := obj.Slice(\"status\", \"conditions\")\n\tif len(conditions) != 0 {\n\t\tresult = append(result, conditions...)\n\t}\n\n\tannoConditions := getAnnotationConditions(obj)\n\tif len(annoConditions) != 0 {\n\t\tresult = append(result, annoConditions...)\n\t}\n\n\treturn result\n}\n\n// getAnnotationConditions extracts conditions from the cattle.io/status annotation.\n// Returns an empty slice if the annotation doesn't exist or cannot be parsed.\nfunc getAnnotationConditions(obj data.Object) []data.Object {\n\tstatusAnn := obj.String(\"metadata\", \"annotations\", \"cattle.io/status\")\n\tif statusAnn == \"\" {\n\t\treturn []data.Object{}\n\t}\n\n\tvar status data.Object\n\tif err := json.Unmarshal([]byte(statusAnn), &status); err != nil {\n\t\treturn []data.Object{}\n\t}\n\n\tconditions := status.Slice(\"conditions\")\n\tif conditions == nil {\n\t\treturn []data.Object{}\n\t}\n\treturn conditions\n}\n\nfunc getConditions(obj data.Object) (result []Condition) {\n\tfor _, condition := range getRawConditions(obj) {\n\t\tresult = append(result, Condition{Object: condition})\n\t}\n\treturn\n}\n\ntype Condition struct {\n\tdata.Object\n}\n\nfunc NewCondition(conditionType, status, reason, message string) Condition {\n\treturn Condition{\n\t\tObject: map[string]interface{}{\n\t\t\t\"type\":    conditionType,\n\t\t\t\"status\":  status,\n\t\t\t\"reason\":  reason,\n\t\t\t\"message\": message,\n\t\t},\n\t}\n}\n\nfunc (c Condition) Type() string {\n\treturn c.String(\"type\")\n}\n\nfunc (c Condition) Status() string {\n\treturn c.String(\"status\")\n}\n\nfunc (c Condition) Reason() string {\n\treturn c.String(\"reason\")\n}\n\nfunc (c Condition) Message() string {\n\treturn c.String(\"message\")\n}\n\nfunc (c Condition) Equals(other Condition) bool {\n\treturn c.Type() == other.Type() &&\n\t\tc.Status() == other.Status() &&\n\t\tc.Reason() == other.Reason() &&\n\t\tc.Message() == other.Message()\n}\n\nfunc NormalizeConditions(runtimeObj runtime.Object) {\n\tif runtimeObj == nil {\n\t\treturn\n\t}\n\n\tunstr, ok := runtimeObj.(*unstructured.Unstructured)\n\tif !ok {\n\t\treturn\n\t}\n\n\tobj := data.Object(unstr.Object)\n\n\tif conditions := obj.Slice(\"status\", \"conditions\"); len(conditions) > 0 {\n\t\tnormalizeAndSetConditions(obj, conditions, \"status\", \"conditions\")\n\t}\n}\n\nfunc normalizeAndSetConditions(obj data.Object, conditions []data.Object, path ...string) {\n\tvar newConditions []interface{}\n\tfor _, condition := range conditions {\n\t\tvar summary Summary\n\t\tfor _, summarizer := range ConditionSummarizers {\n\t\t\tsummary = summarizer(obj, []Condition{{Object: condition}}, summary)\n\t\t}\n\t\tcondition.Set(\"error\", summary.Error)\n\t\tcondition.Set(\"transitioning\", summary.Transitioning)\n\n\t\tif condition.String(\"lastUpdateTime\") == \"\" {\n\t\t\tcondition.Set(\"lastUpdateTime\", condition.String(\"lastTransitionTime\"))\n\t\t}\n\t\tnewConditions = append(newConditions, map[string]interface{}(condition))\n\t}\n\n\tif len(newConditions) > 0 {\n\t\tobj.SetNested(newConditions, path...)\n\t}\n}\n"
  },
  {
    "path": "pkg/summary/condition_test.go",
    "content": "package summary\n\nimport (\n\t\"testing\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n)\n\nfunc TestGetRawConditions(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tinput    data.Object\n\t\texpected []data.Object\n\t}{\n\t\t{\n\t\t\tname: \"standard conditions in status field\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\"conditions\": []interface{}{\n\t\t\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\t\t\"type\":   \"Ready\",\n\t\t\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\t\t\"type\":   \"Available\",\n\t\t\t\t\t\t\t\"status\": \"False\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{\n\t\t\t\t{\n\t\t\t\t\t\"type\":   \"Ready\",\n\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\":   \"Available\",\n\t\t\t\t\t\"status\": \"False\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"annotation conditions are appended to status conditions\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"annotations\": map[string]interface{}{\n\t\t\t\t\t\t\"cattle.io/status\": `{\"conditions\":[{\"type\":\"Annotation\",\"status\":\"True\"}]}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\"conditions\": []interface{}{\n\t\t\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\t\t\"type\":   \"Ready\",\n\t\t\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{\n\t\t\t\t{\n\t\t\t\t\t\"type\":   \"Ready\",\n\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\":   \"Annotation\",\n\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"annotation conditions with CAPI v1beta2 standard conditions\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"annotations\": map[string]interface{}{\n\t\t\t\t\t\t\"cattle.io/status\": `{\"conditions\":[{\"type\":\"Annotation\",\"status\":\"True\"}]}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\"conditions\": []interface{}{\n\t\t\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\t\t\"type\":   \"Ready\",\n\t\t\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{\n\t\t\t\t{\n\t\t\t\t\t\"type\":   \"Ready\",\n\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\":   \"Annotation\",\n\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"only annotation conditions when status field is empty\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"annotations\": map[string]interface{}{\n\t\t\t\t\t\t\"cattle.io/status\": `{\"conditions\":[{\"type\":\"Annotation\",\"status\":\"True\"}]}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{\n\t\t\t\t{\n\t\t\t\t\t\"type\":   \"Annotation\",\n\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:     \"no conditions at all\",\n\t\t\tinput:    data.Object{},\n\t\t\texpected: []data.Object{},\n\t\t},\n\t\t{\n\t\t\tname: \"invalid annotation JSON is ignored\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"annotations\": map[string]interface{}{\n\t\t\t\t\t\t\"cattle.io/status\": `invalid json`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\"conditions\": []interface{}{\n\t\t\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\t\t\"type\":   \"Ready\",\n\t\t\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{\n\t\t\t\t{\n\t\t\t\t\t\"type\":   \"Ready\",\n\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"empty annotation string is ignored\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"annotations\": map[string]interface{}{\n\t\t\t\t\t\t\"cattle.io/status\": \"\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\"conditions\": []interface{}{\n\t\t\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\t\t\"type\":   \"Ready\",\n\t\t\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{\n\t\t\t\t{\n\t\t\t\t\t\"type\":   \"Ready\",\n\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult := getRawConditions(tt.input)\n\t\t\tassert.Equal(t, tt.expected, result)\n\t\t})\n\t}\n}\n\nfunc TestGetAnnotationConditions(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tinput    data.Object\n\t\texpected []data.Object\n\t}{\n\t\t{\n\t\t\tname: \"parses valid annotation conditions\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"annotations\": map[string]interface{}{\n\t\t\t\t\t\t\"cattle.io/status\": `{\"conditions\":[{\"type\":\"Ready\",\"status\":\"True\"}]}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{\n\t\t\t\t{\n\t\t\t\t\t\"type\":   \"Ready\",\n\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"parses multiple annotation conditions\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"annotations\": map[string]interface{}{\n\t\t\t\t\t\t\"cattle.io/status\": `{\"conditions\":[{\"type\":\"Ready\",\"status\":\"True\"},{\"type\":\"Available\",\"status\":\"False\"}]}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{\n\t\t\t\t{\n\t\t\t\t\t\"type\":   \"Ready\",\n\t\t\t\t\t\"status\": \"True\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\":   \"Available\",\n\t\t\t\t\t\"status\": \"False\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"returns empty slice when annotation is empty string\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"annotations\": map[string]interface{}{\n\t\t\t\t\t\t\"cattle.io/status\": \"\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{},\n\t\t},\n\t\t{\n\t\t\tname: \"returns empty slice when annotation doesn't exist\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"annotations\": map[string]interface{}{},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{},\n\t\t},\n\t\t{\n\t\t\tname:     \"returns empty slice when metadata doesn't exist\",\n\t\t\tinput:    data.Object{},\n\t\t\texpected: []data.Object{},\n\t\t},\n\t\t{\n\t\t\tname: \"returns empty slice when JSON is invalid\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"annotations\": map[string]interface{}{\n\t\t\t\t\t\t\"cattle.io/status\": `{invalid json}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{},\n\t\t},\n\t\t{\n\t\t\tname: \"returns empty slice when JSON is valid but has no conditions\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"annotations\": map[string]interface{}{\n\t\t\t\t\t\t\"cattle.io/status\": `{\"other\":\"field\"}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{},\n\t\t},\n\t\t{\n\t\t\tname: \"returns empty slice when conditions array is empty\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"annotations\": map[string]interface{}{\n\t\t\t\t\t\t\"cattle.io/status\": `{\"conditions\":[]}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{},\n\t\t},\n\t\t{\n\t\t\tname: \"handles complex condition objects\",\n\t\t\tinput: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"annotations\": map[string]interface{}{\n\t\t\t\t\t\t\"cattle.io/status\": `{\"conditions\":[{\"type\":\"Ready\",\"status\":\"True\",\"reason\":\"AllGood\",\"message\":\"Everything is fine\",\"lastTransitionTime\":\"2024-01-01T00:00:00Z\"}]}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []data.Object{\n\t\t\t\t{\n\t\t\t\t\t\"type\":               \"Ready\",\n\t\t\t\t\t\"status\":             \"True\",\n\t\t\t\t\t\"reason\":             \"AllGood\",\n\t\t\t\t\t\"message\":            \"Everything is fine\",\n\t\t\t\t\t\"lastTransitionTime\": \"2024-01-01T00:00:00Z\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult := getAnnotationConditions(tt.input)\n\t\t\tassert.Equal(t, tt.expected, result)\n\t\t})\n\t}\n}\n\nfunc TestNormalizeConditions(t *testing.T) {\n\ttests := []struct {\n\t\tname                     string\n\t\tinput                    *unstructured.Unstructured\n\t\texpectedStatusConditions []interface{}\n\t}{\n\t\t{\n\t\t\tname: \"normalizes standard status conditions\",\n\t\t\tinput: &unstructured.Unstructured{\n\t\t\t\tObject: map[string]interface{}{\n\t\t\t\t\t\"apiVersion\": \"apps/v1\",\n\t\t\t\t\t\"kind\":       \"Deployment\",\n\t\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\t\"conditions\": []interface{}{\n\t\t\t\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\t\t\t\"type\":               \"Available\",\n\t\t\t\t\t\t\t\t\"status\":             \"True\",\n\t\t\t\t\t\t\t\t\"lastTransitionTime\": \"2024-01-01T00:00:00Z\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpectedStatusConditions: []interface{}{\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"type\":               \"Available\",\n\t\t\t\t\t\"status\":             \"True\",\n\t\t\t\t\t\"lastTransitionTime\": \"2024-01-01T00:00:00Z\",\n\t\t\t\t\t\"lastUpdateTime\":     \"2024-01-01T00:00:00Z\",\n\t\t\t\t\t\"error\":              false,\n\t\t\t\t\t\"transitioning\":      false,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"normalizes CAPI v1beta2 standard conditions\",\n\t\t\tinput: &unstructured.Unstructured{\n\t\t\t\tObject: map[string]interface{}{\n\t\t\t\t\t\"apiVersion\": \"cluster.x-k8s.io/v1beta2\",\n\t\t\t\t\t\"kind\":       \"Cluster\",\n\t\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\t\"conditions\": []interface{}{\n\t\t\t\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\t\t\t\"type\":               \"Available\",\n\t\t\t\t\t\t\t\t\"status\":             \"True\",\n\t\t\t\t\t\t\t\t\"lastTransitionTime\": \"2024-01-02T00:00:00Z\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpectedStatusConditions: []interface{}{\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"type\":               \"Available\",\n\t\t\t\t\t\"status\":             \"True\",\n\t\t\t\t\t\"lastTransitionTime\": \"2024-01-02T00:00:00Z\",\n\t\t\t\t\t\"lastUpdateTime\":     \"2024-01-02T00:00:00Z\",\n\t\t\t\t\t\"error\":              false,\n\t\t\t\t\t\"transitioning\":      false,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"preserves lastUpdateTime if already set\",\n\t\t\tinput: &unstructured.Unstructured{\n\t\t\t\tObject: map[string]interface{}{\n\t\t\t\t\t\"apiVersion\": \"apps/v1\",\n\t\t\t\t\t\"kind\":       \"Deployment\",\n\t\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\t\"conditions\": []interface{}{\n\t\t\t\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\t\t\t\"type\":               \"Available\",\n\t\t\t\t\t\t\t\t\"status\":             \"True\",\n\t\t\t\t\t\t\t\t\"lastTransitionTime\": \"2024-01-01T00:00:00Z\",\n\t\t\t\t\t\t\t\t\"lastUpdateTime\":     \"2024-01-02T00:00:00Z\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpectedStatusConditions: []interface{}{\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"type\":               \"Available\",\n\t\t\t\t\t\"status\":             \"True\",\n\t\t\t\t\t\"lastTransitionTime\": \"2024-01-01T00:00:00Z\",\n\t\t\t\t\t\"lastUpdateTime\":     \"2024-01-02T00:00:00Z\",\n\t\t\t\t\t\"error\":              false,\n\t\t\t\t\t\"transitioning\":      false,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"handles empty status\",\n\t\t\tinput: &unstructured.Unstructured{\n\t\t\t\tObject: map[string]interface{}{\n\t\t\t\t\t\"apiVersion\": \"apps/v1\",\n\t\t\t\t\t\"kind\":       \"Deployment\",\n\t\t\t\t},\n\t\t\t},\n\t\t\texpectedStatusConditions: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"sets transitioning for False status condition\",\n\t\t\tinput: &unstructured.Unstructured{\n\t\t\t\tObject: map[string]interface{}{\n\t\t\t\t\t\"apiVersion\": \"apps/v1\",\n\t\t\t\t\t\"kind\":       \"Deployment\",\n\t\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\t\"conditions\": []interface{}{\n\t\t\t\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\t\t\t\"type\":               \"Available\",\n\t\t\t\t\t\t\t\t\"status\":             \"False\",\n\t\t\t\t\t\t\t\t\"reason\":             \"MinimumReplicasUnavailable\",\n\t\t\t\t\t\t\t\t\"message\":            \"Deployment does not have minimum availability\",\n\t\t\t\t\t\t\t\t\"lastTransitionTime\": \"2024-01-01T00:00:00Z\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpectedStatusConditions: []interface{}{\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"type\":               \"Available\",\n\t\t\t\t\t\"status\":             \"False\",\n\t\t\t\t\t\"reason\":             \"MinimumReplicasUnavailable\",\n\t\t\t\t\t\"message\":            \"Deployment does not have minimum availability\",\n\t\t\t\t\t\"lastTransitionTime\": \"2024-01-01T00:00:00Z\",\n\t\t\t\t\t\"lastUpdateTime\":     \"2024-01-01T00:00:00Z\",\n\t\t\t\t\t\"error\":              false,\n\t\t\t\t\t\"transitioning\":      true,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tNormalizeConditions(tt.input)\n\n\t\t\t// Check standard status conditions\n\t\t\tstatusConditions, _, _ := unstructured.NestedSlice(tt.input.Object, \"status\", \"conditions\")\n\t\t\tif tt.expectedStatusConditions == nil {\n\t\t\t\tassert.Nil(t, statusConditions)\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, tt.expectedStatusConditions, statusConditions)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNormalizeConditionsNonUnstructured(t *testing.T) {\n\t// Test that NormalizeConditions handles non-unstructured objects gracefully\n\t// Passing nil should not panic because the type assertion will fail\n\tNormalizeConditions(nil) // Should not panic\n}\n"
  },
  {
    "path": "pkg/summary/coretypes.go",
    "content": "package summary\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nfunc checkHasPodTemplate(obj data.Object, condition []Condition, summary Summary) Summary {\n\ttemplate := obj.Map(\"spec\", \"template\")\n\tif template == nil {\n\t\treturn summary\n\t}\n\n\tif !isKind(obj, \"ReplicaSet\", \"apps/\", \"extension/\") &&\n\t\t!isKind(obj, \"DaemonSet\", \"apps/\", \"extension/\") &&\n\t\t!isKind(obj, \"StatefulSet\", \"apps/\", \"extension/\") &&\n\t\t!isKind(obj, \"Deployment\", \"apps/\", \"extension/\") &&\n\t\t!isKind(obj, \"Job\", \"batch/\") &&\n\t\t!isKind(obj, \"Service\") {\n\t\treturn summary\n\t}\n\n\treturn checkPodTemplate(template, condition, summary)\n}\n\nfunc checkHasPodSelector(obj data.Object, condition []Condition, summary Summary) Summary {\n\tselector := obj.Map(\"spec\", \"selector\")\n\tif selector == nil {\n\t\treturn summary\n\t}\n\n\tif !isKind(obj, \"ReplicaSet\", \"apps/\", \"extension/\") &&\n\t\t!isKind(obj, \"DaemonSet\", \"apps/\", \"extension/\") &&\n\t\t!isKind(obj, \"StatefulSet\", \"apps/\", \"extension/\") &&\n\t\t!isKind(obj, \"Deployment\", \"apps/\", \"extension/\") &&\n\t\t!isKind(obj, \"Job\", \"batch/\") &&\n\t\t!isKind(obj, \"Service\") {\n\t\treturn summary\n\t}\n\n\t_, hasMatch := selector[\"matchLabels\"]\n\tif !hasMatch {\n\t\t_, hasMatch = selector[\"matchExpressions\"]\n\t}\n\tsel := metav1.LabelSelector{}\n\tif hasMatch {\n\t\tif err := convert.ToObj(selector, &sel); err != nil {\n\t\t\treturn summary\n\t\t}\n\t} else {\n\t\tsel.MatchLabels = map[string]string{}\n\t\tfor k, v := range selector {\n\t\t\tsel.MatchLabels[k] = convert.ToString(v)\n\t\t}\n\t}\n\n\tt := \"creates\"\n\tif obj[\"kind\"] == \"Service\" {\n\t\tt = \"selects\"\n\t}\n\n\tsummary.Relationships = append(summary.Relationships, Relationship{\n\t\tKind:       \"Pod\",\n\t\tAPIVersion: \"v1\",\n\t\tType:       t,\n\t\tSelector:   &sel,\n\t})\n\treturn summary\n}\n\nfunc checkPod(obj data.Object, condition []Condition, summary Summary) Summary {\n\tif !isKind(obj, \"Pod\") {\n\t\treturn summary\n\t}\n\tif obj.String(\"kind\") != \"Pod\" || obj.String(\"apiVersion\") != \"v1\" {\n\t\treturn summary\n\t}\n\treturn checkPodTemplate(obj, condition, summary)\n}\n\nfunc checkPodTemplate(obj data.Object, condition []Condition, summary Summary) Summary {\n\tsummary = checkPodConfigMaps(obj, condition, summary)\n\tsummary = checkPodSecrets(obj, condition, summary)\n\tsummary = checkPodServiceAccount(obj, condition, summary)\n\tsummary = checkPodProjectedVolume(obj, condition, summary)\n\tsummary = checkPodPullSecret(obj, condition, summary)\n\treturn summary\n}\n\nfunc checkPodPullSecret(obj data.Object, _ []Condition, summary Summary) Summary {\n\tfor _, pullSecret := range obj.Slice(\"imagePullSecrets\") {\n\t\tif name := pullSecret.String(\"name\"); name != \"\" {\n\t\t\tsummary.Relationships = append(summary.Relationships, Relationship{\n\t\t\t\tName:       name,\n\t\t\t\tKind:       \"Secret\",\n\t\t\t\tAPIVersion: \"v1\",\n\t\t\t\tType:       \"uses\",\n\t\t\t})\n\t\t}\n\t}\n\treturn summary\n}\n\nfunc checkPodProjectedVolume(obj data.Object, _ []Condition, summary Summary) Summary {\n\tfor _, vol := range obj.Slice(\"spec\", \"volumes\") {\n\t\tfor _, source := range vol.Slice(\"projected\", \"sources\") {\n\t\t\tif secretName := source.String(\"secret\", \"name\"); secretName != \"\" {\n\t\t\t\tsummary.Relationships = append(summary.Relationships, Relationship{\n\t\t\t\t\tName:       secretName,\n\t\t\t\t\tKind:       \"Secret\",\n\t\t\t\t\tAPIVersion: \"v1\",\n\t\t\t\t\tType:       \"uses\",\n\t\t\t\t})\n\t\t\t}\n\t\t\tif configMap := source.String(\"configMap\", \"name\"); configMap != \"\" {\n\t\t\t\tsummary.Relationships = append(summary.Relationships, Relationship{\n\t\t\t\t\tName:       configMap,\n\t\t\t\t\tKind:       \"ConfigMap\",\n\t\t\t\t\tAPIVersion: \"v1\",\n\t\t\t\t\tType:       \"uses\",\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\treturn summary\n}\n\nfunc addEnvRef(summary Summary, names map[string]bool, obj data.Object, fieldPrefix, kind string) Summary {\n\tfor _, container := range obj.Slice(\"spec\", \"containers\") {\n\t\tfor _, env := range container.Slice(\"envFrom\") {\n\t\t\tname := env.String(fieldPrefix+\"Ref\", \"name\")\n\t\t\tif name == \"\" || names[name] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tnames[name] = true\n\t\t\tsummary.Relationships = append(summary.Relationships, Relationship{\n\t\t\t\tName:       name,\n\t\t\t\tKind:       kind,\n\t\t\t\tAPIVersion: \"v1\",\n\t\t\t\tType:       \"uses\",\n\t\t\t})\n\t\t}\n\t\tfor _, env := range container.Slice(\"env\") {\n\t\t\tname := env.String(\"valueFrom\", fieldPrefix+\"KeyRef\", \"name\")\n\t\t\tif name == \"\" || names[name] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tnames[name] = true\n\t\t\tsummary.Relationships = append(summary.Relationships, Relationship{\n\t\t\t\tName:       name,\n\t\t\t\tKind:       kind,\n\t\t\t\tAPIVersion: \"v1\",\n\t\t\t\tType:       \"uses\",\n\t\t\t})\n\t\t}\n\t}\n\n\treturn summary\n}\n\nfunc checkPodConfigMaps(obj data.Object, _ []Condition, summary Summary) Summary {\n\tnames := map[string]bool{}\n\tfor _, vol := range obj.Slice(\"spec\", \"volumes\") {\n\t\tname := vol.String(\"configMap\", \"name\")\n\t\tif name == \"\" || names[name] {\n\t\t\tcontinue\n\t\t}\n\t\tnames[name] = true\n\t\tsummary.Relationships = append(summary.Relationships, Relationship{\n\t\t\tName:       name,\n\t\t\tKind:       \"ConfigMap\",\n\t\t\tAPIVersion: \"v1\",\n\t\t\tType:       \"uses\",\n\t\t})\n\t}\n\tsummary = addEnvRef(summary, names, obj, \"configMap\", \"ConfigMap\")\n\treturn summary\n}\n\nfunc checkPodSecrets(obj data.Object, _ []Condition, summary Summary) Summary {\n\tnames := map[string]bool{}\n\tfor _, vol := range obj.Slice(\"spec\", \"volumes\") {\n\t\tname := vol.String(\"secret\", \"secretName\")\n\t\tif name == \"\" || names[name] {\n\t\t\tcontinue\n\t\t}\n\t\tnames[name] = true\n\t\tsummary.Relationships = append(summary.Relationships, Relationship{\n\t\t\tName:       name,\n\t\t\tKind:       \"Secret\",\n\t\t\tAPIVersion: \"v1\",\n\t\t\tType:       \"uses\",\n\t\t})\n\t}\n\tsummary = addEnvRef(summary, names, obj, \"secret\", \"Secret\")\n\treturn summary\n}\n\nfunc checkPodServiceAccount(obj data.Object, _ []Condition, summary Summary) Summary {\n\tsaName := obj.String(\"spec\", \"serviceAccountName\")\n\tsummary.Relationships = append(summary.Relationships, Relationship{\n\t\tName:       saName,\n\t\tKind:       \"ServiceAccount\",\n\t\tAPIVersion: \"v1\",\n\t\tType:       \"uses\",\n\t})\n\treturn summary\n\n}\n"
  },
  {
    "path": "pkg/summary/gvk.go",
    "content": "package summary\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"regexp\"\n\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/util/sets\"\n)\n\nvar (\n\tgvkRegExp = regexp.MustCompile(`^(.*?)/(.*),\\s*Kind=(.+)$`)\n)\n\n// conditionTypeStatusJSON is a custom JSON to map a complex object into a standard JSON object. It maps Groups,\n// Versions and Kinds to Conditions, Types, and Status, indicating with a flag if a certain condition with specific\n// status represents an error or not. It is expected to be something like:\n//\n//\t{\n//\t\t\"gvk\": \t\t\t\"helm.cattle.io/v1, Kind=HelmChart\",\n//\t\t\"conditionMappings\": [\n//\t\t\t{\n//\t\t\t\t\"type\": \"JobCreated\"\t// This means JobCreated is mostly informational and True or False\n//\t\t\t},\t\t\t\t// doesn't mean error\n//\t\t\t{\n//\t\t\t\t\"type\": \"Failed\",\t// This means Failed is considered error if it's status is True\n//\t\t\t\t\"status\": [\"True\"]\n//\t\t\t},\n//\t\t}\n//\t}\ntype conditionTypeStatusJSON struct {\n\tGVK               string                     `json:\"gvk\"`\n\tConditionMappings []conditionStatusErrorJSON `json:\"conditionMappings\"`\n}\n\ntype conditionStatusErrorJSON struct {\n\tType   string                   `json:\"type\"`\n\tStatus []metav1.ConditionStatus `json:\"status,omitempty\"`\n}\n\ntype ConditionTypeStatusErrorMapping map[schema.GroupVersionKind]map[string]sets.Set[metav1.ConditionStatus]\n\nfunc (m ConditionTypeStatusErrorMapping) MarshalJSON() ([]byte, error) {\n\toutput := []conditionTypeStatusJSON{}\n\tfor gvk, mapping := range m {\n\t\ttypeStatus := conditionTypeStatusJSON{GVK: gvk.String()}\n\t\tfor condition, statuses := range mapping {\n\t\t\ttypeStatus.ConditionMappings = append(typeStatus.ConditionMappings, conditionStatusErrorJSON{\n\t\t\t\tType:   condition,\n\t\t\t\tStatus: statuses.UnsortedList(),\n\t\t\t})\n\t\t}\n\t\toutput = append(output, typeStatus)\n\t}\n\treturn json.Marshal(output)\n}\n\nfunc (m ConditionTypeStatusErrorMapping) UnmarshalJSON(data []byte) error {\n\tvar conditionMappingsJSON []conditionTypeStatusJSON\n\terr := json.Unmarshal(data, &conditionMappingsJSON)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, mapping := range conditionMappingsJSON {\n\t\t// checking if mapping.GVK is in the right format: <group>/[version], Kind=[kind]\n\t\tmx := gvkRegExp.FindStringSubmatch(mapping.GVK)\n\t\tif len(mx) == 0 {\n\t\t\treturn fmt.Errorf(\"gvk parsing failed: wrong GVK format: <%s>\", mapping.GVK)\n\t\t}\n\n\t\tconditionMappings := map[string]sets.Set[metav1.ConditionStatus]{}\n\t\tfor _, condition := range mapping.ConditionMappings {\n\t\t\tconditionMappings[condition.Type] = sets.New[metav1.ConditionStatus](condition.Status...)\n\t\t}\n\n\t\tm[schema.GroupVersionKind{\n\t\t\tGroup:   mx[1],\n\t\t\tVersion: mx[2],\n\t\t\tKind:    mx[3],\n\t\t}] = conditionMappings\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/summary/gvk_test.go",
    "content": "package summary\n\nimport (\n\t\"encoding/json\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/util/sets\"\n)\n\nfunc TestConditionalTypeStatusErrorMapping_MarshalJSON(t *testing.T) {\n\ttestCases := []struct {\n\t\tname        string\n\t\tinput       ConditionTypeStatusErrorMapping\n\t\texpected    []byte\n\t\texpectedErr error\n\t}{\n\t\t{\n\t\t\tname: \"usual case\",\n\t\t\tinput: ConditionTypeStatusErrorMapping{\n\t\t\t\t{Group: \"helm.cattle.io\", Version: \"v1\", Kind: \"HelmChart\"}: {\n\t\t\t\t\t\"JobCreated\": sets.New[metav1.ConditionStatus](),\n\t\t\t\t\t\"Failed\":     sets.New[metav1.ConditionStatus](metav1.ConditionTrue),\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []byte(`\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\t\"gvk\": \"helm.cattle.io/v1, Kind=HelmChart\",\n\t\t\t\t\t\"conditionMappings\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"JobCreated\"\t\n\t\t\t\t\t\t},\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"Failed\",\t\n\t\t\t\t\t\t\t\"status\": [\"True\"]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t]\n\t\t\t`),\n\t\t\texpectedErr: nil,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\toutput, err := json.Marshal(&tc.input)\n\n\t\t\tif tc.expectedErr == nil {\n\t\t\t\tassert.NoError(t, err)\n\t\t\t} else {\n\t\t\t\tassert.Error(t, err)\n\t\t\t}\n\n\t\t\tactual := []conditionTypeStatusJSON{}\n\t\t\texpected := []conditionTypeStatusJSON{}\n\t\t\tassert.NoError(t, json.Unmarshal(output, &actual))\n\t\t\tassert.NoError(t, json.Unmarshal(tc.expected, &expected))\n\n\t\t\tsort.Slice(actual, func(i, j int) bool { return actual[i].GVK < actual[j].GVK })\n\t\t\tsort.Slice(expected, func(i, j int) bool { return expected[i].GVK < expected[j].GVK })\n\n\t\t\tfor _, act := range actual {\n\t\t\t\tsort.Slice(act.ConditionMappings, func(i, j int) bool {\n\t\t\t\t\treturn act.ConditionMappings[i].Type < act.ConditionMappings[j].Type\n\t\t\t\t})\n\n\t\t\t\tfor _, mappings := range act.ConditionMappings {\n\t\t\t\t\tsort.Slice(mappings.Status, func(i, j int) bool {\n\t\t\t\t\t\treturn mappings.Status[i] < mappings.Status[j]\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor _, exp := range expected {\n\t\t\t\tsort.Slice(exp.ConditionMappings, func(i, j int) bool {\n\t\t\t\t\treturn exp.ConditionMappings[i].Type < exp.ConditionMappings[j].Type\n\t\t\t\t})\n\n\t\t\t\tfor _, mappings := range exp.ConditionMappings {\n\t\t\t\t\tsort.Slice(mappings.Status, func(i, j int) bool {\n\t\t\t\t\t\treturn mappings.Status[i] < mappings.Status[j]\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tassert.Equal(t, expected, actual)\n\t\t})\n\t}\n}\n\nfunc TestConditionalTypeStatusErrorMapping_UnmarshalJSON(t *testing.T) {\n\ttestCases := []struct {\n\t\tname            string\n\t\tinput           []byte\n\t\texpected        ConditionTypeStatusErrorMapping\n\t\terrorIsExpected bool\n\t}{\n\t\t{\n\t\t\tname: \"usual case\",\n\t\t\tinput: []byte(`\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\t\"gvk\": \"helm.cattle.io/v1, Kind=HelmChart\",\n\t\t\t\t\t\"conditionMappings\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"JobCreated\"\t\n\t\t\t\t\t\t},\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"Failed\",\t\n\t\t\t\t\t\t\t\"status\": [\"True\"]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t]\n\t\t\t`),\n\t\t\texpected: ConditionTypeStatusErrorMapping{\n\t\t\t\t{Group: \"helm.cattle.io\", Version: \"v1\", Kind: \"HelmChart\"}: {\n\t\t\t\t\t\"JobCreated\": sets.New[metav1.ConditionStatus](),\n\t\t\t\t\t\"Failed\":     sets.New[metav1.ConditionStatus](metav1.ConditionTrue),\n\t\t\t\t},\n\t\t\t},\n\t\t\terrorIsExpected: false,\n\t\t},\n\t\t{\n\t\t\tname: \"core types (no group)\",\n\t\t\tinput: []byte(`\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\t\"gvk\": \"/v1, Kind=Node\",\n\t\t\t\t\t\"conditionMappings\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"Ready\",\n\t\t\t\t\t\t\t\"status\": [\"False\"]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t]\n\t\t\t`),\n\t\t\texpected: ConditionTypeStatusErrorMapping{\n\t\t\t\t{Group: \"\", Version: \"v1\", Kind: \"Node\"}: {\n\t\t\t\t\t\"Ready\": sets.New[metav1.ConditionStatus](metav1.ConditionFalse),\n\t\t\t\t},\n\t\t\t},\n\t\t\terrorIsExpected: false,\n\t\t},\n\t\t{\n\t\t\tname: \"wrong GVK format\",\n\t\t\tinput: []byte(`\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\t\"gvk\": \"wrong GVK format\",\n\t\t\t\t\t\"conditionMappings\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"Ready\",\n\t\t\t\t\t\t\t\"status\": [\"False\"]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t]\n\t\t\t`),\n\t\t\texpected:        ConditionTypeStatusErrorMapping{},\n\t\t\terrorIsExpected: true,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tgvkConditionMappings := ConditionTypeStatusErrorMapping{}\n\n\t\t\terr := json.Unmarshal(tc.input, &gvkConditionMappings)\n\t\t\tif tc.errorIsExpected {\n\t\t\t\tassert.Error(t, err)\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, err)\n\t\t\t}\n\n\t\t\tassert.Equal(t, tc.expected, gvkConditionMappings)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/summary/informer/informer.go",
    "content": "/*\nCopyright 2018 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage informer\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/summary\"\n\t\"github.com/rancher/wrangler/v3/pkg/summary/client\"\n\t\"github.com/rancher/wrangler/v3/pkg/summary/lister\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/watch\"\n\t\"k8s.io/client-go/informers\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\n// NewSummarySharedInformerFactory constructs a new instance of summarySharedInformerFactory for all namespaces.\nfunc NewSummarySharedInformerFactory(client client.Interface, defaultResync time.Duration) SummarySharedInformerFactory {\n\treturn NewFilteredSummarySharedInformerFactory(client, defaultResync, metav1.NamespaceAll, nil)\n}\n\n// NewFilteredSummarySharedInformerFactory constructs a new instance of summarySharedInformerFactory.\n// Listers obtained via this factory will be subject to the same filters as specified here.\nfunc NewFilteredSummarySharedInformerFactory(client client.Interface, defaultResync time.Duration, namespace string, tweakListOptions TweakListOptionsFunc) SummarySharedInformerFactory {\n\treturn &summarySharedInformerFactory{\n\t\tclient:           client,\n\t\tdefaultResync:    defaultResync,\n\t\tnamespace:        namespace,\n\t\tinformers:        map[schema.GroupVersionResource]informers.GenericInformer{},\n\t\tstartedInformers: make(map[schema.GroupVersionResource]bool),\n\t\ttweakListOptions: tweakListOptions,\n\t}\n}\n\ntype summarySharedInformerFactory struct {\n\tclient        client.Interface\n\tdefaultResync time.Duration\n\tnamespace     string\n\n\tlock      sync.Mutex\n\tinformers map[schema.GroupVersionResource]informers.GenericInformer\n\t// startedInformers is used for tracking which informers have been started.\n\t// This allows Start() to be called multiple times safely.\n\tstartedInformers map[schema.GroupVersionResource]bool\n\ttweakListOptions TweakListOptionsFunc\n}\n\nvar _ SummarySharedInformerFactory = &summarySharedInformerFactory{}\n\nfunc (f *summarySharedInformerFactory) ForResource(gvr schema.GroupVersionResource) informers.GenericInformer {\n\tf.lock.Lock()\n\tdefer f.lock.Unlock()\n\n\tkey := gvr\n\tinformer, exists := f.informers[key]\n\tif exists {\n\t\treturn informer\n\t}\n\n\tinformer = NewFilteredSummaryInformer(f.client, gvr, f.namespace, f.defaultResync, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)\n\tf.informers[key] = informer\n\n\treturn informer\n}\n\n// Start initializes all requested informers.\nfunc (f *summarySharedInformerFactory) Start(stopCh <-chan struct{}) {\n\tf.lock.Lock()\n\tdefer f.lock.Unlock()\n\n\tfor informerType, informer := range f.informers {\n\t\tif !f.startedInformers[informerType] {\n\t\t\tgo informer.Informer().Run(stopCh)\n\t\t\tf.startedInformers[informerType] = true\n\t\t}\n\t}\n}\n\n// WaitForCacheSync waits for all started informers' cache were synced.\nfunc (f *summarySharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[schema.GroupVersionResource]bool {\n\tinformers := func() map[schema.GroupVersionResource]cache.SharedIndexInformer {\n\t\tf.lock.Lock()\n\t\tdefer f.lock.Unlock()\n\n\t\tinformers := map[schema.GroupVersionResource]cache.SharedIndexInformer{}\n\t\tfor informerType, informer := range f.informers {\n\t\t\tif f.startedInformers[informerType] {\n\t\t\t\tinformers[informerType] = informer.Informer()\n\t\t\t}\n\t\t}\n\t\treturn informers\n\t}()\n\n\tres := map[schema.GroupVersionResource]bool{}\n\tfor informType, informer := range informers {\n\t\tres[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced)\n\t}\n\treturn res\n}\n\nfunc NewFilteredSummaryInformerWithOptions(\n\tclient client.ExtendedInterface,\n\tgvr schema.GroupVersionResource,\n\topts *client.Options,\n\tnamespace string,\n\tresyncPeriod time.Duration,\n\tindexers cache.Indexers,\n\ttweakListOptions TweakListOptionsFunc,\n) informers.GenericInformer {\n\tlw := &cache.ListWatch{\n\t\tListFunc: func(options metav1.ListOptions) (runtime.Object, error) {\n\t\t\tif tweakListOptions != nil {\n\t\t\t\ttweakListOptions(&options)\n\t\t\t}\n\t\t\treturn client.ResourceWithOptions(gvr, opts).Namespace(namespace).List(context.TODO(), options)\n\t\t},\n\t\tWatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {\n\t\t\tif tweakListOptions != nil {\n\t\t\t\ttweakListOptions(&options)\n\t\t\t}\n\t\t\treturn client.ResourceWithOptions(gvr, opts).Namespace(namespace).Watch(context.TODO(), options)\n\t\t},\n\t}\n\treturn &summaryInformer{\n\t\tgvr: gvr,\n\t\tinformer: cache.NewSharedIndexInformer(\n\t\t\ttoListWatcherWithWatchListSemantics(lw, client),\n\t\t\t&summary.SummarizedObject{},\n\t\t\tresyncPeriod,\n\t\t\tindexers,\n\t\t),\n\t}\n}\n\n// NewFilteredSummaryInformer constructs a new informer for a summary type.\nfunc NewFilteredSummaryInformer(client client.Interface, gvr schema.GroupVersionResource, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions TweakListOptionsFunc) informers.GenericInformer {\n\tlw := &cache.ListWatch{\n\t\tListFunc: func(options metav1.ListOptions) (runtime.Object, error) {\n\t\t\tif tweakListOptions != nil {\n\t\t\t\ttweakListOptions(&options)\n\t\t\t}\n\t\t\treturn client.Resource(gvr).Namespace(namespace).List(context.TODO(), options)\n\t\t},\n\t\tWatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {\n\t\t\tif tweakListOptions != nil {\n\t\t\t\ttweakListOptions(&options)\n\t\t\t}\n\t\t\treturn client.Resource(gvr).Namespace(namespace).Watch(context.TODO(), options)\n\t\t},\n\t}\n\treturn &summaryInformer{\n\t\tgvr: gvr,\n\t\tinformer: cache.NewSharedIndexInformer(\n\t\t\ttoListWatcherWithWatchListSemantics(lw, client),\n\t\t\t&summary.SummarizedObject{},\n\t\t\tresyncPeriod,\n\t\t\tindexers,\n\t\t),\n\t}\n}\n\ntype summaryInformer struct {\n\tinformer cache.SharedIndexInformer\n\tgvr      schema.GroupVersionResource\n}\n\nvar _ informers.GenericInformer = &summaryInformer{}\n\nfunc (d *summaryInformer) Informer() cache.SharedIndexInformer {\n\treturn d.informer\n}\n\nfunc (d *summaryInformer) Lister() cache.GenericLister {\n\treturn lister.NewRuntimeObjectShim(lister.New(d.informer.GetIndexer(), d.gvr))\n}\n"
  },
  {
    "path": "pkg/summary/informer/informer_test.go",
    "content": "package informer\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/summary\"\n\t\"github.com/rancher/wrangler/v3/pkg/summary/client\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/watch\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\nvar _ client.ExtendedInterface = (*mockClient)(nil)\n\ntype mockClient struct {\n\tlistCalled  chan struct{}\n\twatchCalled chan struct{}\n}\n\nfunc (m *mockClient) Resource(resource schema.GroupVersionResource) client.NamespaceableResourceInterface {\n\treturn m\n}\n\nfunc (m *mockClient) ResourceWithOptions(resource schema.GroupVersionResource, opts *client.Options) client.NamespaceableResourceInterface {\n\treturn m\n}\n\nfunc (m *mockClient) Namespace(string) client.ResourceInterface {\n\treturn m\n}\n\nfunc (m *mockClient) List(ctx context.Context, opts metav1.ListOptions) (*summary.SummarizedObjectList, error) {\n\tif m.listCalled != nil {\n\t\tm.listCalled <- struct{}{}\n\t}\n\treturn &summary.SummarizedObjectList{\n\t\tListMeta: metav1.ListMeta{\n\t\t\tResourceVersion: \"1\",\n\t\t},\n\t}, nil\n}\n\nfunc (m *mockClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {\n\tif m.watchCalled != nil {\n\t\tm.watchCalled <- struct{}{}\n\t}\n\treturn watch.NewFake(), nil\n}\n\ntype mockClientUnsupported struct {\n\tmockClient\n}\n\nfunc (m *mockClientUnsupported) IsWatchListSemanticsUnSupported() bool {\n\treturn true\n}\n\ntype mockClientSupported struct {\n\tmockClient\n}\n\nfunc (m *mockClientSupported) IsWatchListSemanticsUnSupported() bool {\n\treturn false\n}\n\nfunc TestNewFilteredSummaryInformer_WatchListSupport(t *testing.T) {\n\tgvr := schema.GroupVersionResource{Group: \"test\", Version: \"v1\", Resource: \"tests\"}\n\tnamespace := \"default\"\n\n\ttests := []struct {\n\t\tname        string\n\t\tclient      client.Interface\n\t\texpectList  bool\n\t\texpectWatch bool\n\t}{\n\t\t{\n\t\t\tname: \"Client supporting watchlist (default)\",\n\t\t\tclient: &mockClient{\n\t\t\t\tlistCalled:  make(chan struct{}, 1),\n\t\t\t\twatchCalled: make(chan struct{}, 1),\n\t\t\t},\n\t\t\texpectList: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Client explicitly supporting watchlist\",\n\t\t\tclient: &mockClientSupported{\n\t\t\t\tmockClient: mockClient{\n\t\t\t\t\tlistCalled:  make(chan struct{}, 1),\n\t\t\t\t\twatchCalled: make(chan struct{}, 1),\n\t\t\t\t},\n\t\t\t},\n\t\t\texpectList: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Client explicitly NOT supporting watchlist\",\n\t\t\tclient: &mockClientUnsupported{\n\t\t\t\tmockClient: mockClient{\n\t\t\t\t\tlistCalled:  make(chan struct{}, 1),\n\t\t\t\t\twatchCalled: make(chan struct{}, 1),\n\t\t\t\t},\n\t\t\t},\n\t\t\texpectList: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tinformer := NewFilteredSummaryInformer(tt.client, gvr, namespace, 0, cache.Indexers{}, nil)\n\t\t\tstopCh := make(chan struct{})\n\t\t\tdefer close(stopCh)\n\n\t\t\tgo informer.Informer().Run(stopCh)\n\n\t\t\t// Wait for either List or Watch to be called\n\t\t\tvar mc *mockClient\n\t\t\tswitch c := tt.client.(type) {\n\t\t\tcase *mockClient:\n\t\t\t\tmc = c\n\t\t\tcase *mockClientSupported:\n\t\t\t\tmc = &c.mockClient\n\t\t\tcase *mockClientUnsupported:\n\t\t\t\tmc = &c.mockClient\n\t\t\t}\n\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\t\t\tlistCalled := false\n\t\t\twatchCalled := false\n\n\t\t\tselect {\n\t\t\tcase <-time.After(100 * time.Millisecond):\n\t\t\tcase <-mc.listCalled:\n\t\t\t\tlistCalled = true\n\t\t\t}\n\n\t\t\tselect {\n\t\t\tcase <-time.After(100 * time.Millisecond):\n\t\t\tcase <-mc.watchCalled:\n\t\t\t\twatchCalled = true\n\t\t\t}\n\n\t\t\tif tt.expectList && !listCalled {\n\t\t\t\tt.Fatal(\"Expected list call but didn't get it\")\n\t\t\t}\n\n\t\t\tif !tt.expectList && listCalled {\n\t\t\t\tt.Fatal(\"Expected NO list call\")\n\t\t\t}\n\n\t\t\tif !watchCalled {\n\t\t\t\tt.Fatal(\"Expected watch call but didn't get it\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNewFilteredSummaryInformerWithOptions_WatchListSupport(t *testing.T) {\n\tgvr := schema.GroupVersionResource{Group: \"test\", Version: \"v1\", Resource: \"tests\"}\n\tnamespace := \"default\"\n\n\ttests := []struct {\n\t\tname        string\n\t\tclient      client.ExtendedInterface\n\t\texpectList  bool\n\t\texpectWatch bool\n\t}{\n\t\t{\n\t\t\tname: \"Client supporting watchlist (default)\",\n\t\t\tclient: &mockClient{\n\t\t\t\tlistCalled:  make(chan struct{}, 1),\n\t\t\t\twatchCalled: make(chan struct{}, 1),\n\t\t\t},\n\t\t\texpectList: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Client explicitly supporting watchlist\",\n\t\t\tclient: &mockClientSupported{\n\t\t\t\tmockClient: mockClient{\n\t\t\t\t\tlistCalled:  make(chan struct{}, 1),\n\t\t\t\t\twatchCalled: make(chan struct{}, 1),\n\t\t\t\t},\n\t\t\t},\n\t\t\texpectList: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Client explicitly NOT supporting watchlist\",\n\t\t\tclient: &mockClientUnsupported{\n\t\t\t\tmockClient: mockClient{\n\t\t\t\t\tlistCalled:  make(chan struct{}, 1),\n\t\t\t\t\twatchCalled: make(chan struct{}, 1),\n\t\t\t\t},\n\t\t\t},\n\t\t\texpectList: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tinformer := NewFilteredSummaryInformerWithOptions(tt.client, gvr, nil, namespace, 0, cache.Indexers{}, nil)\n\t\t\tstopCh := make(chan struct{})\n\t\t\tdefer close(stopCh)\n\n\t\t\tgo informer.Informer().Run(stopCh)\n\n\t\t\t// Wait for either List or Watch to be called\n\t\t\tvar mc *mockClient\n\t\t\tswitch c := tt.client.(type) {\n\t\t\tcase *mockClient:\n\t\t\t\tmc = c\n\t\t\tcase *mockClientSupported:\n\t\t\t\tmc = &c.mockClient\n\t\t\tcase *mockClientUnsupported:\n\t\t\t\tmc = &c.mockClient\n\t\t\t}\n\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\t\t\tlistCalled := false\n\t\t\twatchCalled := false\n\n\t\t\tselect {\n\t\t\tcase <-time.After(100 * time.Millisecond):\n\t\t\tcase <-mc.listCalled:\n\t\t\t\tlistCalled = true\n\t\t\t}\n\n\t\t\tselect {\n\t\t\tcase <-time.After(100 * time.Millisecond):\n\t\t\tcase <-mc.watchCalled:\n\t\t\t\twatchCalled = true\n\t\t\t}\n\n\t\t\tif tt.expectList && !listCalled {\n\t\t\t\tt.Fatal(\"Expected list call but didn't get it\")\n\t\t\t}\n\n\t\t\tif !tt.expectList && listCalled {\n\t\t\t\tt.Fatal(\"Expected NO list call\")\n\t\t\t}\n\n\t\t\tif !watchCalled {\n\t\t\t\tt.Fatal(\"Expected watch call but didn't get it\")\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/summary/informer/interface.go",
    "content": "/*\nCopyright 2018 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage informer\n\nimport (\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/client-go/informers\"\n)\n\n// SummarySharedInformerFactory provides access to a shared informer and lister for dynamic client\ntype SummarySharedInformerFactory interface {\n\tStart(stopCh <-chan struct{})\n\tForResource(gvr schema.GroupVersionResource) informers.GenericInformer\n\tWaitForCacheSync(stopCh <-chan struct{}) map[schema.GroupVersionResource]bool\n}\n\n// TweakListOptionsFunc defines the signature of a helper function\n// that wants to provide more listing options to API\ntype TweakListOptionsFunc func(*metav1.ListOptions)\n"
  },
  {
    "path": "pkg/summary/informer/watchlist.go",
    "content": "package informer\n\nimport (\n\t\"k8s.io/client-go/tools/cache\"\n)\n\n// Copied from Kubernetes' source, only available in k8s 1.35\n\ntype listWatcherWithWatchListSemanticsWrapper struct {\n\t*cache.ListWatch\n\n\t// unsupportedWatchListSemantics indicates whether a client explicitly does NOT support\n\t// WatchList semantics.\n\t//\n\t// Over the years, unit tests in kube have been written in many different ways.\n\t// After enabling the WatchListClient feature by default, existing tests started failing.\n\t// To avoid breaking lots of existing client-go users after upgrade,\n\t// we introduced this field as an opt-in.\n\t//\n\t// When true, the reflector disables WatchList even if the feature gate is enabled.\n\tunsupportedWatchListSemantics bool\n}\n\nfunc (lw *listWatcherWithWatchListSemanticsWrapper) IsWatchListSemanticsUnSupported() bool {\n\treturn lw.unsupportedWatchListSemantics\n}\n\n// toListWatcherWithWatchListSemantics returns a ListerWatcher\n// that knows whether the provided client explicitly\n// does NOT support the WatchList semantics. This allows Reflectors\n// to adapt their behavior based on client capabilities.\nfunc toListWatcherWithWatchListSemantics(lw *cache.ListWatch, client any) cache.ListerWatcher {\n\treturn &listWatcherWithWatchListSemanticsWrapper{\n\t\tlw,\n\t\tdoesClientNotSupportWatchListSemantics(client),\n\t}\n}\n\ntype unSupportedWatchListSemantics interface {\n\tIsWatchListSemanticsUnSupported() bool\n}\n\n// doesClientNotSupportWatchListSemantics reports whether the given client\n// does NOT support WatchList semantics.\n//\n// A client does NOT support WatchList only if\n// it implements `IsWatchListSemanticsUnSupported` and that returns true.\nfunc doesClientNotSupportWatchListSemantics(client any) bool {\n\tlw, ok := client.(unSupportedWatchListSemantics)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn lw.IsWatchListSemanticsUnSupported()\n}\n"
  },
  {
    "path": "pkg/summary/lister/interface.go",
    "content": "/*\nCopyright 2018 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage lister\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/summary\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n)\n\n// Lister helps list resources.\ntype Lister interface {\n\t// List lists all resources in the indexer.\n\tList(selector labels.Selector) (ret []*summary.SummarizedObject, err error)\n\t// Get retrieves a resource from the indexer with the given name\n\tGet(name string) (*summary.SummarizedObject, error)\n\t// Namespace returns an object that can list and get resources in a given namespace.\n\tNamespace(namespace string) NamespaceLister\n}\n\n// NamespaceLister helps list and get resources.\ntype NamespaceLister interface {\n\t// List lists all resources in the indexer for a given namespace.\n\tList(selector labels.Selector) (ret []*summary.SummarizedObject, err error)\n\t// Get retrieves a resource from the indexer for a given namespace and name.\n\tGet(name string) (*summary.SummarizedObject, error)\n}\n"
  },
  {
    "path": "pkg/summary/lister/lister.go",
    "content": "/*\nCopyright 2018 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage lister\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/summary\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\nvar _ Lister = &summaryLister{}\nvar _ NamespaceLister = &summaryNamespaceLister{}\n\n// summaryLister implements the Lister interface.\ntype summaryLister struct {\n\tindexer cache.Indexer\n\tgvr     schema.GroupVersionResource\n}\n\n// New returns a new Lister.\nfunc New(indexer cache.Indexer, gvr schema.GroupVersionResource) Lister {\n\treturn &summaryLister{indexer: indexer, gvr: gvr}\n}\n\n// List lists all resources in the indexer.\nfunc (l *summaryLister) List(selector labels.Selector) (ret []*summary.SummarizedObject, err error) {\n\terr = cache.ListAll(l.indexer, selector, func(m interface{}) {\n\t\tret = append(ret, m.(*summary.SummarizedObject))\n\t})\n\treturn ret, err\n}\n\n// Get retrieves a resource from the indexer with the given name\nfunc (l *summaryLister) Get(name string) (*summary.SummarizedObject, error) {\n\tobj, exists, err := l.indexer.GetByKey(name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !exists {\n\t\treturn nil, errors.NewNotFound(l.gvr.GroupResource(), name)\n\t}\n\treturn obj.(*summary.SummarizedObject), nil\n}\n\n// Namespace returns an object that can list and get resources from a given namespace.\nfunc (l *summaryLister) Namespace(namespace string) NamespaceLister {\n\treturn &summaryNamespaceLister{indexer: l.indexer, namespace: namespace, gvr: l.gvr}\n}\n\n// summaryNamespaceLister implements the NamespaceLister interface.\ntype summaryNamespaceLister struct {\n\tindexer   cache.Indexer\n\tnamespace string\n\tgvr       schema.GroupVersionResource\n}\n\n// List lists all resources in the indexer for a given namespace.\nfunc (l *summaryNamespaceLister) List(selector labels.Selector) (ret []*summary.SummarizedObject, err error) {\n\terr = cache.ListAllByNamespace(l.indexer, l.namespace, selector, func(m interface{}) {\n\t\tret = append(ret, m.(*summary.SummarizedObject))\n\t})\n\treturn ret, err\n}\n\n// Get retrieves a resource from the indexer for a given namespace and name.\nfunc (l *summaryNamespaceLister) Get(name string) (*summary.SummarizedObject, error) {\n\tobj, exists, err := l.indexer.GetByKey(l.namespace + \"/\" + name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !exists {\n\t\treturn nil, errors.NewNotFound(l.gvr.GroupResource(), name)\n\t}\n\treturn obj.(*summary.SummarizedObject), nil\n}\n"
  },
  {
    "path": "pkg/summary/lister/shim.go",
    "content": "/*\nCopyright 2018 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage lister\n\nimport (\n\t\"k8s.io/apimachinery/pkg/labels\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/client-go/tools/cache\"\n)\n\nvar _ cache.GenericLister = &summaryListerShim{}\nvar _ cache.GenericNamespaceLister = &summaryNamespaceListerShim{}\n\n// summaryListerShim implements the cache.GenericLister interface.\ntype summaryListerShim struct {\n\tlister Lister\n}\n\n// NewRuntimeObjectShim returns a new shim for Lister.\n// It wraps Lister so that it implements cache.GenericLister interface\nfunc NewRuntimeObjectShim(lister Lister) cache.GenericLister {\n\treturn &summaryListerShim{lister: lister}\n}\n\n// List will return all objects across namespaces\nfunc (s *summaryListerShim) List(selector labels.Selector) (ret []runtime.Object, err error) {\n\tobjs, err := s.lister.List(selector)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tret = make([]runtime.Object, len(objs))\n\tfor index, obj := range objs {\n\t\tret[index] = obj\n\t}\n\treturn ret, err\n}\n\n// Get will attempt to retrieve assuming that name==key\nfunc (s *summaryListerShim) Get(name string) (runtime.Object, error) {\n\treturn s.lister.Get(name)\n}\n\nfunc (s *summaryListerShim) ByNamespace(namespace string) cache.GenericNamespaceLister {\n\treturn &summaryNamespaceListerShim{\n\t\tnamespaceLister: s.lister.Namespace(namespace),\n\t}\n}\n\n// summaryNamespaceListerShim implements the NamespaceLister interface.\n// It wraps NamespaceLister so that it implements cache.GenericNamespaceLister interface\ntype summaryNamespaceListerShim struct {\n\tnamespaceLister NamespaceLister\n}\n\n// List will return all objects in this namespace\nfunc (ns *summaryNamespaceListerShim) List(selector labels.Selector) (ret []runtime.Object, err error) {\n\tobjs, err := ns.namespaceLister.List(selector)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tret = make([]runtime.Object, len(objs))\n\tfor index, obj := range objs {\n\t\tret[index] = obj\n\t}\n\treturn ret, err\n}\n\n// Get will attempt to retrieve by namespace and name\nfunc (ns *summaryNamespaceListerShim) Get(name string) (runtime.Object, error) {\n\treturn ns.namespaceLister.Get(name)\n}\n"
  },
  {
    "path": "pkg/summary/summarized.go",
    "content": "package summary\n\nimport (\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n)\n\ntype SummarizedObject struct {\n\tmetav1.PartialObjectMetadata\n\tSummary\n}\n\ntype SummarizedObjectList struct {\n\tmetav1.TypeMeta `json:\",inline\"`\n\tmetav1.ListMeta `json:\"metadata,omitempty\" protobuf:\"bytes,1,opt,name=metadata\"`\n\tItems           []SummarizedObject `json:\"items\" protobuf:\"bytes,2,rep,name=items\"`\n}\n\nfunc Summarized(u runtime.Object) *SummarizedObject {\n\treturn SummarizedWithOptions(u, nil)\n}\n\nfunc SummarizedWithOptions(u runtime.Object, opts *SummarizeOptions) *SummarizedObject {\n\tif s, ok := u.(*SummarizedObject); ok {\n\t\treturn s\n\t}\n\n\ts := &SummarizedObject{\n\t\tSummary: SummarizeWithOptions(u, opts),\n\t}\n\ts.APIVersion, s.Kind = u.GetObjectKind().GroupVersionKind().ToAPIVersionAndKind()\n\n\tmeta, err := meta.Accessor(u)\n\tif err == nil {\n\t\ts.Name = meta.GetName()\n\t\ts.Namespace = meta.GetNamespace()\n\t\ts.Generation = meta.GetGeneration()\n\t\ts.UID = meta.GetUID()\n\t\ts.ResourceVersion = meta.GetResourceVersion()\n\t\ts.CreationTimestamp = meta.GetCreationTimestamp()\n\t\ts.DeletionTimestamp = meta.GetDeletionTimestamp()\n\t\ts.Labels = meta.GetLabels()\n\t\ts.Annotations = meta.GetAnnotations()\n\t}\n\n\treturn s\n}\n\nfunc (in *SummarizedObjectList) DeepCopyInto(out *SummarizedObjectList) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tin.ListMeta.DeepCopyInto(&out.ListMeta)\n\tif in.Items != nil {\n\t\tin, out := &in.Items, &out.Items\n\t\t*out = make([]SummarizedObject, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n}\n\nfunc (in *SummarizedObjectList) DeepCopy() *SummarizedObjectList {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(SummarizedObjectList)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\nfunc (in *SummarizedObjectList) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n\nfunc (in *SummarizedObject) DeepCopyInto(out *SummarizedObject) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tout.ObjectMeta = *in.ObjectMeta.DeepCopy()\n\tout.Summary = *in.Summary.DeepCopy()\n}\n\nfunc (in *SummarizedObject) DeepCopy() *SummarizedObject {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(SummarizedObject)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\nfunc (in *SummarizedObject) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/summary/summarizers.go",
    "content": "package summary\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n\t\"github.com/rancher/wrangler/v3/pkg/kv\"\n\t\"github.com/sirupsen/logrus\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\tkstatus \"sigs.k8s.io/cli-utils/pkg/kstatus/status\"\n\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/util/sets\"\n)\n\nconst (\n\tkindSep                    = \", Kind=\"\n\treason                     = \"%REASON%\"\n\tcheckGVKErrorMappingEnvVar = \"CATTLE_WRANGLER_CHECK_GVK_ERROR_MAPPING\"\n)\n\nvar (\n\t// True ==\n\t// False == error\n\t// Unknown == transitioning\n\tTransitioningUnknown = map[string]string{\n\t\t\"Active\":                      \"activating\",\n\t\t\"AddonDeploy\":                 \"provisioning\",\n\t\t\"AgentDeployed\":               \"provisioning\",\n\t\t\"BackingNamespaceCreated\":     \"configuring\",\n\t\t\"Built\":                       \"building\",\n\t\t\"CertsGenerated\":              \"provisioning\",\n\t\t\"ConfigOK\":                    \"configuring\",\n\t\t\"Created\":                     \"creating\",\n\t\t\"CreatorMadeOwner\":            \"configuring\",\n\t\t\"DefaultNamespaceAssigned\":    \"configuring\",\n\t\t\"DefaultNetworkPolicyCreated\": \"configuring\",\n\t\t\"DefaultProjectCreated\":       \"configuring\",\n\t\t\"DockerProvisioned\":           \"provisioning\",\n\t\t\"Deployed\":                    \"deploying\",\n\t\t\"Drained\":                     \"draining\",\n\t\t\"Downloaded\":                  \"downloading\",\n\t\t\"etcd\":                        \"provisioning\",\n\t\t\"Inactive\":                    \"deactivating\",\n\t\t\"Initialized\":                 \"initializing\",\n\t\t\"Installed\":                   \"installing\",\n\t\t\"NodesCreated\":                \"provisioning\",\n\t\t\"Pending\":                     \"pending\",\n\t\t\"PodScheduled\":                \"scheduling\",\n\t\t\"Provisioned\":                 \"provisioning\",\n\t\t\"Reconciled\":                  \"reconciling\", // CAPI Machine, RKEControlPlane\n\t\t\"Refreshed\":                   \"refreshed\",\n\t\t\"Registered\":                  \"registering\",\n\t\t\"Removed\":                     \"removing\",\n\t\t\"Saved\":                       \"saving\",\n\t\t\"Updated\":                     \"updating\",\n\t\t\"Updating\":                    \"updating\",\n\t\t\"Upgraded\":                    \"upgrading\",\n\t\t\"Waiting\":                     \"waiting\",\n\t\t\"InitialRolesPopulated\":       \"activating\",\n\t\t\"ScalingActive\":               \"pending\",\n\t\t\"AbleToScale\":                 \"pending\",\n\t\t\"RunCompleted\":                \"running\",\n\t\t\"Processed\":                   \"processed\",\n\t\t\"NodeHealthy\":                 reason, // CAPI Machine\n\t\t\"NodeReady\":                   reason, // CAPI Machine\n\t}\n\n\t// For given GVK, This condition Type and this Status, indicates an error or not\n\t// e.g.: GVK: helm.cattle.io/v1, HelmChart\n\t//\t\t--> JobCreated: [], indicates True or False are not errors\n\t//\t\t--> Failed: [\"True\"], indicates \"True\" status is considered error\n\t//\t\t--> Worked: [\"False\"], indicates \"False\" status is considered error\n\t//\t\t--> Unknown: [\"True\", \"False\"] indicated \"True\" or \"False\" are considered errors\n\tGVKConditionErrorMapping = ConditionTypeStatusErrorMapping{\n\t\t{Group: \"helm.cattle.io\", Version: \"v1\", Kind: \"HelmChart\"}: {\n\t\t\t\"JobCreated\": sets.New[metav1.ConditionStatus](),\n\t\t\t\"Failed\":     sets.New[metav1.ConditionStatus](metav1.ConditionTrue),\n\t\t},\n\t\t{Group: \"\", Version: \"v1\", Kind: \"Node\"}: {\n\t\t\t\"OutOfDisk\":          sets.New[metav1.ConditionStatus](metav1.ConditionTrue),\n\t\t\t\"MemoryPressure\":     sets.New[metav1.ConditionStatus](metav1.ConditionTrue),\n\t\t\t\"DiskPressure\":       sets.New[metav1.ConditionStatus](metav1.ConditionTrue),\n\t\t\t\"NetworkUnavailable\": sets.New[metav1.ConditionStatus](metav1.ConditionTrue),\n\t\t},\n\t\t{Group: \"apps\", Version: \"v1\", Kind: \"Deployment\"}: {\n\t\t\t\"ReplicaFailure\": sets.New[metav1.ConditionStatus](metav1.ConditionTrue),\n\t\t\t\"Progressing\":    sets.New[metav1.ConditionStatus](metav1.ConditionFalse),\n\t\t},\n\t\t{Group: \"apps\", Version: \"v1\", Kind: \"ReplicaSet\"}: {\n\t\t\t\"ReplicaFailure\": sets.New[metav1.ConditionStatus](metav1.ConditionTrue),\n\t\t},\n\n\t\t// FALLBACK: In case we cannot match any Groups, Versions and Kinds then we fallback to this mapping.\n\t\t{Group: \"\", Version: \"\", Kind: \"\"}: {\n\t\t\t\"Stalled\": sets.New[metav1.ConditionStatus](metav1.ConditionTrue),\n\t\t\t\"Failed\":  sets.New[metav1.ConditionStatus](metav1.ConditionTrue),\n\t\t},\n\t}\n\n\t// True ==\n\t// False == transitioning\n\t// Unknown == error\n\tTransitioningFalse = map[string]string{\n\t\t\"Completed\":            \"activating\",\n\t\t\"Ready\":                \"unavailable\",\n\t\t\"Available\":            \"updating\",\n\t\t\"BootstrapConfigReady\": reason,     // CAPI Machine\n\t\t\"InfrastructureReady\":  reason,     // CAPI Machine\n\t\t\"MachinesReady\":        \"updating\", // CAPI MachineDeployment, MachineSet\n\t}\n\n\t// True == transitioning\n\t// False ==\n\t// Unknown == error\n\tTransitioningTrue = map[string]string{\n\t\t\"Reconciling\": \"reconciling\",\n\t\t\"ScalingUp\":   reason, // CAPI Cluster, MachineDeployment, MachineSet\n\t\t\"ScalingDown\": reason, // CAPI Cluster, MachineDeployment, MachineSet\n\t\t\"Deleting\":    reason, // CAPI Cluster, MachineDeployment, MachineSet, Machine\n\t\t\"Paused\":      reason, // CAPI Cluster, MachineDeployment, MachineSet, Machine\n\t}\n\n\tSummarizers          []Summarizer\n\tConditionSummarizers []Summarizer\n)\n\ntype Summarizer func(obj data.Object, conditions []Condition, summary Summary) Summary\n\nfunc init() {\n\tConditionSummarizers = []Summarizer{\n\t\tcheckErrors,\n\t\tcheckGenericTransitioning,\n\t\tcheckRemoving,\n\t\tcheckCattleReady,\n\t}\n\n\tSummarizers = []Summarizer{\n\t\tcheckStatusSummary,\n\t\tcheckErrors,\n\t\tcheckTransitioning,\n\t\tcheckActive,\n\t\tcheckPhase,\n\t\tcheckInitializing,\n\t\tcheckRemoving,\n\t\tcheckStandard,\n\t\tcheckLoadBalancer,\n\t\tcheckPod,\n\t\tcheckHasPodSelector,\n\t\tcheckHasPodTemplate,\n\t\tcheckOwner,\n\t\tcheckApplyOwned,\n\t\tcheckCattleTypes,\n\t\tcheckGeneration,\n\t}\n\n\tinitializeCheckErrors()\n}\n\nfunc initializeCheckErrors() {\n\tgvkConfig := os.Getenv(checkGVKErrorMappingEnvVar)\n\tif gvkConfig != \"\" {\n\t\tlogrus.Debugf(\"GVK Error Mapping Provided\")\n\t\tgvkErrorMapping := ConditionTypeStatusErrorMapping{}\n\t\tif err := json.Unmarshal([]byte(gvkConfig), &gvkErrorMapping); err != nil {\n\t\t\tlogrus.Errorln(\"Unable to parse GVK config: \", err.Error())\n\t\t\treturn\n\t\t}\n\n\t\t// Merging GVK + Conditions\n\t\t//\n\t\t// IMPORTANT: In case you add a condition that exists already, we replace the set that holds the Status\n\t\t// completely of that condition by yours, this makes it possible to deactivate certain statuses for\n\t\t// debugging reasons.\n\t\t//\n\t\t// eg.:\n\t\t//\n\t\t// Existing one:\n\t\t//\n\t\t// helm.cattle.io, Kind=HelmChart\n\t\t// JobCreated => []\n\t\t// Failed => [\"True\"]\n\t\t//\n\t\t// In case you set Failed = [\"False\"] and add Ready = [\"False\"]:\n\t\t//\n\t\t// helm.cattle.io, Kind=HelmChart\n\t\t// JobCreated => []\t\t\t<<<= not changed\n\t\t// Failed => [\"False\"]\t\t<<<= replaced completely the set.\n\t\t// Ready => [\"False\"] \t\t<<<= merged to existing conditions.\n\t\t//\n\t\t// So, we've merged the conditions, but not the status set values.\n\t\tfor gvk, newConditionsMap := range gvkErrorMapping {\n\t\t\tif _, exists := GVKConditionErrorMapping[gvk]; !exists {\n\t\t\t\tGVKConditionErrorMapping[gvk] = map[string]sets.Set[metav1.ConditionStatus]{}\n\t\t\t}\n\n\t\t\texistingConditionsMap := GVKConditionErrorMapping[gvk]\n\t\t\tfor condition, errorMapping := range newConditionsMap {\n\t\t\t\texistingConditionsMap[condition] = errorMapping\n\t\t\t}\n\t\t\tGVKConditionErrorMapping[gvk] = existingConditionsMap\n\t\t}\n\t\tlogrus.Debugf(\"GVK Error Mapping Set\")\n\t\treturn\n\t}\n\tlogrus.Debugf(\"GVK Error Mapping not provided, using predefined values\")\n}\n\nfunc checkGeneration(obj data.Object, _ []Condition, summary Summary) Summary {\n\tif summary.State != \"\" {\n\t\treturn summary\n\t}\n\n\tif summary.HasObservedGeneration {\n\t\tmetadataGeneration, metadataFound, errMetadata := unstructured.NestedInt64(obj, \"metadata\", \"generation\")\n\t\tif errMetadata != nil {\n\t\t\treturn summary\n\t\t}\n\t\tif !metadataFound {\n\t\t\treturn summary\n\t\t}\n\n\t\tobservedGeneration, _, errObserved := unstructured.NestedInt64(obj, \"status\", \"observedGeneration\")\n\t\tif errObserved != nil {\n\t\t\treturn summary\n\t\t}\n\n\t\tif observedGeneration != metadataGeneration {\n\t\t\tsummary.State = \"in-progress\"\n\t\t\tsummary.Transitioning = true\n\t\t}\n\t}\n\n\treturn summary\n}\n\nfunc checkOwner(obj data.Object, conditions []Condition, summary Summary) Summary {\n\tustr := &unstructured.Unstructured{\n\t\tObject: obj,\n\t}\n\tfor _, ownerref := range ustr.GetOwnerReferences() {\n\t\trel := Relationship{\n\t\t\tName:       ownerref.Name,\n\t\t\tKind:       ownerref.Kind,\n\t\t\tAPIVersion: ownerref.APIVersion,\n\t\t\tType:       \"owner\",\n\t\t\tInbound:    true,\n\t\t}\n\t\tif ownerref.Controller != nil && *ownerref.Controller {\n\t\t\trel.ControlledBy = true\n\t\t}\n\n\t\tsummary.Relationships = append(summary.Relationships, rel)\n\t}\n\n\treturn summary\n}\n\nfunc checkStatusSummary(obj data.Object, _ []Condition, summary Summary) Summary {\n\tsummaryObj := obj.Map(\"status\", \"display\")\n\tif len(summaryObj) == 0 {\n\t\tsummaryObj = obj.Map(\"status\", \"summary\")\n\t\tif len(summaryObj) == 0 {\n\t\t\treturn summary\n\t\t}\n\t}\n\tobj = summaryObj\n\n\tif _, ok := obj[\"state\"]; ok {\n\t\tsummary.State = obj.String(\"state\")\n\t}\n\tif _, ok := obj[\"transitioning\"]; ok {\n\t\tsummary.Transitioning = obj.Bool(\"transitioning\")\n\t}\n\tif _, ok := obj[\"error\"]; ok {\n\t\tsummary.Error = obj.Bool(\"error\")\n\t}\n\tif _, ok := obj[\"message\"]; ok {\n\t\tsummary.Message = append(summary.Message, obj.String(\"message\"))\n\t}\n\n\treturn summary\n}\n\nfunc checkStandard(obj data.Object, _ []Condition, summary Summary) Summary {\n\tif summary.State != \"\" {\n\t\treturn summary\n\t}\n\n\t// this is a hack to not call the standard summarizers on norman mapped objects\n\tif strings.HasPrefix(obj.String(\"type\"), \"/\") {\n\t\treturn summary\n\t}\n\n\tresult, err := kstatus.Compute(&unstructured.Unstructured{Object: obj})\n\tif err != nil {\n\t\treturn summary\n\t}\n\n\tswitch result.Status {\n\tcase kstatus.InProgressStatus:\n\t\tsummary.State = \"in-progress\"\n\t\tsummary.Message = append(summary.Message, result.Message)\n\t\tsummary.Transitioning = true\n\tcase kstatus.FailedStatus:\n\t\tsummary.State = \"failed\"\n\t\tsummary.Message = append(summary.Message, result.Message)\n\t\tsummary.Error = true\n\tcase kstatus.CurrentStatus:\n\t\tsummary.State = \"active\"\n\t\tsummary.Message = append(summary.Message, result.Message)\n\tcase kstatus.TerminatingStatus:\n\t\tsummary.State = \"removing\"\n\t\tsummary.Message = append(summary.Message, result.Message)\n\t\tsummary.Transitioning = true\n\t}\n\n\treturn summary\n}\n\nfunc checkErrors(data data.Object, conditions []Condition, summary Summary) Summary {\n\tif len(conditions) == 0 {\n\t\treturn summary\n\t}\n\n\tustr := &unstructured.Unstructured{\n\t\tObject: data,\n\t}\n\n\tconditionMapping, found := GVKConditionErrorMapping[ustr.GroupVersionKind()]\n\tif !found {\n\t\tconditionMapping = GVKConditionErrorMapping[schema.GroupVersionKind{}]\n\t}\n\n\tfor _, c := range conditions {\n\t\tstatus, found := conditionMapping[c.Type()]\n\t\treasonIsError := c.Reason() == \"Error\"\n\n\t\tif !found && !reasonIsError {\n\t\t\tcontinue\n\t\t}\n\n\t\tif reasonIsError || status.Has(metav1.ConditionStatus(c.Status())) {\n\t\t\tsummary.Error = true\n\t\t\tsummary.Message = append(summary.Message, c.Message())\n\t\t\tif summary.State == \"active\" || summary.State == \"\" {\n\t\t\t\tsummary.State = \"error\"\n\t\t\t}\n\t\t}\n\n\t}\n\n\treturn summary\n}\n\nfunc checkTransitioning(obj data.Object, conditions []Condition, summary Summary) Summary {\n\tif isCAPIMachine(obj) {\n\t\treturn checkCAPIMachineTransitioning(conditions, summary)\n\t}\n\tif isCAPIMachineSet(obj) || isCAPIMachineDeployment(obj) {\n\t\treturn checkCAPIMachineSetAndDeploymentTransitioning(obj, conditions, summary)\n\t}\n\tif isCAPICluster(obj) {\n\t\treturn checkCAPIClusterTransitioning(obj, conditions, summary)\n\t}\n\treturn checkGenericTransitioning(obj, conditions, summary)\n}\n\n// checkCAPIMachineTransitioning computes summary state for CAPI Machine objects\n// using the Ready (aggregate) and Reconciled conditions from v1beta2.\n//\n// The Ready condition is a composite condition whose message aggregates\n// sub-condition issues as bullet points (e.g., \"* InfrastructureReady: <detail>\").\n// The Reconciled condition is a Rancher-specific condition that indicates\n// whether the machine's desired state has been fully applied.\n//\n// Priority order (first match wins):\n//  1. Deleting=True -> state=\"deleting\", transitioning=true\n//  2. Paused=True   -> state=\"paused\", transitioning=true\n//  3. Reconciled present and NOT True -> state=\"reconciling\",\n//     message=Reconciled.message(if has message), transitioning=true(if status=Unknown), error=true(if status=False)\n//  4. Ready=False (NotReady) — parse first bullet of Ready.message:\n//     a. First bullet starts with \"* BootstrapConfigReady:\" -> state=\"waitingforinfrastructure\"\n//     b. First bullet starts with \"* InfrastructureReady:\"  -> state=\"waitingfornoderef\"\n//     c. Otherwise -> pass through (no state set)\n//     Message is the stripped detail text from the first bullet.\n//  5. Ready=Unknown -> state=\"reconciling\", message=stripped detail from first\n//     bullet of Ready.message, transitioning=true\n//  6. Ready=True (or any other case) -> pass through, no state set\nfunc checkCAPIMachineTransitioning(conditions []Condition, summary Summary) Summary {\n\tvar reconciled *Condition\n\tvar ready *Condition\n\n\tfor i := range conditions {\n\t\tc := conditions[i]\n\t\ttyp := c.Type()\n\n\t\t// Check Deleting and Paused first — these take absolute priority.\n\t\tif typ == \"Deleting\" && c.Status() == \"True\" {\n\t\t\tsummary.State = \"deleting\"\n\t\t\tsummary.Transitioning = true\n\t\t\tif msg := c.Message(); msg != \"\" {\n\t\t\t\tif strings.HasPrefix(msg, \"Drain not completed yet\") {\n\t\t\t\t\tmsg = \"Draining node\"\n\t\t\t\t}\n\t\t\t\tsummary.Message = append(summary.Message, msg)\n\t\t\t}\n\t\t\treturn summary\n\t\t}\n\t\tif typ == \"Paused\" && c.Status() == \"True\" {\n\t\t\tsummary.State = \"paused\"\n\t\t\tsummary.Transitioning = true\n\t\t\treturn summary\n\t\t}\n\n\t\t// Collect key conditions for priority evaluation below.\n\t\tswitch typ {\n\t\tcase \"Reconciled\":\n\t\t\treconciled = &conditions[i]\n\t\tcase \"Ready\":\n\t\t\tready = &conditions[i]\n\t\t}\n\t}\n\n\t// Priority 3: Reconciled condition (when present and not True).\n\tif reconciled != nil && reconciled.Status() != \"True\" {\n\t\tswitch reconciled.Status() {\n\t\tcase \"Unknown\":\n\t\t\tsummary.Transitioning = true\n\t\t\tsummary.State = \"reconciling\"\n\t\tcase \"False\":\n\t\t\tsummary.Error = true\n\t\t\tsummary.State = \"reconciling\"\n\t\t}\n\t\tif msg := reconciled.Message(); msg != \"\" {\n\t\t\tsummary.Message = append(summary.Message, msg)\n\t\t}\n\t\treturn summary\n\t}\n\n\t// Priority 4 & 5: Ready condition.\n\tif ready != nil {\n\t\tswitch ready.Status() {\n\t\tcase \"False\":\n\t\t\t// Parse the first bullet line to determine the state.\n\t\t\tdetail, prefix := parseMessage(ready.Message())\n\t\t\tswitch prefix {\n\t\t\tcase \"BootstrapConfigReady\":\n\t\t\t\tsummary.Transitioning = true\n\t\t\t\tsummary.State = \"waitingforinfrastructure\"\n\t\t\t\tif strings.HasSuffix(detail, \"status.initialization.dataSecretCreated is false\") {\n\t\t\t\t\tdetail = \"\"\n\t\t\t\t}\n\t\t\t\tif detail != \"\" {\n\t\t\t\t\tsummary.Message = append(summary.Message, detail)\n\t\t\t\t}\n\t\t\tcase \"InfrastructureReady\":\n\t\t\t\tsummary.Transitioning = true\n\t\t\t\tsummary.State = \"waitingfornoderef\"\n\t\t\t\tif strings.HasSuffix(detail, \"status.initialization.provisioned is false\") {\n\t\t\t\t\tdetail = \"\"\n\t\t\t\t}\n\t\t\t\tif detail != \"\" {\n\t\t\t\t\tsummary.Message = append(summary.Message, detail)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If the first bullet doesn't match known patterns, pass through.\n\t\t\treturn summary\n\n\t\tcase \"Unknown\":\n\t\t\tsummary.Transitioning = true\n\t\t\tsummary.State = \"reconciling\"\n\t\t\tdetail, prefix := parseMessage(ready.Message())\n\t\t\tif prefix == \"NodeHealthy\" {\n\t\t\t\tif strings.HasSuffix(detail, \"to report spec.providerID\") {\n\t\t\t\t\tdetail = \"\"\n\t\t\t\t}\n\t\t\t}\n\t\t\tif detail != \"\" {\n\t\t\t\tsummary.Message = append(summary.Message, detail)\n\t\t\t}\n\t\t\treturn summary\n\t\t}\n\t\t// Ready=True: pass through, let downstream summarizers handle it.\n\t}\n\n\treturn summary\n}\n\n// checkCAPIMachineSetAndDeploymentTransitioning computes summary state for CAPI MachineSet\n// and MachineDeployment objects using v1beta2 conditions and replica counts.\n//\n// The function reads spec.replicas, status.replicas, and status.readyReplicas\n// to detect scaling operations, even when the controller hasn't yet updated\n// the ScalingUp/ScalingDown conditions (stale observedGeneration).\n//\n// Priority order (first match wins):\n//  1. Deleting=True                                          -> state=\"deleting\", transitioning=true\n//  2. Paused=True                                            -> state=\"paused\", transitioning=true\n//  3. RollingOut=True                                        -> state=\"rollingout\", transitioning=true\n//  4. ScalingDown=True OR status.replicas > spec.replicas    -> state=\"scalingdown\", transitioning=true\n//  5. ScalingUp=True OR spec.replicas > status.readyReplicas -> state=\"scalingup\", transitioning=true\n//  6. Otherwise                                              -> pass through (no state set)\nfunc checkCAPIMachineSetAndDeploymentTransitioning(obj data.Object, conditions []Condition, summary Summary) Summary {\n\t// Read replica counts from the object. We use nestedInt64 instead of\n\t// unstructured.NestedInt64 because YAML/JSON decoders store numbers as\n\t// float64, not int64.\n\tspecReplicas, specFound, _ := nestedInt64(obj, \"spec\", \"replicas\")\n\tstatusReplicas, statusFound, _ := nestedInt64(obj, \"status\", \"replicas\")\n\treadyReplicas, readyFound, _ := nestedInt64(obj, \"status\", \"readyReplicas\")\n\tupToDateReplicas, upToDateFound, _ := nestedInt64(obj, \"status\", \"upToDateReplicas\")\n\n\tvar scalingUp *Condition\n\tvar scalingDown *Condition\n\tvar rollingOut *Condition\n\n\tfor i := range conditions {\n\t\tc := conditions[i]\n\t\ttyp := c.Type()\n\n\t\t// Priority 1 & 2: Deleting and Paused take absolute priority.\n\t\tif typ == \"Deleting\" && c.Status() == \"True\" {\n\t\t\tsummary.State = \"deleting\"\n\t\t\tsummary.Transitioning = true\n\t\t\tif msg := c.Message(); msg != \"\" {\n\t\t\t\tsummary.Message = append(summary.Message, msg)\n\t\t\t}\n\t\t\treturn summary\n\t\t}\n\t\tif typ == \"Paused\" && c.Status() == \"True\" {\n\t\t\tsummary.State = \"paused\"\n\t\t\tsummary.Transitioning = true\n\t\t\treturn summary\n\t\t}\n\n\t\tswitch typ {\n\t\tcase \"ScalingUp\":\n\t\t\tscalingUp = &conditions[i]\n\t\tcase \"ScalingDown\":\n\t\t\tscalingDown = &conditions[i]\n\t\tcase \"RollingOut\":\n\t\t\trollingOut = &conditions[i]\n\t\t}\n\t}\n\n\t// Priority 3: Rolling out — RollingOut=True indicates a rolling upgrade\n\t// is in progress. This takes priority over ScalingDown/ScalingUp because\n\t// those are side effects of the rollout strategy (maxSurge creates extra\n\t// machines, then old ones are deleted). Only MachineDeployments have\n\t// rolling upgrades; MachineSets do not set this condition.\n\tif rollingOut != nil && rollingOut.Status() == \"True\" {\n\t\tsummary.State = \"rollingout\"\n\t\tsummary.Transitioning = true\n\t\tif statusFound && upToDateFound {\n\t\t\tnotUpToDate := statusReplicas - upToDateReplicas\n\t\t\tsummary.Message = append(summary.Message,\n\t\t\t\tfmt.Sprintf(\"rolling out %d not up-to-date replicas\", notUpToDate))\n\t\t}\n\t\treturn summary\n\t}\n\n\t// Priority 4: Scaling down — detected by condition OR replica count mismatch.\n\tscalingDownByCondition := scalingDown != nil && scalingDown.Status() == \"True\"\n\tscalingDownByReplicas := specFound && statusFound && statusReplicas > specReplicas\n\tif scalingDownByCondition || scalingDownByReplicas {\n\t\tsummary.State = \"scalingdown\"\n\t\tsummary.Transitioning = true\n\t\tif specFound && statusFound {\n\t\t\tsummary.Message = append(summary.Message,\n\t\t\t\tfmt.Sprintf(\"Scaling down from %d to %d replicas, waiting for machines to be deleted\", statusReplicas, specReplicas))\n\t\t}\n\t\treturn summary\n\t}\n\n\t// Priority 5: Scaling up — detected by spec.replicas > status.readyReplicas.\n\t// This catches both the transient ScalingUp=True period and the long tail\n\t// where ScalingUp has already gone False but the new machine isn't ready yet.\n\tscalingUpByReplicas := specFound && readyFound && specReplicas > readyReplicas\n\tscalingUpByCondition := scalingUp != nil && scalingUp.Status() == \"True\"\n\tif scalingUpByReplicas || scalingUpByCondition {\n\t\tsummary.State = \"scalingup\"\n\t\tsummary.Transitioning = true\n\t\tif specFound && readyFound {\n\t\t\tsummary.Message = append(summary.Message,\n\t\t\t\tfmt.Sprintf(\"Scaling up from %d to %d replicas, waiting for machines to be ready\", readyReplicas, specReplicas))\n\t\t}\n\t\treturn summary\n\t}\n\n\treturn summary\n}\n\n// checkCAPIClusterTransitioning computes summary state for CAPI Cluster objects\n// using v1beta2 conditions and worker replica counts.\n//\n// The Cluster object has conditions like Available, ScalingUp, ScalingDown,\n// Deleting, Paused, WorkersAvailable, WorkerMachinesReady, ControlPlaneAvailable,\n// ControlPlaneInitialized, etc. Worker replica counts are under status.workers.*.\n//\n// Priority order (first match wins):\n//  1. Deleting=True                                                                    -> state=\"deleting\", transitioning=true\n//  2. Paused=True                                                                      -> state=\"paused\", transitioning=true\n//  3. RollingOut=True                                                                  -> state=\"rollingout\", transitioning=true\n//  4. ScalingDown=True OR status.workers.replicas > status.workers.desiredReplicas     -> state=\"updating\", transitioning=true\n//  5. ScalingUp=True OR status.workers.desiredReplicas > status.workers.readyReplicas  -> state=\"updating\", transitioning=true\n//  6. Available=False                                                                  -> state=\"updating\", transitioning=true\n//  7. Available=True (or any other case)                                               -> pass through (no state set)\nfunc checkCAPIClusterTransitioning(obj data.Object, conditions []Condition, summary Summary) Summary {\n\t// Read worker replica counts from status.workers.*.\n\tdesiredReplicas, desiredFound, _ := nestedInt64(obj, \"status\", \"workers\", \"desiredReplicas\")\n\tworkersReplicas, workersFound, _ := nestedInt64(obj, \"status\", \"workers\", \"replicas\")\n\treadyReplicas, readyFound, _ := nestedInt64(obj, \"status\", \"workers\", \"readyReplicas\")\n\tupToDateReplicas, upToDateFound, _ := nestedInt64(obj, \"status\", \"workers\", \"upToDateReplicas\")\n\n\tvar scalingUp *Condition\n\tvar scalingDown *Condition\n\tvar available *Condition\n\tvar rollingOut *Condition\n\n\tfor i := range conditions {\n\t\tc := conditions[i]\n\t\ttyp := c.Type()\n\n\t\t// Priority 1 & 2: Deleting and Paused take absolute priority.\n\t\tif typ == \"Deleting\" && c.Status() == \"True\" {\n\t\t\tsummary.State = \"deleting\"\n\t\t\tsummary.Transitioning = true\n\t\t\tif msg := c.Message(); msg != \"\" {\n\t\t\t\tif strings.HasPrefix(msg, \"* MachineDeployments\") {\n\t\t\t\t\tmsg = \"waiting for workers deletion\"\n\t\t\t\t}\n\t\t\t\tsummary.Message = append(summary.Message, msg)\n\t\t\t}\n\t\t\treturn summary\n\t\t}\n\t\tif typ == \"Paused\" && c.Status() == \"True\" {\n\t\t\tsummary.State = \"paused\"\n\t\t\tsummary.Transitioning = true\n\t\t\treturn summary\n\t\t}\n\n\t\tswitch typ {\n\t\tcase \"ScalingUp\":\n\t\t\tscalingUp = &conditions[i]\n\t\tcase \"ScalingDown\":\n\t\t\tscalingDown = &conditions[i]\n\t\tcase \"Available\":\n\t\t\tavailable = &conditions[i]\n\t\tcase \"RollingOut\":\n\t\t\trollingOut = &conditions[i]\n\t\t}\n\t}\n\n\t// Priority 3: Rolling out — RollingOut=True indicates one or more\n\t// MachineDeployments are performing a rolling upgrade. This takes\n\t// priority over ScalingDown/ScalingUp because those are side effects\n\t// of the rollout strategy.\n\tif rollingOut != nil && rollingOut.Status() == \"True\" {\n\t\tsummary.State = \"rollingout\"\n\t\tsummary.Transitioning = true\n\t\tif workersFound && upToDateFound {\n\t\t\tnotUpToDate := workersReplicas - upToDateReplicas\n\t\t\tsummary.Message = append(summary.Message,\n\t\t\t\tfmt.Sprintf(\"rolling out %d not up-to-date replicas\", notUpToDate))\n\t\t}\n\t\treturn summary\n\t}\n\n\t// Priority 4: Scaling down — detected by condition OR replica count mismatch.\n\tscalingDownByCondition := scalingDown != nil && scalingDown.Status() == \"True\"\n\tscalingDownByReplicas := desiredFound && workersFound && workersReplicas > desiredReplicas\n\tif scalingDownByCondition || scalingDownByReplicas {\n\t\tsummary.State = \"updating\"\n\t\tsummary.Transitioning = true\n\t\tif desiredFound && workersFound {\n\t\t\tsummary.Message = append(summary.Message,\n\t\t\t\tfmt.Sprintf(\"Scaling down from %d to %d machines\", workersReplicas, desiredReplicas))\n\t\t}\n\t\treturn summary\n\t}\n\n\t// Priority 5: Scaling up — Scaling up — detected by condition OR desired > readyReplicas.\n\tscalingUpByCondition := scalingUp != nil && scalingUp.Status() == \"True\"\n\tscalingUpByReplicas := desiredFound && readyFound && desiredReplicas > readyReplicas\n\tif scalingUpByCondition || scalingUpByReplicas {\n\t\tsummary.State = \"updating\"\n\t\tsummary.Transitioning = true\n\t\tif desiredFound && readyFound {\n\t\t\tsummary.Message = append(summary.Message,\n\t\t\t\tfmt.Sprintf(\"Scaling up from %d to %d machines\", readyReplicas, desiredReplicas))\n\t\t}\n\t\treturn summary\n\t}\n\n\t// Priority 5: Available=False — the cluster is not yet available.\n\tif available != nil {\n\t\tswitch available.Status() {\n\t\tcase \"False\":\n\t\t\tsummary.Transitioning = true\n\t\t\tsummary.State = \"updating\"\n\t\t\t// Parse the first bullet line to determine the state.\n\t\t\tdetail, prefix := parseMessage(available.Message())\n\t\t\tswitch prefix {\n\t\t\tcase \"RemoteConnectionProbe\":\n\t\t\t\tsummary.Message = append(summary.Message, \"establishing connection to control plane\")\n\t\t\tcase \"ControlPlaneAvailable\":\n\t\t\t\tsummary.Message = append(summary.Message, \"Waiting for control plane to be available\")\n\t\t\tcase \"MachineDeployment\":\n\t\t\t\tsummary.Message = append(summary.Message, \"Waiting for workers to be available\")\n\t\t\tdefault:\n\t\t\t\t// If the first bullet doesn't match known patterns, pass through.\n\t\t\t\tif detail != \"\" {\n\t\t\t\t\tsummary.Message = append(summary.Message, detail)\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase \"Unknown\":\n\t\t\tsummary.Error = true\n\t\t\tsummary.State = \"unavailable\"\n\t\t\tsummary.Message = append(summary.Message, available.Message())\n\t\t}\n\t\t// Ready=True: pass through, let downstream summarizers handle it.\n\t}\n\n\t// Priority 6: Available=True or no conditions — pass through.\n\treturn summary\n}\n\nfunc checkGenericTransitioning(_ data.Object, conditions []Condition, summary Summary) Summary {\n\tfor _, c := range conditions {\n\t\tnewState, ok := TransitioningUnknown[c.Type()]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif newState == reason {\n\t\t\tnewState = c.Reason()\n\t\t}\n\n\t\tif c.Status() == \"False\" {\n\t\t\tsummary.Error = true\n\t\t\tsummary.State = newState\n\t\t\tsummary.Message = append(summary.Message, c.Message())\n\t\t} else if c.Status() == \"Unknown\" && summary.State == \"\" {\n\t\t\tsummary.Transitioning = true\n\t\t\tsummary.State = newState\n\t\t\tsummary.Message = append(summary.Message, c.Message())\n\t\t}\n\t}\n\n\tfor _, c := range conditions {\n\t\tif summary.State != \"\" {\n\t\t\tbreak\n\t\t}\n\t\tnewState, ok := TransitioningTrue[c.Type()]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif newState == reason {\n\t\t\tnewState = c.Reason()\n\t\t}\n\n\t\tif c.Status() == \"True\" {\n\t\t\tsummary.Transitioning = true\n\t\t\tsummary.State = newState\n\t\t\tsummary.Message = append(summary.Message, c.Message())\n\t\t}\n\t}\n\n\tready := true\n\treadyMessage := \"\"\n\tfor _, c := range conditions {\n\t\tif summary.State != \"\" {\n\t\t\tbreak\n\t\t}\n\n\t\tif c.Type() == \"Ready\" && c.Status() == \"False\" {\n\t\t\tready = false\n\t\t\treadyMessage = c.Message()\n\t\t\tcontinue\n\t\t}\n\t\tnewState, ok := TransitioningFalse[c.Type()]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tif newState == reason {\n\t\t\tnewState = c.Reason()\n\t\t}\n\t\tif c.Status() == \"False\" {\n\t\t\tsummary.Transitioning = true\n\t\t\tsummary.State = newState\n\t\t\tsummary.Message = append(summary.Message, c.Message())\n\t\t} else if c.Status() == \"Unknown\" {\n\t\t\tsummary.Error = true\n\t\t\tsummary.State = newState\n\t\t\tsummary.Message = append(summary.Message, c.Message())\n\t\t}\n\t}\n\n\tif summary.State == \"\" && !ready {\n\t\tsummary.Transitioning = true\n\t\tsummary.State = \"unavailable\"\n\t\tsummary.Message = append(summary.Message, readyMessage)\n\t}\n\n\treturn summary\n}\n\nfunc checkActive(obj data.Object, _ []Condition, summary Summary) Summary {\n\tif summary.State != \"\" {\n\t\treturn summary\n\t}\n\n\tswitch obj.String(\"spec\", \"active\") {\n\tcase \"true\":\n\t\tsummary.State = \"active\"\n\tcase \"false\":\n\t\tsummary.State = \"inactive\"\n\t}\n\n\treturn summary\n}\n\nfunc checkPhase(obj data.Object, _ []Condition, summary Summary) Summary {\n\tphase := obj.String(\"status\", \"phase\")\n\tif phase == \"Succeeded\" {\n\t\tsummary.State = \"succeeded\"\n\t\tsummary.Transitioning = false\n\t} else if phase == \"Bound\" {\n\t\tsummary.State = \"bound\"\n\t\tsummary.Transitioning = false\n\t} else if phase != \"\" && summary.State == \"\" {\n\t\tsummary.State = phase\n\t}\n\treturn summary\n}\n\nfunc checkInitializing(obj data.Object, conditions []Condition, summary Summary) Summary {\n\tapiVersion := obj.String(\"apiVersion\")\n\t_, hasConditions := obj.Map(\"status\")[\"conditions\"]\n\tif summary.State == \"\" && hasConditions && len(conditions) == 0 && strings.Contains(apiVersion, \"cattle.io\") {\n\t\tval := obj.String(\"metadata\", \"created\")\n\t\tif i, err := convert.ToTimestamp(val); err == nil {\n\t\t\tif time.Unix(i/1000, 0).Add(5 * time.Second).After(time.Now()) {\n\t\t\t\tsummary.State = \"initializing\"\n\t\t\t\tsummary.Transitioning = true\n\t\t\t}\n\t\t}\n\t}\n\treturn summary\n}\n\nfunc checkRemoving(obj data.Object, conditions []Condition, summary Summary) Summary {\n\tremoved := obj.String(\"metadata\", \"removed\")\n\tif removed == \"\" {\n\t\treturn summary\n\t}\n\n\tsummary.State = \"removing\"\n\tsummary.Transitioning = true\n\n\tfinalizers := obj.StringSlice(\"metadata\", \"finalizers\")\n\tif len(finalizers) == 0 {\n\t\tfinalizers = obj.StringSlice(\"spec\", \"finalizers\")\n\t}\n\n\tfor _, cond := range conditions {\n\t\tif cond.Type() == \"Removed\" && (cond.Status() == \"Unknown\" || cond.Status() == \"False\") && cond.Message() != \"\" {\n\t\t\tsummary.Message = append(summary.Message, cond.Message())\n\t\t}\n\t}\n\n\tif len(finalizers) == 0 {\n\t\treturn summary\n\t}\n\n\t_, f := kv.RSplit(finalizers[0], \"controller.cattle.io/\")\n\tif f == \"foregroundDeletion\" {\n\t\tf = \"object cleanup\"\n\t}\n\n\tsummary.Message = append(summary.Message, \"waiting on \"+f)\n\tif i, err := convert.ToTimestamp(removed); err == nil {\n\t\tif time.Unix(i/1000, 0).Add(5 * time.Minute).Before(time.Now()) {\n\t\t\tsummary.Error = true\n\t\t}\n\t}\n\n\treturn summary\n}\n\nfunc checkLoadBalancer(obj data.Object, _ []Condition, summary Summary) Summary {\n\tif (summary.State == \"active\" || summary.State == \"\") &&\n\t\tobj.String(\"kind\") == \"Service\" &&\n\t\t(obj.String(\"spec\", \"serviceKind\") == \"LoadBalancer\" ||\n\t\t\tobj.String(\"spec\", \"type\") == \"LoadBalancer\") {\n\t\taddresses := obj.Slice(\"status\", \"loadBalancer\", \"ingress\")\n\t\tif len(addresses) == 0 {\n\t\t\tsummary.State = \"pending\"\n\t\t\tsummary.Transitioning = true\n\t\t\tsummary.Message = append(summary.Message, \"Load balancer is being provisioned\")\n\t\t}\n\t}\n\n\treturn summary\n}\n\nfunc isKind(obj data.Object, kind string, apiGroups ...string) bool {\n\tif obj.String(\"kind\") != kind {\n\t\treturn false\n\t}\n\n\tif len(apiGroups) == 0 {\n\t\treturn obj.String(\"apiVersion\") == \"v1\"\n\t}\n\n\tif len(apiGroups) == 0 {\n\t\tapiGroups = []string{\"\"}\n\t}\n\n\tfor _, group := range apiGroups {\n\t\tswitch {\n\t\tcase group == \"\":\n\t\t\tif obj.String(\"apiVersion\") == \"v1\" {\n\t\t\t\treturn true\n\t\t\t}\n\t\tcase group[len(group)-1] == '/':\n\t\t\tif strings.HasPrefix(obj.String(\"apiVersion\"), group) {\n\t\t\t\treturn true\n\t\t\t}\n\t\tdefault:\n\t\t\tif obj.String(\"apiVersion\") != group {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc checkApplyOwned(obj data.Object, conditions []Condition, summary Summary) Summary {\n\tif len(obj.Slice(\"metadata\", \"ownerReferences\")) > 0 {\n\t\treturn summary\n\t}\n\n\tannotations := obj.Map(\"metadata\", \"annotations\")\n\tgvkString := convert.ToString(annotations[\"objectset.rio.cattle.io/owner-gvk\"])\n\ti := strings.Index(gvkString, kindSep)\n\tif i <= 0 {\n\t\treturn summary\n\t}\n\n\tname := convert.ToString(annotations[\"objectset.rio.cattle.io/owner-name\"])\n\tnamespace := convert.ToString(annotations[\"objectset.rio.cattle.io/owner-namespace\"])\n\n\tapiVersion := gvkString[:i]\n\tkind := gvkString[i+len(kindSep):]\n\n\trel := Relationship{\n\t\tName:       name,\n\t\tNamespace:  namespace,\n\t\tKind:       kind,\n\t\tAPIVersion: apiVersion,\n\t\tType:       \"applies\",\n\t\tInbound:    true,\n\t}\n\n\tsummary.Relationships = append(summary.Relationships, rel)\n\n\treturn summary\n}\n\n// isCAPIMachine returns true if the object is a CAPI Machine (cluster.x-k8s.io/*/Machine).\nfunc isCAPIMachine(obj data.Object) bool {\n\treturn strings.HasPrefix(obj.String(\"apiVersion\"), \"cluster.x-k8s.io/\") &&\n\t\tobj.String(\"kind\") == \"Machine\"\n}\n\n// isCAPIMachineSet returns true if the object is a CAPI MachineSet (cluster.x-k8s.io/*/MachineSet).\nfunc isCAPIMachineSet(obj data.Object) bool {\n\treturn strings.HasPrefix(obj.String(\"apiVersion\"), \"cluster.x-k8s.io/\") &&\n\t\tobj.String(\"kind\") == \"MachineSet\"\n}\n\n// isCAPIMachineDeployment returns true if the object is a CAPI MachineDeployment (cluster.x-k8s.io/*/MachineDeployment).\nfunc isCAPIMachineDeployment(obj data.Object) bool {\n\treturn strings.HasPrefix(obj.String(\"apiVersion\"), \"cluster.x-k8s.io/\") &&\n\t\tobj.String(\"kind\") == \"MachineDeployment\"\n}\n\n// isCAPICluster returns true if the object is a CAPI Cluster (cluster.x-k8s.io/*/Cluster).\nfunc isCAPICluster(obj data.Object) bool {\n\treturn strings.HasPrefix(obj.String(\"apiVersion\"), \"cluster.x-k8s.io/\") &&\n\t\tobj.String(\"kind\") == \"Cluster\"\n}\n\n// parseMessage parses the first bullet line from a condition message.\n// The condition message format is:\n//\n//\t\\* <ConditionType>: <detail message>\n//\t\\* <ConditionType>: <detail message>\n//\n// or with nested sub-bullets when the top-level detail is empty:\n//\n//\t\\* <ConditionType>:\n//\t  \\* <SubCondition>: <detail message>\n//\n// Multiple lines are separated by newlines. This function returns:\n//   - detail: the stripped detail text (without \"* ConditionType: \" prefix).\n//     When the top-level bullet has no inline detail, the detail from\n//     the first indented sub-bullet is returned instead.\n//   - prefix: the condition type name from the top-level bullet.\nfunc parseMessage(message string) (detail, prefix string) {\n\tif message == \"\" {\n\t\treturn \"\", \"\"\n\t}\n\n\tlines := strings.Split(message, \"\\n\")\n\n\t// Parse the first line.\n\tfirstLine := strings.TrimPrefix(lines[0], \"* \")\n\n\t// Split on \": \" to separate condition type from detail.\n\tcolonIdx := strings.Index(firstLine, \": \")\n\tif colonIdx < 0 {\n\t\t// No \": \" separator. Check if the line ends with \":\" (empty detail\n\t\t// with sub-bullets on subsequent lines).\n\t\tif strings.HasSuffix(firstLine, \":\") {\n\t\t\tprefix = strings.TrimSuffix(firstLine, \":\")\n\t\t} else {\n\t\t\treturn firstLine, \"\"\n\t\t}\n\t} else {\n\t\tprefix = firstLine[:colonIdx]\n\t\tdetail = firstLine[colonIdx+2:]\n\t}\n\n\t// If the top-level bullet has inline detail, return it.\n\tif detail != \"\" {\n\t\treturn detail, prefix\n\t}\n\n\t// The top-level bullet has no inline detail (e.g., \"* NodeHealthy:\").\n\t// Look for indented sub-bullets to extract the detail from.\n\tfor _, line := range lines[1:] {\n\t\ttrimmed := strings.TrimSpace(line)\n\t\tif !strings.HasPrefix(trimmed, \"* \") {\n\t\t\tcontinue\n\t\t}\n\t\t// Found a sub-bullet: \"* SubCondition: detail message\"\n\t\tsubContent := strings.TrimPrefix(trimmed, \"* \")\n\t\tsubColonIdx := strings.Index(subContent, \": \")\n\t\tif subColonIdx >= 0 {\n\t\t\tdetail = subContent[subColonIdx+2:]\n\t\t} else {\n\t\t\tdetail = subContent\n\t\t}\n\t\tbreak // Only use the first sub-bullet.\n\t}\n\n\treturn detail, prefix\n}\n\n// nestedInt64 retrieves a nested numeric field from an unstructured object and\n// returns it as int64. Unlike unstructured.NestedInt64, which requires the value\n// to be exactly int64, this function also handles float64 (produced by JSON/YAML\n// decoders), json.Number, and string representations of integers.\nfunc nestedInt64(obj data.Object, fields ...string) (int64, bool, error) {\n\tval, found, err := unstructured.NestedFieldNoCopy(obj, fields...)\n\tif !found || err != nil {\n\t\treturn 0, found, err\n\t}\n\tn, err := convert.ToNumber(val)\n\tif err != nil {\n\t\treturn 0, false, err\n\t}\n\treturn n, true, nil\n}\n"
  },
  {
    "path": "pkg/summary/summarizers_test.go",
    "content": "package summary\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCheckErrors(t *testing.T) {\n\ttype input struct {\n\t\tdata       data.Object\n\t\tconditions []Condition\n\t\tsummary    Summary\n\t}\n\n\ttype output struct {\n\t\tsummary Summary\n\t}\n\n\ttestCases := []struct {\n\t\tname           string\n\t\tloadConditions func()\n\t\tinput          input\n\t\texpected       output\n\t}{\n\t\t{\n\t\t\tname: \"gvk not detected - summary remains the same\",\n\t\t\tinput: input{\n\t\t\t\tdata: data.Object{},\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: output{\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"gvk not found - summary remains the same\",\n\t\t\tinput: input{\n\t\t\t\tdata: data.Object{\n\t\t\t\t\t\"APIVersion\": \"sample.cattle.io/v1\",\n\t\t\t\t\t\"Kind\":       \"Sample\",\n\t\t\t\t},\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: output{\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"gvk found, no conditions provided\",\n\t\t\tinput: input{\n\t\t\t\tdata: data.Object{\n\t\t\t\t\t\"APIVersion\": \"helm.cattle.io/v1\",\n\t\t\t\t\t\"Kind\":       \"HelmChart\",\n\t\t\t\t},\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: output{\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"gvk found, condition not found\",\n\t\t\tinput: input{\n\t\t\t\tdata: data.Object{\n\t\t\t\t\t\"APIVersion\": \"helm.cattle.io/v1\",\n\t\t\t\t\t\"Kind\":       \"HelmChart\",\n\t\t\t\t},\n\t\t\t\tconditions: []Condition{\n\t\t\t\t\tNewCondition(\"JobFailed\", \"True\", \"\", \"\"),\n\t\t\t\t},\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: output{\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"gvk found, condition is error\",\n\t\t\tinput: input{\n\t\t\t\tdata: data.Object{\n\t\t\t\t\t\"APIVersion\": \"helm.cattle.io/v1\",\n\t\t\t\t\t\"Kind\":       \"HelmChart\",\n\t\t\t\t},\n\t\t\t\tconditions: []Condition{\n\t\t\t\t\tNewCondition(\"Failed\", \"True\", \"\", \"Helm Install Error\"),\n\t\t\t\t},\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: output{\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: true,\n\t\t\t\t\tMessage: []string{\n\t\t\t\t\t\t\"Helm Install Error\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"gvk found, condition is not an error\",\n\t\t\tinput: input{\n\t\t\t\tdata: data.Object{\n\t\t\t\t\t\"APIVersion\": \"helm.cattle.io/v1\",\n\t\t\t\t\t\"Kind\":       \"HelmChart\",\n\t\t\t\t},\n\t\t\t\tconditions: []Condition{\n\t\t\t\t\tNewCondition(\"Failed\", \"False\", \"\", \"\"),\n\t\t\t\t},\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: output{\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"load conditions - gvk not found\",\n\t\t\tinput: input{\n\t\t\t\tdata: data.Object{\n\t\t\t\t\t\"APIVersion\": \"helm.cattle.io/v1\",\n\t\t\t\t\t\"Kind\":       \"HelmChart\",\n\t\t\t\t},\n\t\t\t\tconditions: []Condition{\n\t\t\t\t\tNewCondition(\"Failed\", \"False\", \"\", \"\"),\n\t\t\t\t},\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: output{\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\tloadConditions: func() {\n\t\t\t\tos.Setenv(checkGVKErrorMappingEnvVar, `\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"gvk\": \"sample.cattle.io/v1, Kind=Sample\",\n\t\t\t\t\t\t\t\"conditionMappings\": [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"type\": \"Failed\",\n\t\t\t\t\t\t\t\t\t\"status\": [\"True\"]\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t`)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"load conditions - gvk found - condition is only informational\",\n\t\t\tinput: input{\n\t\t\t\tdata: data.Object{\n\t\t\t\t\t\"APIVersion\": \"sample.cattle.io/v1\",\n\t\t\t\t\t\"Kind\":       \"Sample\",\n\t\t\t\t},\n\t\t\t\tconditions: []Condition{\n\t\t\t\t\tNewCondition(\"Created\", \"True\", \"\", \"\"),\n\t\t\t\t},\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: output{\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\tloadConditions: func() {\n\t\t\t\tos.Setenv(checkGVKErrorMappingEnvVar, `\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"gvk\": \"sample.cattle.io/v1, Kind=Sample\",\n\t\t\t\t\t\t\t\"conditionMappings\": [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"type\": \"Created\",\n\t\t\t\t\t\t\t\t\t\"status\": []\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t`)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"load conditions - gvk found - is not an error\",\n\t\t\tinput: input{\n\t\t\t\tdata: data.Object{\n\t\t\t\t\t\"APIVersion\": \"sample.cattle.io/v1\",\n\t\t\t\t\t\"Kind\":       \"Sample\",\n\t\t\t\t},\n\t\t\t\tconditions: []Condition{\n\t\t\t\t\tNewCondition(\"Failed\", \"False\", \"\", \"\"),\n\t\t\t\t},\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: output{\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\tloadConditions: func() {\n\t\t\t\tos.Setenv(checkGVKErrorMappingEnvVar, `\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"gvk\": \"sample.cattle.io/v1, Kind=Sample\",\n\t\t\t\t\t\t\t\"conditionMappings\": [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"type\": \"Failed\",\n\t\t\t\t\t\t\t\t\t\"status\": [\"True\"]\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t`)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"load conditions - gvk found - is error\",\n\t\t\tinput: input{\n\t\t\t\tdata: data.Object{\n\t\t\t\t\t\"APIVersion\": \"sample.cattle.io/v1\",\n\t\t\t\t\t\"Kind\":       \"Sample\",\n\t\t\t\t},\n\t\t\t\tconditions: []Condition{\n\t\t\t\t\tNewCondition(\"Failed\", \"True\", \"\", \"Sample Failure\"),\n\t\t\t\t},\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: output{\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: true,\n\t\t\t\t\tMessage: []string{\n\t\t\t\t\t\t\"Sample Failure\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tloadConditions: func() {\n\t\t\t\tos.Setenv(checkGVKErrorMappingEnvVar, `\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"gvk\": \"sample.cattle.io/v1, Kind=Sample\",\n\t\t\t\t\t\t\t\"conditionMappings\": [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"type\": \"Failed\",\n\t\t\t\t\t\t\t\t\t\"status\": [\"True\"]\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t`)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"fallback conditions\",\n\t\t\tinput: input{\n\t\t\t\tdata: data.Object{\n\t\t\t\t\t\"APIVersion\": \"fallback.cattle.io/v1\",\n\t\t\t\t\t\"Kind\":       \"Fallback\",\n\t\t\t\t},\n\t\t\t\tconditions: []Condition{\n\t\t\t\t\tNewCondition(\"Failed\", \"True\", \"\", \"Sample Failure\"),\n\t\t\t\t},\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: output{\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: true,\n\t\t\t\t\tMessage: []string{\n\t\t\t\t\t\t\"Sample Failure\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"condition has error at reason field\",\n\t\t\tinput: input{\n\t\t\t\tdata: data.Object{\n\t\t\t\t\t\"APIVersion\": \"sample.cattle.io/v1\",\n\t\t\t\t\t\"Kind\":       \"Sample\",\n\t\t\t\t},\n\t\t\t\tconditions: []Condition{\n\t\t\t\t\tNewCondition(\"SampleFailed\", \"True\", \"Error\", \"Error in Reason\"),\n\t\t\t\t},\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: output{\n\t\t\t\tsummary: Summary{\n\t\t\t\t\tState: \"testing\",\n\t\t\t\t\tError: true,\n\t\t\t\t\tMessage: []string{\n\t\t\t\t\t\t\"Error in Reason\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tif tc.loadConditions != nil {\n\t\t\t\ttc.loadConditions()\n\t\t\t}\n\t\t\tinitializeCheckErrors()\n\t\t\tsummary := checkErrors(tc.input.data, tc.input.conditions, tc.input.summary)\n\n\t\t\tassert.Equal(t, tc.expected.summary, summary)\n\t\t})\n\t}\n\n}\n\nfunc TestCheckGeneration(t *testing.T) {\n\ttests := []struct {\n\t\tname      string\n\t\tobj       data.Object\n\t\tsummary   Summary\n\t\twantState string\n\t\twantTrans bool\n\t}{\n\t\t{\n\t\t\tname: \"generation is int, observedGeneration is int, does nothing\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"generation\": int(7),\n\t\t\t\t},\n\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\"observedGeneration\": int(6),\n\t\t\t\t},\n\t\t\t},\n\t\t\tsummary:   Summary{HasObservedGeneration: true, State: \"\"},\n\t\t\twantState: \"\",\n\t\t\twantTrans: false,\n\t\t},\n\t\t{\n\t\t\tname: \"generation is int32, observedGeneration is int32, does nothing\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"generation\": int32(5),\n\t\t\t\t},\n\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\"observedGeneration\": int32(5),\n\t\t\t\t},\n\t\t\t},\n\t\t\tsummary:   Summary{HasObservedGeneration: true, State: \"\"},\n\t\t\twantState: \"\",\n\t\t\twantTrans: false,\n\t\t},\n\t\t{\n\t\t\tname: \"HasObservedGeneration false, does nothing\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"generation\": int64(2),\n\t\t\t\t},\n\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\"observedGeneration\": int64(2),\n\t\t\t\t},\n\t\t\t},\n\t\t\tsummary:   Summary{HasObservedGeneration: false, State: \"\"},\n\t\t\twantState: \"\",\n\t\t\twantTrans: false,\n\t\t},\n\t\t{\n\t\t\tname: \"metadata.generation not found, does nothing\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\"observedGeneration\": int64(2),\n\t\t\t\t},\n\t\t\t},\n\t\t\tsummary:   Summary{HasObservedGeneration: true, State: \"\"},\n\t\t\twantState: \"\",\n\t\t\twantTrans: false,\n\t\t},\n\t\t{\n\t\t\tname: \"observedGeneration equals generation, does nothing\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"generation\": int64(2),\n\t\t\t\t},\n\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\"observedGeneration\": int64(2),\n\t\t\t\t},\n\t\t\t},\n\t\t\tsummary:   Summary{HasObservedGeneration: true, State: \"\"},\n\t\t\twantState: \"\",\n\t\t\twantTrans: false,\n\t\t},\n\t\t{\n\t\t\tname: \"observedGeneration does not equal generation, sets in-progress and transitioning\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"generation\": int64(3),\n\t\t\t\t},\n\t\t\t\t\"status\": map[string]interface{}{\n\t\t\t\t\t\"observedGeneration\": int64(2),\n\t\t\t\t},\n\t\t\t},\n\t\t\tsummary:   Summary{HasObservedGeneration: true, State: \"\"},\n\t\t\twantState: \"in-progress\",\n\t\t\twantTrans: true,\n\t\t},\n\t\t{\n\t\t\tname: \"status does not exist, should set in-progress and transitioning\",\n\t\t\tobj: data.Object{\n\t\t\t\t\"metadata\": map[string]interface{}{\n\t\t\t\t\t\"generation\": int64(5),\n\t\t\t\t},\n\t\t\t},\n\t\t\tsummary:   Summary{HasObservedGeneration: true, State: \"\"},\n\t\t\twantState: \"in-progress\",\n\t\t\twantTrans: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := checkGeneration(tt.obj, nil, tt.summary)\n\t\t\tassert.Equal(t, tt.wantState, got.State)\n\t\t\tassert.Equal(t, tt.wantTrans, got.Transitioning)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/summary/summary.go",
    "content": "package summary\n\nimport (\n\t\"strings\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/data\"\n\tunstructured2 \"github.com/rancher/wrangler/v3/pkg/unstructured\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n)\n\ntype Summary struct {\n\tState                 string                 `json:\"state,omitempty\"`\n\tError                 bool                   `json:\"error,omitempty\"`\n\tTransitioning         bool                   `json:\"transitioning,omitempty\"`\n\tMessage               []string               `json:\"message,omitempty\"`\n\tAttributes            map[string]interface{} `json:\"-\"`\n\tRelationships         []Relationship         `json:\"-\"`\n\tHasObservedGeneration bool                   `json:\"-\"`\n}\n\ntype SummarizeOptions struct {\n\tHasObservedGeneration bool\n}\n\ntype Relationship struct {\n\tName         string\n\tNamespace    string\n\tControlledBy bool\n\tKind         string\n\tAPIVersion   string\n\tInbound      bool\n\tType         string\n\tSelector     *metav1.LabelSelector\n}\n\nfunc (s Summary) String() string {\n\tif !s.Transitioning && !s.Error {\n\t\treturn s.State\n\t}\n\tvar msg string\n\tif s.Transitioning {\n\t\tmsg = \"[progressing\"\n\t}\n\tif s.Error {\n\t\tif len(msg) > 0 {\n\t\t\tmsg += \",error]\"\n\t\t} else {\n\t\t\tmsg = \"error]\"\n\t\t}\n\t} else {\n\t\tmsg += \"]\"\n\t}\n\tif len(s.Message) > 0 {\n\t\tmsg = msg + \" \" + strings.Join(s.Message, \", \")\n\t}\n\treturn msg\n}\n\nfunc (s Summary) IsReady() bool {\n\treturn !s.Error && !s.Transitioning\n}\n\nfunc (s *Summary) DeepCopy() *Summary {\n\tv := *s\n\treturn &v\n}\n\nfunc (s *Summary) DeepCopyInto(v *Summary) {\n\t*v = *s\n}\n\nfunc dedupMessage(messages []string) []string {\n\tseen := map[string]bool{}\n\tvar result []string\n\n\tfor _, message := range messages {\n\t\tmessage = strings.TrimSpace(message)\n\t\tif message == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif seen[message] {\n\t\t\tcontinue\n\t\t}\n\t\tseen[message] = true\n\t\tresult = append(result, message)\n\t}\n\n\treturn result\n}\n\nfunc Summarize(runtimeObj runtime.Object) Summary {\n\treturn SummarizeWithOptions(runtimeObj, nil)\n}\n\nfunc SummarizeWithOptions(runtimeObj runtime.Object, opts *SummarizeOptions) Summary {\n\tvar (\n\t\tobj     data.Object\n\t\terr     error\n\t\tsummary Summary\n\t)\n\n\tif s, ok := runtimeObj.(*SummarizedObject); ok {\n\t\treturn s.Summary\n\t}\n\n\tunstr, ok := runtimeObj.(*unstructured.Unstructured)\n\tif !ok {\n\t\tunstr, err = unstructured2.ToUnstructured(runtimeObj)\n\t\tif err != nil {\n\t\t\treturn summary\n\t\t}\n\t}\n\n\tif unstr != nil {\n\t\tobj = unstr.Object\n\t}\n\n\tconditions := getConditions(obj)\n\tif opts != nil {\n\t\tsummary.HasObservedGeneration = opts.HasObservedGeneration\n\t}\n\n\tfor _, summarizer := range Summarizers {\n\t\tsummary = summarizer(obj, conditions, summary)\n\t}\n\n\tif summary.State == \"\" {\n\t\tsummary.State = \"active\"\n\t}\n\n\tsummary.State = strings.ToLower(summary.State)\n\tsummary.Message = dedupMessage(summary.Message)\n\treturn summary\n}\n"
  },
  {
    "path": "pkg/ticker/ticker.go",
    "content": "package ticker\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\nfunc Context(ctx context.Context, duration time.Duration) <-chan time.Time {\n\tticker := time.NewTicker(duration)\n\tc := make(chan time.Time)\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase t := <-ticker.C:\n\t\t\t\tc <- t\n\t\t\tcase <-ctx.Done():\n\t\t\t\tclose(c)\n\t\t\t\tticker.Stop()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\treturn c\n}\n"
  },
  {
    "path": "pkg/trigger/evalall.go",
    "content": "package trigger\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync/atomic\"\n\n\t\"github.com/rancher/wrangler/v3/pkg/generic\"\n\t\"github.com/rancher/wrangler/v3/pkg/relatedresource\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nvar (\n\tcounter int64\n)\n\ntype AllHandler func() error\n\ntype Controller interface {\n\tAddGenericHandler(ctx context.Context, name string, handler generic.Handler)\n\tGroupVersionKind() schema.GroupVersionKind\n\tEnqueue(namespace, name string)\n}\n\ntype Trigger interface {\n\tTrigger()\n\tOnTrigger(ctx context.Context, name string, handler AllHandler)\n\tKey() relatedresource.Key\n}\n\ntype trigger struct {\n\tkey        string\n\tcontroller Controller\n}\n\nfunc New(controller Controller) Trigger {\n\treturn &trigger{\n\t\tkey:        fmt.Sprintf(\"__trigger__%d__\", atomic.AddInt64(&counter, 1)),\n\t\tcontroller: controller,\n\t}\n}\n\nfunc (e *trigger) Key() relatedresource.Key {\n\treturn relatedresource.Key{\n\t\tNamespace: \"__trigger__\",\n\t\tName:      e.key,\n\t}\n}\n\nfunc (e *trigger) Trigger() {\n\te.controller.Enqueue(\"__trigger__\", e.key)\n}\n\nfunc (e *trigger) OnTrigger(ctx context.Context, name string, handler AllHandler) {\n\te.controller.AddGenericHandler(ctx, name, func(queueKey string, _ runtime.Object) (runtime.Object, error) {\n\t\tif queueKey == \"__trigger__/\"+e.key {\n\t\t\treturn nil, handler()\n\t\t}\n\t\treturn nil, nil\n\t})\n\te.Trigger()\n}\n"
  },
  {
    "path": "pkg/unstructured/unstructured.go",
    "content": "package unstructured\n\nimport (\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n)\n\nfunc ToUnstructured(obj runtime.Object) (*unstructured.Unstructured, error) {\n\tif ustr, ok := obj.(*unstructured.Unstructured); ok {\n\t\treturn ustr, nil\n\t}\n\n\tdata, err := convert.EncodeToMap(obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &unstructured.Unstructured{\n\t\tObject: data,\n\t}, nil\n}\n"
  },
  {
    "path": "pkg/webhook/match.go",
    "content": "package webhook\n\nimport (\n\tv1 \"k8s.io/api/admission/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n)\n\n// RouteMatch type matching of admission Request to Handlers.\ntype RouteMatch struct {\n\thandler     Handler\n\tkind        string\n\tresource    string\n\tversion     string\n\tsubResource string\n\tdryRun      *bool\n\tgroup       string\n\tname        string\n\tnamespace   string\n\toperation   v1.Operation\n\tobjType     runtime.Object\n}\n\nfunc (r *RouteMatch) admit(response *Response, request *Request) error {\n\tif r.handler != nil {\n\t\treturn r.handler.Admit(response, request)\n\t}\n\treturn nil\n}\n\nfunc (r *RouteMatch) matches(req *v1.AdmissionRequest) bool {\n\tvar group, version, kind, resource string\n\n\tif req.RequestKind != nil {\n\t\tgroup, version, kind = req.RequestKind.Group, req.RequestKind.Version, req.RequestKind.Kind\n\t}\n\tif req.RequestResource != nil {\n\t\tgroup, version, resource = req.RequestResource.Group, req.RequestResource.Version, req.RequestResource.Resource\n\t}\n\n\treturn checkString(r.kind, kind) &&\n\t\tcheckString(r.resource, resource) &&\n\t\tcheckString(r.subResource, req.SubResource) &&\n\t\tcheckString(r.version, version) &&\n\t\tcheckString(r.group, group) &&\n\t\tcheckString(r.name, req.Name) &&\n\t\tcheckString(r.namespace, req.Namespace) &&\n\t\tcheckString(string(r.operation), string(req.Operation)) &&\n\t\tcheckBool(r.dryRun, req.DryRun)\n}\n\nfunc (r *RouteMatch) getObjType() runtime.Object {\n\tif r.objType == nil {\n\t\treturn defObjType\n\t}\n\treturn r.objType\n}\n\nfunc checkString(expected, actual string) bool {\n\tif expected == \"\" {\n\t\treturn true\n\t}\n\treturn expected == actual\n}\n\nfunc checkBool(expected, actual *bool) bool {\n\tif expected == nil {\n\t\treturn true\n\t}\n\tif actual == nil {\n\t\treturn false\n\t}\n\treturn *expected == *actual\n}\n\n// Pretty methods\n\n// DryRun matches admission request with the matching DryRun value.\nfunc (r *RouteMatch) DryRun(dryRun bool) *RouteMatch { r.dryRun = &dryRun; return r }\n\n// Group matches admission request with the matching Group value.\nfunc (r *RouteMatch) Group(group string) *RouteMatch { r.group = group; return r }\n\n// HandleFunc sets the handler to be called for matching admission request.\nfunc (r *RouteMatch) HandleFunc(handler HandlerFunc) *RouteMatch { r.handler = handler; return r }\n\n// Handle sets the Handler to be called for matching admission request.\nfunc (r *RouteMatch) Handle(handler Handler) *RouteMatch { r.handler = handler; return r }\n\n// Kind matches admission request with the matching Kind value.\nfunc (r *RouteMatch) Kind(kind string) *RouteMatch { r.kind = kind; return r }\n\n// Name matches admission request with the matching Name value.\nfunc (r *RouteMatch) Name(name string) *RouteMatch { r.name = name; return r }\n\n// Namespace matches admission request with the matching Namespace value.\nfunc (r *RouteMatch) Namespace(namespace string) *RouteMatch { r.namespace = namespace; return r }\n\n// Operation matches admission request with the matching Operation value.\nfunc (r *RouteMatch) Operation(operation v1.Operation) *RouteMatch { r.operation = operation; return r }\n\n// Resource matches admission request with the matching Resource value.\nfunc (r *RouteMatch) Resource(resource string) *RouteMatch { r.resource = resource; return r }\n\n// SubResource matches admission request with the matching SubResource value.\nfunc (r *RouteMatch) SubResource(sr string) *RouteMatch { r.subResource = sr; return r }\n\n// Type specifies the runtime.Object to use for decoding.\nfunc (r *RouteMatch) Type(objType runtime.Object) *RouteMatch { r.objType = objType; return r }\n\n// Version matches admission request with the matching Version value.\nfunc (r *RouteMatch) Version(version string) *RouteMatch { r.version = version; return r }\n\n// Wrappers for pretty methods\n\n// DryRun matches admission request with the matching DryRun value.\nfunc (r *Router) DryRun(dryRun bool) *RouteMatch { return r.next().DryRun(dryRun) }\n\n// Group matches admission request with the matching Group value.\nfunc (r *Router) Group(group string) *RouteMatch { return r.next().Group(group) }\n\n// HandleFunc sets the handler to be called for matching admission request.\nfunc (r *Router) HandleFunc(hf HandlerFunc) *RouteMatch { return r.next().HandleFunc(hf) }\n\n// Handle sets the Handler to be called for matching admission request.\nfunc (r *Router) Handle(handler Handler) *RouteMatch { return r.next().Handle(handler) }\n\n// Kind matches admission request with the matching Kind value.\nfunc (r *Router) Kind(kind string) *RouteMatch { return r.next().Kind(kind) }\n\n// Name matches admission request with the matching Name value.\nfunc (r *Router) Name(name string) *RouteMatch { return r.next().Name(name) }\n\n// Namespace matches admission request with the matching Namespace value.\nfunc (r *Router) Namespace(namespace string) *RouteMatch { return r.next().Namespace(namespace) }\n\n// Operation matches admission request with the matching Operation value.\nfunc (r *Router) Operation(operation v1.Operation) *RouteMatch { return r.next().Operation(operation) }\n\n// Resource matches admission request with the matching Resource value.\nfunc (r *Router) Resource(resource string) *RouteMatch { return r.next().Resource(resource) }\n\n// SubResource matches admission request with the matching SubResource value.\nfunc (r *Router) SubResource(subResource string) *RouteMatch {\n\treturn r.next().SubResource(subResource)\n}\n\n// Type specifies the runtime.Object to use for decoding.\nfunc (r *Router) Type(objType runtime.Object) *RouteMatch { return r.next().Type(objType) }\n\n// Version matches admission request with the matching Version value.\nfunc (r *Router) Version(version string) *RouteMatch { return r.next().Version(version) }\n"
  },
  {
    "path": "pkg/webhook/router.go",
    "content": "// Package webhook holds shared code related to routing for webhook admission.\npackage webhook\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\n\tjsonpatch \"github.com/evanphx/json-patch\"\n\t\"github.com/sirupsen/logrus\"\n\tv1 \"k8s.io/api/admission/v1\"\n\t\"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n)\n\nvar (\n\tdefObjType    = &unstructured.Unstructured{}\n\tjsonPatchType = v1.PatchTypeJSONPatch\n)\n\n// NewRouter returns a newly allocated Router.\nfunc NewRouter() *Router {\n\treturn &Router{}\n}\n\n// Router manages request and the calling of matching handlers.\ntype Router struct {\n\tmatches []*RouteMatch\n}\n\nfunc (r *Router) sendError(rw http.ResponseWriter, review *v1.AdmissionReview, err error) {\n\tlogrus.Error(err)\n\tif review == nil || review.Request == nil {\n\t\thttp.Error(rw, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\treview.Response.Result = &errors.NewInternalError(err).ErrStatus\n\twriteResponse(rw, review)\n}\n\nfunc writeResponse(rw http.ResponseWriter, review *v1.AdmissionReview) {\n\trw.Header().Set(\"Content-Type\", \"application/json\")\n\terr := json.NewEncoder(rw).Encode(review)\n\tif err != nil {\n\t\tlogrus.Errorf(\"Failed to write response: %s\", err)\n\t}\n}\n\n// ServeHTTP inspects the http.Request and calls the Admit function on all matching handlers.\nfunc (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) {\n\treview := &v1.AdmissionReview{}\n\terr := json.NewDecoder(req.Body).Decode(review)\n\tif err != nil {\n\t\tr.sendError(rw, review, err)\n\t\treturn\n\t}\n\n\tif review.Request == nil {\n\t\tr.sendError(rw, review, fmt.Errorf(\"request is not set\"))\n\t\treturn\n\t}\n\n\tresponse := &Response{\n\t\tAdmissionResponse: v1.AdmissionResponse{\n\t\t\tUID: review.Request.UID,\n\t\t},\n\t}\n\n\treview.Response = &response.AdmissionResponse\n\n\tif err := r.admit(response, review.Request, req); err != nil {\n\t\tr.sendError(rw, review, err)\n\t\treturn\n\t}\n\n\twriteResponse(rw, review)\n}\n\nfunc (r *Router) admit(response *Response, request *v1.AdmissionRequest, req *http.Request) error {\n\tfor _, m := range r.matches {\n\t\tif m.matches(request) {\n\t\t\terr := m.admit(response, &Request{\n\t\t\t\tAdmissionRequest: *request,\n\t\t\t\tContext:          req.Context(),\n\t\t\t\tObjTemplate:      m.getObjType(),\n\t\t\t})\n\t\t\tlogrus.Debugf(\"admit result: %s %s %s user=%s allowed=%v err=%v\", request.Operation, request.Kind.String(), resourceString(request.Namespace, request.Name), request.UserInfo.Username, response.Allowed, err)\n\t\t\treturn err\n\t\t}\n\t}\n\treturn fmt.Errorf(\"no route match found for %s %s %s\", request.Operation, request.Kind.String(), resourceString(request.Namespace, request.Name))\n}\n\nfunc (r *Router) next() *RouteMatch {\n\tmatch := &RouteMatch{}\n\tr.matches = append(r.matches, match)\n\treturn match\n}\n\n// Request wrapper for an AdmissionRequest.\ntype Request struct {\n\tv1.AdmissionRequest\n\n\tContext     context.Context\n\tObjTemplate runtime.Object\n}\n\n// DecodeOldObject decodes the OldObject in the request into a new runtime.Object of type specified by Type().\n// If Type() was not set the runtime.Object will be of type *unstructured.Unstructured.\nfunc (r *Request) DecodeOldObject() (runtime.Object, error) {\n\tobj := r.ObjTemplate.DeepCopyObject()\n\terr := json.Unmarshal(r.OldObject.Raw, obj)\n\treturn obj, err\n}\n\n// DecodeObject decodes the Object in the request into a new runtime.Object of type specified by Type().\n// If Type() was not set the runtime.Object will be of type *unstructured.Unstructured.\nfunc (r *Request) DecodeObject() (runtime.Object, error) {\n\tobj := r.ObjTemplate.DeepCopyObject()\n\terr := json.Unmarshal(r.Object.Raw, obj)\n\treturn obj, err\n}\n\n// Response a wrapper for AdmissionResponses object\ntype Response struct {\n\tv1.AdmissionResponse\n}\n\n// CreatePatch will patch the Object in the request with the given object.\n// An error will be returned if on subsequent calls to the same request.\nfunc (r *Response) CreatePatch(request *Request, newObj runtime.Object) error {\n\tif len(r.Patch) > 0 {\n\t\treturn fmt.Errorf(\"response patch has already been already been assigned\")\n\t}\n\n\tnewBytes, err := json.Marshal(newObj)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tpatch, err := jsonpatch.CreateMergePatch(request.Object.Raw, newBytes)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tr.Patch = patch\n\tr.PatchType = &jsonPatchType\n\treturn nil\n}\n\n// The Handler type is an adapter to allow admission checking on a given request.\n// Handlers should update the response to control admission.\ntype Handler interface {\n\tAdmit(resp *Response, req *Request) error\n}\n\n// HandlerFunc type is used to add regular functions as Handler.\ntype HandlerFunc func(resp *Response, req *Request) error\n\n// Admit calls the handler function so that the function conforms to the Handler interface.\nfunc (h HandlerFunc) Admit(resp *Response, req *Request) error {\n\treturn h(resp, req)\n}\n\n// resourceString returns the resource formatted as a string.\nfunc resourceString(ns, name string) string {\n\tif ns == \"\" {\n\t\treturn name\n\t}\n\treturn fmt.Sprintf(\"%s/%s\", ns, name)\n}\n"
  },
  {
    "path": "pkg/yaml/objects_test.go",
    "content": "package yaml\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n)\n\n// JSONstruct is a test struct for verifying Unmarshal.\ntype JSONstruct struct {\n\tEmbeddedStruct\n\tCustomField   CustomStruct\n\tMismatchField bool `json:\"newFieldName\"`\n\tNestedField   EmbeddedStruct\n\tNormalField   int\n}\n\ntype EmbeddedStruct struct {\n\tEmbeddedField string\n}\ntype CustomStruct struct {\n\tName      string\n\tNamespace string\n}\n\nfunc (c *CustomStruct) UnmarshalJSON(data []byte) error {\n\tvar tmp string\n\tif err := json.Unmarshal(data, &tmp); err != nil {\n\t\treturn err\n\t}\n\tparts := strings.Split(tmp, \"/\")\n\tif len(parts) != 2 {\n\t\treturn fmt.Errorf(\"invalid test string\")\n\t}\n\tc.Name = parts[0]\n\tc.Namespace = parts[1]\n\treturn nil\n}\n\nvar (\n\temptyDoc     = []byte(\"\")\n\tsingleString = []byte(\"string\")\n\tunknownDoc   = []byte(\"unknown: value\")\n\tinvalidYAML  = []byte(`umm:\n\t\t21:`)\n\tsingleDeployment = []byte(`apiVersion: apps/v1\nkind: Deployment\nmetadata:\n    name: singleDeployment\nspec:\n    replicas: 3\n`)\n\tmultipleDeployments = []byte(`apiVersion: apps/v1\nkind: Deployment\nmetadata:\n    name: dep1\nspec:\n    replicas: 3\n---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n    name: dep2\n    namespace: dep2-ns\nspec:\n    paused: true\n---\nmetadata:\n    name: dep3\n    namespace: dep3-ns\n    labels:\n       app: testapp\nstatus:\n    readyReplicas: 4\n`)\n\tjsonYAML = []byte(`normalField: 28\nembeddedField: \"embeddedValue\"\ncustomField: \"testName/testNamespace\"\nnewFieldName: true\nnestedField:\n    embeddedField: \"nestedValue\"\n`)\n)\n"
  },
  {
    "path": "pkg/yaml/yaml.go",
    "content": "// Package yaml handles the unmarshaling of YAML objects to strusts\npackage yaml\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/ghodss/yaml\"\n\t\"github.com/rancher/wrangler/v3/pkg/data/convert\"\n\t\"github.com/rancher/wrangler/v3/pkg/gvk\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\tyamlDecoder \"k8s.io/apimachinery/pkg/util/yaml\"\n)\n\nvar (\n\tcleanPrefix = []string{\n\t\t\"kubectl.kubernetes.io/\",\n\t}\n\tcleanContains = []string{\n\t\t\"cattle.io/\",\n\t}\n)\n\nconst buffSize = 4096\n\n// Unmarshal decodes YAML bytes into document (as defined by\n// the YAML spec) then converting it to JSON via\n// \"k8s.io/apimachinery/pkg/util/yaml\".YAMLToJSON and then decoding the json in the the v interface{}\nfunc Unmarshal(data []byte, v interface{}) error {\n\treturn yamlDecoder.NewYAMLToJSONDecoder(bytes.NewBuffer(data)).Decode(v)\n}\n\n// UnmarshalWithJSONDecoder expects a reader of raw YAML.\n// It converts the document or documents to JSON,\n// then decodes the JSON bytes into a slice of values of type T.\n// Type T must be a pointer, or the function will panic.\nfunc UnmarshalWithJSONDecoder[T any](yamlReader io.Reader) ([]T, error) {\n\t// verify the object is a pointer\n\tvar obj T\n\tobjPtrType := reflect.TypeOf(obj)\n\tif objPtrType.Kind() != reflect.Pointer {\n\t\tpanic(fmt.Sprintf(\"Object T must be a pointer not %v\", objPtrType))\n\t}\n\tobjType := objPtrType.Elem()\n\tvar result []T\n\n\t// create a reader that reads one YAML document at a time.\n\treader := yamlDecoder.NewYAMLReader(bufio.NewReaderSize(yamlReader, buffSize))\n\tfor {\n\t\t// get raw yaml for a single document\n\t\trawYAML, err := reader.Read()\n\t\tif errors.Is(err, io.EOF) {\n\t\t\t// finished reading\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to read yaml: %w\", err)\n\t\t}\n\t\t// convert YAML to JSON\n\t\trawJSON, err := yamlDecoder.ToJSON(rawYAML)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to convert YAML to JSON: %w\", err)\n\t\t}\n\t\t// unmarshal JSON to the provided object\n\t\tnewObj := reflect.New(objType).Interface().(T)\n\t\terr = json.Unmarshal(rawJSON, newObj)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to unmarshal converted JSON: %w\", err)\n\t\t}\n\t\tresult = append(result, newObj)\n\t}\n\treturn result, nil\n}\n\n// ToObjects takes a reader of yaml bytes and returns a list of unstructured.Unstructured runtime.Objects that are read.\n// If one of the objects read is an unstructured.UnstructuredList then the list is flattened to individual objects.\nfunc ToObjects(in io.Reader) ([]runtime.Object, error) {\n\tvar result []runtime.Object\n\treader := yamlDecoder.NewYAMLReader(bufio.NewReaderSize(in, buffSize))\n\tfor {\n\t\traw, err := reader.Read()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tobj, err := toObjects(raw)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tresult = append(result, obj...)\n\t}\n\n\treturn result, nil\n}\n\nfunc toObjects(bytes []byte) ([]runtime.Object, error) {\n\tbytes, err := yamlDecoder.ToJSON(bytes)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcheck := map[string]interface{}{}\n\tif err := json.Unmarshal(bytes, &check); err != nil || len(check) == 0 {\n\t\treturn nil, err\n\t}\n\n\tobj, _, err := unstructured.UnstructuredJSONScheme.Decode(bytes, nil, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif l, ok := obj.(*unstructured.UnstructuredList); ok {\n\t\tvar result []runtime.Object\n\t\tfor _, obj := range l.Items {\n\t\t\tcopy := obj\n\t\t\tresult = append(result, &copy)\n\t\t}\n\t\treturn result, nil\n\t}\n\n\treturn []runtime.Object{obj}, nil\n}\n\n// Export will attempt to clean up the objects a bit before\n// rendering to yaml so that they can easily be imported into another\n// cluster\nfunc Export(objects ...runtime.Object) ([]byte, error) {\n\tif len(objects) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tbuffer := &bytes.Buffer{}\n\tfor i, obj := range objects {\n\t\tif i > 0 {\n\t\t\tbuffer.WriteString(\"\\n---\\n\")\n\t\t}\n\n\t\tobj, err := CleanObjectForExport(obj)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tbytes, err := yaml.Marshal(obj)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to encode %s: %w\", obj.GetObjectKind().GroupVersionKind(), err)\n\t\t}\n\t\tbuffer.Write(bytes)\n\t}\n\n\treturn buffer.Bytes(), nil\n}\n\nfunc CleanObjectForExport(obj runtime.Object) (runtime.Object, error) {\n\tobj = obj.DeepCopyObject()\n\tif obj.GetObjectKind().GroupVersionKind().Kind == \"\" {\n\t\tif gvk, err := gvk.Get(obj); err == nil {\n\t\t\tobj.GetObjectKind().SetGroupVersionKind(gvk)\n\t\t} else if err != nil {\n\t\t\treturn nil, fmt.Errorf(\"kind and/or apiVersion is not set on input object: %v: %w\", obj, err)\n\t\t}\n\t}\n\n\tdata, err := convert.EncodeToMap(obj)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tunstr := &unstructured.Unstructured{\n\t\tObject: data,\n\t}\n\n\tmetadata := map[string]interface{}{}\n\n\tif name := unstr.GetName(); len(name) > 0 {\n\t\tmetadata[\"name\"] = name\n\t} else if generated := unstr.GetGenerateName(); len(generated) > 0 {\n\t\tmetadata[\"generateName\"] = generated\n\t} else {\n\t\treturn nil, fmt.Errorf(\"either name or generateName must be set on obj: %v\", obj)\n\t}\n\n\tif unstr.GetNamespace() != \"\" {\n\t\tmetadata[\"namespace\"] = unstr.GetNamespace()\n\t}\n\tif annotations := unstr.GetAnnotations(); len(annotations) > 0 {\n\t\tcleanMap(annotations)\n\t\tif len(annotations) > 0 {\n\t\t\tmetadata[\"annotations\"] = annotations\n\t\t} else {\n\t\t\tdelete(metadata, \"annotations\")\n\t\t}\n\t}\n\tif labels := unstr.GetLabels(); len(labels) > 0 {\n\t\tcleanMap(labels)\n\t\tif len(labels) > 0 {\n\t\t\tmetadata[\"labels\"] = labels\n\t\t} else {\n\t\t\tdelete(metadata, \"labels\")\n\t\t}\n\t}\n\n\tif spec, ok := data[\"spec\"]; ok {\n\t\tif spec == nil {\n\t\t\tdelete(data, \"spec\")\n\t\t} else if m, ok := spec.(map[string]interface{}); ok && len(m) == 0 {\n\t\t\tdelete(data, \"spec\")\n\t\t}\n\t}\n\n\tdata[\"metadata\"] = metadata\n\tdelete(data, \"status\")\n\n\treturn unstr, nil\n}\n\nfunc CleanAnnotationsForExport(annotations map[string]string) map[string]string {\n\tresult := make(map[string]string, len(annotations))\n\nouter:\n\tfor k := range annotations {\n\t\tfor _, prefix := range cleanPrefix {\n\t\t\tif strings.HasPrefix(k, prefix) {\n\t\t\t\tcontinue outer\n\t\t\t}\n\t\t}\n\t\tfor _, contains := range cleanContains {\n\t\t\tif strings.Contains(k, contains) {\n\t\t\t\tcontinue outer\n\t\t\t}\n\t\t}\n\t\tresult[k] = annotations[k]\n\t}\n\treturn result\n}\n\nfunc cleanMap(annoLabels map[string]string) {\n\tfor k := range annoLabels {\n\t\tfor _, prefix := range cleanPrefix {\n\t\t\tif strings.HasPrefix(k, prefix) {\n\t\t\t\tdelete(annoLabels, k)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc ToBytes(objects []runtime.Object) ([]byte, error) {\n\tif len(objects) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tbuffer := &bytes.Buffer{}\n\tfor i, obj := range objects {\n\t\tif i > 0 {\n\t\t\tbuffer.WriteString(\"\\n---\\n\")\n\t\t}\n\n\t\tbytes, err := yaml.Marshal(obj)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to encode %s: %w\", obj.GetObjectKind().GroupVersionKind(), err)\n\t\t}\n\t\tbuffer.Write(bytes)\n\t}\n\n\treturn buffer.Bytes(), nil\n}\n"
  },
  {
    "path": "pkg/yaml/yaml_test.go",
    "content": "package yaml\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\tappsv1 \"k8s.io/api/apps/v1\"\n)\n\nfunc TestUnmarshalWithJSONDecoder_deployment(t *testing.T) {\n\n\ttests := []struct {\n\t\tname    string\n\t\tinput   []byte\n\t\twant    func() []*appsv1.Deployment\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:  \"single deployment\",\n\t\t\tinput: singleDeployment,\n\t\t\twant: func() []*appsv1.Deployment {\n\t\t\t\tdep := &appsv1.Deployment{}\n\t\t\t\tdep.Name = \"singleDeployment\"\n\t\t\t\tdep.APIVersion = appsv1.SchemeGroupVersion.String()\n\t\t\t\tdep.Kind = \"Deployment\"\n\t\t\t\tthree := int32(3)\n\t\t\t\tdep.Spec.Replicas = &three\n\t\t\t\treturn []*appsv1.Deployment{dep}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"multiple deployment\",\n\t\t\tinput: multipleDeployments,\n\t\t\twant: func() []*appsv1.Deployment {\n\t\t\t\tdep := &appsv1.Deployment{}\n\t\t\t\tdep.Name = \"dep1\"\n\t\t\t\tdep.APIVersion = appsv1.SchemeGroupVersion.String()\n\t\t\t\tdep.Kind = \"Deployment\"\n\t\t\t\tthree := int32(3)\n\t\t\t\tdep.Spec.Replicas = &three\n\n\t\t\t\tdep2 := &appsv1.Deployment{}\n\t\t\t\tdep2.APIVersion = appsv1.SchemeGroupVersion.String()\n\t\t\t\tdep2.Kind = \"Deployment\"\n\t\t\t\tdep2.Name = \"dep2\"\n\t\t\t\tdep2.Namespace = \"dep2-ns\"\n\t\t\t\tdep2.Spec.Paused = true\n\n\t\t\t\tdep3 := &appsv1.Deployment{}\n\t\t\t\tdep3.Name = \"dep3\"\n\t\t\t\tdep3.Namespace = \"dep3-ns\"\n\t\t\t\tdep3.Status.ReadyReplicas = 4\n\t\t\t\tdep3.Labels = map[string]string{\"app\": \"testapp\"}\n\t\t\t\treturn []*appsv1.Deployment{dep, dep2, dep3}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"empty document\",\n\t\t\tinput: emptyDoc,\n\t\t\twant: func() []*appsv1.Deployment {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"unknown document\",\n\t\t\tinput: unknownDoc,\n\t\t\twant: func() []*appsv1.Deployment {\n\t\t\t\tdep := &appsv1.Deployment{}\n\t\t\t\treturn []*appsv1.Deployment{dep}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"invalid YAML\",\n\t\t\tinput:   invalidYAML,\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname:    \"invalid JSON marshal\",\n\t\t\tinput:   singleString,\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := UnmarshalWithJSONDecoder[*appsv1.Deployment](bytes.NewReader(tt.input))\n\t\t\tif tt.wantErr {\n\t\t\t\trequire.Error(t, err, \"expected an error but got nil\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, err, \"UnmarshalWithJSONDecoder received an unexpected error\")\n\t\t\tvar want []*appsv1.Deployment\n\t\t\tif tt.want != nil {\n\t\t\t\twant = tt.want()\n\t\t\t}\n\t\t\trequire.Equal(t, got, want, \"UnmarshalWithJSONDecoder received unexpected results\")\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalWithJSONDecoder_jsonSruct(t *testing.T) {\n\tt.Parallel()\n\ttests := []struct {\n\t\tname    string\n\t\tinput   []byte\n\t\twant    func() []*JSONstruct\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:  \"all expected fields\",\n\t\t\tinput: jsonYAML,\n\t\t\twant: func() []*JSONstruct {\n\t\t\t\tobj := &JSONstruct{}\n\t\t\t\tobj.EmbeddedField = \"embeddedValue\"\n\t\t\t\tobj.NestedField.EmbeddedField = \"nestedValue\"\n\t\t\t\tobj.CustomField.Name = \"testName\"\n\t\t\t\tobj.CustomField.Namespace = \"testNamespace\"\n\t\t\t\tobj.NormalField = 28\n\t\t\t\tobj.MismatchField = true\n\t\t\t\treturn []*JSONstruct{obj}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"unknown type\",\n\t\t\tinput: singleDeployment,\n\t\t\twant: func() []*JSONstruct {\n\t\t\t\tobj := &JSONstruct{}\n\t\t\t\treturn []*JSONstruct{obj}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"empty document\",\n\t\t\tinput: emptyDoc,\n\t\t},\n\t\t{\n\t\t\tname:    \"invalid YAML\",\n\t\t\tinput:   invalidYAML,\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname:    \"invalid JSON marshal\",\n\t\t\tinput:   singleString,\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor i := range tests {\n\t\ttt := &tests[i]\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tgot, err := UnmarshalWithJSONDecoder[*JSONstruct](bytes.NewReader(tt.input))\n\t\t\tif tt.wantErr {\n\t\t\t\trequire.Error(t, err, \"expected an error but got nil\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, err, \"UnmarshalWithJSONDecoder received an unexpected error\")\n\t\t\tvar want []*JSONstruct\n\t\t\tif tt.want != nil {\n\t\t\t\twant = tt.want()\n\t\t\t}\n\t\t\trequire.Equal(t, got, want, \"UnmarshalWithJSONDecoder received unexpected results\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "scripts/boilerplate.go.txt",
    "content": "/*\nCopyright The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n"
  },
  {
    "path": "scripts/ci",
    "content": "#! /bin/bash\nset -e\n\ncd $(dirname $0)/..\necho \"Validating\"\n\nif [[ -z $(type -p \"mockgen\") ]]; then\n    echo \"'mockgen': executable file not found in \\$PATH. mockgen is needed to compelete code generation.\"\n    echo \"Install mockgen with 'go install go.uber.org/mock/mockgen@v0.6.0'\"\n    exit 1\nfi\n\necho Running: go generate\ngo generate ./...\n\necho Tidying up modules\ngo mod tidy\n\necho Verifying modules\ngo mod verify\n\nif [ -n \"$(git status --porcelain --untracked-files=no)\" ]; then\n    echo \"Encountered dirty repo!\"\n    exit 1\nfi\n\necho \"Running Test\"\ngo test ./...\n"
  }
]