[
  {
    "path": ".coderabbit.yaml",
    "content": "# CodeRabbit Configuration for Crossplane Runtime\n# This configuration is optimized for the Crossplane Runtime Go library\n\n# =============================================================================\n# GLOBAL SETTINGS\n# =============================================================================\n\n# Language for CodeRabbit reviews and comments (default: en-US, keeping explicit)\nlanguage: \"en-US\"\n\n# Instructions for CodeRabbit's tone and style in reviews (max 250 chars)\ntone_instructions: |\n  Be collaborative and supportive. Ask clarifying questions rather than making\n  assumptions. Focus on the 'why' behind decisions. Frame concerns\n  constructively and thank contributors.\n\n# Disable early-access features for stability\nearly_access: false\n\n# =============================================================================\n# REVIEWS\n# =============================================================================\n\nreviews:\n  # We tested assertive and found it too verbose, e.g. approxing 200 comments on\n  # https://github.com/crossplane/crossplane/pull/6777. Some of the nitpicks do\n  # look valuable to me, but the signal to noise ratio isn't good enough.\n  profile: \"chill\"\n  \n  # Don't generate summary in PR description - let authors write their own\n  high_level_summary: false\n  \n  # Include the high-level summary in the walkthrough comment instead\n  high_level_summary_in_walkthrough: true\n  \n  # Collapse walkthrough comment to reduce visual clutter in PRs\n  collapse_walkthrough: true\n  \n  # Enable automatic label application\n  auto_apply_labels: true\n  \n  # Automatically assign suggested reviewers (disabled - let maintainers control)\n  auto_assign_reviewers: false\n  \n  # Disable poem generation in walkthrough comments\n  poem: false\n  \n  # Disable review status messages to reduce comment noise\n  review_status: false\n  \n  # Focus reviews on source code, exclude generated and vendor files\n  path_filters:\n    # Include source code\n    - \"**/*.go\"\n    - \"**/*.yaml\"\n    - \"**/*.yml\"\n    - \"**/*.md\"\n    - \"**/*.proto\"\n    - \"**/Dockerfile*\"\n    - \"**/flake.nix\"\n    - \"**/*.sh\"\n    \n    # Exclude generated and vendor files\n    - \"!**/zz_generated*.go\"\n    - \"!**/vendor/**\"\n    - \"!**/node_modules/**\"\n    - \"!**/*.pb.go\"\n    - \"!**/*.pb.gw.go\"\n    - \"!**/mock_*.go\"\n    - \"!**/fake/**\"\n    - \"!**/testdata/**\"\n    - \"!**/dist/**\"\n    - \"!**/build/**\"\n  \n  # Path-specific instructions for different areas of the codebase\n  path_instructions:\n    - path: \"**/*.go\"\n      instructions: |\n        Enforce Crossplane-specific patterns: Use crossplane-runtime/pkg/errors\n        for wrapping. Check variable naming (short for local scope, descriptive\n        for wider scope). Ensure 'return early' pattern. Verify error scoping\n        (declare in conditionals when possible). For nolint directives, require\n        specific linter names and explanations. CRITICAL: Ensure all error\n        messages are meaningful to end users, not just developers - avoid\n        technical jargon, include context about what the user was trying to do,\n        and suggest next steps when possible.\n    \n    - path: \"**/*_test.go\"\n      instructions: |\n        Enforce table-driven test structure: PascalCase test names (no\n        underscores), args/want pattern, use cmp.Diff with\n        cmpopts.EquateErrors() for error testing. Check for proper test case\n        naming and reason fields. Ensure no third-party test frameworks (no\n        Ginkgo, Gomega, Testify).\n    \n    - path: \"**/*.md\"\n      instructions: |\n        Ensure Markdown files are wrapped at 100 columns for consistency and\n        readability. Lines can be longer if it makes links more readable, but\n        otherwise should wrap at 100 characters. Check for proper heading\n        structure, clear language, and that documentation is helpful for users.\n    \n    - path: \"**/apis/**\"\n      instructions: |\n        Focus on API design following Kubernetes API conventions from\n        https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md.\n        Check for proper field naming (camelCase), appropriate types, validation\n        tags, and documentation. Ask about backward compatibility and the impact\n        on existing users and upgrade paths. Consider if changes need feature\n        gates or alpha/beta graduation. Ensure error messages in validation are\n        user-friendly. Pay attention to API consistency, proper use of optional\n        vs required fields, and following established Kubernetes patterns.\n    \n    - path: \"**/pkg/reconciler/**\"\n      instructions: |\n        Review reconciler logic for proper reconciliation patterns, error\n        handling, and resource management. Pay special attention to conditions\n        and events: Conditions must be actionable for users (not developers),\n        stable/deterministic, with proper Type/Reason/Message format. Events\n        only when something actually happens, with specific details about what\n        changed. No transient errors in conditions/events. All error messages\n        must be meaningful to end users - include context about what\n        resource/operation failed and why.\n    \n    - path: \"**/test/**\"\n      instructions: |\n        Focus on test coverage, test clarity, and proper use of testing\n        utilities. Ask about testing scenarios and edge cases. Ensure tests are\n        maintainable and cover the happy path and error conditions. Verify\n        error testing uses proper patterns (cmpopts.EquateErrors, sentinel\n        errors for complex cases).\n    \n\n  \n  # Automatic review settings\n  auto_review:\n    # Skip reviewing draft PRs until they're ready for review (default: false, keeping explicit)\n    drafts: false\n    \n    # Skip reviews if PR title contains these keywords (case-insensitive)\n    ignore_title_keywords:\n      - \"wip\"\n      - \"draft\"\n      - \"do not merge\"\n      - \"dnm\"\n    \n    # Skip reviews from these automated bot accounts\n    ignore_usernames:\n      - \"dependabot[bot]\"\n      - \"renovate[bot]\"\n      - \"github-actions[bot]\"\n  \n  # Quality gates that run during CodeRabbit's review to check PR readiness\n  pre_merge_checks:\n    # Check PR title for length and descriptiveness\n    title:\n      requirements: \"Keep under 72 characters and be descriptive about what the change does.\"\n    \n    # Disable docstring coverage check (too noisy for Go projects)\n    docstrings:\n      mode: \"off\"\n    \n    # Custom checks specific to Crossplane Runtime development practices\n    custom_checks:\n      - name: \"Breaking Changes\"\n        mode: \"error\"\n        instructions: |\n          Fails if any public Go code (exported functions, types, methods, or\n          fields) in '**/*.go' (excluding *_test.go and generated files) is\n          removed, renamed, has signature changes, or has behavior changes that\n          could break existing users, without the 'breaking-change' label. This\n          is a library repo - all exported APIs are public.\n  \n  # Disable automatic code generation features\n  finishing_touches:\n    # Disable automatic docstring generation\n    docstrings:\n      enabled: false\n\n    # Disable automatic unit test generation  \n    unit_tests:\n      enabled: false\n\n  # Tools - DISABLED: We prefer to run linting tools directly in CI\n  # Our comprehensive golangci-lint setup with \"default: all\" already covers\n  # most static analysis. Additional tools can be added to CI as needed.\n  tools:\n    # Go linting - disabled (we run golangci-lint with comprehensive config)\n    golangci-lint:\n      enabled: false\n    \n    # Security and vulnerability scanning - disabled (prefer direct CI integration)\n    gitleaks:\n      enabled: false\n    \n    semgrep:\n      enabled: false\n    \n    osvScanner:\n      enabled: false\n    \n    # File format linting - disabled (prefer direct CI integration)\n    yamllint:\n      enabled: false\n    \n    markdownlint:\n      enabled: false\n    \n    shellcheck:\n      enabled: false\n    \n    hadolint:\n      enabled: false\n    \n    actionlint:\n      enabled: false\n    \n    buf:\n      enabled: false\n    \n    # GitHub integration - disabled for now\n    github-checks:\n      enabled: false\n\n# =============================================================================\n# CHAT\n# Interactive chat with CodeRabbit in PR comments. You can ask questions like:\n# - @coderabbitai explain this error handling approach\n# - @coderabbitai what are the edge cases for this function?\n# - @coderabbitai how does this affect backward compatibility?\n# - @coderabbitai generate unit tests for this function\n# =============================================================================\n\nchat:\n  # Disable ASCII/emoji art in responses\n  art: false\n\n# =============================================================================\n# KNOWLEDGE BASE\n# =============================================================================\n\nknowledge_base:\n  # Enable MCP integration to provide context about external libraries and APIs\n  mcp:\n    usage: \"enabled\"\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug Report\nabout: Help us diagnose and fix bugs in Crossplane\nlabels: bug\n---\n<!--\nThank you for helping to improve Crossplane!\n\nPlease be sure to search for open issues before raising a new one. We use issues\nfor bug reports and feature requests. Please find us at https://slack.crossplane.io\nfor questions, support, and discussion.\n-->\n\n### What happened?\n<!--\nPlease let us know what behaviour you expected and how Crossplane diverged from\nthat behaviour.\n-->\n\n\n### How can we reproduce it?\n<!--\nHelp us to reproduce your bug as succinctly and precisely as possible. Artifacts\nsuch as example manifests or a script that triggers the issue are highly\nappreciated!\n-->\n\n### What environment did it happen in?\nCrossplane version: \n\n<!--\nInclude at least the version or commit of Crossplane you were running. Consider\nalso including your:\n\n* Cloud provider or hardware configuration\n* Kubernetes version (use `kubectl version`)\n* Kubernetes distribution (e.g. Tectonic, GKE, OpenShift)\n* OS (e.g. from /etc/os-release)\n* Kernel (e.g. `uname -a`)\n-->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature Request\nabout: Help us make Crossplane more useful\nlabels: enhancement\n---\n<!--\nThank you for helping to improve Crossplane!\n\nPlease be sure to search for open issues before raising a new one. We use issues\nfor bug reports and feature requests. Please find us at https://slack.crossplane.io\nfor questions, support, and discussion.\n-->\n\n### What problem are you facing?\n<!--\nPlease tell us a little about your use case - it's okay if it's hypothetical!\nLeading with this context helps frame the feature request so we can ensure we\nimplement it sensibly.\n--->\n\n### How could Crossplane help solve your problem?\n<!--\nLet us know how you think Crossplane could help with your use case. \n-->\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nThank you for helping to improve Crossplane! Please read the contribution docs\n(linked below) if this is your first Crossplane pull request.\n-->\n\n### Description of your changes\n\n<!--\nBriefly describe what this pull request does, and how it is covered by tests.\nBe proactive - direct your reviewers' attention to anything that needs special\nconsideration.\n\nWe love pull requests that fix an open issue. If yours does, use the below line\nto indicate which issue it fixes, for example \"Fixes #500\".\n-->\n\nFixes # \n\nI have: <!--You MUST either [x] check or [ ] ~strike through~ every item.-->\n\n- [ ] Read and followed Crossplane's [contribution process].\n- [ ] Run `./nix.sh flake check` to ensure this PR is ready for review.\n- [ ] Added or updated unit tests.\n- [ ] Linked a PR or a [docs tracking issue] to [document this change].\n- [ ] Added `backport release-x.y` labels to auto-backport this PR.\n\nNeed help with this checklist? See the [cheat sheet].\n\n[contribution process]: https://github.com/crossplane/crossplane/tree/main/contributing\n[docs tracking issue]: https://github.com/crossplane/docs/issues/new\n[document this change]: https://docs.crossplane.io/contribute/contribute\n[cheat sheet]: https://github.com/crossplane/crossplane/tree/main/contributing#checklist-cheat-sheet\n"
  },
  {
    "path": ".github/renovate-base.json5",
    "content": "{\n  $schema: 'https://docs.renovatebot.com/renovate-schema.json',\n  extends: [\n    'config:recommended',\n    'helpers:pinGitHubActionDigests',\n    ':semanticCommits',\n  ],\n  // We only want renovate to rebase PRs when they have conflicts, default\n  // \"auto\" mode is not required.\n  rebaseWhen: 'conflicted',\n  // The maximum number of PRs to be created in parallel\n  prConcurrentLimit: 5,\n  // The branches renovate should target\n  // PLEASE UPDATE THIS WHEN RELEASING.\n  baseBranches: [\n    'main',\n    'release-1.20',\n    'release-2.0',\n    'release-2.1',\n    'release-2.2',\n    'release-2.3',\n  ],\n  ignorePaths: [\n    'design/**',\n  ],\n  postUpdateOptions: [\n    'gomodTidy',\n  ],\n  // All PRs should have a label\n  labels: [\n    'automated',\n  ],\n  // PackageRules disabled below should be enabled in case of vulnerabilities\n  vulnerabilityAlerts: {\n    enabled: true,\n  },\n  osvVulnerabilityAlerts: true,\n  // Renovate evaluates all packageRules in order, so low priority rules should\n  // be at the beginning, high priority at the end\n  packageRules: [\n    {\n      description: 'Ignore non-security related updates to release branches',\n      matchBaseBranches: [\n        '/^release-.*/',\n      ],\n      enabled: false,\n    },\n    {\n      description: 'Still update Docker images on release branches though',\n      matchDatasources: [\n        'docker',\n      ],\n      matchBaseBranches: [\n        '/^release-.*/',\n      ],\n      enabled: true,\n    },\n    {\n      description: 'Only get Docker image updates every 2 weeks to reduce noise',\n      matchDatasources: [\n        'docker',\n      ],\n      schedule: [\n        'every 2 week on monday',\n      ],\n      enabled: true,\n    },\n    {\n      description: \"Ignore k8s.io/client-go older versions, they switched to semantic version and old tags are still available in the repo\",\n      matchDatasources: [\n        'go',\n      ],\n      matchDepNames: [\n        'k8s.io/client-go',\n      ],\n      allowedVersions: '<1.0',\n    },\n    {\n      description: 'Only get dependency digest updates every month to reduce noise',\n      matchDatasources: [\n        'go',\n      ],\n      matchUpdateTypes: [\n        'digest',\n      ],\n      extends: [\n        'schedule:monthly',\n      ],\n    },\n    {\n      description: \"Ignore oss-fuzz, it's not using tags, we'll stick to master\",\n      matchDepTypes: [\n        'action',\n      ],\n      matchDepNames: [\n        'google/oss-fuzz',\n      ],\n      enabled: false,\n    },\n    {\n      description: 'Group all go version updates',\n      matchDatasources: [\n        'golang-version',\n      ],\n      groupName: 'golang version',\n    },\n  ],\n}\n"
  },
  {
    "path": ".github/renovate-earthly.json5",
    "content": "{\n  // Earthly-specific configuration for release branches.\n  // Main branch uses Nix - see renovate-nix.json5.\n  customManagers: [\n    {\n      customType: 'regex',\n      description: 'Bump Earthly version in GitHub workflows',\n      fileMatch: [\n        '^\\\\.github\\\\/workflows\\\\/[^/]+\\\\.ya?ml$',\n      ],\n      matchStrings: [\n        \"EARTHLY_VERSION: '(?<currentValue>.*?)'\\\\n\",\n      ],\n      datasourceTemplate: 'github-releases',\n      depNameTemplate: 'earthly/earthly',\n      extractVersionTemplate: '^v(?<version>.*)$',\n    },\n    {\n      customType: 'regex',\n      description: 'Bump Go version in Earthfile',\n      fileMatch: [\n        '^Earthfile$',\n      ],\n      matchStrings: [\n        'ARG --global GO_VERSION=(?<currentValue>.*?)\\\\n',\n      ],\n      datasourceTemplate: 'golang-version',\n      depNameTemplate: 'golang',\n    },\n    {\n      customType: 'regex',\n      description: 'Bump golangci-lint version in the Earthfile',\n      fileMatch: [\n        '^Earthfile$',\n      ],\n      matchStrings: [\n        'ARG GOLANGCI_LINT_VERSION=(?<currentValue>.*?)\\\\n',\n      ],\n      datasourceTemplate: 'github-releases',\n      depNameTemplate: 'golangci/golangci-lint',\n    },\n    {\n      customType: 'regex',\n      description: 'Bump codeql version in the Earthfile',\n      fileMatch: [\n        '^Earthfile$',\n      ],\n      matchStrings: [\n        'ARG CODEQL_VERSION=(?<currentValue>.*?)\\\\n',\n      ],\n      datasourceTemplate: 'github-releases',\n      depNameTemplate: 'github/codeql-action',\n      extractVersionTemplate: '^codeql-bundle-(?<version>.*)$',\n    },\n  ],\n  // Renovate doesn't have native Earthfile support, but because Earthfile\n  // syntax is a superset of Dockerfile syntax this works to update FROM images.\n  // https://github.com/renovatebot/renovate/issues/15975\n  dockerfile: {\n    fileMatch: [\n      '(^|/)Earthfile$',\n    ],\n  },\n  packageRules: [\n    {\n      description: 'Generate code after upgrading go dependencies (Earthly)',\n      matchDatasources: [\n        'go',\n      ],\n      matchBaseBranches: [\n        // Release 2.1 and older use earthly.\n        '/^release-1\\..*/',\n        '/^release-2\\.[0-1]$/',\n      ],\n      postUpgradeTasks: {\n        commands: [\n          'earthly --strict +go-generate',\n        ],\n        fileFilters: [\n          '**/*',\n        ],\n        executionMode: 'update',\n      },\n    },\n    {\n      description: 'Lint code after upgrading golangci-lint (Earthly)',\n      matchDepNames: [\n        'golangci/golangci-lint',\n      ],\n      matchBaseBranches: [\n        // Release 2.1 and older use earthly.\n        '/^release-1\\..*/',\n        '/^release-2\\.[0-1]$/',\n      ],\n      postUpgradeTasks: {\n        commands: [\n          'earthly --strict +go-lint',\n        ],\n        fileFilters: [\n          '**/*',\n        ],\n        executionMode: 'update',\n      },\n    },\n  ],\n}\n"
  },
  {
    "path": ".github/renovate-entrypoint.sh",
    "content": "#!/bin/bash\n\nset -e\n\n# Install Earthly (for release branches)\necho \"Installing Earthly...\"\ncurl -fsSLo /usr/local/bin/earthly https://github.com/earthly/earthly/releases/latest/download/earthly-linux-amd64\nchmod +x /usr/local/bin/earthly\n/usr/local/bin/earthly bootstrap\n\n# Install Nix (for main branch)\necho \"Installing Nix...\"\napt-get update && apt-get install -y nix-bin\n\n# Configure Nix\nmkdir -p /etc/nix\ncat > /etc/nix/nix.conf << 'EOF'\n# Enable flakes and the nix command (e.g. nix run, nix build).\nexperimental-features = nix-command flakes\n\n# Run builds as the calling user, not dedicated nixbld users. This avoids\n# needing to create the nixbld group and users in this ephemeral container.\nbuild-users-group =\n\n# Build derivations in parallel, one per CPU core.\nmax-jobs = auto\n\n# Use the Crossplane Cachix cache to download pre-built binaries from CI.\nextra-substituters = https://crossplane.cachix.org\nextra-trusted-public-keys = crossplane.cachix.org-1:NJluVUN9TX0rY/zAxHYaT19Y5ik4ELH4uFuxje+62d4=\nEOF\n\necho \"Nix $(nix --version) installed successfully\"\n\nrenovate\n"
  },
  {
    "path": ".github/renovate-nix.json5",
    "content": "{\n  // Nix-specific configuration for main branch.\n  // Release branches use Earthly - see renovate-earthly.json5.\n\n  // Enable the nix manager to update flake.lock when flake inputs change.\n  nix: {\n    enabled: true,\n  },\n\n  packageRules: [\n    {\n      description: 'Update flake.lock monthly',\n      matchManagers: [\n        'nix',\n      ],\n      extends: [\n        'schedule:monthly',\n      ],\n    },\n    {\n      description: 'Regenerate gomod2nix.toml and generated code after upgrading go dependencies (Nix)',\n      matchDatasources: [\n        'go',\n      ],\n      matchBaseBranches: [\n        'main',\n        // Release 2.2 and newer use nix.\n        '/^release-2\\.([2-9]|..+)$/',\n      ],\n      postUpgradeTasks: {\n        commands: [\n          'nix run .#tidy',\n          'nix run .#generate',\n        ],\n        fileFilters: [\n          '**/*',\n        ],\n        executionMode: 'update',\n      },\n    },\n    {\n      description: 'Lint code after upgrading golangci-lint (Nix)',\n      matchDepNames: [\n        'golangci/golangci-lint',\n      ],\n      matchBaseBranches: [\n        'main',\n        // Release 2.2 and newer use nix.\n        '/^release-2\\.([2-9]|..+)$/',\n      ],\n      postUpgradeTasks: {\n        commands: [\n          'nix run .#lint',\n        ],\n        fileFilters: [\n          '**/*',\n        ],\n        executionMode: 'update',\n      },\n    },\n  ],\n}\n"
  },
  {
    "path": ".github/renovate.json5",
    "content": "{\n  // This is the main Renovate configuration file.\n  // It extends base config and build-tool-specific configs.\n  //\n  // - renovate-base.json5: Common configuration for all branches\n  // - renovate-earthly.json5: Earthly-specific config for release branches\n  // - renovate-nix.json5: Nix-specific config for main branch\n  //\n  // The local> syntax requires owner/repo format with // for paths.\n  // See: https://docs.renovatebot.com/config-presets/#local-presets\n  extends: [\n    'local>crossplane/crossplane-runtime//.github/renovate-base.json5',\n    'local>crossplane/crossplane-runtime//.github/renovate-earthly.json5',\n    'local>crossplane/crossplane-runtime//.github/renovate-nix.json5',\n  ],\n}\n"
  },
  {
    "path": ".github/workflows/backport.yml",
    "content": "name: Backport\n\non:\n  # NOTE(negz): This is a risky target, but we run this action only when and if\n  # a PR is closed, then filter down to specifically merged PRs. We also don't\n  # invoke any scripts, etc from within the repo. I believe the fact that we'll\n  # be able to review PRs before this runs makes this fairly safe.\n  # https://securitylab.github.com/research/github-actions-preventing-pwn-requests/\n  pull_request_target:\n    types: [closed]\n  # See also commands.yml for the /backport triggered variant of this workflow.\n\njobs:\n  # NOTE(negz): I tested many backport GitHub actions before landing on this\n  # one. Many do not support merge commits, or do not support pull requests with\n  # more than one commit. This one does. It also handily links backport PRs with\n  # new PRs, and provides commentary and instructions when it can't backport.\n  # The main gotchas with this action are that it _only_ supports merge commits,\n  # and that PRs _must_ be labelled before they're merged to trigger a backport.\n  open-pr:\n    runs-on: ubuntu-22.04\n    if: github.event.pull_request.merged\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          fetch-depth: 0\n\n      - name: Open Backport PR\n        uses: zeebe-io/backport-action@3c06f323a58619da1e8522229ebc8d5de2633e46 # v4.3.0\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          github_workspace: ${{ github.workspace }}\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches:\n      - main\n      - release-*\n  pull_request: {}\n  workflow_dispatch: {}\n\nenv:\n  # We can't run a step 'if secrets.FOO != \"\"' but we can run a step\n  # 'if env.FOO' != \"\"', so we copy secrets to env vars for conditional checks.\n  DOCKER_USR: ${{ secrets.DOCKER_USR }}\n\njobs:\n  check-diff:\n    runs-on: ubuntu-22.04\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n\n      - name: Install Nix\n        uses: cachix/install-nix-action@616559265b40713947b9c190a8ff4b507b5df49b # v31\n\n      - name: Setup Cachix\n        uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16\n        with:\n          name: crossplane\n          authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}\n\n      - name: Verify Generated Code\n        run: nix build .#checks.x86_64-linux.generate --print-build-logs\n\n  validate-renovate-config:\n    runs-on: ubuntu-22.04\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n\n      # renovate-config-validator only looks at the top-level file and does\n      # not recursively resolve local> presets, so we also syntax-check every\n      # renovate*.json5 with the json5 CLI to catch preset parse errors at\n      # PR time rather than 24h later in the scheduled Renovate job.\n      - name: Validate Renovate preset syntax\n        run: |\n          for f in .github/renovate*.json5; do\n            npx --yes json5 \"$f\" > /dev/null\n          done\n\n      - name: Validate Renovate JSON\n        run: npx --yes --package renovate -- renovate-config-validator\n\n  lint:\n    runs-on: ubuntu-22.04\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n\n      - name: Install Nix\n        uses: cachix/install-nix-action@616559265b40713947b9c190a8ff4b507b5df49b # v31\n\n      - name: Setup Cachix\n        uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16\n        with:\n          name: crossplane\n          authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}\n\n      - name: Lint\n        run: nix build .#checks.x86_64-linux.go-lint --print-build-logs\n\n  codeql:\n    runs-on: ubuntu-22.04\n    permissions:\n      contents: read\n      security-events: write\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n\n      - name: Install Nix\n        uses: cachix/install-nix-action@616559265b40713947b9c190a8ff4b507b5df49b # v31\n\n      - name: Setup Cachix\n        uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16\n        with:\n          name: crossplane\n          authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}\n\n      - name: Setup Nix Environment\n        uses: nicknovitski/nix-develop@9be7cfb4b10451d3390a75dc18ad0465bed4932a # v1\n\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4\n        with:\n          languages: go\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4\n\n  unit-tests:\n    runs-on: ubuntu-22.04\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n\n      - name: Install Nix\n        uses: cachix/install-nix-action@616559265b40713947b9c190a8ff4b507b5df49b # v31\n\n      - name: Setup Cachix\n        uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16\n        with:\n          name: crossplane\n          authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}\n\n      - name: Run Unit Tests\n        run: nix build .#checks.x86_64-linux.test --print-build-logs\n\n      - name: Publish Unit Test Coverage\n        uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4\n        with:\n          flags: unittests\n          file: result/coverage.txt\n          token: ${{ secrets.CODECOV_TOKEN }}\n\n  protobuf-schemas:\n    runs-on: ubuntu-22.04\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n\n      - name: Setup Buf\n        uses: bufbuild/buf-setup-action@v1\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Lint Protocol Buffers\n        uses: bufbuild/buf-lint-action@v1\n\n      # buf-breaking-action doesn't support branches\n      # https://github.com/bufbuild/buf-push-action/issues/34\n      - name: Detect Breaking Changes in Protocol Buffers\n        uses: bufbuild/buf-breaking-action@a074e988ee34efcd4927079e79c611f428354c01 # v1\n        # We want to run this for the main branch, and PRs against main.\n        if: ${{ github.ref == 'refs/heads/main' || github.base_ref == 'main' }}\n        with:\n          against: \"https://github.com/${GITHUB_REPOSITORY}.git#branch=main\"\n\n      - name: Push Protocol Buffers to Buf Schema Registry\n        if: ${{ github.repository == 'crossplane/crossplane-runtime' && github.ref == 'refs/heads/main' }}\n        uses: bufbuild/buf-push-action@v1\n        with:\n          buf_token: ${{ secrets.BUF_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/commands.yml",
    "content": "name: Comment Commands\n\non: issue_comment\n\njobs:\n  points:\n    runs-on: ubuntu-22.04\n    if: startsWith(github.event.comment.body, '/points')\n\n    steps:\n    - name: Extract Command\n      id: command\n      uses: xt0rted/slash-command-action@bf51f8f5f4ea3d58abc7eca58f77104182b23e88 # v2\n      with:\n        repo-token: ${{ secrets.GITHUB_TOKEN }}\n        command: points\n        reaction: \"true\"\n        reaction-type: \"eyes\"\n        allow-edits: \"false\"\n        permission-level: write\n    - name: Handle Command\n      uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8\n      env:\n        POINTS: ${{ steps.command.outputs.command-arguments }}\n      with:\n        github-token: ${{ secrets.GITHUB_TOKEN }}\n        script: |\n          const points = process.env.POINTS\n\n          if (isNaN(parseInt(points))) {\n            console.log(\"Malformed command - expected '/points <int>'\")\n            github.reactions.createForIssueComment({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              comment_id: context.payload.comment.id,\n              content: \"confused\"\n            })\n            return\n          }\n          const label = \"points/\" + points\n\n          // Delete our needs-points-label label.\n          try {\n            await github.issues.deleteLabel({\n              issue_number: context.issue.number,\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              name: ['needs-points-label']\n            })\n            console.log(\"Deleted 'needs-points-label' label.\")\n          }\n          catch(e) {\n            console.log(\"Label 'needs-points-label' probably didn't exist.\")\n          }\n\n          // Add our points label.\n          github.issues.addLabels({\n            issue_number: context.issue.number,\n            owner: context.repo.owner,\n            repo: context.repo.repo,\n            labels: [label]\n          })\n          console.log(\"Added '\" + label + \"' label.\")\n\n  # NOTE(negz): See also backport.yml, which is the variant that triggers on PR\n  # merge rather than on comment.\n  backport:\n    runs-on: ubuntu-22.04\n    if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/backport')\n    steps:\n    - name: Extract Command\n      id: command\n      uses: xt0rted/slash-command-action@bf51f8f5f4ea3d58abc7eca58f77104182b23e88 # v2\n      with:\n        repo-token: ${{ secrets.GITHUB_TOKEN }}\n        command: backport\n        reaction: \"true\"\n        reaction-type: \"eyes\"\n        allow-edits: \"false\"\n        permission-level: write\n\n    - name: Checkout\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n      with:\n        fetch-depth: 0\n\n    - name: Open Backport PR\n      uses: zeebe-io/backport-action@3c06f323a58619da1e8522229ebc8d5de2633e46 # v4.3.0\n      with:\n        github_token: ${{ secrets.GITHUB_TOKEN }}\n        github_workspace: ${{ github.workspace }}\n  fresh:\n    runs-on: ubuntu-22.04\n    if: startsWith(github.event.comment.body, '/fresh')\n\n    steps:\n      - name: Extract Command\n        id: command\n        uses: xt0rted/slash-command-action@bf51f8f5f4ea3d58abc7eca58f77104182b23e88 # v2\n        with:\n          repo-token: ${{ secrets.GITHUB_TOKEN }}\n          command: fresh\n          reaction: \"true\"\n          reaction-type: \"eyes\"\n          allow-edits: \"false\"\n          permission-level: read\n      - name: Handle Command\n        uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 # v1\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          labels: stale\n"
  },
  {
    "path": ".github/workflows/renovate.yml",
    "content": "name: Renovate\non:\n  # Allows manual/automated trigger for debugging purposes\n  workflow_dispatch:\n    inputs:\n      logLevel:\n        description: \"Renovate's log level\"\n        required: true\n        default: \"info\"\n        type: string\n  schedule:\n    - cron: '0 8 * * *'\n\nenv:\n  LOG_LEVEL: \"info\"\n\njobs:\n  renovate:\n    runs-on: ubuntu-latest\n    if: |\n      !github.event.repository.fork &&\n      !github.event.pull_request.head.repo.fork\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      # Don't waste time starting Renovate if any preset is unparseable.\n      # renovate-config-validator only looks at the top-level file and does\n      # not recursively resolve local> presets, so we also syntax-check every\n      # renovate*.json5 with the json5 CLI.\n      - name: Validate Renovate preset syntax\n        run: |\n          for f in .github/renovate*.json5; do\n            npx --yes json5 \"$f\" > /dev/null\n          done\n\n      - name: Validate Renovate JSON\n        run: npx --yes --package renovate -- renovate-config-validator\n\n      - name: Get token\n        id: get-github-app-token\n        uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2\n        with:\n          app-id: ${{ secrets.RENOVATE_GITHUB_APP_ID }}\n          private-key: ${{ secrets.RENOVATE_GITHUB_APP_PRIVATE_KEY }}\n\n      - name: Self-hosted Renovate\n        uses: renovatebot/github-action@eb932558ad942cccfd8211cf535f17ff183a9f74 # v46.1.9\n        env:\n          RENOVATE_REPOSITORIES: ${{ github.repository }}\n          # Use GitHub API to create commits\n          RENOVATE_PLATFORM_COMMIT: \"true\"\n          LOG_LEVEL: ${{ github.event.inputs.logLevel || env.LOG_LEVEL }}\n          RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '[\"^nix .+\", \"^earthly .+\"]'\n        with:\n          configurationFile: .github/renovate.json5\n          token: '${{ steps.get-github-app-token.outputs.token }}'\n          mount-docker-socket: true\n          docker-user: root\n          docker-cmd-file: .github/renovate-entrypoint.sh\n"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: Stale Issues and PRs\non:\n  schedule:\n    # Process new stale issues once a day. Folks can /fresh for a fast un-stale\n    # per the commands workflow. Run at 1:15 mostly as a somewhat unique time to\n    # help correlate any issues with this workflow.\n    - cron: '15 1 * * *'\n  workflow_dispatch: {}\n\npermissions:\n  issues: write\n  pull-requests: write\n\njobs:\n  stale:\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9\n        with:\n          # This action uses ~2 operations per stale issue per run to determine\n          # whether it's still stale. It also uses 2-3 operations to mark an issue\n          # stale or not. During steady state (no issues to mark stale, check, or\n          # close) we seem to use less than 10 operations with ~150 issues and PRs\n          # open.\n          #\n          # Our hourly rate-limit budget for all workflows that use GITHUB_TOKEN\n          # is 1,000 requests per the below docs.\n          # https://docs.github.com/en/rest/overview/resources-in-the-rest-api#requests-from-github-actions\n          operations-per-run: 100\n          days-before-stale: 90\n          days-before-close: 14\n          stale-issue-label: stale\n          exempt-issue-labels: exempt-from-stale\n          stale-issue-message: >\n            Crossplane does not currently have enough maintainers to address every\n            issue and pull request. This issue has been automatically marked as\n            `stale` because it has had no activity in the last 90 days. It will be\n            closed in 14 days if no further activity occurs. Leaving a comment\n            **starting with** `/fresh` will mark this issue as not stale.\n          stale-pr-label: stale\n          exempt-pr-labels: exempt-from-stale\n          stale-pr-message:\n            Crossplane does not currently have enough maintainers to address every\n            issue and pull request. This pull request has been automatically\n            marked as `stale` because it has had no activity in the last 90 days.\n            It will be closed in 14 days if no further activity occurs.\n            Adding a comment **starting with** `/fresh` will mark this PR as not stale."
  },
  {
    "path": ".github/workflows/tag.yml",
    "content": "name: Tag\n\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: 'Release version (e.g. v0.1.0)'\n        required: true\n      message:\n        description: 'Tag message'\n        required: true\n\njobs:\n  create-tag:\n    runs-on: ubuntu-22.04\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n\n      - name: Create Tag\n        uses: negz/create-tag@39bae1e0932567a58c20dea5a1a0d18358503320 # v1\n        with:\n          version: ${{ github.event.inputs.version }}\n          message: ${{ github.event.inputs.message }}\n          token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "/.cache\n/.work\n/_output\n/config/\n/config\ncover.out\n/vendor\n/.vendor-new\n\n# Nix build output\nresult\nresult-*\n\n# gitlab example\n# exclude files generate by running the example\nexternal-dns-*.tgz\ngitlab-*.tgz\ngitlab-gcp.yaml\ngitlab/\n\n# ignore IDE folders\n.vscode/\n.idea/"
  },
  {
    "path": ".golangci.yml",
    "content": "version: \"2\"\n\noutput:\n  formats:\n    text:\n      path: stderr\n\nlinters:\n  default: all\n  disable:\n    # These are linters we'd like to enable, but that will be labor intensive to\n    # make existing code compliant.\n    - wrapcheck\n    - varnamelen\n    - testpackage\n    - paralleltest\n    - nilnil\n    - funcorder\n\n    # Below are linters that lint for things we don't value. Each entry below\n    # this line must have a comment explaining the rationale.\n\n    # These linters add whitespace in an attempt to make code more readable.\n    # This isn't a widely accepted Go best practice, and would be laborious to\n    # apply to existing code.\n    - wsl\n    - wsl_v5\n    - nlreturn\n\n    # Warns about uses of fmt.Sprintf that are less performant than alternatives\n    # such as string concatenation. We value readability more than performance\n    # unless performance is measured to be an issue.\n    - perfsprint\n\n    # This linter:\n    #\n    # 1. Requires errors.Is/errors.As to test equality.\n    # 2. Requires all errors be wrapped with fmt.Errorf specifically.\n    # 3. Disallows errors.New inline - requires package level errors.\n    #\n    # 1 is covered by other linters. 2 is covered by wrapcheck, which can also\n    # handle our use of crossplane-runtime's errors package. 3 is more strict\n    # than we need. Not every error needs to be tested for equality.\n    - err113\n\n    # These linters duplicate gocognit, but calculate complexity differently.\n    - gocyclo\n    - cyclop\n    - nestif\n    - funlen\n    - maintidx\n\n    # Enforces max line length. It's not idiomatic to enforce a strict limit on\n    # line length in Go. We'd prefer to lint for things that often cause long\n    # lines, like functions with too many parameters or long parameter names\n    # that duplicate their types.\n    - lll\n\n    # Warns about struct instantiations that don't specify every field. Could be\n    # useful in theory to catch fields that are accidentally omitted. Seems like\n    # it would have many more false positives than useful catches, though.\n    - exhaustruct\n\n    # Warns about TODO comments. The rationale being they should be issues\n    # instead. We're okay with using TODO to track minor cleanups for next time\n    # we touch a particular file.\n    - godox\n\n    # Warns about duplicated code blocks within the same file. Could be useful\n    # to prompt folks to think about whether code should be broken out into a\n    # function, but generally we're less worried about DRY and fine with a\n    # little copying. We don't want to give folks the impression that we require\n    # every duplicated code block to be factored out into a function.\n    - dupl\n\n    # Warns about returning interfaces rather than concrete types. We do think\n    # it's best to avoid returning interfaces where possible. However, at the\n    # time of writing enabling this linter would only catch the (many) cases\n    # where we must return an interface.\n    - ireturn\n\n    # Warns about returning named variables. We do think it's best to avoid\n    # returning named variables where possible. However, at the time of writing\n    # enabling this linter would only catch the (many) cases where returning\n    # named variables is useful to document what the variables are. For example\n    # we believe it makes sense to return (ready bool) rather than just (bool)\n    # to communicate what the bool means.\n    - nonamedreturns\n\n    # Warns about using magic numbers. We do think it's best to avoid magic\n    # numbers, but we should not be strict about it.\n    - mnd\n\n    # Warns about if err := Foo(); err != nil style error checks. Seems to go\n    # against idiomatic Go programming, which encourages this approach - e.g.\n    # to scope errors.\n    - noinlineerr\n  settings:\n    depguard:\n      rules:\n        no_third_party_test_libraries:\n          list-mode: lax\n          files:\n            - $test\n          deny:\n            - pkg: github.com/stretchr/testify\n              desc: See https://go.dev/wiki/TestComments#assert-libraries\n            - pkg: github.com/onsi/ginkgo\n              desc: See https://go.dev/wiki/TestComments#assert-libraries\n            - pkg: github.com/onsi/gomega\n              desc: See https://go.dev/wiki/TestComments#assert-libraries\n    dupl:\n      threshold: 100\n    errcheck:\n      check-type-assertions: false\n      check-blank: false\n    goconst:\n      min-len: 3\n      min-occurrences: 5\n    gocritic:\n      enabled-tags:\n        - performance\n      settings:\n        captLocal:\n          paramsOnly: true\n        rangeValCopy:\n          sizeThreshold: 32\n    govet:\n      disable:\n        - shadow\n    interfacebloat:\n      max: 5\n    lll:\n      tab-width: 1\n    nakedret:\n      max-func-lines: 30\n    nolintlint:\n      require-explanation: true\n      require-specific: true\n    prealloc:\n      simple: true\n      range-loops: true\n      for-loops: false\n    tagliatelle:\n      case:\n        rules:\n          json: goCamel\n    unparam:\n      check-exported: false\n    unused:\n      exported-fields-are-used: true\n  exclusions:\n    generated: lax\n    rules:\n      - linters:\n          - containedctx\n          - errcheck\n          - forcetypeassert\n          - gochecknoglobals\n          - gochecknoinits\n          - gocognit\n          - goconst\n          - gosec\n          - scopelint\n          - unparam\n          - embeddedstructfieldcheck\n        path: _test(ing)?\\.go\n\n      - linters:\n          - gocritic\n        path: _test\\.go\n        text: (unnamedResult|exitAfterDefer)\n\n      # It's idiomatic to register Kubernetes types with a package scoped\n      # SchemeBuilder using an init function.\n      - linters:\n          - gochecknoglobals\n          - gochecknoinits\n        path: apis/\n\n      # These are performance optimisations rather than style issues per se.\n      # They warn when function arguments or range values copy a lot of memory\n      # rather than using a pointer.\n      - linters:\n          - gocritic\n        text: '(hugeParam|rangeValCopy):'\n\n      # This \"TestMain should call os.Exit to set exit code\" warning is not clever\n      # enough to notice that we call a helper method that calls os.Exit.\n      - linters:\n          - staticcheck\n        text: 'SA3000:'\n\n      # This is a \"potential hardcoded credentials\" warning. It's triggered by\n      # any variable with 'secret' in the same, and thus hits a lot of false\n      # positives in Kubernetes land where a Secret is an object type.\n      - linters:\n          - gosec\n        text: 'G101:'\n\n      # This is an 'errors unhandled' warning that duplicates errcheck.\n      - linters:\n          - gosec\n        text: 'G104:'\n\n      # This is about implicit memory aliasing in a range loop.\n      # This is a false positive with Go v1.22 and above.\n      - linters:\n          - gosec\n        text: 'G601:'\n\n      # Some k8s dependencies do not have JSON tags on all fields in structs.\n      - linters:\n          - musttag\n        path: k8s.io/\n\n      # Various fields related to native patch and transform Composition are\n      # deprecated, but we can't drop support from Crossplane 1.x. We ignore the\n      # warnings globally instead of suppressing them with comments everywhere.\n      - linters:\n          - staticcheck\n        text: 'SA1019: .+ is deprecated: Use Composition Functions instead.'\n\n      # The runtime library defines deprecated types like LegacyManaged and\n      # LegacyProviderConfigUsage. Code in the runtime must use these types to\n      # support legacy resources, even though they're deprecated.\n      - linters:\n          - staticcheck\n        text: 'SA1019: resource\\.Legacy.+ is deprecated:'\n\n      # Some shared structs in apis/common/v1 are moved to\n      # apis/common. To preserve a backward-compatible directory structure\n      # package had to be named common, which we suppress.\n      - linters:\n          - revive\n        text: \"var-naming: avoid meaningless package names\"\n        path: apis/common\n\n      # The errors package intentionally shadows the stdlib errors package\n      # to provide a drop-in compatible API with additional functionality.\n      - linters:\n          - revive\n        text: \"var-naming: avoid package names that conflict with Go standard library package names\"\n        path: pkg/errors/\n    paths:\n      - zz_generated\\..+\\.go$\n      - .+\\.pb.go$\n      - third_party$\n      - builtin$\n      - examples$\n\nissues:\n  max-issues-per-linter: 0\n  max-same-issues: 0\n  new: false\n\nformatters:\n  enable:\n    - gci\n    - gofmt\n    - gofumpt\n    - goimports\n  settings:\n    gci:\n      sections:\n        - standard\n        - default\n        - prefix(github.com/crossplane/crossplane-runtime)\n        - blank\n        - dot\n      custom-order: true\n    gofmt:\n      simplify: true\n  exclusions:\n    generated: lax\n    paths:\n      - zz_generated\\..+\\.go$\n      - .+\\.pb.go$\n      - third_party$\n      - builtin$\n      - examples$\n"
  },
  {
    "path": "CODEOWNERS",
    "content": "# This file controls automatic PR reviewer assignment. See the following docs:\n#\n# * https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners\n# * https://docs.github.com/en/organizations/organizing-members-into-teams/managing-code-review-settings-for-your-team\n#\n# The goal of this file is for most PRs to automatically and fairly have one\n# maintainer and two reviewers set as PR reviewers. All maintainers have\n# permission to approve and merge PRs, but reviewers do not. Most PRs should be\n# reviewed by members of the reviewers group before being passed to a maintainer\n# for final review.\n#\n# This in part depends on how the groups in this file are configured.\n#\n# @crossplane/steering-committee - Assigns 3 members. Admin perms to this repo.\n# @crossplane/crossplane-maintainers - Assigns 1 member. Maintain perms to this repo.\n# @crossplane/crossplane-reviewers - Assigns 2 members. Write perms to this repo.\n#\n# Where possible, prefer explicitly specifying a maintainer who is a subject\n# matter expert for a particular part of the codebase rather than using the\n# @crossplane/crossplane-maintainers group.\n#\n# See also OWNERS.md for governance details\n\n# Fallback owners\n*                                   @crossplane/crossplane-maintainers\n\n# Governance owners - steering committee\n/README.md                           @crossplane/steering-committee\n/OWNERS.md                           @crossplane/steering-committee\n/LICENSE                             @crossplane/steering-committee\n"
  },
  {
    "path": "DCO",
    "content": "Developer Certificate of Origin\nVersion 1.1\n\nCopyright (C) 2004, 2006 The Linux Foundation and its contributors.\n660 York Street, Suite 102,\nSan Francisco, CA 94110 USA\n\nEveryone is permitted to copy and distribute verbatim copies of this\nlicense document, but changing it is not allowed.\n\n\nDeveloper's Certificate of Origin 1.1\n\nBy making a contribution to this project, I certify that:\n\n(a) The contribution was created in whole or in part by me and I\n    have the right to submit it under the open source license\n    indicated in the file; or\n\n(b) The contribution is based upon previous work that, to the best\n    of my knowledge, is covered under an appropriate open source\n    license and I have the right under that license to submit that\n    work with modifications, whether created in whole or in part\n    by me, under the same open source license (unless I am\n    permitted to submit under a different license), as indicated\n    in the file; or\n\n(c) The contribution was provided directly to me by some other\n    person who certified (a), (b) or (c) and I have not modified\n    it.\n\n(d) I understand and agree that this project and the contribution\n    are public and that a record of the contribution (including all\n    personal information I submit with it, including my sign-off) is\n    maintained indefinitely and may be redistributed consistent with\n    this project or the open source license(s) involved.\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 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   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2016 The Crossplane Authors. All rights reserved.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "OWNERS.md",
    "content": "# Crossplane Maintainers\n\nThis page lists all active maintainers and reviewers for **this** repository.\nEach repository in the [Crossplane organization](https://github.com/crossplane/)\nwill list their repository maintainers and reviewers in their own `OWNERS.md`\nfile.\n\nPlease see [GOVERNANCE.md](https://github.com/crossplane/crossplane/blob/main/GOVERNANCE.md)\nfor governance guidelines and responsibilities for maintainers, and reviewers.\n\nSee [CODEOWNERS](CODEOWNERS) for automatic PR assignment.\n\n\n## Maintainers\n\n* Nic Cope <negz@upbound.io> ([negz](https://github.com/negz))\n* Bob Haddleton <bob.haddleton@nokia.com> ([bobh66](https://github.com/bobh66))\n* Philippe Scorsolini <philippe.scorsolini@upbound.io> ([phisco](https://github.com/phisco))\n* Jared Watts <jared@upbound.io> ([jbw976](https://github.com/jbw976))\n* Adam Wolfe Gordon <adam.wolfegordon@upbound.io> ([adamwg](https://github.com/adamwg))\n* Christopher Haar <christopher.haar@upbound.io> ([haarchri](https://github.com/haarchri))\n\n## Reviewers\n\n* Yury Tsarev <yury@upbound.io> ([ytsarev](https://github.com/ytsarev))\n* Ezgi Demirel <ezgi@upbound.io> ([ezgidemirel](https://github.com/ezgidemirel))\n* Max Blatt ([MisterMX](https://github.com/MisterMX))\n\n## Emeritus maintainers\n\n* Illya Chekrygin <illya.chekrygin@gmail.com> ([ichekrygin](https://github.com/ichekrygin))\n* Hasan Turken <hasan@upbound.io> ([turkenh](https://github.com/turkenh))"
  },
  {
    "path": "PROJECT",
    "content": "version: \"1\"\ndomain: crossplane.io\nrepo: github.com/crossplane/crossplane-runtime\n"
  },
  {
    "path": "README.md",
    "content": "# crossplane-runtime\n[![CI](https://github.com/crossplane/crossplane-runtime/actions/workflows/ci.yml/badge.svg)](https://github.com/crossplane/crossplane-runtime/actions/workflows/ci.yml) ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/crossplane/crossplane-runtime) [![Godoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/crossplane/crossplane-runtime)\n\n## Overview\n\ncrossplane-runtime is a set of go libraries used to build Kubernetes controllers\nin Crossplane and its related stacks. Take a look at our [developer guide] and\n[API documentation] for help getting started with crossplane-runtime.\n\n## Contributing\n\ncrossplane-runtime is a community driven project and we welcome contributions.\nSee the Crossplane [contributing] guidelines to get started.\n\n## Report a Bug\n\nFor filing bugs, suggesting improvements, or requesting new features, please\nopen an [issue].\n\n## Contact\n\nPlease use the following to reach members of the community:\n\n- Slack: Join our [slack channel]\n- Forums: [crossplane-dev]\n- Twitter: [@crossplane_io]\n- Email: [info@crossplane.io]\n\n## Roadmap\n\ncrossplane-runtime goals and milestones are currently tracked in Crossplane's\n[roadmap].\n\n## Governance and Owners\n\ncrossplane-runtime is run according to the same [governance] and [ownership]\nstructure as the core Crossplane project.\n\n## Code of Conduct\n\ncrossplane-runtime adheres to the same [code of conduct] as the core Crossplane\nproject.\n\n## Licensing\n\ncrossplane-runtime is under the Apache 2.0 license.\n\n[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fcrossplane%2Fcrossplane-runtime.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fcrossplane%2Fcrossplane-runtime?ref=badge_large)\n\n[developer guide]: https://github.com/crossplane/crossplane/tree/main/contributing\n[API documentation]: https://godoc.org/github.com/crossplane/crossplane-runtime\n[contributing]: https://github.com/crossplane/crossplane/blob/main/CONTRIBUTING.md\n[issue]: https://github.com/crossplane/crossplane-runtime/issues\n[slack channel]: https://slack.crossplane.io\n[crossplane-dev]: https://groups.google.com/forum/#!forum/crossplane-dev\n[@crossplane_io]: https://twitter.com/crossplane_io\n[info@crossplane.io]: mailto:info@crossplane.io\n[roadmap]: https://github.com/crossplane/crossplane/blob/main/ROADMAP.md\n[governance]: https://github.com/crossplane/crossplane/blob/main/GOVERNANCE.md\n[ownership]: https://github.com/crossplane/crossplane/blob/main/OWNERS.md\n[code of conduct]: https://github.com/crossplane/crossplane/blob/main/CODE_OF_CONDUCT.md\n"
  },
  {
    "path": "RELEASE.md",
    "content": "# Release Process\n\n## New Patch Release (vX.Y.Z)\n\nIn order to cut a new patch release from an existing release branch `release-X.Y`, follow these steps:\n\n- Run the [Tag workflow][tag-workflow] on the `release-X.Y` branch with the proper release version, `vX.Y.Z`. Message suggested, but not required: `Release vX.Y.Z`.\n- Draft the [new release notes], and share them with the rest of the team to ensure that all the required information is included.\n- Publish the above release notes.\n\n## New Minor Release (vX.Y.0)\n\nIn order to cut a new minor release, follow these steps:\n\n- Create a new release branch `release-X.Y` from `main`, using the [GitHub UI][create-branch].\n- Create and merge an empty commit to the `main` branch, if required to have it at least one commit ahead of the release branch.\n- Run the [Tag workflow][tag-workflow] on the `main` branch with the release candidate tag for the next release, so `vX.<Y+1>.0-rc.0`.\n- Run the [Tag workflow][tag-workflow] on the `release-X.Y` branch with the proper release version, `vX.Y.0`. Message suggested, but not required: `Release vX.Y.0`.\n- Draft the [new release notes], and share them with the rest of the team to ensure that all the required information is included.\n- Publish the above release notes.\n\n<!-- Named Links -->\n[create-branch]: https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-and-deleting-branches-within-your-repository\n[new release notes]: https://github.com/crossplane/crossplane-runtime/releases/new\n[tag-workflow]: https://github.com/crossplane/crossplane-runtime/actions/workflows/tag.yml\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Reporting a Vulnerability\n\nInstructions for reporting a vulnerability can be found on the\n[crossplane repository](https://github.com/crossplane/crossplane/blob/main/SECURITY.md).\n\n"
  },
  {
    "path": "apis/apis.go",
    "content": "/*\nCopyright 2019 The Crossplane 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// Package apis contains Kubernetes API groups.\npackage apis\n"
  },
  {
    "path": "apis/changelogs/proto/v1alpha1/changelog.pb.go",
    "content": "//\n//Copyright 2024 The Crossplane Authors.\n//Licensed under the Apache License, Version 2.0 (the \"License\");\n//you may not use this file except in compliance with the License.\n//You may obtain a copy of the License at\n//http://www.apache.org/licenses/LICENSE-2.0\n//Unless required by applicable law or agreed to in writing, software\n//distributed under the License is distributed on an \"AS IS\" BASIS,\n//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n//See the License for the specific language governing permissions and\n//limitations under the License.\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.10\n// \tprotoc        (unknown)\n// source: apis/changelogs/proto/v1alpha1/changelog.proto\n\n// buf:lint:ignore PACKAGE_DIRECTORY_MATCH\n\npackage v1alpha1\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\tstructpb \"google.golang.org/protobuf/types/known/structpb\"\n\ttimestamppb \"google.golang.org/protobuf/types/known/timestamppb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n\tunsafe \"unsafe\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// OperationType represents the type of operation that was performed on a\n// resource.\ntype OperationType int32\n\nconst (\n\tOperationType_OPERATION_TYPE_UNSPECIFIED OperationType = 0\n\tOperationType_OPERATION_TYPE_CREATE      OperationType = 1\n\tOperationType_OPERATION_TYPE_UPDATE      OperationType = 2\n\tOperationType_OPERATION_TYPE_DELETE      OperationType = 3\n)\n\n// Enum value maps for OperationType.\nvar (\n\tOperationType_name = map[int32]string{\n\t\t0: \"OPERATION_TYPE_UNSPECIFIED\",\n\t\t1: \"OPERATION_TYPE_CREATE\",\n\t\t2: \"OPERATION_TYPE_UPDATE\",\n\t\t3: \"OPERATION_TYPE_DELETE\",\n\t}\n\tOperationType_value = map[string]int32{\n\t\t\"OPERATION_TYPE_UNSPECIFIED\": 0,\n\t\t\"OPERATION_TYPE_CREATE\":      1,\n\t\t\"OPERATION_TYPE_UPDATE\":      2,\n\t\t\"OPERATION_TYPE_DELETE\":      3,\n\t}\n)\n\nfunc (x OperationType) Enum() *OperationType {\n\tp := new(OperationType)\n\t*p = x\n\treturn p\n}\n\nfunc (x OperationType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (OperationType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_apis_changelogs_proto_v1alpha1_changelog_proto_enumTypes[0].Descriptor()\n}\n\nfunc (OperationType) Type() protoreflect.EnumType {\n\treturn &file_apis_changelogs_proto_v1alpha1_changelog_proto_enumTypes[0]\n}\n\nfunc (x OperationType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use OperationType.Descriptor instead.\nfunc (OperationType) EnumDescriptor() ([]byte, []int) {\n\treturn file_apis_changelogs_proto_v1alpha1_changelog_proto_rawDescGZIP(), []int{0}\n}\n\n// SendChangeLogRequest represents a request to send a single change log entry.\ntype SendChangeLogRequest struct {\n\tstate protoimpl.MessageState `protogen:\"open.v1\"`\n\t// The change log entry to send as part of this request.\n\tEntry         *ChangeLogEntry `protobuf:\"bytes,1,opt,name=entry,proto3\" json:\"entry,omitempty\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *SendChangeLogRequest) Reset() {\n\t*x = SendChangeLogRequest{}\n\tmi := &file_apis_changelogs_proto_v1alpha1_changelog_proto_msgTypes[0]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *SendChangeLogRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SendChangeLogRequest) ProtoMessage() {}\n\nfunc (x *SendChangeLogRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_changelogs_proto_v1alpha1_changelog_proto_msgTypes[0]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use SendChangeLogRequest.ProtoReflect.Descriptor instead.\nfunc (*SendChangeLogRequest) Descriptor() ([]byte, []int) {\n\treturn file_apis_changelogs_proto_v1alpha1_changelog_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *SendChangeLogRequest) GetEntry() *ChangeLogEntry {\n\tif x != nil {\n\t\treturn x.Entry\n\t}\n\treturn nil\n}\n\n// ChangeLogEntry represents a single change log entry, with detailed information\n// about the resource that was changed.\ntype ChangeLogEntry struct {\n\tstate protoimpl.MessageState `protogen:\"open.v1\"`\n\t// The timestamp at which the change occurred.\n\tTimestamp *timestamppb.Timestamp `protobuf:\"bytes,1,opt,name=timestamp,proto3\" json:\"timestamp,omitempty\"`\n\t// The name and version of the provider that is making the change to the\n\t// resource.\n\tProvider string `protobuf:\"bytes,2,opt,name=provider,proto3\" json:\"provider,omitempty\"`\n\t// The API version of the resource that was changed, e.g. Group/Version.\n\tApiVersion string `protobuf:\"bytes,3,opt,name=api_version,json=apiVersion,proto3\" json:\"api_version,omitempty\"`\n\t// The kind of the resource that was changed.\n\tKind string `protobuf:\"bytes,4,opt,name=kind,proto3\" json:\"kind,omitempty\"`\n\t// The name of the resource that was changed.\n\tName string `protobuf:\"bytes,5,opt,name=name,proto3\" json:\"name,omitempty\"`\n\t// The external name of the resource that was changed.\n\tExternalName string `protobuf:\"bytes,6,opt,name=external_name,json=externalName,proto3\" json:\"external_name,omitempty\"`\n\t// The type of operation that was performed on the resource, e.g. Create,\n\t// Update, or Delete.\n\tOperation OperationType `protobuf:\"varint,7,opt,name=operation,proto3,enum=changelogs.proto.v1alpha1.OperationType\" json:\"operation,omitempty\"`\n\t// A full snapshot of the resource's state, as observed directly before the\n\t// resource was changed.\n\tSnapshot *structpb.Struct `protobuf:\"bytes,8,opt,name=snapshot,proto3\" json:\"snapshot,omitempty\"`\n\t// An optional error message that describes any error encountered while\n\t// performing the operation on the resource.\n\tErrorMessage *string `protobuf:\"bytes,9,opt,name=error_message,json=errorMessage,proto3,oneof\" json:\"error_message,omitempty\"`\n\t// An optional additional details that can be provided for further context\n\t// about the change.\n\tAdditionalDetails map[string]string `protobuf:\"bytes,10,rep,name=additional_details,json=additionalDetails,proto3\" json:\"additional_details,omitempty\" protobuf_key:\"bytes,1,opt,name=key\" protobuf_val:\"bytes,2,opt,name=value\"`\n\tunknownFields     protoimpl.UnknownFields\n\tsizeCache         protoimpl.SizeCache\n}\n\nfunc (x *ChangeLogEntry) Reset() {\n\t*x = ChangeLogEntry{}\n\tmi := &file_apis_changelogs_proto_v1alpha1_changelog_proto_msgTypes[1]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *ChangeLogEntry) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChangeLogEntry) ProtoMessage() {}\n\nfunc (x *ChangeLogEntry) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_changelogs_proto_v1alpha1_changelog_proto_msgTypes[1]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ChangeLogEntry.ProtoReflect.Descriptor instead.\nfunc (*ChangeLogEntry) Descriptor() ([]byte, []int) {\n\treturn file_apis_changelogs_proto_v1alpha1_changelog_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *ChangeLogEntry) GetTimestamp() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.Timestamp\n\t}\n\treturn nil\n}\n\nfunc (x *ChangeLogEntry) GetProvider() string {\n\tif x != nil {\n\t\treturn x.Provider\n\t}\n\treturn \"\"\n}\n\nfunc (x *ChangeLogEntry) GetApiVersion() string {\n\tif x != nil {\n\t\treturn x.ApiVersion\n\t}\n\treturn \"\"\n}\n\nfunc (x *ChangeLogEntry) GetKind() string {\n\tif x != nil {\n\t\treturn x.Kind\n\t}\n\treturn \"\"\n}\n\nfunc (x *ChangeLogEntry) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *ChangeLogEntry) GetExternalName() string {\n\tif x != nil {\n\t\treturn x.ExternalName\n\t}\n\treturn \"\"\n}\n\nfunc (x *ChangeLogEntry) GetOperation() OperationType {\n\tif x != nil {\n\t\treturn x.Operation\n\t}\n\treturn OperationType_OPERATION_TYPE_UNSPECIFIED\n}\n\nfunc (x *ChangeLogEntry) GetSnapshot() *structpb.Struct {\n\tif x != nil {\n\t\treturn x.Snapshot\n\t}\n\treturn nil\n}\n\nfunc (x *ChangeLogEntry) GetErrorMessage() string {\n\tif x != nil && x.ErrorMessage != nil {\n\t\treturn *x.ErrorMessage\n\t}\n\treturn \"\"\n}\n\nfunc (x *ChangeLogEntry) GetAdditionalDetails() map[string]string {\n\tif x != nil {\n\t\treturn x.AdditionalDetails\n\t}\n\treturn nil\n}\n\n// SendChangeLogResponse is the response returned by the ChangeLogService after\n// a change log entry is sent. Currently, this is an empty message as the only\n// useful information expected to sent back at this time will be through errors.\ntype SendChangeLogResponse struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *SendChangeLogResponse) Reset() {\n\t*x = SendChangeLogResponse{}\n\tmi := &file_apis_changelogs_proto_v1alpha1_changelog_proto_msgTypes[2]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *SendChangeLogResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SendChangeLogResponse) ProtoMessage() {}\n\nfunc (x *SendChangeLogResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_changelogs_proto_v1alpha1_changelog_proto_msgTypes[2]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use SendChangeLogResponse.ProtoReflect.Descriptor instead.\nfunc (*SendChangeLogResponse) Descriptor() ([]byte, []int) {\n\treturn file_apis_changelogs_proto_v1alpha1_changelog_proto_rawDescGZIP(), []int{2}\n}\n\nvar File_apis_changelogs_proto_v1alpha1_changelog_proto protoreflect.FileDescriptor\n\nconst file_apis_changelogs_proto_v1alpha1_changelog_proto_rawDesc = \"\" +\n\t\"\\n\" +\n\t\".apis/changelogs/proto/v1alpha1/changelog.proto\\x12\\x19changelogs.proto.v1alpha1\\x1a\\x1cgoogle/protobuf/struct.proto\\x1a\\x1fgoogle/protobuf/timestamp.proto\\\"W\\n\" +\n\t\"\\x14SendChangeLogRequest\\x12?\\n\" +\n\t\"\\x05entry\\x18\\x01 \\x01(\\v2).changelogs.proto.v1alpha1.ChangeLogEntryR\\x05entry\\\"\\xc4\\x04\\n\" +\n\t\"\\x0eChangeLogEntry\\x128\\n\" +\n\t\"\\ttimestamp\\x18\\x01 \\x01(\\v2\\x1a.google.protobuf.TimestampR\\ttimestamp\\x12\\x1a\\n\" +\n\t\"\\bprovider\\x18\\x02 \\x01(\\tR\\bprovider\\x12\\x1f\\n\" +\n\t\"\\vapi_version\\x18\\x03 \\x01(\\tR\\n\" +\n\t\"apiVersion\\x12\\x12\\n\" +\n\t\"\\x04kind\\x18\\x04 \\x01(\\tR\\x04kind\\x12\\x12\\n\" +\n\t\"\\x04name\\x18\\x05 \\x01(\\tR\\x04name\\x12#\\n\" +\n\t\"\\rexternal_name\\x18\\x06 \\x01(\\tR\\fexternalName\\x12F\\n\" +\n\t\"\\toperation\\x18\\a \\x01(\\x0e2(.changelogs.proto.v1alpha1.OperationTypeR\\toperation\\x123\\n\" +\n\t\"\\bsnapshot\\x18\\b \\x01(\\v2\\x17.google.protobuf.StructR\\bsnapshot\\x12(\\n\" +\n\t\"\\rerror_message\\x18\\t \\x01(\\tH\\x00R\\ferrorMessage\\x88\\x01\\x01\\x12o\\n\" +\n\t\"\\x12additional_details\\x18\\n\" +\n\t\" \\x03(\\v2@.changelogs.proto.v1alpha1.ChangeLogEntry.AdditionalDetailsEntryR\\x11additionalDetails\\x1aD\\n\" +\n\t\"\\x16AdditionalDetailsEntry\\x12\\x10\\n\" +\n\t\"\\x03key\\x18\\x01 \\x01(\\tR\\x03key\\x12\\x14\\n\" +\n\t\"\\x05value\\x18\\x02 \\x01(\\tR\\x05value:\\x028\\x01B\\x10\\n\" +\n\t\"\\x0e_error_message\\\"\\x17\\n\" +\n\t\"\\x15SendChangeLogResponse*\\x80\\x01\\n\" +\n\t\"\\rOperationType\\x12\\x1e\\n\" +\n\t\"\\x1aOPERATION_TYPE_UNSPECIFIED\\x10\\x00\\x12\\x19\\n\" +\n\t\"\\x15OPERATION_TYPE_CREATE\\x10\\x01\\x12\\x19\\n\" +\n\t\"\\x15OPERATION_TYPE_UPDATE\\x10\\x02\\x12\\x19\\n\" +\n\t\"\\x15OPERATION_TYPE_DELETE\\x10\\x032\\x88\\x01\\n\" +\n\t\"\\x10ChangeLogService\\x12t\\n\" +\n\t\"\\rSendChangeLog\\x12/.changelogs.proto.v1alpha1.SendChangeLogRequest\\x1a0.changelogs.proto.v1alpha1.SendChangeLogResponse\\\"\\x00BLZJgithub.com/crossplane/crossplane-runtime/v2/apis/changelogs/proto/v1alpha1b\\x06proto3\"\n\nvar (\n\tfile_apis_changelogs_proto_v1alpha1_changelog_proto_rawDescOnce sync.Once\n\tfile_apis_changelogs_proto_v1alpha1_changelog_proto_rawDescData []byte\n)\n\nfunc file_apis_changelogs_proto_v1alpha1_changelog_proto_rawDescGZIP() []byte {\n\tfile_apis_changelogs_proto_v1alpha1_changelog_proto_rawDescOnce.Do(func() {\n\t\tfile_apis_changelogs_proto_v1alpha1_changelog_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_apis_changelogs_proto_v1alpha1_changelog_proto_rawDesc), len(file_apis_changelogs_proto_v1alpha1_changelog_proto_rawDesc)))\n\t})\n\treturn file_apis_changelogs_proto_v1alpha1_changelog_proto_rawDescData\n}\n\nvar file_apis_changelogs_proto_v1alpha1_changelog_proto_enumTypes = make([]protoimpl.EnumInfo, 1)\nvar file_apis_changelogs_proto_v1alpha1_changelog_proto_msgTypes = make([]protoimpl.MessageInfo, 4)\nvar file_apis_changelogs_proto_v1alpha1_changelog_proto_goTypes = []any{\n\t(OperationType)(0),            // 0: changelogs.proto.v1alpha1.OperationType\n\t(*SendChangeLogRequest)(nil),  // 1: changelogs.proto.v1alpha1.SendChangeLogRequest\n\t(*ChangeLogEntry)(nil),        // 2: changelogs.proto.v1alpha1.ChangeLogEntry\n\t(*SendChangeLogResponse)(nil), // 3: changelogs.proto.v1alpha1.SendChangeLogResponse\n\tnil,                           // 4: changelogs.proto.v1alpha1.ChangeLogEntry.AdditionalDetailsEntry\n\t(*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp\n\t(*structpb.Struct)(nil),       // 6: google.protobuf.Struct\n}\nvar file_apis_changelogs_proto_v1alpha1_changelog_proto_depIdxs = []int32{\n\t2, // 0: changelogs.proto.v1alpha1.SendChangeLogRequest.entry:type_name -> changelogs.proto.v1alpha1.ChangeLogEntry\n\t5, // 1: changelogs.proto.v1alpha1.ChangeLogEntry.timestamp:type_name -> google.protobuf.Timestamp\n\t0, // 2: changelogs.proto.v1alpha1.ChangeLogEntry.operation:type_name -> changelogs.proto.v1alpha1.OperationType\n\t6, // 3: changelogs.proto.v1alpha1.ChangeLogEntry.snapshot:type_name -> google.protobuf.Struct\n\t4, // 4: changelogs.proto.v1alpha1.ChangeLogEntry.additional_details:type_name -> changelogs.proto.v1alpha1.ChangeLogEntry.AdditionalDetailsEntry\n\t1, // 5: changelogs.proto.v1alpha1.ChangeLogService.SendChangeLog:input_type -> changelogs.proto.v1alpha1.SendChangeLogRequest\n\t3, // 6: changelogs.proto.v1alpha1.ChangeLogService.SendChangeLog:output_type -> changelogs.proto.v1alpha1.SendChangeLogResponse\n\t6, // [6:7] is the sub-list for method output_type\n\t5, // [5:6] is the sub-list for method input_type\n\t5, // [5:5] is the sub-list for extension type_name\n\t5, // [5:5] is the sub-list for extension extendee\n\t0, // [0:5] is the sub-list for field type_name\n}\n\nfunc init() { file_apis_changelogs_proto_v1alpha1_changelog_proto_init() }\nfunc file_apis_changelogs_proto_v1alpha1_changelog_proto_init() {\n\tif File_apis_changelogs_proto_v1alpha1_changelog_proto != nil {\n\t\treturn\n\t}\n\tfile_apis_changelogs_proto_v1alpha1_changelog_proto_msgTypes[1].OneofWrappers = []any{}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: unsafe.Slice(unsafe.StringData(file_apis_changelogs_proto_v1alpha1_changelog_proto_rawDesc), len(file_apis_changelogs_proto_v1alpha1_changelog_proto_rawDesc)),\n\t\t\tNumEnums:      1,\n\t\t\tNumMessages:   4,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_apis_changelogs_proto_v1alpha1_changelog_proto_goTypes,\n\t\tDependencyIndexes: file_apis_changelogs_proto_v1alpha1_changelog_proto_depIdxs,\n\t\tEnumInfos:         file_apis_changelogs_proto_v1alpha1_changelog_proto_enumTypes,\n\t\tMessageInfos:      file_apis_changelogs_proto_v1alpha1_changelog_proto_msgTypes,\n\t}.Build()\n\tFile_apis_changelogs_proto_v1alpha1_changelog_proto = out.File\n\tfile_apis_changelogs_proto_v1alpha1_changelog_proto_goTypes = nil\n\tfile_apis_changelogs_proto_v1alpha1_changelog_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "apis/changelogs/proto/v1alpha1/changelog.proto",
    "content": "/*\n   Copyright 2024 The Crossplane Authors.\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n       http://www.apache.org/licenses/LICENSE-2.0\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n*/\n\nsyntax = \"proto3\";\n\n// buf:lint:ignore PACKAGE_DIRECTORY_MATCH\npackage changelogs.proto.v1alpha1;\n\nimport \"google/protobuf/struct.proto\";\nimport \"google/protobuf/timestamp.proto\";\n\noption go_package = \"github.com/crossplane/crossplane-runtime/v2/apis/changelogs/proto/v1alpha1\";\n\n// ChangeLogService is a service that provides the ability to send change log\n// entries.\nservice ChangeLogService {\n  // SendChangeLog sends a change log entry to the change log service.\n  rpc SendChangeLog(SendChangeLogRequest) returns (SendChangeLogResponse) {}\n}\n\n// SendChangeLogRequest represents a request to send a single change log entry.\nmessage SendChangeLogRequest {\n  // The change log entry to send as part of this request.\n  ChangeLogEntry entry = 1;\n}\n\n// ChangeLogEntry represents a single change log entry, with detailed information\n// about the resource that was changed.\nmessage ChangeLogEntry {\n  // The timestamp at which the change occurred.\n  google.protobuf.Timestamp timestamp = 1;\n\n  // The name and version of the provider that is making the change to the\n  // resource.\n  string provider = 2;\n\n  // The API version of the resource that was changed, e.g. Group/Version.\n  string api_version = 3;\n\n  // The kind of the resource that was changed.\n  string kind = 4;\n\n  // The name of the resource that was changed.\n  string name = 5;\n\n  // The external name of the resource that was changed.\n  string external_name = 6;\n\n  // The type of operation that was performed on the resource, e.g. Create,\n  // Update, or Delete.\n  OperationType operation = 7;\n\n  // A full snapshot of the resource's state, as observed directly before the\n  // resource was changed.\n  google.protobuf.Struct snapshot = 8;\n\n  // An optional error message that describes any error encountered while\n  // performing the operation on the resource.\n  optional string error_message = 9;\n\n  // An optional additional details that can be provided for further context\n  // about the change.\n  map<string, string> additional_details = 10;\n}\n\n// OperationType represents the type of operation that was performed on a\n// resource.\nenum OperationType {\n  OPERATION_TYPE_UNSPECIFIED = 0;\n  OPERATION_TYPE_CREATE = 1;\n  OPERATION_TYPE_UPDATE = 2;\n  OPERATION_TYPE_DELETE = 3;\n}\n\n// SendChangeLogResponse is the response returned by the ChangeLogService after\n// a change log entry is sent. Currently, this is an empty message as the only\n// useful information expected to sent back at this time will be through errors.\nmessage SendChangeLogResponse {}\n"
  },
  {
    "path": "apis/changelogs/proto/v1alpha1/changelog_grpc.pb.go",
    "content": "//\n//Copyright 2024 The Crossplane Authors.\n//Licensed under the Apache License, Version 2.0 (the \"License\");\n//you may not use this file except in compliance with the License.\n//You may obtain a copy of the License at\n//http://www.apache.org/licenses/LICENSE-2.0\n//Unless required by applicable law or agreed to in writing, software\n//distributed under the License is distributed on an \"AS IS\" BASIS,\n//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n//See the License for the specific language governing permissions and\n//limitations under the License.\n\n// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.5.1\n// - protoc             (unknown)\n// source: apis/changelogs/proto/v1alpha1/changelog.proto\n\n// buf:lint:ignore PACKAGE_DIRECTORY_MATCH\n\npackage v1alpha1\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.64.0 or later.\nconst _ = grpc.SupportPackageIsVersion9\n\nconst (\n\tChangeLogService_SendChangeLog_FullMethodName = \"/changelogs.proto.v1alpha1.ChangeLogService/SendChangeLog\"\n)\n\n// ChangeLogServiceClient is the client API for ChangeLogService service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\n//\n// ChangeLogService is a service that provides the ability to send change log\n// entries.\ntype ChangeLogServiceClient interface {\n\t// SendChangeLog sends a change log entry to the change log service.\n\tSendChangeLog(ctx context.Context, in *SendChangeLogRequest, opts ...grpc.CallOption) (*SendChangeLogResponse, error)\n}\n\ntype changeLogServiceClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewChangeLogServiceClient(cc grpc.ClientConnInterface) ChangeLogServiceClient {\n\treturn &changeLogServiceClient{cc}\n}\n\nfunc (c *changeLogServiceClient) SendChangeLog(ctx context.Context, in *SendChangeLogRequest, opts ...grpc.CallOption) (*SendChangeLogResponse, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(SendChangeLogResponse)\n\terr := c.cc.Invoke(ctx, ChangeLogService_SendChangeLog_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// ChangeLogServiceServer is the server API for ChangeLogService service.\n// All implementations must embed UnimplementedChangeLogServiceServer\n// for forward compatibility.\n//\n// ChangeLogService is a service that provides the ability to send change log\n// entries.\ntype ChangeLogServiceServer interface {\n\t// SendChangeLog sends a change log entry to the change log service.\n\tSendChangeLog(context.Context, *SendChangeLogRequest) (*SendChangeLogResponse, error)\n\tmustEmbedUnimplementedChangeLogServiceServer()\n}\n\n// UnimplementedChangeLogServiceServer must be embedded to have\n// forward compatible implementations.\n//\n// NOTE: this should be embedded by value instead of pointer to avoid a nil\n// pointer dereference when methods are called.\ntype UnimplementedChangeLogServiceServer struct{}\n\nfunc (UnimplementedChangeLogServiceServer) SendChangeLog(context.Context, *SendChangeLogRequest) (*SendChangeLogResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method SendChangeLog not implemented\")\n}\nfunc (UnimplementedChangeLogServiceServer) mustEmbedUnimplementedChangeLogServiceServer() {}\nfunc (UnimplementedChangeLogServiceServer) testEmbeddedByValue()                          {}\n\n// UnsafeChangeLogServiceServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to ChangeLogServiceServer will\n// result in compilation errors.\ntype UnsafeChangeLogServiceServer interface {\n\tmustEmbedUnimplementedChangeLogServiceServer()\n}\n\nfunc RegisterChangeLogServiceServer(s grpc.ServiceRegistrar, srv ChangeLogServiceServer) {\n\t// If the following call pancis, it indicates UnimplementedChangeLogServiceServer was\n\t// embedded by pointer and is nil.  This will cause panics if an\n\t// unimplemented method is ever invoked, so we test this at initialization\n\t// time to prevent it from happening at runtime later due to I/O.\n\tif t, ok := srv.(interface{ testEmbeddedByValue() }); ok {\n\t\tt.testEmbeddedByValue()\n\t}\n\ts.RegisterService(&ChangeLogService_ServiceDesc, srv)\n}\n\nfunc _ChangeLogService_SendChangeLog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(SendChangeLogRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ChangeLogServiceServer).SendChangeLog(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ChangeLogService_SendChangeLog_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ChangeLogServiceServer).SendChangeLog(ctx, req.(*SendChangeLogRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// ChangeLogService_ServiceDesc is the grpc.ServiceDesc for ChangeLogService service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar ChangeLogService_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"changelogs.proto.v1alpha1.ChangeLogService\",\n\tHandlerType: (*ChangeLogServiceServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"SendChangeLog\",\n\t\t\tHandler:    _ChangeLogService_SendChangeLog_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"apis/changelogs/proto/v1alpha1/changelog.proto\",\n}\n"
  },
  {
    "path": "apis/pipelineinspector/proto/v1alpha1/pipeline_inspector.pb.go",
    "content": "//\n//Copyright 2026 The Crossplane Authors.\n//\n//Licensed under the Apache License, Version 2.0 (the \"License\");\n//you may not use this file except in compliance with the License.\n//You may obtain a copy of the License at\n//\n//http://www.apache.org/licenses/LICENSE-2.0\n//\n//Unless required by applicable law or agreed to in writing, software\n//distributed under the License is distributed on an \"AS IS\" BASIS,\n//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n//See the License for the specific language governing permissions and\n//limitations under the License.\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.10\n// \tprotoc        (unknown)\n// source: apis/pipelineinspector/proto/v1alpha1/pipeline_inspector.proto\n\n//buf:lint:ignore PACKAGE_DIRECTORY_MATCH\n\npackage v1alpha1\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\ttimestamppb \"google.golang.org/protobuf/types/known/timestamppb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n\tunsafe \"unsafe\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// EmitRequestRequest wraps the function request with correlation metadata.\ntype EmitRequestRequest struct {\n\tstate protoimpl.MessageState `protogen:\"open.v1\"`\n\t// The original function request as JSON bytes (with credentials stripped for security).\n\t// This allows consumers to parse the request without needing the proto schema.\n\tRequest []byte `protobuf:\"bytes,1,opt,name=request,proto3\" json:\"request,omitempty\"`\n\t// Metadata for correlation and identification.\n\tMeta          *StepMeta `protobuf:\"bytes,2,opt,name=meta,proto3\" json:\"meta,omitempty\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *EmitRequestRequest) Reset() {\n\t*x = EmitRequestRequest{}\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[0]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *EmitRequestRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*EmitRequestRequest) ProtoMessage() {}\n\nfunc (x *EmitRequestRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[0]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use EmitRequestRequest.ProtoReflect.Descriptor instead.\nfunc (*EmitRequestRequest) Descriptor() ([]byte, []int) {\n\treturn file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *EmitRequestRequest) GetRequest() []byte {\n\tif x != nil {\n\t\treturn x.Request\n\t}\n\treturn nil\n}\n\nfunc (x *EmitRequestRequest) GetMeta() *StepMeta {\n\tif x != nil {\n\t\treturn x.Meta\n\t}\n\treturn nil\n}\n\n// EmitRequestResponse is empty - this is a fire-and-forget call.\ntype EmitRequestResponse struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *EmitRequestResponse) Reset() {\n\t*x = EmitRequestResponse{}\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[1]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *EmitRequestResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*EmitRequestResponse) ProtoMessage() {}\n\nfunc (x *EmitRequestResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[1]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use EmitRequestResponse.ProtoReflect.Descriptor instead.\nfunc (*EmitRequestResponse) Descriptor() ([]byte, []int) {\n\treturn file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDescGZIP(), []int{1}\n}\n\n// EmitResponseRequest wraps the function response with correlation metadata.\ntype EmitResponseRequest struct {\n\tstate protoimpl.MessageState `protogen:\"open.v1\"`\n\t// The function response as JSON bytes, empty if there was an error.\n\t// This allows consumers to parse the response without needing the proto schema.\n\tResponse []byte `protobuf:\"bytes,1,opt,name=response,proto3\" json:\"response,omitempty\"`\n\t// Error message if the function call failed.\n\tError string `protobuf:\"bytes,2,opt,name=error,proto3\" json:\"error,omitempty\"`\n\t// Metadata for correlation and identification.\n\t// Must match the meta from the corresponding EmitRequest.\n\tMeta          *StepMeta `protobuf:\"bytes,3,opt,name=meta,proto3\" json:\"meta,omitempty\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *EmitResponseRequest) Reset() {\n\t*x = EmitResponseRequest{}\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[2]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *EmitResponseRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*EmitResponseRequest) ProtoMessage() {}\n\nfunc (x *EmitResponseRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[2]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use EmitResponseRequest.ProtoReflect.Descriptor instead.\nfunc (*EmitResponseRequest) Descriptor() ([]byte, []int) {\n\treturn file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *EmitResponseRequest) GetResponse() []byte {\n\tif x != nil {\n\t\treturn x.Response\n\t}\n\treturn nil\n}\n\nfunc (x *EmitResponseRequest) GetError() string {\n\tif x != nil {\n\t\treturn x.Error\n\t}\n\treturn \"\"\n}\n\nfunc (x *EmitResponseRequest) GetMeta() *StepMeta {\n\tif x != nil {\n\t\treturn x.Meta\n\t}\n\treturn nil\n}\n\n// EmitResponseResponse is empty - this is a fire-and-forget call.\ntype EmitResponseResponse struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *EmitResponseResponse) Reset() {\n\t*x = EmitResponseResponse{}\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[3]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *EmitResponseResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*EmitResponseResponse) ProtoMessage() {}\n\nfunc (x *EmitResponseResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[3]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use EmitResponseResponse.ProtoReflect.Descriptor instead.\nfunc (*EmitResponseResponse) Descriptor() ([]byte, []int) {\n\treturn file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDescGZIP(), []int{3}\n}\n\n// StepMeta contains metadata for correlating and identifying a function\n// invocation within a pipeline execution.\ntype StepMeta struct {\n\tstate protoimpl.MessageState `protogen:\"open.v1\"`\n\t// Timestamp when this step was executed.\n\tTimestamp *timestamppb.Timestamp `protobuf:\"bytes,1,opt,name=timestamp,proto3\" json:\"timestamp,omitempty\"`\n\t// ID identifying the entire pipeline execution (all steps in one reconciliation).\n\t// All function invocations within a single reconciliation share the same trace_id.\n\tTraceId string `protobuf:\"bytes,2,opt,name=trace_id,json=traceId,proto3\" json:\"trace_id,omitempty\"`\n\t// ID identifying this specific function invocation.\n\tSpanId string `protobuf:\"bytes,3,opt,name=span_id,json=spanId,proto3\" json:\"span_id,omitempty\"`\n\t// Zero-based index of this step in the function pipeline.\n\tStepIndex int32 `protobuf:\"varint,4,opt,name=step_index,json=stepIndex,proto3\" json:\"step_index,omitempty\"`\n\t// Name of this step in the function pipeline.\n\tStepName string `protobuf:\"bytes,5,opt,name=step_name,json=stepName,proto3\" json:\"step_name,omitempty\"`\n\t// Per-step counter incremented when a function requests additional resources and\n\t// needs to be re-run, starting from 0.\n\tIteration int32 `protobuf:\"varint,6,opt,name=iteration,proto3\" json:\"iteration,omitempty\"`\n\t// Name of the function being invoked.\n\tFunctionName string `protobuf:\"bytes,7,opt,name=function_name,json=functionName,proto3\" json:\"function_name,omitempty\"`\n\t// Only one of these can be set - identifies the pipeline context.\n\t//\n\t// Types that are valid to be assigned to Context:\n\t//\n\t//\t*StepMeta_OperationMeta\n\t//\t*StepMeta_CompositionMeta\n\tContext       isStepMeta_Context `protobuf_oneof:\"context\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *StepMeta) Reset() {\n\t*x = StepMeta{}\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[4]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *StepMeta) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*StepMeta) ProtoMessage() {}\n\nfunc (x *StepMeta) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[4]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use StepMeta.ProtoReflect.Descriptor instead.\nfunc (*StepMeta) Descriptor() ([]byte, []int) {\n\treturn file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *StepMeta) GetTimestamp() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.Timestamp\n\t}\n\treturn nil\n}\n\nfunc (x *StepMeta) GetTraceId() string {\n\tif x != nil {\n\t\treturn x.TraceId\n\t}\n\treturn \"\"\n}\n\nfunc (x *StepMeta) GetSpanId() string {\n\tif x != nil {\n\t\treturn x.SpanId\n\t}\n\treturn \"\"\n}\n\nfunc (x *StepMeta) GetStepIndex() int32 {\n\tif x != nil {\n\t\treturn x.StepIndex\n\t}\n\treturn 0\n}\n\nfunc (x *StepMeta) GetStepName() string {\n\tif x != nil {\n\t\treturn x.StepName\n\t}\n\treturn \"\"\n}\n\nfunc (x *StepMeta) GetIteration() int32 {\n\tif x != nil {\n\t\treturn x.Iteration\n\t}\n\treturn 0\n}\n\nfunc (x *StepMeta) GetFunctionName() string {\n\tif x != nil {\n\t\treturn x.FunctionName\n\t}\n\treturn \"\"\n}\n\nfunc (x *StepMeta) GetContext() isStepMeta_Context {\n\tif x != nil {\n\t\treturn x.Context\n\t}\n\treturn nil\n}\n\nfunc (x *StepMeta) GetOperationMeta() *OperationMeta {\n\tif x != nil {\n\t\tif x, ok := x.Context.(*StepMeta_OperationMeta); ok {\n\t\t\treturn x.OperationMeta\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (x *StepMeta) GetCompositionMeta() *CompositionMeta {\n\tif x != nil {\n\t\tif x, ok := x.Context.(*StepMeta_CompositionMeta); ok {\n\t\t\treturn x.CompositionMeta\n\t\t}\n\t}\n\treturn nil\n}\n\ntype isStepMeta_Context interface {\n\tisStepMeta_Context()\n}\n\ntype StepMeta_OperationMeta struct {\n\tOperationMeta *OperationMeta `protobuf:\"bytes,8,opt,name=operation_meta,json=operationMeta,proto3,oneof\"`\n}\n\ntype StepMeta_CompositionMeta struct {\n\tCompositionMeta *CompositionMeta `protobuf:\"bytes,9,opt,name=composition_meta,json=compositionMeta,proto3,oneof\"`\n}\n\nfunc (*StepMeta_OperationMeta) isStepMeta_Context() {}\n\nfunc (*StepMeta_CompositionMeta) isStepMeta_Context() {}\n\n// CompositionMeta contains metadata about the Composition and Composite Resource\ntype CompositionMeta struct {\n\tstate protoimpl.MessageState `protogen:\"open.v1\"`\n\t// Name of the Composition defining this pipeline.\n\tCompositionName string `protobuf:\"bytes,1,opt,name=composition_name,json=compositionName,proto3\" json:\"composition_name,omitempty\"`\n\t// UID of the composite resource being reconciled.\n\tCompositeResourceUid string `protobuf:\"bytes,2,opt,name=composite_resource_uid,json=compositeResourceUid,proto3\" json:\"composite_resource_uid,omitempty\"`\n\t// Name of the composite resource being reconciled.\n\tCompositeResourceName string `protobuf:\"bytes,3,opt,name=composite_resource_name,json=compositeResourceName,proto3\" json:\"composite_resource_name,omitempty\"`\n\t// Namespace of the composite resource (empty for cluster-scoped resources).\n\tCompositeResourceNamespace string `protobuf:\"bytes,4,opt,name=composite_resource_namespace,json=compositeResourceNamespace,proto3\" json:\"composite_resource_namespace,omitempty\"`\n\t// API version of the composite resource (e.g., \"example.org/v1\").\n\tCompositeResourceApiVersion string `protobuf:\"bytes,5,opt,name=composite_resource_api_version,json=compositeResourceApiVersion,proto3\" json:\"composite_resource_api_version,omitempty\"`\n\t// Kind of the composite resource (e.g., \"XDatabase\").\n\tCompositeResourceKind string `protobuf:\"bytes,6,opt,name=composite_resource_kind,json=compositeResourceKind,proto3\" json:\"composite_resource_kind,omitempty\"`\n\tunknownFields         protoimpl.UnknownFields\n\tsizeCache             protoimpl.SizeCache\n}\n\nfunc (x *CompositionMeta) Reset() {\n\t*x = CompositionMeta{}\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[5]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *CompositionMeta) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*CompositionMeta) ProtoMessage() {}\n\nfunc (x *CompositionMeta) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[5]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use CompositionMeta.ProtoReflect.Descriptor instead.\nfunc (*CompositionMeta) Descriptor() ([]byte, []int) {\n\treturn file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *CompositionMeta) GetCompositionName() string {\n\tif x != nil {\n\t\treturn x.CompositionName\n\t}\n\treturn \"\"\n}\n\nfunc (x *CompositionMeta) GetCompositeResourceUid() string {\n\tif x != nil {\n\t\treturn x.CompositeResourceUid\n\t}\n\treturn \"\"\n}\n\nfunc (x *CompositionMeta) GetCompositeResourceName() string {\n\tif x != nil {\n\t\treturn x.CompositeResourceName\n\t}\n\treturn \"\"\n}\n\nfunc (x *CompositionMeta) GetCompositeResourceNamespace() string {\n\tif x != nil {\n\t\treturn x.CompositeResourceNamespace\n\t}\n\treturn \"\"\n}\n\nfunc (x *CompositionMeta) GetCompositeResourceApiVersion() string {\n\tif x != nil {\n\t\treturn x.CompositeResourceApiVersion\n\t}\n\treturn \"\"\n}\n\nfunc (x *CompositionMeta) GetCompositeResourceKind() string {\n\tif x != nil {\n\t\treturn x.CompositeResourceKind\n\t}\n\treturn \"\"\n}\n\n// OperationMeta contains metadata about the Operation being performed.\ntype OperationMeta struct {\n\tstate protoimpl.MessageState `protogen:\"open.v1\"`\n\t// Name of the Operation.\n\tOperationName string `protobuf:\"bytes,1,opt,name=operation_name,json=operationName,proto3\" json:\"operation_name,omitempty\"`\n\t// UID of the Operation.\n\tOperationUid  string `protobuf:\"bytes,2,opt,name=operation_uid,json=operationUid,proto3\" json:\"operation_uid,omitempty\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *OperationMeta) Reset() {\n\t*x = OperationMeta{}\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[6]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *OperationMeta) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*OperationMeta) ProtoMessage() {}\n\nfunc (x *OperationMeta) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[6]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use OperationMeta.ProtoReflect.Descriptor instead.\nfunc (*OperationMeta) Descriptor() ([]byte, []int) {\n\treturn file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *OperationMeta) GetOperationName() string {\n\tif x != nil {\n\t\treturn x.OperationName\n\t}\n\treturn \"\"\n}\n\nfunc (x *OperationMeta) GetOperationUid() string {\n\tif x != nil {\n\t\treturn x.OperationUid\n\t}\n\treturn \"\"\n}\n\nvar File_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto protoreflect.FileDescriptor\n\nconst file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDesc = \"\" +\n\t\"\\n\" +\n\t\">apis/pipelineinspector/proto/v1alpha1/pipeline_inspector.proto\\x12\\x1ccrossplane.pipeline.v1alpha1\\x1a\\x1fgoogle/protobuf/timestamp.proto\\\"j\\n\" +\n\t\"\\x12EmitRequestRequest\\x12\\x18\\n\" +\n\t\"\\arequest\\x18\\x01 \\x01(\\fR\\arequest\\x12:\\n\" +\n\t\"\\x04meta\\x18\\x02 \\x01(\\v2&.crossplane.pipeline.v1alpha1.StepMetaR\\x04meta\\\"\\x15\\n\" +\n\t\"\\x13EmitRequestResponse\\\"\\x83\\x01\\n\" +\n\t\"\\x13EmitResponseRequest\\x12\\x1a\\n\" +\n\t\"\\bresponse\\x18\\x01 \\x01(\\fR\\bresponse\\x12\\x14\\n\" +\n\t\"\\x05error\\x18\\x02 \\x01(\\tR\\x05error\\x12:\\n\" +\n\t\"\\x04meta\\x18\\x03 \\x01(\\v2&.crossplane.pipeline.v1alpha1.StepMetaR\\x04meta\\\"\\x16\\n\" +\n\t\"\\x14EmitResponseResponse\\\"\\xb4\\x03\\n\" +\n\t\"\\bStepMeta\\x128\\n\" +\n\t\"\\ttimestamp\\x18\\x01 \\x01(\\v2\\x1a.google.protobuf.TimestampR\\ttimestamp\\x12\\x19\\n\" +\n\t\"\\btrace_id\\x18\\x02 \\x01(\\tR\\atraceId\\x12\\x17\\n\" +\n\t\"\\aspan_id\\x18\\x03 \\x01(\\tR\\x06spanId\\x12\\x1d\\n\" +\n\t\"\\n\" +\n\t\"step_index\\x18\\x04 \\x01(\\x05R\\tstepIndex\\x12\\x1b\\n\" +\n\t\"\\tstep_name\\x18\\x05 \\x01(\\tR\\bstepName\\x12\\x1c\\n\" +\n\t\"\\titeration\\x18\\x06 \\x01(\\x05R\\titeration\\x12#\\n\" +\n\t\"\\rfunction_name\\x18\\a \\x01(\\tR\\ffunctionName\\x12T\\n\" +\n\t\"\\x0eoperation_meta\\x18\\b \\x01(\\v2+.crossplane.pipeline.v1alpha1.OperationMetaH\\x00R\\roperationMeta\\x12Z\\n\" +\n\t\"\\x10composition_meta\\x18\\t \\x01(\\v2-.crossplane.pipeline.v1alpha1.CompositionMetaH\\x00R\\x0fcompositionMetaB\\t\\n\" +\n\t\"\\acontext\\\"\\xe9\\x02\\n\" +\n\t\"\\x0fCompositionMeta\\x12)\\n\" +\n\t\"\\x10composition_name\\x18\\x01 \\x01(\\tR\\x0fcompositionName\\x124\\n\" +\n\t\"\\x16composite_resource_uid\\x18\\x02 \\x01(\\tR\\x14compositeResourceUid\\x126\\n\" +\n\t\"\\x17composite_resource_name\\x18\\x03 \\x01(\\tR\\x15compositeResourceName\\x12@\\n\" +\n\t\"\\x1ccomposite_resource_namespace\\x18\\x04 \\x01(\\tR\\x1acompositeResourceNamespace\\x12C\\n\" +\n\t\"\\x1ecomposite_resource_api_version\\x18\\x05 \\x01(\\tR\\x1bcompositeResourceApiVersion\\x126\\n\" +\n\t\"\\x17composite_resource_kind\\x18\\x06 \\x01(\\tR\\x15compositeResourceKind\\\"[\\n\" +\n\t\"\\rOperationMeta\\x12%\\n\" +\n\t\"\\x0eoperation_name\\x18\\x01 \\x01(\\tR\\roperationName\\x12#\\n\" +\n\t\"\\roperation_uid\\x18\\x02 \\x01(\\tR\\foperationUid2\\x89\\x02\\n\" +\n\t\"\\x18PipelineInspectorService\\x12t\\n\" +\n\t\"\\vEmitRequest\\x120.crossplane.pipeline.v1alpha1.EmitRequestRequest\\x1a1.crossplane.pipeline.v1alpha1.EmitRequestResponse\\\"\\x00\\x12w\\n\" +\n\t\"\\fEmitResponse\\x121.crossplane.pipeline.v1alpha1.EmitResponseRequest\\x1a2.crossplane.pipeline.v1alpha1.EmitResponseResponse\\\"\\x00BSZQgithub.com/crossplane/crossplane-runtime/v2/apis/pipelineinspector/proto/v1alpha1b\\x06proto3\"\n\nvar (\n\tfile_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDescOnce sync.Once\n\tfile_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDescData []byte\n)\n\nfunc file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDescGZIP() []byte {\n\tfile_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDescOnce.Do(func() {\n\t\tfile_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDesc), len(file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDesc)))\n\t})\n\treturn file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDescData\n}\n\nvar file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes = make([]protoimpl.MessageInfo, 7)\nvar file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_goTypes = []any{\n\t(*EmitRequestRequest)(nil),    // 0: crossplane.pipeline.v1alpha1.EmitRequestRequest\n\t(*EmitRequestResponse)(nil),   // 1: crossplane.pipeline.v1alpha1.EmitRequestResponse\n\t(*EmitResponseRequest)(nil),   // 2: crossplane.pipeline.v1alpha1.EmitResponseRequest\n\t(*EmitResponseResponse)(nil),  // 3: crossplane.pipeline.v1alpha1.EmitResponseResponse\n\t(*StepMeta)(nil),              // 4: crossplane.pipeline.v1alpha1.StepMeta\n\t(*CompositionMeta)(nil),       // 5: crossplane.pipeline.v1alpha1.CompositionMeta\n\t(*OperationMeta)(nil),         // 6: crossplane.pipeline.v1alpha1.OperationMeta\n\t(*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp\n}\nvar file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_depIdxs = []int32{\n\t4, // 0: crossplane.pipeline.v1alpha1.EmitRequestRequest.meta:type_name -> crossplane.pipeline.v1alpha1.StepMeta\n\t4, // 1: crossplane.pipeline.v1alpha1.EmitResponseRequest.meta:type_name -> crossplane.pipeline.v1alpha1.StepMeta\n\t7, // 2: crossplane.pipeline.v1alpha1.StepMeta.timestamp:type_name -> google.protobuf.Timestamp\n\t6, // 3: crossplane.pipeline.v1alpha1.StepMeta.operation_meta:type_name -> crossplane.pipeline.v1alpha1.OperationMeta\n\t5, // 4: crossplane.pipeline.v1alpha1.StepMeta.composition_meta:type_name -> crossplane.pipeline.v1alpha1.CompositionMeta\n\t0, // 5: crossplane.pipeline.v1alpha1.PipelineInspectorService.EmitRequest:input_type -> crossplane.pipeline.v1alpha1.EmitRequestRequest\n\t2, // 6: crossplane.pipeline.v1alpha1.PipelineInspectorService.EmitResponse:input_type -> crossplane.pipeline.v1alpha1.EmitResponseRequest\n\t1, // 7: crossplane.pipeline.v1alpha1.PipelineInspectorService.EmitRequest:output_type -> crossplane.pipeline.v1alpha1.EmitRequestResponse\n\t3, // 8: crossplane.pipeline.v1alpha1.PipelineInspectorService.EmitResponse:output_type -> crossplane.pipeline.v1alpha1.EmitResponseResponse\n\t7, // [7:9] is the sub-list for method output_type\n\t5, // [5:7] is the sub-list for method input_type\n\t5, // [5:5] is the sub-list for extension type_name\n\t5, // [5:5] is the sub-list for extension extendee\n\t0, // [0:5] is the sub-list for field type_name\n}\n\nfunc init() { file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_init() }\nfunc file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_init() {\n\tif File_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto != nil {\n\t\treturn\n\t}\n\tfile_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes[4].OneofWrappers = []any{\n\t\t(*StepMeta_OperationMeta)(nil),\n\t\t(*StepMeta_CompositionMeta)(nil),\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: unsafe.Slice(unsafe.StringData(file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDesc), len(file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_rawDesc)),\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   7,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_goTypes,\n\t\tDependencyIndexes: file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_depIdxs,\n\t\tMessageInfos:      file_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_msgTypes,\n\t}.Build()\n\tFile_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto = out.File\n\tfile_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_goTypes = nil\n\tfile_apis_pipelineinspector_proto_v1alpha1_pipeline_inspector_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "apis/pipelineinspector/proto/v1alpha1/pipeline_inspector.proto",
    "content": "/*\n   Copyright 2026 The Crossplane Authors.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n*/\n\nsyntax = \"proto3\";\n\n//buf:lint:ignore PACKAGE_DIRECTORY_MATCH\npackage crossplane.pipeline.v1alpha1;\n\nimport \"google/protobuf/timestamp.proto\";\n\noption go_package = \"github.com/crossplane/crossplane-runtime/v2/apis/pipelineinspector/proto/v1alpha1\";\n\n// PipelineInspectorService receives pipeline execution data from Crossplane.\n// This service is implemented by a sidecar that captures function pipeline\n// execution data for debugging and observability purposes.\nservice PipelineInspectorService {\n  // EmitRequest receives the function request before execution.\n  // This is a fire-and-forget call; errors do not affect pipeline execution.\n  rpc EmitRequest(EmitRequestRequest) returns (EmitRequestResponse) {}\n\n  // EmitResponse receives the function response after execution.\n  // This is a fire-and-forget call; errors do not affect pipeline execution.\n  rpc EmitResponse(EmitResponseRequest) returns (EmitResponseResponse) {}\n}\n\n// EmitRequestRequest wraps the function request with correlation metadata.\nmessage EmitRequestRequest {\n  // The original function request as JSON bytes (with credentials stripped for security).\n  // This allows consumers to parse the request without needing the proto schema.\n  bytes request = 1;\n\n  // Metadata for correlation and identification.\n  StepMeta meta = 2;\n}\n\n// EmitRequestResponse is empty - this is a fire-and-forget call.\nmessage EmitRequestResponse {}\n\n// EmitResponseRequest wraps the function response with correlation metadata.\nmessage EmitResponseRequest {\n  // The function response as JSON bytes, empty if there was an error.\n  // This allows consumers to parse the response without needing the proto schema.\n  bytes response = 1;\n\n  // Error message if the function call failed.\n  string error = 2;\n\n  // Metadata for correlation and identification.\n  // Must match the meta from the corresponding EmitRequest.\n  StepMeta meta = 3;\n}\n\n// EmitResponseResponse is empty - this is a fire-and-forget call.\nmessage EmitResponseResponse {}\n\n// StepMeta contains metadata for correlating and identifying a function\n// invocation within a pipeline execution.\nmessage StepMeta {\n  // Timestamp when this step was executed.\n  google.protobuf.Timestamp timestamp = 1;\n\n  // ID identifying the entire pipeline execution (all steps in one reconciliation).\n  // All function invocations within a single reconciliation share the same trace_id.\n  string trace_id = 2;\n\n  // ID identifying this specific function invocation.\n  string span_id = 3;\n\n  // Zero-based index of this step in the function pipeline.\n  int32 step_index = 4;\n\n  // Name of this step in the function pipeline.\n  string step_name = 5;\n\n  // Per-step counter incremented when a function requests additional resources and\n  // needs to be re-run, starting from 0.\n  int32 iteration = 6;\n\n  // Name of the function being invoked.\n  string function_name = 7;\n\n  // Only one of these can be set - identifies the pipeline context.\n  oneof context {\n    OperationMeta operation_meta = 8;\n    CompositionMeta composition_meta = 9;\n  }\n}\n\n// CompositionMeta contains metadata about the Composition and Composite Resource\nmessage CompositionMeta {\n  // Name of the Composition defining this pipeline.\n  string composition_name = 1;\n\n  // UID of the composite resource being reconciled.\n  string composite_resource_uid = 2;\n\n  // Name of the composite resource being reconciled.\n  string composite_resource_name = 3;\n\n  // Namespace of the composite resource (empty for cluster-scoped resources).\n  string composite_resource_namespace = 4;\n\n  // API version of the composite resource (e.g., \"example.org/v1\").\n  string composite_resource_api_version = 5;\n\n  // Kind of the composite resource (e.g., \"XDatabase\").\n  string composite_resource_kind = 6;\n}\n\n// OperationMeta contains metadata about the Operation being performed.\nmessage OperationMeta {\n  // Name of the Operation.\n  string operation_name = 1;\n  // UID of the Operation.\n  string operation_uid = 2;\n}\n"
  },
  {
    "path": "apis/pipelineinspector/proto/v1alpha1/pipeline_inspector_grpc.pb.go",
    "content": "//\n//Copyright 2026 The Crossplane Authors.\n//\n//Licensed under the Apache License, Version 2.0 (the \"License\");\n//you may not use this file except in compliance with the License.\n//You may obtain a copy of the License at\n//\n//http://www.apache.org/licenses/LICENSE-2.0\n//\n//Unless required by applicable law or agreed to in writing, software\n//distributed under the License is distributed on an \"AS IS\" BASIS,\n//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n//See the License for the specific language governing permissions and\n//limitations under the License.\n\n// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.5.1\n// - protoc             (unknown)\n// source: apis/pipelineinspector/proto/v1alpha1/pipeline_inspector.proto\n\n//buf:lint:ignore PACKAGE_DIRECTORY_MATCH\n\npackage v1alpha1\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.64.0 or later.\nconst _ = grpc.SupportPackageIsVersion9\n\nconst (\n\tPipelineInspectorService_EmitRequest_FullMethodName  = \"/crossplane.pipeline.v1alpha1.PipelineInspectorService/EmitRequest\"\n\tPipelineInspectorService_EmitResponse_FullMethodName = \"/crossplane.pipeline.v1alpha1.PipelineInspectorService/EmitResponse\"\n)\n\n// PipelineInspectorServiceClient is the client API for PipelineInspectorService service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\n//\n// PipelineInspectorService receives pipeline execution data from Crossplane.\n// This service is implemented by a sidecar that captures function pipeline\n// execution data for debugging and observability purposes.\ntype PipelineInspectorServiceClient interface {\n\t// EmitRequest receives the function request before execution.\n\t// This is a fire-and-forget call; errors do not affect pipeline execution.\n\tEmitRequest(ctx context.Context, in *EmitRequestRequest, opts ...grpc.CallOption) (*EmitRequestResponse, error)\n\t// EmitResponse receives the function response after execution.\n\t// This is a fire-and-forget call; errors do not affect pipeline execution.\n\tEmitResponse(ctx context.Context, in *EmitResponseRequest, opts ...grpc.CallOption) (*EmitResponseResponse, error)\n}\n\ntype pipelineInspectorServiceClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewPipelineInspectorServiceClient(cc grpc.ClientConnInterface) PipelineInspectorServiceClient {\n\treturn &pipelineInspectorServiceClient{cc}\n}\n\nfunc (c *pipelineInspectorServiceClient) EmitRequest(ctx context.Context, in *EmitRequestRequest, opts ...grpc.CallOption) (*EmitRequestResponse, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(EmitRequestResponse)\n\terr := c.cc.Invoke(ctx, PipelineInspectorService_EmitRequest_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *pipelineInspectorServiceClient) EmitResponse(ctx context.Context, in *EmitResponseRequest, opts ...grpc.CallOption) (*EmitResponseResponse, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(EmitResponseResponse)\n\terr := c.cc.Invoke(ctx, PipelineInspectorService_EmitResponse_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// PipelineInspectorServiceServer is the server API for PipelineInspectorService service.\n// All implementations must embed UnimplementedPipelineInspectorServiceServer\n// for forward compatibility.\n//\n// PipelineInspectorService receives pipeline execution data from Crossplane.\n// This service is implemented by a sidecar that captures function pipeline\n// execution data for debugging and observability purposes.\ntype PipelineInspectorServiceServer interface {\n\t// EmitRequest receives the function request before execution.\n\t// This is a fire-and-forget call; errors do not affect pipeline execution.\n\tEmitRequest(context.Context, *EmitRequestRequest) (*EmitRequestResponse, error)\n\t// EmitResponse receives the function response after execution.\n\t// This is a fire-and-forget call; errors do not affect pipeline execution.\n\tEmitResponse(context.Context, *EmitResponseRequest) (*EmitResponseResponse, error)\n\tmustEmbedUnimplementedPipelineInspectorServiceServer()\n}\n\n// UnimplementedPipelineInspectorServiceServer must be embedded to have\n// forward compatible implementations.\n//\n// NOTE: this should be embedded by value instead of pointer to avoid a nil\n// pointer dereference when methods are called.\ntype UnimplementedPipelineInspectorServiceServer struct{}\n\nfunc (UnimplementedPipelineInspectorServiceServer) EmitRequest(context.Context, *EmitRequestRequest) (*EmitRequestResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method EmitRequest not implemented\")\n}\nfunc (UnimplementedPipelineInspectorServiceServer) EmitResponse(context.Context, *EmitResponseRequest) (*EmitResponseResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method EmitResponse not implemented\")\n}\nfunc (UnimplementedPipelineInspectorServiceServer) mustEmbedUnimplementedPipelineInspectorServiceServer() {\n}\nfunc (UnimplementedPipelineInspectorServiceServer) testEmbeddedByValue() {}\n\n// UnsafePipelineInspectorServiceServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to PipelineInspectorServiceServer will\n// result in compilation errors.\ntype UnsafePipelineInspectorServiceServer interface {\n\tmustEmbedUnimplementedPipelineInspectorServiceServer()\n}\n\nfunc RegisterPipelineInspectorServiceServer(s grpc.ServiceRegistrar, srv PipelineInspectorServiceServer) {\n\t// If the following call pancis, it indicates UnimplementedPipelineInspectorServiceServer was\n\t// embedded by pointer and is nil.  This will cause panics if an\n\t// unimplemented method is ever invoked, so we test this at initialization\n\t// time to prevent it from happening at runtime later due to I/O.\n\tif t, ok := srv.(interface{ testEmbeddedByValue() }); ok {\n\t\tt.testEmbeddedByValue()\n\t}\n\ts.RegisterService(&PipelineInspectorService_ServiceDesc, srv)\n}\n\nfunc _PipelineInspectorService_EmitRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(EmitRequestRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PipelineInspectorServiceServer).EmitRequest(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: PipelineInspectorService_EmitRequest_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PipelineInspectorServiceServer).EmitRequest(ctx, req.(*EmitRequestRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _PipelineInspectorService_EmitResponse_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(EmitResponseRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PipelineInspectorServiceServer).EmitResponse(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: PipelineInspectorService_EmitResponse_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PipelineInspectorServiceServer).EmitResponse(ctx, req.(*EmitResponseRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// PipelineInspectorService_ServiceDesc is the grpc.ServiceDesc for PipelineInspectorService service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar PipelineInspectorService_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"crossplane.pipeline.v1alpha1.PipelineInspectorService\",\n\tHandlerType: (*PipelineInspectorServiceServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"EmitRequest\",\n\t\t\tHandler:    _PipelineInspectorService_EmitRequest_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"EmitResponse\",\n\t\t\tHandler:    _PipelineInspectorService_EmitResponse_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"apis/pipelineinspector/proto/v1alpha1/pipeline_inspector.proto\",\n}\n"
  },
  {
    "path": "apis/proto/v1alpha1/ess.pb.go",
    "content": "//\n//Copyright 2023 The Crossplane Authors.\n//Licensed under the Apache License, Version 2.0 (the \"License\");\n//you may not use this file except in compliance with the License.\n//You may obtain a copy of the License at\n//http://www.apache.org/licenses/LICENSE-2.0\n//Unless required by applicable law or agreed to in writing, software\n//distributed under the License is distributed on an \"AS IS\" BASIS,\n//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n//See the License for the specific language governing permissions and\n//limitations under the License.\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.10\n// \tprotoc        (unknown)\n// source: apis/proto/v1alpha1/ess.proto\n\n// buf:lint:ignore PACKAGE_DIRECTORY_MATCH\n\npackage v1alpha1\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n\tunsafe \"unsafe\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// ConfigReference is used to refer a StoreConfig object.\ntype ConfigReference struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tApiVersion    string                 `protobuf:\"bytes,1,opt,name=api_version,json=apiVersion,proto3\" json:\"api_version,omitempty\"`\n\tKind          string                 `protobuf:\"bytes,2,opt,name=kind,proto3\" json:\"kind,omitempty\"`\n\tName          string                 `protobuf:\"bytes,3,opt,name=name,proto3\" json:\"name,omitempty\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *ConfigReference) Reset() {\n\t*x = ConfigReference{}\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[0]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *ConfigReference) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ConfigReference) ProtoMessage() {}\n\nfunc (x *ConfigReference) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[0]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ConfigReference.ProtoReflect.Descriptor instead.\nfunc (*ConfigReference) Descriptor() ([]byte, []int) {\n\treturn file_apis_proto_v1alpha1_ess_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *ConfigReference) GetApiVersion() string {\n\tif x != nil {\n\t\treturn x.ApiVersion\n\t}\n\treturn \"\"\n}\n\nfunc (x *ConfigReference) GetKind() string {\n\tif x != nil {\n\t\treturn x.Kind\n\t}\n\treturn \"\"\n}\n\nfunc (x *ConfigReference) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\n// Secret defines the structure of a secret.\ntype Secret struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tScopedName    string                 `protobuf:\"bytes,1,opt,name=scoped_name,json=scopedName,proto3\" json:\"scoped_name,omitempty\"`\n\tMetadata      map[string]string      `protobuf:\"bytes,2,rep,name=metadata,proto3\" json:\"metadata,omitempty\" protobuf_key:\"bytes,1,opt,name=key\" protobuf_val:\"bytes,2,opt,name=value\"`\n\tData          map[string][]byte      `protobuf:\"bytes,3,rep,name=data,proto3\" json:\"data,omitempty\" protobuf_key:\"bytes,1,opt,name=key\" protobuf_val:\"bytes,2,opt,name=value\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *Secret) Reset() {\n\t*x = Secret{}\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[1]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *Secret) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Secret) ProtoMessage() {}\n\nfunc (x *Secret) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[1]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Secret.ProtoReflect.Descriptor instead.\nfunc (*Secret) Descriptor() ([]byte, []int) {\n\treturn file_apis_proto_v1alpha1_ess_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *Secret) GetScopedName() string {\n\tif x != nil {\n\t\treturn x.ScopedName\n\t}\n\treturn \"\"\n}\n\nfunc (x *Secret) GetMetadata() map[string]string {\n\tif x != nil {\n\t\treturn x.Metadata\n\t}\n\treturn nil\n}\n\nfunc (x *Secret) GetData() map[string][]byte {\n\tif x != nil {\n\t\treturn x.Data\n\t}\n\treturn nil\n}\n\n// GetSecretRequest requests secret from the secret store.\ntype GetSecretRequest struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tConfig        *ConfigReference       `protobuf:\"bytes,1,opt,name=config,proto3\" json:\"config,omitempty\"`\n\tSecret        *Secret                `protobuf:\"bytes,2,opt,name=secret,proto3\" json:\"secret,omitempty\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *GetSecretRequest) Reset() {\n\t*x = GetSecretRequest{}\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[2]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *GetSecretRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetSecretRequest) ProtoMessage() {}\n\nfunc (x *GetSecretRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[2]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GetSecretRequest.ProtoReflect.Descriptor instead.\nfunc (*GetSecretRequest) Descriptor() ([]byte, []int) {\n\treturn file_apis_proto_v1alpha1_ess_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *GetSecretRequest) GetConfig() *ConfigReference {\n\tif x != nil {\n\t\treturn x.Config\n\t}\n\treturn nil\n}\n\nfunc (x *GetSecretRequest) GetSecret() *Secret {\n\tif x != nil {\n\t\treturn x.Secret\n\t}\n\treturn nil\n}\n\n// GetSecretResponse returns the secret from the secret store.\ntype GetSecretResponse struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tSecret        *Secret                `protobuf:\"bytes,1,opt,name=secret,proto3\" json:\"secret,omitempty\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *GetSecretResponse) Reset() {\n\t*x = GetSecretResponse{}\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[3]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *GetSecretResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetSecretResponse) ProtoMessage() {}\n\nfunc (x *GetSecretResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[3]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GetSecretResponse.ProtoReflect.Descriptor instead.\nfunc (*GetSecretResponse) Descriptor() ([]byte, []int) {\n\treturn file_apis_proto_v1alpha1_ess_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *GetSecretResponse) GetSecret() *Secret {\n\tif x != nil {\n\t\treturn x.Secret\n\t}\n\treturn nil\n}\n\n// ApplySecretRequest applies the secret data update to the secret store.\ntype ApplySecretRequest struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tConfig        *ConfigReference       `protobuf:\"bytes,1,opt,name=config,proto3\" json:\"config,omitempty\"`\n\tSecret        *Secret                `protobuf:\"bytes,2,opt,name=secret,proto3\" json:\"secret,omitempty\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *ApplySecretRequest) Reset() {\n\t*x = ApplySecretRequest{}\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[4]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *ApplySecretRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ApplySecretRequest) ProtoMessage() {}\n\nfunc (x *ApplySecretRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[4]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ApplySecretRequest.ProtoReflect.Descriptor instead.\nfunc (*ApplySecretRequest) Descriptor() ([]byte, []int) {\n\treturn file_apis_proto_v1alpha1_ess_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *ApplySecretRequest) GetConfig() *ConfigReference {\n\tif x != nil {\n\t\treturn x.Config\n\t}\n\treturn nil\n}\n\nfunc (x *ApplySecretRequest) GetSecret() *Secret {\n\tif x != nil {\n\t\treturn x.Secret\n\t}\n\treturn nil\n}\n\n// ApplySecretResponse returns if the secret is changed or not.\ntype ApplySecretResponse struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tChanged       bool                   `protobuf:\"varint,1,opt,name=changed,proto3\" json:\"changed,omitempty\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *ApplySecretResponse) Reset() {\n\t*x = ApplySecretResponse{}\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[5]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *ApplySecretResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ApplySecretResponse) ProtoMessage() {}\n\nfunc (x *ApplySecretResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[5]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ApplySecretResponse.ProtoReflect.Descriptor instead.\nfunc (*ApplySecretResponse) Descriptor() ([]byte, []int) {\n\treturn file_apis_proto_v1alpha1_ess_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *ApplySecretResponse) GetChanged() bool {\n\tif x != nil {\n\t\treturn x.Changed\n\t}\n\treturn false\n}\n\n// DeleteKeysRequest deletes the secret from the secret store.\ntype DeleteKeysRequest struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tConfig        *ConfigReference       `protobuf:\"bytes,1,opt,name=config,proto3\" json:\"config,omitempty\"`\n\tSecret        *Secret                `protobuf:\"bytes,2,opt,name=secret,proto3\" json:\"secret,omitempty\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *DeleteKeysRequest) Reset() {\n\t*x = DeleteKeysRequest{}\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[6]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *DeleteKeysRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeleteKeysRequest) ProtoMessage() {}\n\nfunc (x *DeleteKeysRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[6]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DeleteKeysRequest.ProtoReflect.Descriptor instead.\nfunc (*DeleteKeysRequest) Descriptor() ([]byte, []int) {\n\treturn file_apis_proto_v1alpha1_ess_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *DeleteKeysRequest) GetConfig() *ConfigReference {\n\tif x != nil {\n\t\treturn x.Config\n\t}\n\treturn nil\n}\n\nfunc (x *DeleteKeysRequest) GetSecret() *Secret {\n\tif x != nil {\n\t\treturn x.Secret\n\t}\n\treturn nil\n}\n\n// DeleteKeysResponse is returned if the secret is deleted.\ntype DeleteKeysResponse struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *DeleteKeysResponse) Reset() {\n\t*x = DeleteKeysResponse{}\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[7]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *DeleteKeysResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeleteKeysResponse) ProtoMessage() {}\n\nfunc (x *DeleteKeysResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_apis_proto_v1alpha1_ess_proto_msgTypes[7]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DeleteKeysResponse.ProtoReflect.Descriptor instead.\nfunc (*DeleteKeysResponse) Descriptor() ([]byte, []int) {\n\treturn file_apis_proto_v1alpha1_ess_proto_rawDescGZIP(), []int{7}\n}\n\nvar File_apis_proto_v1alpha1_ess_proto protoreflect.FileDescriptor\n\nconst file_apis_proto_v1alpha1_ess_proto_rawDesc = \"\" +\n\t\"\\n\" +\n\t\"\\x1dapis/proto/v1alpha1/ess.proto\\x12\\x12ess.proto.v1alpha1\\\"Z\\n\" +\n\t\"\\x0fConfigReference\\x12\\x1f\\n\" +\n\t\"\\vapi_version\\x18\\x01 \\x01(\\tR\\n\" +\n\t\"apiVersion\\x12\\x12\\n\" +\n\t\"\\x04kind\\x18\\x02 \\x01(\\tR\\x04kind\\x12\\x12\\n\" +\n\t\"\\x04name\\x18\\x03 \\x01(\\tR\\x04name\\\"\\x9f\\x02\\n\" +\n\t\"\\x06Secret\\x12\\x1f\\n\" +\n\t\"\\vscoped_name\\x18\\x01 \\x01(\\tR\\n\" +\n\t\"scopedName\\x12D\\n\" +\n\t\"\\bmetadata\\x18\\x02 \\x03(\\v2(.ess.proto.v1alpha1.Secret.MetadataEntryR\\bmetadata\\x128\\n\" +\n\t\"\\x04data\\x18\\x03 \\x03(\\v2$.ess.proto.v1alpha1.Secret.DataEntryR\\x04data\\x1a;\\n\" +\n\t\"\\rMetadataEntry\\x12\\x10\\n\" +\n\t\"\\x03key\\x18\\x01 \\x01(\\tR\\x03key\\x12\\x14\\n\" +\n\t\"\\x05value\\x18\\x02 \\x01(\\tR\\x05value:\\x028\\x01\\x1a7\\n\" +\n\t\"\\tDataEntry\\x12\\x10\\n\" +\n\t\"\\x03key\\x18\\x01 \\x01(\\tR\\x03key\\x12\\x14\\n\" +\n\t\"\\x05value\\x18\\x02 \\x01(\\fR\\x05value:\\x028\\x01\\\"\\x83\\x01\\n\" +\n\t\"\\x10GetSecretRequest\\x12;\\n\" +\n\t\"\\x06config\\x18\\x01 \\x01(\\v2#.ess.proto.v1alpha1.ConfigReferenceR\\x06config\\x122\\n\" +\n\t\"\\x06secret\\x18\\x02 \\x01(\\v2\\x1a.ess.proto.v1alpha1.SecretR\\x06secret\\\"G\\n\" +\n\t\"\\x11GetSecretResponse\\x122\\n\" +\n\t\"\\x06secret\\x18\\x01 \\x01(\\v2\\x1a.ess.proto.v1alpha1.SecretR\\x06secret\\\"\\x85\\x01\\n\" +\n\t\"\\x12ApplySecretRequest\\x12;\\n\" +\n\t\"\\x06config\\x18\\x01 \\x01(\\v2#.ess.proto.v1alpha1.ConfigReferenceR\\x06config\\x122\\n\" +\n\t\"\\x06secret\\x18\\x02 \\x01(\\v2\\x1a.ess.proto.v1alpha1.SecretR\\x06secret\\\"/\\n\" +\n\t\"\\x13ApplySecretResponse\\x12\\x18\\n\" +\n\t\"\\achanged\\x18\\x01 \\x01(\\bR\\achanged\\\"\\x84\\x01\\n\" +\n\t\"\\x11DeleteKeysRequest\\x12;\\n\" +\n\t\"\\x06config\\x18\\x01 \\x01(\\v2#.ess.proto.v1alpha1.ConfigReferenceR\\x06config\\x122\\n\" +\n\t\"\\x06secret\\x18\\x02 \\x01(\\v2\\x1a.ess.proto.v1alpha1.SecretR\\x06secret\\\"\\x14\\n\" +\n\t\"\\x12DeleteKeysResponse2\\xbf\\x02\\n\" +\n\t\" ExternalSecretStorePluginService\\x12Z\\n\" +\n\t\"\\tGetSecret\\x12$.ess.proto.v1alpha1.GetSecretRequest\\x1a%.ess.proto.v1alpha1.GetSecretResponse\\\"\\x00\\x12`\\n\" +\n\t\"\\vApplySecret\\x12&.ess.proto.v1alpha1.ApplySecretRequest\\x1a'.ess.proto.v1alpha1.ApplySecretResponse\\\"\\x00\\x12]\\n\" +\n\t\"\\n\" +\n\t\"DeleteKeys\\x12%.ess.proto.v1alpha1.DeleteKeysRequest\\x1a&.ess.proto.v1alpha1.DeleteKeysResponse\\\"\\x00BAZ?github.com/crossplane/crossplane-runtime/v2/apis/proto/v1alpha1b\\x06proto3\"\n\nvar (\n\tfile_apis_proto_v1alpha1_ess_proto_rawDescOnce sync.Once\n\tfile_apis_proto_v1alpha1_ess_proto_rawDescData []byte\n)\n\nfunc file_apis_proto_v1alpha1_ess_proto_rawDescGZIP() []byte {\n\tfile_apis_proto_v1alpha1_ess_proto_rawDescOnce.Do(func() {\n\t\tfile_apis_proto_v1alpha1_ess_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_apis_proto_v1alpha1_ess_proto_rawDesc), len(file_apis_proto_v1alpha1_ess_proto_rawDesc)))\n\t})\n\treturn file_apis_proto_v1alpha1_ess_proto_rawDescData\n}\n\nvar file_apis_proto_v1alpha1_ess_proto_msgTypes = make([]protoimpl.MessageInfo, 10)\nvar file_apis_proto_v1alpha1_ess_proto_goTypes = []any{\n\t(*ConfigReference)(nil),     // 0: ess.proto.v1alpha1.ConfigReference\n\t(*Secret)(nil),              // 1: ess.proto.v1alpha1.Secret\n\t(*GetSecretRequest)(nil),    // 2: ess.proto.v1alpha1.GetSecretRequest\n\t(*GetSecretResponse)(nil),   // 3: ess.proto.v1alpha1.GetSecretResponse\n\t(*ApplySecretRequest)(nil),  // 4: ess.proto.v1alpha1.ApplySecretRequest\n\t(*ApplySecretResponse)(nil), // 5: ess.proto.v1alpha1.ApplySecretResponse\n\t(*DeleteKeysRequest)(nil),   // 6: ess.proto.v1alpha1.DeleteKeysRequest\n\t(*DeleteKeysResponse)(nil),  // 7: ess.proto.v1alpha1.DeleteKeysResponse\n\tnil,                         // 8: ess.proto.v1alpha1.Secret.MetadataEntry\n\tnil,                         // 9: ess.proto.v1alpha1.Secret.DataEntry\n}\nvar file_apis_proto_v1alpha1_ess_proto_depIdxs = []int32{\n\t8,  // 0: ess.proto.v1alpha1.Secret.metadata:type_name -> ess.proto.v1alpha1.Secret.MetadataEntry\n\t9,  // 1: ess.proto.v1alpha1.Secret.data:type_name -> ess.proto.v1alpha1.Secret.DataEntry\n\t0,  // 2: ess.proto.v1alpha1.GetSecretRequest.config:type_name -> ess.proto.v1alpha1.ConfigReference\n\t1,  // 3: ess.proto.v1alpha1.GetSecretRequest.secret:type_name -> ess.proto.v1alpha1.Secret\n\t1,  // 4: ess.proto.v1alpha1.GetSecretResponse.secret:type_name -> ess.proto.v1alpha1.Secret\n\t0,  // 5: ess.proto.v1alpha1.ApplySecretRequest.config:type_name -> ess.proto.v1alpha1.ConfigReference\n\t1,  // 6: ess.proto.v1alpha1.ApplySecretRequest.secret:type_name -> ess.proto.v1alpha1.Secret\n\t0,  // 7: ess.proto.v1alpha1.DeleteKeysRequest.config:type_name -> ess.proto.v1alpha1.ConfigReference\n\t1,  // 8: ess.proto.v1alpha1.DeleteKeysRequest.secret:type_name -> ess.proto.v1alpha1.Secret\n\t2,  // 9: ess.proto.v1alpha1.ExternalSecretStorePluginService.GetSecret:input_type -> ess.proto.v1alpha1.GetSecretRequest\n\t4,  // 10: ess.proto.v1alpha1.ExternalSecretStorePluginService.ApplySecret:input_type -> ess.proto.v1alpha1.ApplySecretRequest\n\t6,  // 11: ess.proto.v1alpha1.ExternalSecretStorePluginService.DeleteKeys:input_type -> ess.proto.v1alpha1.DeleteKeysRequest\n\t3,  // 12: ess.proto.v1alpha1.ExternalSecretStorePluginService.GetSecret:output_type -> ess.proto.v1alpha1.GetSecretResponse\n\t5,  // 13: ess.proto.v1alpha1.ExternalSecretStorePluginService.ApplySecret:output_type -> ess.proto.v1alpha1.ApplySecretResponse\n\t7,  // 14: ess.proto.v1alpha1.ExternalSecretStorePluginService.DeleteKeys:output_type -> ess.proto.v1alpha1.DeleteKeysResponse\n\t12, // [12:15] is the sub-list for method output_type\n\t9,  // [9:12] is the sub-list for method input_type\n\t9,  // [9:9] is the sub-list for extension type_name\n\t9,  // [9:9] is the sub-list for extension extendee\n\t0,  // [0:9] is the sub-list for field type_name\n}\n\nfunc init() { file_apis_proto_v1alpha1_ess_proto_init() }\nfunc file_apis_proto_v1alpha1_ess_proto_init() {\n\tif File_apis_proto_v1alpha1_ess_proto != nil {\n\t\treturn\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: unsafe.Slice(unsafe.StringData(file_apis_proto_v1alpha1_ess_proto_rawDesc), len(file_apis_proto_v1alpha1_ess_proto_rawDesc)),\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   10,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_apis_proto_v1alpha1_ess_proto_goTypes,\n\t\tDependencyIndexes: file_apis_proto_v1alpha1_ess_proto_depIdxs,\n\t\tMessageInfos:      file_apis_proto_v1alpha1_ess_proto_msgTypes,\n\t}.Build()\n\tFile_apis_proto_v1alpha1_ess_proto = out.File\n\tfile_apis_proto_v1alpha1_ess_proto_goTypes = nil\n\tfile_apis_proto_v1alpha1_ess_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "apis/proto/v1alpha1/ess.proto",
    "content": "/*\n   Copyright 2023 The Crossplane Authors.\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n       http://www.apache.org/licenses/LICENSE-2.0\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n*/\n\nsyntax = \"proto3\";\n\n// buf:lint:ignore PACKAGE_DIRECTORY_MATCH\npackage ess.proto.v1alpha1;\n\noption go_package = \"github.com/crossplane/crossplane-runtime/v2/apis/proto/v1alpha1\";\n\n// ExternalSecretStorePluginService defines the APIs for an External Secret Store plugin.\nservice ExternalSecretStorePluginService {\n  rpc GetSecret(GetSecretRequest) returns (GetSecretResponse) {}\n  rpc ApplySecret(ApplySecretRequest) returns (ApplySecretResponse) {}\n  rpc DeleteKeys(DeleteKeysRequest) returns (DeleteKeysResponse) {}\n}\n\n// ConfigReference is used to refer a StoreConfig object.\nmessage ConfigReference {\n  string api_version = 1;\n  string kind = 2;\n  string name = 3;\n}\n\n// Secret defines the structure of a secret.\nmessage Secret {\n  string scoped_name = 1;\n  map<string, string> metadata = 2;\n  map<string, bytes> data = 3;\n}\n\n// GetSecretRequest requests secret from the secret store.\nmessage GetSecretRequest {\n  ConfigReference config = 1;\n  Secret secret = 2;\n}\n\n// GetSecretResponse returns the secret from the secret store.\nmessage GetSecretResponse {\n  Secret secret = 1;\n}\n\n// ApplySecretRequest applies the secret data update to the secret store.\nmessage ApplySecretRequest {\n  ConfigReference config = 1;\n  Secret secret = 2;\n}\n\n// ApplySecretResponse returns if the secret is changed or not.\nmessage ApplySecretResponse {\n  bool changed = 1;\n}\n\n// DeleteKeysRequest deletes the secret from the secret store.\nmessage DeleteKeysRequest {\n  ConfigReference config = 1;\n  Secret secret = 2;\n}\n\n// DeleteKeysResponse is returned if the secret is deleted.\nmessage DeleteKeysResponse {}\n"
  },
  {
    "path": "apis/proto/v1alpha1/ess_grpc.pb.go",
    "content": "//\n//Copyright 2023 The Crossplane Authors.\n//Licensed under the Apache License, Version 2.0 (the \"License\");\n//you may not use this file except in compliance with the License.\n//You may obtain a copy of the License at\n//http://www.apache.org/licenses/LICENSE-2.0\n//Unless required by applicable law or agreed to in writing, software\n//distributed under the License is distributed on an \"AS IS\" BASIS,\n//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n//See the License for the specific language governing permissions and\n//limitations under the License.\n\n// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.5.1\n// - protoc             (unknown)\n// source: apis/proto/v1alpha1/ess.proto\n\n// buf:lint:ignore PACKAGE_DIRECTORY_MATCH\n\npackage v1alpha1\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.64.0 or later.\nconst _ = grpc.SupportPackageIsVersion9\n\nconst (\n\tExternalSecretStorePluginService_GetSecret_FullMethodName   = \"/ess.proto.v1alpha1.ExternalSecretStorePluginService/GetSecret\"\n\tExternalSecretStorePluginService_ApplySecret_FullMethodName = \"/ess.proto.v1alpha1.ExternalSecretStorePluginService/ApplySecret\"\n\tExternalSecretStorePluginService_DeleteKeys_FullMethodName  = \"/ess.proto.v1alpha1.ExternalSecretStorePluginService/DeleteKeys\"\n)\n\n// ExternalSecretStorePluginServiceClient is the client API for ExternalSecretStorePluginService service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\n//\n// ExternalSecretStorePluginService defines the APIs for an External Secret Store plugin.\ntype ExternalSecretStorePluginServiceClient interface {\n\tGetSecret(ctx context.Context, in *GetSecretRequest, opts ...grpc.CallOption) (*GetSecretResponse, error)\n\tApplySecret(ctx context.Context, in *ApplySecretRequest, opts ...grpc.CallOption) (*ApplySecretResponse, error)\n\tDeleteKeys(ctx context.Context, in *DeleteKeysRequest, opts ...grpc.CallOption) (*DeleteKeysResponse, error)\n}\n\ntype externalSecretStorePluginServiceClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewExternalSecretStorePluginServiceClient(cc grpc.ClientConnInterface) ExternalSecretStorePluginServiceClient {\n\treturn &externalSecretStorePluginServiceClient{cc}\n}\n\nfunc (c *externalSecretStorePluginServiceClient) GetSecret(ctx context.Context, in *GetSecretRequest, opts ...grpc.CallOption) (*GetSecretResponse, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(GetSecretResponse)\n\terr := c.cc.Invoke(ctx, ExternalSecretStorePluginService_GetSecret_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *externalSecretStorePluginServiceClient) ApplySecret(ctx context.Context, in *ApplySecretRequest, opts ...grpc.CallOption) (*ApplySecretResponse, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(ApplySecretResponse)\n\terr := c.cc.Invoke(ctx, ExternalSecretStorePluginService_ApplySecret_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *externalSecretStorePluginServiceClient) DeleteKeys(ctx context.Context, in *DeleteKeysRequest, opts ...grpc.CallOption) (*DeleteKeysResponse, error) {\n\tcOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)\n\tout := new(DeleteKeysResponse)\n\terr := c.cc.Invoke(ctx, ExternalSecretStorePluginService_DeleteKeys_FullMethodName, in, out, cOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// ExternalSecretStorePluginServiceServer is the server API for ExternalSecretStorePluginService service.\n// All implementations must embed UnimplementedExternalSecretStorePluginServiceServer\n// for forward compatibility.\n//\n// ExternalSecretStorePluginService defines the APIs for an External Secret Store plugin.\ntype ExternalSecretStorePluginServiceServer interface {\n\tGetSecret(context.Context, *GetSecretRequest) (*GetSecretResponse, error)\n\tApplySecret(context.Context, *ApplySecretRequest) (*ApplySecretResponse, error)\n\tDeleteKeys(context.Context, *DeleteKeysRequest) (*DeleteKeysResponse, error)\n\tmustEmbedUnimplementedExternalSecretStorePluginServiceServer()\n}\n\n// UnimplementedExternalSecretStorePluginServiceServer must be embedded to have\n// forward compatible implementations.\n//\n// NOTE: this should be embedded by value instead of pointer to avoid a nil\n// pointer dereference when methods are called.\ntype UnimplementedExternalSecretStorePluginServiceServer struct{}\n\nfunc (UnimplementedExternalSecretStorePluginServiceServer) GetSecret(context.Context, *GetSecretRequest) (*GetSecretResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method GetSecret not implemented\")\n}\nfunc (UnimplementedExternalSecretStorePluginServiceServer) ApplySecret(context.Context, *ApplySecretRequest) (*ApplySecretResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ApplySecret not implemented\")\n}\nfunc (UnimplementedExternalSecretStorePluginServiceServer) DeleteKeys(context.Context, *DeleteKeysRequest) (*DeleteKeysResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method DeleteKeys not implemented\")\n}\nfunc (UnimplementedExternalSecretStorePluginServiceServer) mustEmbedUnimplementedExternalSecretStorePluginServiceServer() {\n}\nfunc (UnimplementedExternalSecretStorePluginServiceServer) testEmbeddedByValue() {}\n\n// UnsafeExternalSecretStorePluginServiceServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to ExternalSecretStorePluginServiceServer will\n// result in compilation errors.\ntype UnsafeExternalSecretStorePluginServiceServer interface {\n\tmustEmbedUnimplementedExternalSecretStorePluginServiceServer()\n}\n\nfunc RegisterExternalSecretStorePluginServiceServer(s grpc.ServiceRegistrar, srv ExternalSecretStorePluginServiceServer) {\n\t// If the following call pancis, it indicates UnimplementedExternalSecretStorePluginServiceServer was\n\t// embedded by pointer and is nil.  This will cause panics if an\n\t// unimplemented method is ever invoked, so we test this at initialization\n\t// time to prevent it from happening at runtime later due to I/O.\n\tif t, ok := srv.(interface{ testEmbeddedByValue() }); ok {\n\t\tt.testEmbeddedByValue()\n\t}\n\ts.RegisterService(&ExternalSecretStorePluginService_ServiceDesc, srv)\n}\n\nfunc _ExternalSecretStorePluginService_GetSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(GetSecretRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ExternalSecretStorePluginServiceServer).GetSecret(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ExternalSecretStorePluginService_GetSecret_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ExternalSecretStorePluginServiceServer).GetSecret(ctx, req.(*GetSecretRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ExternalSecretStorePluginService_ApplySecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ApplySecretRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ExternalSecretStorePluginServiceServer).ApplySecret(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ExternalSecretStorePluginService_ApplySecret_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ExternalSecretStorePluginServiceServer).ApplySecret(ctx, req.(*ApplySecretRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _ExternalSecretStorePluginService_DeleteKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DeleteKeysRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(ExternalSecretStorePluginServiceServer).DeleteKeys(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: ExternalSecretStorePluginService_DeleteKeys_FullMethodName,\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(ExternalSecretStorePluginServiceServer).DeleteKeys(ctx, req.(*DeleteKeysRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// ExternalSecretStorePluginService_ServiceDesc is the grpc.ServiceDesc for ExternalSecretStorePluginService service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar ExternalSecretStorePluginService_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"ess.proto.v1alpha1.ExternalSecretStorePluginService\",\n\tHandlerType: (*ExternalSecretStorePluginServiceServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"GetSecret\",\n\t\t\tHandler:    _ExternalSecretStorePluginService_GetSecret_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ApplySecret\",\n\t\t\tHandler:    _ExternalSecretStorePluginService_ApplySecret_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"DeleteKeys\",\n\t\t\tHandler:    _ExternalSecretStorePluginService_DeleteKeys_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"apis/proto/v1alpha1/ess.proto\",\n}\n"
  },
  {
    "path": "buf.gen.yaml",
    "content": "# This file contains configuration for the `buf generate` command.\n# See generate.go for more details.\nversion: v1\nplugins:\n  - plugin: go\n    out: .\n    opt: paths=source_relative\n  - plugin: go-grpc\n    out: .\n    opt: paths=source_relative\n"
  },
  {
    "path": "buf.yaml",
    "content": "version: v1\nname: buf.build/crossplane/crossplane-runtime\nbreaking:\n  use:\n    - WIRE_JSON\nlint:\n  use:\n    - DEFAULT\n  allow_comment_ignores: true\n"
  },
  {
    "path": "flake.nix",
    "content": "# New to Nix? Start here:\n#   Language basics:  https://nix.dev/tutorials/nix-language\n#   Flakes intro:     https://zero-to-nix.com/concepts/flakes\n{\n  description = \"Crossplane Runtime - Go library for building Crossplane providers and controllers\";\n\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/nixos-25.11\";\n    nixpkgs-unstable.url = \"github:NixOS/nixpkgs/nixpkgs-unstable\";\n\n    # TODO(negz): Unpin once https://github.com/nix-community/gomod2nix/pull/231 is released.\n    gomod2nix = {\n      url = \"github:nix-community/gomod2nix/49662a44272806ff785df2990a420edaaca15db4\";\n      inputs.nixpkgs.follows = \"nixpkgs\";\n    };\n  };\n\n  outputs =\n    {\n      self,\n      nixpkgs,\n      nixpkgs-unstable,\n      gomod2nix,\n    }:\n    let\n      # Systems where Nix runs (dev machines, CI).\n      supportedSystems = [\n        \"x86_64-linux\"\n        \"aarch64-linux\"\n        \"x86_64-darwin\"\n        \"aarch64-darwin\"\n      ];\n\n      # Helpers for per-system outputs.\n      forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: forSystem system f);\n      forSystem =\n        system: f:\n        f {\n          inherit system;\n          pkgs = import nixpkgs {\n            inherit system;\n            overlays = [\n              gomod2nix.overlays.default\n              (_final: _prev: {\n                go = nixpkgs-unstable.legacyPackages.${system}.go_1_25;\n                inherit (nixpkgs-unstable.legacyPackages.${system}) go_1_25;\n              })\n            ];\n          };\n        };\n\n    in\n    {\n      # CI checks (nix flake check).\n      checks = forAllSystems (\n        { pkgs, ... }:\n        let\n          checks = import ./nix/checks.nix { inherit pkgs self; };\n        in\n        {\n          test = checks.test { };\n          generate = checks.generate { };\n          go-lint = checks.goLint { };\n          nix-lint = checks.nixLint { };\n        }\n      );\n\n      # Development commands (nix run .#<app>).\n      apps = forAllSystems (\n        { pkgs, ... }:\n        let\n          apps = import ./nix/apps.nix { inherit pkgs; };\n        in\n        {\n          test = apps.test { };\n          lint = apps.lint { fix = true; };\n          generate = apps.generate { };\n          tidy = apps.tidy { };\n        }\n      );\n\n      # Development shell (nix develop).\n      devShells = forAllSystems (\n        { pkgs, ... }:\n        {\n          default = pkgs.mkShell {\n            buildInputs = [\n              pkgs.coreutils\n              pkgs.gnused\n              pkgs.ncurses\n              pkgs.go\n              pkgs.golangci-lint\n              pkgs.gomod2nix\n\n              # Code generation\n              pkgs.buf\n              pkgs.protoc-gen-go\n              pkgs.protoc-gen-go-grpc\n              pkgs.kubernetes-controller-tools\n\n              # Nix\n              pkgs.nixfmt-rfc-style\n            ];\n\n            shellHook = ''\n              export PS1='\\[\\033[38;2;243;128;123m\\][cros\\[\\033[38;2;255;205;60m\\]spla\\[\\033[38;2;53;208;186m\\]ne-rt]\\[\\033[0m\\] \\w \\$ '\n\n              echo \"Crossplane Runtime development shell ($(go version | cut -d' ' -f3))\"\n              echo \"\"\n              echo \"  nix run .#test          nix run .#generate\"\n              echo \"  nix run .#lint          nix run .#tidy\"\n              echo \"\"\n              echo \"  nix flake check\"\n              echo \"\"\n            '';\n          };\n        }\n      );\n    };\n}\n"
  },
  {
    "path": "generate.go",
    "content": "//go:build generate\n// +build generate\n\n/*\nCopyright 2019 The Crossplane 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 generation tools (controller-gen, buf, etc.) must be in your $PATH. Use\n// './nix.sh develop' or './nix.sh run .#generate' to ensure they are.\n\n// Generate deepcopy methodsets\n//go:generate controller-gen object:headerFile=./hack/boilerplate.go.txt paths=./apis/...\n\n// Generate gRPC types and stubs. See buf.gen.yaml for buf's configuration.\n// The protoc-gen-go and protoc-gen-go-grpc plugins must be in $PATH.\n// Note that the vendor dir does temporarily exist during a Nix build.\n//go:generate buf generate --exclude-path vendor\n\npackage generate\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/crossplane/crossplane-runtime/v2\n\ngo 1.25.9\n\nrequire (\n\tdario.cat/mergo v1.0.2\n\tgithub.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6\n\tgithub.com/Masterminds/semver/v3 v3.4.0\n\tgithub.com/crossplane/crossplane/apis/v2 v2.0.0-20260424160951-8f231230ebb6\n\tgithub.com/evanphx/json-patch v5.9.11+incompatible\n\tgithub.com/go-logr/logr v1.4.3\n\tgithub.com/google/go-cmp v0.7.0\n\tgithub.com/google/go-containerregistry v0.20.7\n\tgithub.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20230919002926-dbcd01c402b2\n\tgithub.com/in-toto/attestation v1.1.2\n\tgithub.com/in-toto/in-toto-golang v0.11.0\n\tgithub.com/prometheus/client_golang v1.23.2\n\tgithub.com/sigstore/cosign/v3 v3.0.5\n\tgithub.com/sigstore/sigstore v1.10.5\n\tgithub.com/sirupsen/logrus v1.9.4\n\tgithub.com/spf13/afero v1.15.0\n\tgolang.org/x/time v0.15.0\n\tgoogle.golang.org/grpc v1.79.3\n\tgoogle.golang.org/protobuf v1.36.11\n\tk8s.io/api v0.35.1\n\tk8s.io/apiextensions-apiserver v0.35.0\n\tk8s.io/apimachinery v0.35.1\n\tk8s.io/client-go v0.35.1\n\tk8s.io/component-base v0.35.0\n\tk8s.io/klog/v2 v2.130.1\n\tk8s.io/utils v0.0.0-20260108192941-914a6e750570\n\tsigs.k8s.io/controller-runtime v0.23.1\n\tsigs.k8s.io/controller-tools v0.20.0\n\tsigs.k8s.io/yaml v1.6.0\n)\n\nrequire (\n\tcloud.google.com/go/compute/metadata v0.9.0 // indirect\n\tgithub.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect\n\tgithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect\n\tgithub.com/Azure/go-autorest v14.2.0+incompatible // indirect\n\tgithub.com/Azure/go-autorest/autorest v0.11.30 // indirect\n\tgithub.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect\n\tgithub.com/Azure/go-autorest/autorest/azure/auth v0.5.13 // indirect\n\tgithub.com/Azure/go-autorest/autorest/azure/cli v0.4.7 // indirect\n\tgithub.com/Azure/go-autorest/autorest/date v0.3.1 // indirect\n\tgithub.com/Azure/go-autorest/logger v0.2.2 // indirect\n\tgithub.com/Azure/go-autorest/tracing v0.6.1 // indirect\n\tgithub.com/antlr4-go/antlr/v4 v4.13.1 // indirect\n\tgithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect\n\tgithub.com/aws/aws-sdk-go-v2 v1.41.4 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/ecr v1.55.3 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.10 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect\n\tgithub.com/aws/smithy-go v1.24.2 // indirect\n\tgithub.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/blang/semver v3.5.1+incompatible // indirect\n\tgithub.com/blang/semver/v4 v4.0.0 // indirect\n\tgithub.com/cenkalti/backoff/v5 v5.0.3 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect\n\tgithub.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect\n\tgithub.com/coreos/go-oidc/v3 v3.17.0 // indirect\n\tgithub.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect\n\tgithub.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect\n\tgithub.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect\n\tgithub.com/dimchansky/utfbom v1.1.1 // indirect\n\tgithub.com/docker/cli v29.4.0+incompatible // indirect\n\tgithub.com/docker/distribution v2.8.3+incompatible // indirect\n\tgithub.com/docker/docker-credential-helpers v0.9.5 // indirect\n\tgithub.com/dustin/go-humanize v1.0.1 // indirect\n\tgithub.com/emicklei/go-restful/v3 v3.13.0 // indirect\n\tgithub.com/evanphx/json-patch/v5 v5.9.11 // indirect\n\tgithub.com/fatih/color v1.18.0 // indirect\n\tgithub.com/fsnotify/fsnotify v1.9.0 // indirect\n\tgithub.com/fxamacker/cbor/v2 v2.9.0 // indirect\n\tgithub.com/go-chi/chi/v5 v5.2.5 // indirect\n\tgithub.com/go-jose/go-jose/v4 v4.1.4 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-openapi/analysis v0.24.3 // indirect\n\tgithub.com/go-openapi/errors v0.22.7 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.22.5 // indirect\n\tgithub.com/go-openapi/jsonreference v0.21.5 // indirect\n\tgithub.com/go-openapi/loads v0.23.3 // indirect\n\tgithub.com/go-openapi/runtime v0.29.3 // indirect\n\tgithub.com/go-openapi/spec v0.22.4 // indirect\n\tgithub.com/go-openapi/strfmt v0.26.1 // indirect\n\tgithub.com/go-openapi/swag v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/cmdutils v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/conv v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/fileutils v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/jsonname v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/jsonutils v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/loading v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/mangling v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/netutils v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/stringutils v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/typeutils v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/yamlutils v0.25.5 // indirect\n\tgithub.com/go-openapi/validate v0.25.2 // indirect\n\tgithub.com/go-viper/mapstructure/v2 v2.5.0 // indirect\n\tgithub.com/gobuffalo/flect v1.0.3 // indirect\n\tgithub.com/golang-jwt/jwt/v4 v4.5.2 // indirect\n\tgithub.com/golang/snappy v0.0.4 // indirect\n\tgithub.com/google/btree v1.1.3 // indirect\n\tgithub.com/google/cel-go v0.26.1 // indirect\n\tgithub.com/google/certificate-transparency-go v1.3.3 // indirect\n\tgithub.com/google/gnostic-models v0.7.1 // indirect\n\tgithub.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20250225234217-098045d5e61f // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect\n\tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\n\tgithub.com/hashicorp/go-retryablehttp v0.7.8 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/klauspost/compress v1.18.5 // indirect\n\tgithub.com/letsencrypt/boulder v0.20260223.0 // indirect\n\tgithub.com/mattn/go-colorable v0.1.14 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/moby/term v0.5.2 // 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/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect\n\tgithub.com/oklog/ulid/v2 v2.1.1 // indirect\n\tgithub.com/opencontainers/go-digest v1.0.0 // indirect\n\tgithub.com/opencontainers/image-spec v1.1.1 // indirect\n\tgithub.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect\n\tgithub.com/prometheus/client_model v0.6.2 // indirect\n\tgithub.com/prometheus/common v0.67.5 // indirect\n\tgithub.com/prometheus/procfs v0.19.2 // indirect\n\tgithub.com/sassoftware/relic v7.2.1+incompatible // indirect\n\tgithub.com/secure-systems-lab/go-securesystemslib v0.10.0 // indirect\n\tgithub.com/shibumi/go-pathspec v1.3.0 // indirect\n\tgithub.com/sigstore/protobuf-specs v0.5.0 // indirect\n\tgithub.com/sigstore/rekor v1.5.1 // indirect\n\tgithub.com/sigstore/rekor-tiles/v2 v2.2.1 // indirect\n\tgithub.com/sigstore/sigstore-go v1.1.4 // indirect\n\tgithub.com/sigstore/timestamp-authority/v2 v2.0.6 // indirect\n\tgithub.com/spf13/cobra v1.10.2 // indirect\n\tgithub.com/spf13/pflag v1.0.10 // indirect\n\tgithub.com/stoewer/go-strcase v1.3.1 // indirect\n\tgithub.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect\n\tgithub.com/theupdateframework/go-tuf v0.7.0 // indirect\n\tgithub.com/theupdateframework/go-tuf/v2 v2.4.1 // indirect\n\tgithub.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect\n\tgithub.com/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c // indirect\n\tgithub.com/transparency-dev/merkle v0.0.2 // indirect\n\tgithub.com/vbatts/tar-split v0.12.2 // indirect\n\tgithub.com/x448/float16 v0.8.4 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.2.1 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect\n\tgo.opentelemetry.io/otel v1.42.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 // indirect\n\tgo.opentelemetry.io/otel/metric v1.42.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.42.0 // indirect\n\tgo.uber.org/multierr v1.11.0 // indirect\n\tgo.uber.org/zap v1.27.1 // 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/crypto v0.50.0 // indirect\n\tgolang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect\n\tgolang.org/x/mod v0.35.0 // indirect\n\tgolang.org/x/net v0.53.0 // indirect\n\tgolang.org/x/oauth2 v0.36.0 // indirect\n\tgolang.org/x/sync v0.20.0 // indirect\n\tgolang.org/x/sys v0.43.0 // indirect\n\tgolang.org/x/term v0.42.0 // indirect\n\tgolang.org/x/text v0.36.0 // indirect\n\tgolang.org/x/tools v0.44.0 // indirect\n\tgomodules.xyz/jsonpatch/v2 v2.5.0 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5 // 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/code-generator v0.35.0 // indirect\n\tk8s.io/gengo/v2 v2.0.0-20251215205346-5ee0d033ba5b // indirect\n\tk8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 // indirect\n\tsigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // 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.2-0.20260122202528-d9cc6641c482 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=\ncel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=\ncloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=\ncloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=\ncloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=\ncloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=\ncloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=\ncloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=\ncloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=\ncloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=\ncloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=\ncloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=\ncloud.google.com/go/kms v1.26.0 h1:cK9mN2cf+9V63D3H1f6koxTatWy39aTI/hCjz1I+adU=\ncloud.google.com/go/kms v1.26.0/go.mod h1:pHKOdFJm63hxBsiPkYtowZPltu9dW0MWvBa6IA4HM58=\ncloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8=\ncloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk=\ndario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=\ndario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=\nfilippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=\nfilippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=\ngithub.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=\ngithub.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=\ngithub.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg=\ngithub.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d/go.mod h1:XNqJ7hv2kY++g8XEHREpi+JqZo3+0l+CH2egBVN4yqM=\ngithub.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=\ngithub.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=\ngithub.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0 h1:E4MgwLBGeVB5f2MdcIVD3ELVAWpr+WD6MUe1i+tM/PA=\ngithub.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0/go.mod h1:Y2b/1clN4zsAoUd/pgNAQHjLDnTis/6ROkUfyob6psM=\ngithub.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4=\ngithub.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA=\ngithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=\ngithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=\ngithub.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=\ngithub.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=\ngithub.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=\ngithub.com/Azure/go-autorest/autorest v0.11.30 h1:iaZ1RGz/ALZtN5eq4Nr1SOFSlf2E4pDI3Tcsl+dZPVE=\ngithub.com/Azure/go-autorest/autorest v0.11.30/go.mod h1:t1kpPIOpIVX7annvothKvb0stsrXa37i7b+xpmBW8Fs=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.24 h1:BHZfgGsGwdkHDyZdtQRQk1WeUdW0m2WPAwuHZwUi5i4=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.24/go.mod h1:7T1+g0PYFmACYW5LlG2fcoPiPlFHjClyRGL7dRlP5c8=\ngithub.com/Azure/go-autorest/autorest/azure/auth v0.5.13 h1:Ov8avRZi2vmrE2JcXw+tu5K/yB41r7xK9GZDiBF7NdM=\ngithub.com/Azure/go-autorest/autorest/azure/auth v0.5.13/go.mod h1:5BAVfWLWXihP47vYrPuBKKf4cS0bXI+KM9Qx6ETDJYo=\ngithub.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0=\ngithub.com/Azure/go-autorest/autorest/azure/cli v0.4.7 h1:Q9R3utmFg9K1B4OYtAZ7ZUUvIUdzQt7G2MN5Hi/d670=\ngithub.com/Azure/go-autorest/autorest/azure/cli v0.4.7/go.mod h1:bVrAueELJ0CKLBpUHDIvD516TwmHmzqwCpvONWRsw3s=\ngithub.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=\ngithub.com/Azure/go-autorest/autorest/date v0.3.1 h1:o9Z8Jyt+VJJTCZ/UORishuHOusBwolhjokt9s5k8I4w=\ngithub.com/Azure/go-autorest/autorest/date v0.3.1/go.mod h1:Dz/RDmXlfiFFS/eW+b/xMUSFs1tboPVy6UjgADToWDM=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=\ngithub.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=\ngithub.com/Azure/go-autorest/logger v0.2.2 h1:hYqBsEBywrrOSW24kkOCXRcKfKhK76OzLTfF+MYDE2o=\ngithub.com/Azure/go-autorest/logger v0.2.2/go.mod h1:I5fg9K52o+iuydlWfa9T5K6WFos9XYr9dYTFzpqgibw=\ngithub.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=\ngithub.com/Azure/go-autorest/tracing v0.6.1 h1:YUMSrC/CeD1ZnnXcNYU4a/fzsO35u2Fsful9L/2nyR0=\ngithub.com/Azure/go-autorest/tracing v0.6.1/go.mod h1:/3EgjbsjraOqiicERAeu3m7/z0x1TzjQGAwDrJrXGkc=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=\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/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=\ngithub.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=\ngithub.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=\ngithub.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=\ngithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=\ngithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=\ngithub.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=\ngithub.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=\ngithub.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k=\ngithub.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=\ngithub.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0=\ngithub.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g=\ngithub.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8=\ngithub.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE=\ngithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo=\ngithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE=\ngithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc=\ngithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o=\ngithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw=\ngithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc=\ngithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=\ngithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=\ngithub.com/aws/aws-sdk-go-v2/service/ecr v1.55.3 h1:RtGctYMmkTerGClvdY6bHXdtly4FeYw9wz/NPz62LF8=\ngithub.com/aws/aws-sdk-go-v2/service/ecr v1.55.3/go.mod h1:vBfBu24Ka3/5UZtepbTV0gnc9VPLT8ok+0oDDaYAzn4=\ngithub.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.10 h1:1A/sI3LNMi3fhRI5TFLMwwo7ALAALSFVCSGvFlr1Iys=\ngithub.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.10/go.mod h1:Diyyyz0b43X13pdi1mVMqlTwDjOmRbJMvDsqnduUYWM=\ngithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=\ngithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=\ngithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y=\ngithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk=\ngithub.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY=\ngithub.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw=\ngithub.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow=\ngithub.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE=\ngithub.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o=\ngithub.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8=\ngithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU=\ngithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA=\ngithub.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU=\ngithub.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk=\ngithub.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=\ngithub.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=\ngithub.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0 h1:JFWXO6QPihCknDdnL6VaQE57km4ZKheHIGd9YiOGcTo=\ngithub.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0/go.mod h1:046/oLyFlYdAghYQE2yHXi/E//VM5Cf3/dFmA+3CZ0c=\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 v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=\ngithub.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=\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/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=\ngithub.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=\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/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 h1:krfRl01rzPzxSxyLyrChD+U+MzsBXbm0OwYYB67uF+4=\ngithub.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589/go.mod h1:OuDyvmLnMCwa2ep4Jkm6nyA0ocJuZlGyk2gGseVzERM=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=\ngithub.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=\ngithub.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw=\ngithub.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY=\ngithub.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=\ngithub.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=\ngithub.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=\ngithub.com/crossplane/crossplane/apis/v2 v2.0.0-20260424160951-8f231230ebb6 h1:9ki6AJQgBJIcLNjK+scUZp2ZDenuAo18d0JSNOlkY2Y=\ngithub.com/crossplane/crossplane/apis/v2 v2.0.0-20260424160951-8f231230ebb6/go.mod h1:h7KE74Z4TFs1L/FFv3RdsiG9Uax7L56oHpcggSZnONg=\ngithub.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q=\ngithub.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=\ngithub.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ=\ngithub.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs=\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/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc=\ngithub.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE=\ngithub.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc=\ngithub.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1GUYL7P0MlNa00M67axePTq+9nBSGddR8I=\ngithub.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y=\ngithub.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=\ngithub.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=\ngithub.com/docker/cli v29.4.0+incompatible h1:+IjXULMetlvWJiuSI0Nbor36lcJ5BTcVpUmB21KBoVM=\ngithub.com/docker/cli v29.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=\ngithub.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=\ngithub.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=\ngithub.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=\ngithub.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=\ngithub.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=\ngithub.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=\ngithub.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=\ngithub.com/emicklei/go-restful/v3 v3.13.0/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/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=\ngithub.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=\ngithub.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=\ngithub.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=\ngithub.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\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/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=\ngithub.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=\ngithub.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=\ngithub.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=\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-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=\ngithub.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=\ngithub.com/go-openapi/analysis v0.24.3 h1:a1hrvMr8X0Xt69KP5uVTu5jH62DscmDifrLzNglAayk=\ngithub.com/go-openapi/analysis v0.24.3/go.mod h1:Nc+dWJ/FxZbhSow5Yh3ozg5CLJioB+XXT6MdLvJUsUw=\ngithub.com/go-openapi/errors v0.22.7 h1:JLFBGC0Apwdzw3484MmBqspjPbwa2SHvpDm0u5aGhUA=\ngithub.com/go-openapi/errors v0.22.7/go.mod h1://QW6SD9OsWtH6gHllUCddOXDL0tk0ZGNYHwsw4sW3w=\ngithub.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=\ngithub.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=\ngithub.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=\ngithub.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=\ngithub.com/go-openapi/loads v0.23.3 h1:g5Xap1JfwKkUnZdn+S0L3SzBDpcTIYzZ5Qaag0YDkKQ=\ngithub.com/go-openapi/loads v0.23.3/go.mod h1:NOH07zLajXo8y55hom0omlHWDVVvCwBM/S+csCK8LqA=\ngithub.com/go-openapi/runtime v0.29.3 h1:h5twGaEqxtQg40ePiYm9vFFH1q06Czd7Ot6ufdK0w/Y=\ngithub.com/go-openapi/runtime v0.29.3/go.mod h1:8A1W0/L5eyNJvKciqZtvIVQvYO66NlB7INMSZ9bw/oI=\ngithub.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ=\ngithub.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ=\ngithub.com/go-openapi/strfmt v0.26.1 h1:7zGCHji7zSYDC2tCXIusoxYQz/48jAf2q+sF6wXTG+c=\ngithub.com/go-openapi/strfmt v0.26.1/go.mod h1:Zslk5VZPOISLwmWTMBIS7oiVFem1o1EI6zULY8Uer7Y=\ngithub.com/go-openapi/swag v0.25.5 h1:pNkwbUEeGwMtcgxDr+2GBPAk4kT+kJ+AaB+TMKAg+TU=\ngithub.com/go-openapi/swag v0.25.5/go.mod h1:B3RT6l8q7X803JRxa2e59tHOiZlX1t8viplOcs9CwTA=\ngithub.com/go-openapi/swag/cmdutils v0.25.5 h1:yh5hHrpgsw4NwM9KAEtaDTXILYzdXh/I8Whhx9hKj7c=\ngithub.com/go-openapi/swag/cmdutils v0.25.5/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=\ngithub.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g=\ngithub.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k=\ngithub.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk=\ngithub.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc=\ngithub.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo=\ngithub.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU=\ngithub.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo=\ngithub.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4=\ngithub.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U=\ngithub.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo=\ngithub.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU=\ngithub.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g=\ngithub.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw=\ngithub.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY=\ngithub.com/go-openapi/swag/netutils v0.25.5 h1:LZq2Xc2QI8+7838elRAaPCeqJnHODfSyOa7ZGfxDKlU=\ngithub.com/go-openapi/swag/netutils v0.25.5/go.mod h1:lHbtmj4m57APG/8H7ZcMMSWzNqIQcu0RFiXrPUara14=\ngithub.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M=\ngithub.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII=\ngithub.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E=\ngithub.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc=\ngithub.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=\ngithub.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=\ngithub.com/go-openapi/testify/enable/yaml/v2 v2.4.1 h1:NZOrZmIb6PTv5LTFxr5/mKV/FjbUzGE7E6gLz7vFoOQ=\ngithub.com/go-openapi/testify/enable/yaml/v2 v2.4.1/go.mod h1:r7dwsujEHawapMsxA69i+XMGZrQ5tRauhLAjV/sxg3Q=\ngithub.com/go-openapi/testify/v2 v2.4.1 h1:zB34HDKj4tHwyUQHrUkpV0Q0iXQ6dUCOQtIqn8hE6Iw=\ngithub.com/go-openapi/testify/v2 v2.4.1/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=\ngithub.com/go-openapi/validate v0.25.2 h1:12NsfLAwGegqbGWr2CnvT65X/Q2USJipmJ9b7xDJZz0=\ngithub.com/go-openapi/validate v0.25.2/go.mod h1:Pgl1LpPPGFnZ+ys4/hTlDiRYQdI1ocKypgE+8Q8BLfY=\ngithub.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA=\ngithub.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg=\ngithub.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=\ngithub.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=\ngithub.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=\ngithub.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=\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/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=\ngithub.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=\ngithub.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4=\ngithub.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=\ngithub.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=\ngithub.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=\ngithub.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=\ngithub.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=\ngithub.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=\ngithub.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=\ngithub.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=\ngithub.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=\ngithub.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\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.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ=\ngithub.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=\ngithub.com/google/certificate-transparency-go v1.3.3 h1:hq/rSxztSkXN2tx/3jQqF6Xc0O565UQPdHrOWvZwybo=\ngithub.com/google/certificate-transparency-go v1.3.3/go.mod h1:iR17ZgSaXRzSa5qvjFl8TnVD5h8ky2JMVio+dzoKMgA=\ngithub.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c=\ngithub.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/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/go-containerregistry v0.20.7 h1:24VGNpS0IwrOZ2ms2P1QE3Xa5X9p4phx0aUgzYzHW6I=\ngithub.com/google/go-containerregistry v0.20.7/go.mod h1:Lx5LCZQjLH1QBaMPeGwsME9biPeo1lPx6lbGj/UmzgM=\ngithub.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20230919002926-dbcd01c402b2 h1:ChuUQ1y5Vf+Eev+UgEed/ljibTIcWY7mYPtWYLK7fxU=\ngithub.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20230919002926-dbcd01c402b2/go.mod h1:Ek+8PQrShkA7aHEj3/zSW33wU0V/Bx3zW/gFh7l21xY=\ngithub.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20250225234217-098045d5e61f h1:GJRzEBoJv/A/E7JbTekq1Q0jFtAfY7TIxUFAK89Mmic=\ngithub.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20250225234217-098045d5e61f/go.mod h1:ZT74/OE6eosKneM9/LQItNxIMBV6CI5S46EXAnvkTBI=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=\ngithub.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20250602020802-c6617b811d0e h1:FJta/0WsADCe1r9vQjdHbd3KuiLPu7Y9WlyLGwMUNyE=\ngithub.com/google/pprof v0.0.0-20250602020802-c6617b811d0e/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=\ngithub.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=\ngithub.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=\ngithub.com/google/trillian v1.7.2 h1:EPBxc4YWY4Ak8tcuhyFleY+zYlbCDCa4Sn24e1Ka8Js=\ngithub.com/google/trillian v1.7.2/go.mod h1:mfQJW4qRH6/ilABtPYNBerVJAJ/upxHLX81zxNQw05s=\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/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8=\ngithub.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=\ngithub.com/googleapis/gax-go/v2 v2.19.0 h1:fYQaUOiGwll0cGj7jmHT/0nPlcrZDFPrZRhTsoCr8hE=\ngithub.com/googleapis/gax-go/v2 v2.19.0/go.mod h1:w2ROXVdfGEVFXzmlciUU4EdjHgWvB5h2n6x/8XSTTJA=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=\ngithub.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=\ngithub.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=\ngithub.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\ngithub.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=\ngithub.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=\ngithub.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=\ngithub.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=\ngithub.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=\ngithub.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=\ngithub.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=\ngithub.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=\ngithub.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM=\ngithub.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0=\ngithub.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=\ngithub.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=\ngithub.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw=\ngithub.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=\ngithub.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I=\ngithub.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=\ngithub.com/hashicorp/vault/api v1.22.0 h1:+HYFquE35/B74fHoIeXlZIP2YADVboaPjaSicHEZiH0=\ngithub.com/hashicorp/vault/api v1.22.0/go.mod h1:IUZA2cDvr4Ok3+NtK2Oq/r+lJeXkeCrHRmqdyWfpmGM=\ngithub.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM=\ngithub.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/in-toto/attestation v1.1.2 h1:MBFn6lsMq6dptQZJBhalXTcWMb/aJy3V+GX3VYj/V1E=\ngithub.com/in-toto/attestation v1.1.2/go.mod h1:gYFddHMZj3DiQ0b62ltNi1Vj5rC879bTmBbrv9CRHpM=\ngithub.com/in-toto/in-toto-golang v0.11.0 h1:nfidMYBFx+E0lnmX5KUnN2Pdm8zdNKal1ayjJuzzRoA=\ngithub.com/in-toto/in-toto-golang v0.11.0/go.mod h1:u3PjTnwFKjp5a1YCcw8SJg0G+tMeKfVoWsWeFMDCMtw=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=\ngithub.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=\ngithub.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=\ngithub.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=\ngithub.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=\ngithub.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY=\ngithub.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E=\ngithub.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY=\ngithub.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4=\ngithub.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY=\ngithub.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=\ngithub.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs=\ngithub.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI=\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.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=\ngithub.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\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/letsencrypt/boulder v0.20260223.0 h1:xdS2OnJNUasR6TgVIOpqqcvdkOu47+PQQMBk9ThuWBw=\ngithub.com/letsencrypt/boulder v0.20260223.0/go.mod h1:r3aTSA7UZ7dbDfiGK+HLHJz0bWNbHk6YSPiXgzl23sA=\ngithub.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=\ngithub.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE=\ngithub.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=\ngithub.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=\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/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A=\ngithub.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM=\ngithub.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5/zdSLo48H9Iaq0UQGthrhWC6pCxzE=\ngithub.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481/go.mod h1:yKZQO8QE2bHlgozqWDiRVqTFlLQSj30K/6SAK8EeYFw=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=\ngithub.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=\ngithub.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=\ngithub.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=\ngithub.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=\ngithub.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=\ngithub.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=\ngithub.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=\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.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=\ngithub.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=\ngithub.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM=\ngithub.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=\ngithub.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=\ngithub.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=\ngithub.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=\ngithub.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=\ngithub.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\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.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=\ngithub.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=\ngithub.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=\ngithub.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=\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/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=\ngithub.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=\ngithub.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A=\ngithub.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk=\ngithub.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4=\ngithub.com/sassoftware/relic/v7 v7.6.2/go.mod h1:kjmP0IBVkJZ6gXeAu35/KCEfca//+PKM6vTAsyDPY+k=\ngithub.com/secure-systems-lab/go-securesystemslib v0.10.0 h1:l+H5ErcW0PAehBNrBxoGv1jjNpGYdZ9RcheFkB2WI14=\ngithub.com/secure-systems-lab/go-securesystemslib v0.10.0/go.mod h1:MRKONWmRoFzPNQ9USRF9i1mc7MvAVvF1LlW8X5VWDvk=\ngithub.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=\ngithub.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=\ngithub.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=\ngithub.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=\ngithub.com/sigstore/cosign/v3 v3.0.5 h1:c1zPqjU+H4wmirgysC+AkWMg7a7fykyOYF/m+F1150I=\ngithub.com/sigstore/cosign/v3 v3.0.5/go.mod h1:ble1vMvJagCFyTIDkibCq6MIHiWDw00JNYl0f9rB4T4=\ngithub.com/sigstore/protobuf-specs v0.5.0 h1:F8YTI65xOHw70NrvPwJ5PhAzsvTnuJMGLkA4FIkofAY=\ngithub.com/sigstore/protobuf-specs v0.5.0/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc=\ngithub.com/sigstore/rekor v1.5.1 h1:Ca1egHRWRuDvXV4tZu9aXEXc3Gej9FG+HKeapV9OAMQ=\ngithub.com/sigstore/rekor v1.5.1/go.mod h1:gTLDuZuo3SyQCuZvKqwRPA79Qo/2rw39/WtLP/rZjUQ=\ngithub.com/sigstore/rekor-tiles/v2 v2.2.1 h1:UmV1CBQ3SjxxPGpFmwDoOhoIwiKpM2Qm1pU5tPGmvNk=\ngithub.com/sigstore/rekor-tiles/v2 v2.2.1/go.mod h1:z8n6l6oidpaLjjE6rJERuQqY9X38ulnHZCXyL+DEL7U=\ngithub.com/sigstore/sigstore v1.10.5 h1:KqrOjDhNOVY+uOzQFat2FrGLClPPCb3uz8pK3wuI+ow=\ngithub.com/sigstore/sigstore v1.10.5/go.mod h1:k/mcVVXw3I87dYG/iCVTSW2xTrW7vPzxxGic4KqsqXs=\ngithub.com/sigstore/sigstore-go v1.1.4 h1:wTTsgCHOfqiEzVyBYA6mDczGtBkN7cM8mPpjJj5QvMg=\ngithub.com/sigstore/sigstore-go v1.1.4/go.mod h1:2U/mQOT9cjjxrtIUeKDVhL+sHBKsnWddn8URlswdBsg=\ngithub.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.5 h1:aqHRubTITULckG9JAcq2FEhtKkT/RRE8oErfuV3smSI=\ngithub.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.5/go.mod h1:h9eK9QyPqpFskF/ewFkRLtwh4/Q3FLc2/DXbym4IHN8=\ngithub.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.5 h1:+9C6CUkv+J4iT67Lx+H1EGBfAdoAHqXumHadeIj9jA4=\ngithub.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.5/go.mod h1:myZsg7wRiy/vf102g5uUAitYhtXCwepmAGxgHG1VHuE=\ngithub.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.5 h1:BpQx6AhjwIN9LmlO4ypkcMcHiWiepgZQGSw5U69frHU=\ngithub.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.5/go.mod h1:ejMD/17lMJ4HykQRPdj5NNr+OQYIEZto8HjDKghVMOA=\ngithub.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.5 h1:OFwQZgWkB/6J6W5sy3SkXE4pJnhNRnE2cJd8ySXmHpo=\ngithub.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.5/go.mod h1:Ee/enmyxi/RFLVlajbnjgH2wOWQwlJ0wY8qZrk43hEw=\ngithub.com/sigstore/timestamp-authority/v2 v2.0.6 h1:1Vh7/SdmLsVLG6Br6/bisd1SnlicfDm0MJYiA+D7Ppw=\ngithub.com/sigstore/timestamp-authority/v2 v2.0.6/go.mod h1:Nk5ucGBDyH0tXAIMZ0prf6xn8qfTnbJhSq+CDabYcfc=\ngithub.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=\ngithub.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=\ngithub.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=\ngithub.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=\ngithub.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=\ngithub.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=\ngithub.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs=\ngithub.com/stoewer/go-strcase v1.3.1/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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=\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.8.2/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/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=\ngithub.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=\ngithub.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI=\ngithub.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug=\ngithub.com/theupdateframework/go-tuf/v2 v2.4.1 h1:K6ewW064rKZCPkRo1W/CTbTtm/+IB4+coG1iNURAGCw=\ngithub.com/theupdateframework/go-tuf/v2 v2.4.1/go.mod h1:Nex2enPVYDFCklrnbTzl3OVwD7fgIAj0J5++z/rvCj8=\ngithub.com/tink-crypto/tink-go-awskms/v2 v2.1.0 h1:N9UxlsOzu5mttdjhxkDLbzwtEecuXmlxZVo/ds7JKJI=\ngithub.com/tink-crypto/tink-go-awskms/v2 v2.1.0/go.mod h1:PxSp9GlOkKL9rlybW804uspnHuO9nbD98V/fDX4uSis=\ngithub.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0 h1:3B9i6XBXNTRspfkTC0asN5W0K6GhOSgcujNiECNRNb0=\ngithub.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0/go.mod h1:jY5YN2BqD/KSCHM9SqZPIpJNG/u3zwfLXHgws4x2IRw=\ngithub.com/tink-crypto/tink-go-hcvault/v2 v2.4.0 h1:j+S+WKBQ5ya26A5EM/uXoVe+a2IaPQN8KgBJZ22cJ+4=\ngithub.com/tink-crypto/tink-go-hcvault/v2 v2.4.0/go.mod h1:OCKJIujnTzDq7f+73NhVs99oA2c1TR6nsOpuasYM6Yo=\ngithub.com/tink-crypto/tink-go/v2 v2.6.0 h1:+KHNBHhWH33Vn+igZWcsgdEPUxKwBMEe0QC60t388v4=\ngithub.com/tink-crypto/tink-go/v2 v2.6.0/go.mod h1:2WbBA6pfNsAfBwDCggboaHeB2X29wkU8XHtGwh2YIk8=\ngithub.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=\ngithub.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=\ngithub.com/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c h1:5a2XDQ2LiAUV+/RjckMyq9sXudfrPSuCY4FuPC1NyAw=\ngithub.com/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c/go.mod h1:g85IafeFJZLxlzZCDRu4JLpfS7HKzR+Hw9qRh3bVzDI=\ngithub.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4=\ngithub.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A=\ngithub.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=\ngithub.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=\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/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ=\ngithub.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns=\ngithub.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ=\ngithub.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18=\ngithub.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q=\ngithub.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg=\ngithub.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE=\ngithub.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=\ngithub.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU=\ngithub.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngithub.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms=\ngithub.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=\ngo.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=\ngo.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ=\ngo.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=\ngo.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c=\ngo.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=\ngo.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=\ngo.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8=\ngo.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90=\ngo.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8=\ngo.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y=\ngo.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=\ngo.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=\ngo.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=\ngo.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=\ngo.step.sm/crypto v0.77.2 h1:qFjjei+RHc5kP5R7NW9OUWT7SqWIuAOvOkXqg4fNWj8=\ngo.step.sm/crypto v0.77.2/go.mod h1:W0YJb9onM5l78qgkXIJ2Up6grnwW8EtpCKIza/NCg0o=\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/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.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=\ngo.uber.org/zap v1.27.1/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/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=\ngolang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=\ngolang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=\ngolang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=\ngolang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=\ngolang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=\ngolang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=\ngolang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=\ngolang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=\ngolang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0/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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\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.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=\ngolang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=\ngolang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=\ngolang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=\ngolang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=\ngolang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=\ngolang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=\ngolang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=\ngolang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=\ngolang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=\ngolang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=\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=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=\ngomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=\ngomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=\ngonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=\ngonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=\ngoogle.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA=\ngoogle.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA=\ngoogle.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5 h1:JNfk58HZ8lfmXbYK2vx/UvsqIL59TzByCxPIX4TDmsE=\ngoogle.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:x5julN69+ED4PcFk/XWayw35O0lf/nGa4aNgODCmNmw=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5 h1:CogIeEXn4qWYzzQU0QqvYBM8yDF9cFYzDq9ojSpv0Js=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5 h1:aJmi6DVGGIStN9Mobk/tZOOQUBbj0BPjZjjnOdoZKts=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=\ngoogle.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=\ngoogle.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/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=\ngotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk=\ngotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ=\nk8s.io/api v0.35.1 h1:0PO/1FhlK/EQNVK5+txc4FuhQibV25VLSdLMmGpDE/Q=\nk8s.io/api v0.35.1/go.mod h1:28uR9xlXWml9eT0uaGo6y71xK86JBELShLy4wR1XtxM=\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.1 h1:yxO6gV555P1YV0SANtnTjXYfiivaTPvCTKX6w6qdDsU=\nk8s.io/apimachinery v0.35.1/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.1 h1:+eSfZHwuo/I19PaSxqumjqZ9l5XiTEKbIaJ+j1wLcLM=\nk8s.io/client-go v0.35.1/go.mod h1:1p1KxDt3a0ruRfc/pG4qT/3oHmUj1AhSHEcxNSGg+OA=\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/v2 v2.0.0-20251215205346-5ee0d033ba5b h1:0YkdvW3rX2vaBWsqCGZAekxPRwaI5NuYNprOsMNVLns=\nk8s.io/gengo/v2 v2.0.0-20251215205346-5ee0d033ba5b/go.mod h1:yvyl3l9E+UxlqOMUULdKTAYB0rEhsmjr7+2Vb/1pCSo=\nk8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=\nk8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=\nk8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 h1:HhDfevmPS+OalTjQRKbTHppRIz01AWi8s45TMXStgYY=\nk8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=\nk8s.io/utils v0.0.0-20260108192941-914a6e750570 h1:JT4W8lsdrGENg9W+YwwdLJxklIuKWdRm+BC+xt33FOY=\nk8s.io/utils v0.0.0-20260108192941-914a6e750570/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=\nsigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec=\nsigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=\nsigs.k8s.io/controller-runtime v0.23.1 h1:TjJSM80Nf43Mg21+RCy3J70aj/W6KyvDtOlpKf+PupE=\nsigs.k8s.io/controller-runtime v0.23.1/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0=\nsigs.k8s.io/controller-tools v0.20.0 h1:VWZF71pwSQ2lZZCt7hFGJsOfDc5dVG28/IysjjMWXL8=\nsigs.k8s.io/controller-tools v0.20.0/go.mod h1:b4qPmjGU3iZwqn34alUU5tILhNa9+VXK+J3QV0fT/uU=\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.2-0.20260122202528-d9cc6641c482 h1:2WOzJpHUBVrrkDjU4KBT8n5LDcj824eX0I5UKcgeRUs=\nsigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=\nsigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=\nsigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=\nsoftware.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=\nsoftware.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=\n"
  },
  {
    "path": "gomod2nix.toml",
    "content": "schema = 3\n\n[mod]\n  [mod.\"cloud.google.com/go/compute/metadata\"]\n    version = \"v0.9.0\"\n    hash = \"sha256-VFqQwLJKyH1zReR/XtygEHP5UkI01T9BHEL0hvXtauo=\"\n  [mod.\"dario.cat/mergo\"]\n    version = \"v1.0.2\"\n    hash = \"sha256-p6jdiHlLEfZES8vJnDywG4aVzIe16p0CU6iglglIweA=\"\n  [mod.\"github.com/AdaLogics/go-fuzz-headers\"]\n    version = \"v0.0.0-20240806141605-e8a1dd7889d6\"\n    hash = \"sha256-4h0bcF0eg/Y9DnurD91SbGAzLz0DwffJsF//YJGfPJg=\"\n  [mod.\"github.com/Azure/azure-sdk-for-go\"]\n    version = \"v68.0.0+incompatible\"\n    hash = \"sha256-xaa9LgrrLjgbOh/XM1dZMWH/sZeGMb59gK0CTB6JUUI=\"\n  [mod.\"github.com/Azure/go-ansiterm\"]\n    version = \"v0.0.0-20250102033503-faa5f7b0171c\"\n    hash = \"sha256-4WYKJtxjnm3egDAh9ocTR+gy5UUqVoY3knHy9c17XIY=\"\n  [mod.\"github.com/Azure/go-autorest\"]\n    version = \"v14.2.0+incompatible\"\n    hash = \"sha256-dvWOcudtx0NP6U2RDt40hwtELFRdYdLEklRWYterRN0=\"\n  [mod.\"github.com/Azure/go-autorest/autorest\"]\n    version = \"v0.11.30\"\n    hash = \"sha256-CykvDRDHHCyhIZOxbvpT/a0VEhuJmIwKw/MEjS3hmEs=\"\n  [mod.\"github.com/Azure/go-autorest/autorest/adal\"]\n    version = \"v0.9.24\"\n    hash = \"sha256-itnCV0BJlMi5MHFlxePRUA/XPwofDzTksUVh7jcqarE=\"\n  [mod.\"github.com/Azure/go-autorest/autorest/azure/auth\"]\n    version = \"v0.5.13\"\n    hash = \"sha256-s901woJ0T3B+1QUUOMcjz0ops2pXzZQ+x7/XEuC91Ko=\"\n  [mod.\"github.com/Azure/go-autorest/autorest/azure/cli\"]\n    version = \"v0.4.7\"\n    hash = \"sha256-ljC1ag2fX8jLdlgr1wgLx66QdRHYa9VdOu0r9RFDtLo=\"\n  [mod.\"github.com/Azure/go-autorest/autorest/date\"]\n    version = \"v0.3.1\"\n    hash = \"sha256-DqCnDxzYgcAPEpnlHqa+eL3msZvbkYNSMq6ftSEMSQo=\"\n  [mod.\"github.com/Azure/go-autorest/logger\"]\n    version = \"v0.2.2\"\n    hash = \"sha256-fmbHaafgS17KXIXpqqChOF8qqi+lfJHZM4o+i0pmNSs=\"\n  [mod.\"github.com/Azure/go-autorest/tracing\"]\n    version = \"v0.6.1\"\n    hash = \"sha256-nstDZC8Btx78yzqIR4clfu+R93rebUOZalEW1ZaQfIY=\"\n  [mod.\"github.com/Masterminds/semver/v3\"]\n    version = \"v3.4.0\"\n    hash = \"sha256-75kRraVwYVjYLWZvuSlts4Iu28Eh3SpiF0GHc7vCYHI=\"\n  [mod.\"github.com/antlr4-go/antlr/v4\"]\n    version = \"v4.13.1\"\n    hash = \"sha256-beAuxHNRUuhzcSJUh/8ztVf1zCUiaT72fg2Jvx0AuNQ=\"\n  [mod.\"github.com/asaskevich/govalidator\"]\n    version = \"v0.0.0-20230301143203-a9d515a09cc2\"\n    hash = \"sha256-UCENzt1c1tFgsAzK2TNq5s2g0tQMQ5PxFaQKe8hTL/A=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2\"]\n    version = \"v1.41.4\"\n    hash = \"sha256-k9xv4f8YPSzZ1yR3/zuyNDGenZKk0DD4lceL713yXtc=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/config\"]\n    version = \"v1.32.12\"\n    hash = \"sha256-aTkdSRe8KPmVZdsunU8j/hZQLhGw1ckKpLN/ryRBZM0=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/credentials\"]\n    version = \"v1.19.12\"\n    hash = \"sha256-xEIT1ARA9RYrQtLZIus71E6niNHIOVM1J7mUnA5AhJQ=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/feature/ec2/imds\"]\n    version = \"v1.18.20\"\n    hash = \"sha256-dCTpdKZheVCSt+R+NnFOnlS0bCt4gPavlDh15Kl/sMQ=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/internal/configsources\"]\n    version = \"v1.4.20\"\n    hash = \"sha256-aATIk4oLd7aaV66ereBdjINLMDwmIHxu+NNsgKWH1t4=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/internal/endpoints/v2\"]\n    version = \"v2.7.20\"\n    hash = \"sha256-G6266uj64sgfDTJ9V1UY1sQs3UmryB0CFgxzmbjjChY=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/internal/ini\"]\n    version = \"v1.8.6\"\n    hash = \"sha256-oIRPqu99vnGINAWKnCEytpv7N0gRWO7S72tb1r8oxvk=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/service/ecr\"]\n    version = \"v1.55.3\"\n    hash = \"sha256-J9v9A2bMBTPM0K/aHM3TrS0nBkuTNFVQyqtnc1ZwE7w=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/service/ecrpublic\"]\n    version = \"v1.38.10\"\n    hash = \"sha256-uJtfhtkG4pfehKHyc2dsqafvt6fKMnMgMe/uPemJrPY=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding\"]\n    version = \"v1.13.7\"\n    hash = \"sha256-AfYJdpmnW01Bk/jfHATlNU6lddjqcigFkHw/zcT9WO4=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/service/internal/presigned-url\"]\n    version = \"v1.13.20\"\n    hash = \"sha256-a5TifKunIoqKd2uAceYh6F1LvMHMyEQcWvJf0sxKhPM=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/service/signin\"]\n    version = \"v1.0.8\"\n    hash = \"sha256-o4pWg3yMZHxdI94x5Z6qbiRg7gpmzbpJnJWsR1BOc44=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/service/sso\"]\n    version = \"v1.30.13\"\n    hash = \"sha256-V277a0ikm/H0paIeDLtPGEyav2a69Kdb9d5bh+JLAeY=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/service/ssooidc\"]\n    version = \"v1.35.17\"\n    hash = \"sha256-r5V5DoCIR4yzN1Ttg+dIA85GVkWMPgeD6Zu0rWGqNJE=\"\n  [mod.\"github.com/aws/aws-sdk-go-v2/service/sts\"]\n    version = \"v1.41.9\"\n    hash = \"sha256-I15uxeoKxDURsZrEVDzCRtVIu/HE756M1Rt7PPpdZ7c=\"\n  [mod.\"github.com/aws/smithy-go\"]\n    version = \"v1.24.2\"\n    hash = \"sha256-v0y+Lir61fgdCwdVoca5mK+FcGh9OD3cTEwHIfLytcI=\"\n  [mod.\"github.com/awslabs/amazon-ecr-credential-helper/ecr-login\"]\n    version = \"v0.12.0\"\n    hash = \"sha256-PHoHWGX9RVkAQNQF2fz5Hv4JEaGBvLhcuUwlu5IQjU0=\"\n  [mod.\"github.com/beorn7/perks\"]\n    version = \"v1.0.1\"\n    hash = \"sha256-h75GUqfwJKngCJQVE5Ao5wnO3cfKD9lSIteoLp/3xJ4=\"\n  [mod.\"github.com/blang/semver\"]\n    version = \"v3.5.1+incompatible\"\n    hash = \"sha256-vmoIH5J0esVFmLDT2ecwtalvJqRRoLwomysyvlIRmo8=\"\n  [mod.\"github.com/blang/semver/v4\"]\n    version = \"v4.0.0\"\n    hash = \"sha256-dJC22MjnfT5WqJ7x7Tc3Bvpw9tFnBn9HqfWFiM57JVc=\"\n  [mod.\"github.com/cenkalti/backoff/v5\"]\n    version = \"v5.0.3\"\n    hash = \"sha256-bKq43PPD8RM6e7HePxHaO27traqm76bkvHcTVTQ+jeY=\"\n  [mod.\"github.com/cespare/xxhash/v2\"]\n    version = \"v2.3.0\"\n    hash = \"sha256-7hRlwSR+fos1kx4VZmJ/7snR7zHh8ZFKX+qqqqGcQpY=\"\n  [mod.\"github.com/chrismellard/docker-credential-acr-env\"]\n    version = \"v0.0.0-20230304212654-82a0ddb27589\"\n    hash = \"sha256-EWyO62fm/zhWdo4/96bscr3POG/5tKsWXYqp5mTwP0Y=\"\n  [mod.\"github.com/containerd/stargz-snapshotter/estargz\"]\n    version = \"v0.18.2\"\n    hash = \"sha256-6KS9ObQ1tKXkvvKQy1BmxJ59aisDGvEtqhj1Oo54IRY=\"\n  [mod.\"github.com/coreos/go-oidc/v3\"]\n    version = \"v3.17.0\"\n    hash = \"sha256-b9dCq5GN5ac64UG23Rijv1qcmUZNcxb8DJQycAa96EQ=\"\n  [mod.\"github.com/crossplane/crossplane/apis/v2\"]\n    version = \"v2.0.0-20260424160951-8f231230ebb6\"\n    hash = \"sha256-wtcG/nMC4A9nebFxsfSlWZjdupAQ6IjHinFhvFB6KNk=\"\n  [mod.\"github.com/cyberphone/json-canonicalization\"]\n    version = \"v0.0.0-20241213102144-19d51d7fe467\"\n    hash = \"sha256-eqH3UKAZ9eOlZjYdN7nWuJ1hFm2JAP1PVbJInQk6OLw=\"\n  [mod.\"github.com/davecgh/go-spew\"]\n    version = \"v1.1.2-0.20180830191138-d8f796af33cc\"\n    hash = \"sha256-fV9oI51xjHdOmEx6+dlq7Ku2Ag+m/bmbzPo6A4Y74qc=\"\n  [mod.\"github.com/digitorus/pkcs7\"]\n    version = \"v0.0.0-20230818184609-3a137a874352\"\n    hash = \"sha256-zhgLL+kS2vkOhiK3kkI6yMhr71JOYo/uuxDo1dsC2k0=\"\n  [mod.\"github.com/digitorus/timestamp\"]\n    version = \"v0.0.0-20231217203849-220c5c2851b7\"\n    hash = \"sha256-uNkyMBsdbLN1PiDLHAGWUYf6sZ08ENbxpv9RkNtzaW0=\"\n  [mod.\"github.com/dimchansky/utfbom\"]\n    version = \"v1.1.1\"\n    hash = \"sha256-w8KEprK54zJkMat78T6zldjDwvhbc/O8s6pVFzfmg1I=\"\n  [mod.\"github.com/docker/cli\"]\n    version = \"v29.4.0+incompatible\"\n    hash = \"sha256-mUN7Fu9e4ahtUJBUvCHUk+ICFq1d6vs7MoJf0/cw+mA=\"\n  [mod.\"github.com/docker/distribution\"]\n    version = \"v2.8.3+incompatible\"\n    hash = \"sha256-XhRURCGNpJC83QZTtgCxHHFL76HaxIxjt70HwUa847E=\"\n  [mod.\"github.com/docker/docker-credential-helpers\"]\n    version = \"v0.9.5\"\n    hash = \"sha256-7fm66H8bvqjiEssTy/oiAMmQd7T15aVS+EANrw+4H4U=\"\n  [mod.\"github.com/dustin/go-humanize\"]\n    version = \"v1.0.1\"\n    hash = \"sha256-yuvxYYngpfVkUg9yAmG99IUVmADTQA0tMbBXe0Fq0Mc=\"\n  [mod.\"github.com/emicklei/go-restful/v3\"]\n    version = \"v3.13.0\"\n    hash = \"sha256-lB2Z29RDLiVQE5NrsV1s2iHeQ4ciGwNj5OG1zJxwZV8=\"\n  [mod.\"github.com/evanphx/json-patch\"]\n    version = \"v5.9.11+incompatible\"\n    hash = \"sha256-1iyZpBaeBLmNkJ3T4A9fAEXEYB9nk9V02ug4pwl5dy0=\"\n  [mod.\"github.com/evanphx/json-patch/v5\"]\n    version = \"v5.9.11\"\n    hash = \"sha256-DaWzRi5dIr3U7kJlV3Qm1DWoKh5W+FI2BW/ATXT40J4=\"\n  [mod.\"github.com/fatih/color\"]\n    version = \"v1.18.0\"\n    hash = \"sha256-pP5y72FSbi4j/BjyVq/XbAOFjzNjMxZt2R/lFFxGWvY=\"\n  [mod.\"github.com/fsnotify/fsnotify\"]\n    version = \"v1.9.0\"\n    hash = \"sha256-WtpE1N6dpHwEvIub7Xp/CrWm0fd6PX7MKA4PV44rp2g=\"\n  [mod.\"github.com/fxamacker/cbor/v2\"]\n    version = \"v2.9.0\"\n    hash = \"sha256-/IZK76MRCrz9XCiilieH5tKaLnIWyPJhwxDoVKB8dFc=\"\n  [mod.\"github.com/go-chi/chi/v5\"]\n    version = \"v5.2.5\"\n    hash = \"sha256-Y1+17ky94849aqk3iKf30F1u+G6K3nzZzLOBSeqIUow=\"\n  [mod.\"github.com/go-jose/go-jose/v4\"]\n    version = \"v4.1.4\"\n    hash = \"sha256-MKoJKXup1jfwOyN8mHXu1CQ8fvFJTaEf3K2LVtNSRhc=\"\n  [mod.\"github.com/go-logr/logr\"]\n    version = \"v1.4.3\"\n    hash = \"sha256-Nnp/dEVNMxLp3RSPDHZzGbI8BkSNuZMX0I0cjWKXXLA=\"\n  [mod.\"github.com/go-logr/stdr\"]\n    version = \"v1.2.2\"\n    hash = \"sha256-rRweAP7XIb4egtT1f2gkz4sYOu7LDHmcJ5iNsJUd0sE=\"\n  [mod.\"github.com/go-openapi/analysis\"]\n    version = \"v0.24.3\"\n    hash = \"sha256-jBLHbyhrdLwc0x/P3MlUik0xZPV6xOaKAa5aPV77sBY=\"\n  [mod.\"github.com/go-openapi/errors\"]\n    version = \"v0.22.7\"\n    hash = \"sha256-Iy5ieFbpjjbVEQ+UWXIeI8jzN/TjtsOoEX5IB6/v6gc=\"\n  [mod.\"github.com/go-openapi/jsonpointer\"]\n    version = \"v0.22.5\"\n    hash = \"sha256-btK8c3hxbO9mAlvRYuAaNdjaHNYyLvNl/RfNxPsqQuw=\"\n  [mod.\"github.com/go-openapi/jsonreference\"]\n    version = \"v0.21.5\"\n    hash = \"sha256-fBeVESt+JKLthLDTyuABzPV/hOmg4t8dPtwvAs1Ojog=\"\n  [mod.\"github.com/go-openapi/loads\"]\n    version = \"v0.23.3\"\n    hash = \"sha256-KeSh5BVDAkNBw50Yoyi19lEzODOrTcASyjtnpcrcZ10=\"\n  [mod.\"github.com/go-openapi/runtime\"]\n    version = \"v0.29.3\"\n    hash = \"sha256-nid3RStsHKEZgCNPU1+4NkqDsIOAc52JOF68zH3/bC4=\"\n  [mod.\"github.com/go-openapi/spec\"]\n    version = \"v0.22.4\"\n    hash = \"sha256-nwNLmtrjR3w+vFGlBFSq4vM2ZsDQS4RDBQGYmlGv7BY=\"\n  [mod.\"github.com/go-openapi/strfmt\"]\n    version = \"v0.26.1\"\n    hash = \"sha256-HcLytF6mvc2I+ReaKTjL5/LJzKfdwhb0iIpxjjV9+6M=\"\n  [mod.\"github.com/go-openapi/swag\"]\n    version = \"v0.25.5\"\n    hash = \"sha256-ptIgtll6FVI7viEVxM/imWUjgaeLP/oViAarM/EhsWU=\"\n  [mod.\"github.com/go-openapi/swag/cmdutils\"]\n    version = \"v0.25.5\"\n    hash = \"sha256-sEGS7K9gzBuKgkoIiHn5Mgv7+SvPqJ1iFZRsXrso/2M=\"\n  [mod.\"github.com/go-openapi/swag/conv\"]\n    version = \"v0.25.5\"\n    hash = \"sha256-+yLC40AK2pyn62zStk7Q13Bsb4/HDsJUKTTNBSWSTvg=\"\n  [mod.\"github.com/go-openapi/swag/fileutils\"]\n    version = \"v0.25.5\"\n    hash = \"sha256-zYxEpqJuZ97vFLQxfYwegDQhffKhpsDtYF1xhOVxL4c=\"\n  [mod.\"github.com/go-openapi/swag/jsonname\"]\n    version = \"v0.25.5\"\n    hash = \"sha256-ypcI24qrUOd0lbZUJcFByQr07U7WtQvIu/YhuewUWDo=\"\n  [mod.\"github.com/go-openapi/swag/jsonutils\"]\n    version = \"v0.25.5\"\n    hash = \"sha256-e6OOoTIH/zrI/unpNIu3foYEvSWKb7Jvf+6E6/nvpMg=\"\n  [mod.\"github.com/go-openapi/swag/loading\"]\n    version = \"v0.25.5\"\n    hash = \"sha256-gwy+xJkF3PHT5YMYnXgSX9XhuvwwOVpH60QTLsAh6/E=\"\n  [mod.\"github.com/go-openapi/swag/mangling\"]\n    version = \"v0.25.5\"\n    hash = \"sha256-SXSdvYE+wIm95KHRUPYjPEdFU6hc85/7H5rJH7bdTSM=\"\n  [mod.\"github.com/go-openapi/swag/netutils\"]\n    version = \"v0.25.5\"\n    hash = \"sha256-FzjcovD9ZGR/dNyU019KC/CRVn/OJ6XUJ3hS5J4w6go=\"\n  [mod.\"github.com/go-openapi/swag/stringutils\"]\n    version = \"v0.25.5\"\n    hash = \"sha256-Ze2Y056Imqyq6kHPcACuqHt992WbXfC9LDziSCFuO/c=\"\n  [mod.\"github.com/go-openapi/swag/typeutils\"]\n    version = \"v0.25.5\"\n    hash = \"sha256-A1mGLvoaLCT0iORn4tiyKWB8L69dMJzFjBFpt80Xzkg=\"\n  [mod.\"github.com/go-openapi/swag/yamlutils\"]\n    version = \"v0.25.5\"\n    hash = \"sha256-+EumuV+qkhYn08XfR1ngIKMh79Mkj8vItpg0y0spX+c=\"\n  [mod.\"github.com/go-openapi/validate\"]\n    version = \"v0.25.2\"\n    hash = \"sha256-jH7GfH+JyC1tD2Ejz8ioI5U7IKYqQbllU381qSo5D30=\"\n  [mod.\"github.com/go-viper/mapstructure/v2\"]\n    version = \"v2.5.0\"\n    hash = \"sha256-LbrCBANBprVI84M0CWrXc7rriJL5ac5VKbh58LBTw7U=\"\n  [mod.\"github.com/gobuffalo/flect\"]\n    version = \"v1.0.3\"\n    hash = \"sha256-gpA1fe9XTjZ9r+yYCysCgXKo1AmYNuNFwWn7ZQ4Ky1M=\"\n  [mod.\"github.com/golang-jwt/jwt/v4\"]\n    version = \"v4.5.2\"\n    hash = \"sha256-rTSqYEPooi8Uu4aXMW6k9dynOV+URYTGzVmbG3EQ7uo=\"\n  [mod.\"github.com/golang/snappy\"]\n    version = \"v0.0.4\"\n    hash = \"sha256-Umx+5xHAQCN/Gi4HbtMhnDCSPFAXSsjVbXd8n5LhjAA=\"\n  [mod.\"github.com/google/btree\"]\n    version = \"v1.1.3\"\n    hash = \"sha256-/6Us2eNRFi2IIp7p5uPUXLridilAdk4SmZhcTYR0csw=\"\n  [mod.\"github.com/google/cel-go\"]\n    version = \"v0.26.1\"\n    hash = \"sha256-XVL+pNGjmLrQefpB7qj419zWl1qhPVHmVxsP/Ro1AtE=\"\n  [mod.\"github.com/google/certificate-transparency-go\"]\n    version = \"v1.3.3\"\n    hash = \"sha256-CdAOfBmZ7xs51YUxLhg5edjxeCQqE2Kw0jOe+jHgvfA=\"\n  [mod.\"github.com/google/gnostic-models\"]\n    version = \"v0.7.1\"\n    hash = \"sha256-dfSFaYzgD4HrdL6ZsN8V9w0SMzx0WXl38dIy4dnjhhc=\"\n  [mod.\"github.com/google/go-cmp\"]\n    version = \"v0.7.0\"\n    hash = \"sha256-JbxZFBFGCh/Rj5XZ1vG94V2x7c18L8XKB0N9ZD5F2rM=\"\n  [mod.\"github.com/google/go-containerregistry\"]\n    version = \"v0.20.7\"\n    hash = \"sha256-IkvePl7PwjCaHPealmpkxpqeNo7Cn1I/xzHyHV1x18c=\"\n  [mod.\"github.com/google/go-containerregistry/pkg/authn/k8schain\"]\n    version = \"v0.0.0-20230919002926-dbcd01c402b2\"\n    hash = \"sha256-y/xHODMYpIsday3XuwTS8bO5+1CMjgazalC2fijnC6c=\"\n  [mod.\"github.com/google/go-containerregistry/pkg/authn/kubernetes\"]\n    version = \"v0.0.0-20250225234217-098045d5e61f\"\n    hash = \"sha256-UZyDwMt9qQw5XHHDOlTyYMRvG1BiDfBHeZLmoMzunB4=\"\n  [mod.\"github.com/google/uuid\"]\n    version = \"v1.6.0\"\n    hash = \"sha256-VWl9sqUzdOuhW0KzQlv0gwwUQClYkmZwSydHG2sALYw=\"\n  [mod.\"github.com/grpc-ecosystem/grpc-gateway/v2\"]\n    version = \"v2.27.7\"\n    hash = \"sha256-5TAHtwMLAbyk4iRQ574kb+EPEGhcZ0ZsoLQplck7zFA=\"\n  [mod.\"github.com/hashicorp/go-cleanhttp\"]\n    version = \"v0.5.2\"\n    hash = \"sha256-N9GOKYo7tK6XQUFhvhImtL7PZW/mr4C4Manx/yPVvcQ=\"\n  [mod.\"github.com/hashicorp/go-retryablehttp\"]\n    version = \"v0.7.8\"\n    hash = \"sha256-4LZwKaFBbpKi9lSq5y6lOlYHU6WMnQdGNMxTd33rN80=\"\n  [mod.\"github.com/in-toto/attestation\"]\n    version = \"v1.1.2\"\n    hash = \"sha256-BdRbWCnzMCMyZmo8lkovtvGWQq2qCB7S2XBZWClJ6TM=\"\n  [mod.\"github.com/in-toto/in-toto-golang\"]\n    version = \"v0.11.0\"\n    hash = \"sha256-Rcp+UkWKBYomxPpoJqVINL26AhYNd88npXl2ryNqZ+k=\"\n  [mod.\"github.com/inconshreveable/mousetrap\"]\n    version = \"v1.1.0\"\n    hash = \"sha256-XWlYH0c8IcxAwQTnIi6WYqq44nOKUylSWxWO/vi+8pE=\"\n  [mod.\"github.com/jedisct1/go-minisign\"]\n    version = \"v0.0.0-20230811132847-661be99b8267\"\n    hash = \"sha256-tWufMmbfSlJRLsD1/ye5H+9b/uEQnBCQwORLJ1KwRh8=\"\n  [mod.\"github.com/json-iterator/go\"]\n    version = \"v1.1.12\"\n    hash = \"sha256-To8A0h+lbfZ/6zM+2PpRpY3+L6725OPC66lffq6fUoM=\"\n  [mod.\"github.com/klauspost/compress\"]\n    version = \"v1.18.5\"\n    hash = \"sha256-H9b5iFJf4XbEnkGQCjGQAJ3aYhVDiolKrDewTbhuzQo=\"\n  [mod.\"github.com/letsencrypt/boulder\"]\n    version = \"v0.20260223.0\"\n    hash = \"sha256-p/AuDyJr7chBqbXT+LLa3ShKX96aC3SsfzR2ekb2+xM=\"\n  [mod.\"github.com/mattn/go-colorable\"]\n    version = \"v0.1.14\"\n    hash = \"sha256-JC60PjKj7MvhZmUHTZ9p372FV72I9Mxvli3fivTbxuA=\"\n  [mod.\"github.com/mattn/go-isatty\"]\n    version = \"v0.0.20\"\n    hash = \"sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ=\"\n  [mod.\"github.com/mitchellh/go-homedir\"]\n    version = \"v1.1.0\"\n    hash = \"sha256-oduBKXHAQG8X6aqLEpqZHs5DOKe84u6WkBwi4W6cv3k=\"\n  [mod.\"github.com/moby/term\"]\n    version = \"v0.5.2\"\n    hash = \"sha256-/G20jUZKx36ktmPU/nEw/gX7kRTl1Dbu7zvNBYNt4xU=\"\n  [mod.\"github.com/modern-go/concurrent\"]\n    version = \"v0.0.0-20180306012644-bacd9c7ef1dd\"\n    hash = \"sha256-OTySieAgPWR4oJnlohaFTeK1tRaVp/b0d1rYY8xKMzo=\"\n  [mod.\"github.com/modern-go/reflect2\"]\n    version = \"v1.0.3-0.20250322232337-35a7c28c31ee\"\n    hash = \"sha256-0pkWWZRB3lGFyzmlxxrm0KWVQo9HNXNafaUu3k+rE1g=\"\n  [mod.\"github.com/munnerz/goautoneg\"]\n    version = \"v0.0.0-20191010083416-a7dc8b61c822\"\n    hash = \"sha256-79URDDFenmGc9JZu+5AXHToMrtTREHb3BC84b/gym9Q=\"\n  [mod.\"github.com/nozzle/throttler\"]\n    version = \"v0.0.0-20180817012639-2ea982251481\"\n    hash = \"sha256-pufLisYZW//uJXtCkobaU0Etnu+ZPQCqaRzRItx65hk=\"\n  [mod.\"github.com/oklog/ulid/v2\"]\n    version = \"v2.1.1\"\n    hash = \"sha256-kPNLaZMGwGc7ngPCivf/n4Bis219yOkGAaa6mt7+yTY=\"\n  [mod.\"github.com/opencontainers/go-digest\"]\n    version = \"v1.0.0\"\n    hash = \"sha256-cfVDjHyWItmUGZ2dzQhCHgmOmou8v7N+itDkLZVkqkQ=\"\n  [mod.\"github.com/opencontainers/image-spec\"]\n    version = \"v1.1.1\"\n    hash = \"sha256-bxBjtl+6846Ed3QHwdssOrNvlHV6b+Dn17zPISSQGP8=\"\n  [mod.\"github.com/pkg/browser\"]\n    version = \"v0.0.0-20240102092130-5ac0b6a4141c\"\n    hash = \"sha256-9iaSHHpcA1fXVF5f8RlKyo1DSoHx7eGXIC2/4LFaoBY=\"\n  [mod.\"github.com/pkg/errors\"]\n    version = \"v0.9.1\"\n    hash = \"sha256-mNfQtcrQmu3sNg/7IwiieKWOgFQOVVe2yXgKBpe/wZw=\"\n  [mod.\"github.com/pmezard/go-difflib\"]\n    version = \"v1.0.1-0.20181226105442-5d4384ee4fb2\"\n    hash = \"sha256-XA4Oj1gdmdV/F/+8kMI+DBxKPthZ768hbKsO3d9Gx90=\"\n  [mod.\"github.com/prometheus/client_golang\"]\n    version = \"v1.23.2\"\n    hash = \"sha256-3GD4fBFa1tJu8MS4TNP6r2re2eViUE+kWUaieIOQXCg=\"\n  [mod.\"github.com/prometheus/client_model\"]\n    version = \"v0.6.2\"\n    hash = \"sha256-q6Fh6v8iNJN9ypD47LjWmx66YITa3FyRjZMRsuRTFeQ=\"\n  [mod.\"github.com/prometheus/common\"]\n    version = \"v0.67.5\"\n    hash = \"sha256-pDzmYsAANsaIf3W9HxpbgRnZ4BkPhJBBwzKq2E58FRw=\"\n  [mod.\"github.com/prometheus/procfs\"]\n    version = \"v0.19.2\"\n    hash = \"sha256-PJW21pew9v+XA7Miow8JVPct+FPIHmQHphwO+g2kNWA=\"\n  [mod.\"github.com/sassoftware/relic\"]\n    version = \"v7.2.1+incompatible\"\n    hash = \"sha256-vHyTdLRh6OlfoGzVgvx7I0+E6tpE7V43lCQaHD/e8J4=\"\n  [mod.\"github.com/secure-systems-lab/go-securesystemslib\"]\n    version = \"v0.10.0\"\n    hash = \"sha256-KY68WNnb3tgNTi0QWsmirkPfmU0xyaP23QVuSuawtHQ=\"\n  [mod.\"github.com/shibumi/go-pathspec\"]\n    version = \"v1.3.0\"\n    hash = \"sha256-ZHLft/o+xyJrUlaCwnCDqbjkPj6iIxlOuA0fFBuwVvM=\"\n  [mod.\"github.com/sigstore/cosign/v3\"]\n    version = \"v3.0.5\"\n    hash = \"sha256-wN5iAfcBCDTvhbvSar4DBw7w1sxIFWcMKv8qkx07mfo=\"\n  [mod.\"github.com/sigstore/protobuf-specs\"]\n    version = \"v0.5.0\"\n    hash = \"sha256-nImiBItjCQwskGHqYYthBjUfHHxy8VnVwSMWkK6GiNo=\"\n  [mod.\"github.com/sigstore/rekor\"]\n    version = \"v1.5.1\"\n    hash = \"sha256-4+wM/pNyOtFZM9WQDSLubScfbquWmvDRY93vHjIrf9k=\"\n  [mod.\"github.com/sigstore/rekor-tiles/v2\"]\n    version = \"v2.2.1\"\n    hash = \"sha256-mRnRvIp0UE7o5CUJiG8hs5xFJABuEAWWnjvTwz/4cKo=\"\n  [mod.\"github.com/sigstore/sigstore\"]\n    version = \"v1.10.5\"\n    hash = \"sha256-t9oup+yS4jWxEoVbYLjUrI+Hu1XlWe+bu7KVOCIf+aE=\"\n  [mod.\"github.com/sigstore/sigstore-go\"]\n    version = \"v1.1.4\"\n    hash = \"sha256-EsPVloCbJXMXOUKsNU00WQzzR2DfkbmCGYGdYgnH94I=\"\n  [mod.\"github.com/sigstore/timestamp-authority/v2\"]\n    version = \"v2.0.6\"\n    hash = \"sha256-k1LVuwm+cgCotsNxZbGI+c8jmMTI0itBvXc5TGVu27I=\"\n  [mod.\"github.com/sirupsen/logrus\"]\n    version = \"v1.9.4\"\n    hash = \"sha256-ltRvmtM3XTCAFwY0IesfRqYIivyXPPuvkFjL4ARh1wg=\"\n  [mod.\"github.com/spf13/afero\"]\n    version = \"v1.15.0\"\n    hash = \"sha256-LhcezbOqfuBzacytbqck0hNUxi6NbWNhifUc5/9uHQ8=\"\n  [mod.\"github.com/spf13/cobra\"]\n    version = \"v1.10.2\"\n    hash = \"sha256-nbRCTFiDCC2jKK7AHi79n7urYCMP5yDZnWtNVJrDi+k=\"\n  [mod.\"github.com/spf13/pflag\"]\n    version = \"v1.0.10\"\n    hash = \"sha256-uDPnWjHpSrzXr17KEYEA1yAbizfcsfo5AyztY2tS6ZU=\"\n  [mod.\"github.com/stoewer/go-strcase\"]\n    version = \"v1.3.1\"\n    hash = \"sha256-yptboRvbZtX+OEdlTgJnrCT+bvaMgCJp5B9ifEaSfw0=\"\n  [mod.\"github.com/syndtr/goleveldb\"]\n    version = \"v1.0.1-0.20220721030215-126854af5e6d\"\n    hash = \"sha256-z7HzuNVmpAJalsebJ+X7jdXq7BykcOyfzhFT8os+euM=\"\n  [mod.\"github.com/theupdateframework/go-tuf\"]\n    version = \"v0.7.0\"\n    hash = \"sha256-YwQTq6V20iI46KufNAi+1P1qrn0ldZxsFRY7dhXbO1s=\"\n  [mod.\"github.com/theupdateframework/go-tuf/v2\"]\n    version = \"v2.4.1\"\n    hash = \"sha256-v9ULpLPiK+0wBn+36zwA2ci8bX1ugO+qf3+1nd7xI4g=\"\n  [mod.\"github.com/titanous/rocacheck\"]\n    version = \"v0.0.0-20171023193734-afe73141d399\"\n    hash = \"sha256-r5XUB1A/doHNd5pu1cL0J8Jwy5IBtc8gQtG5NmKEYPU=\"\n  [mod.\"github.com/transparency-dev/formats\"]\n    version = \"v0.0.0-20251017110053-404c0d5b696c\"\n    hash = \"sha256-IaDd91Eeh6DasW5UcQaUpYobBwSNJO2nC64rySBs4wI=\"\n  [mod.\"github.com/transparency-dev/merkle\"]\n    version = \"v0.0.2\"\n    hash = \"sha256-4KsqpIqgXlypi1X88PekMRfWJ/Y8tuww6DAuXar2+FY=\"\n  [mod.\"github.com/vbatts/tar-split\"]\n    version = \"v0.12.2\"\n    hash = \"sha256-6gOHl4puCV9T2EWpFpqMCkV9N2PEPSiWbNZNp20q7iM=\"\n  [mod.\"github.com/x448/float16\"]\n    version = \"v0.8.4\"\n    hash = \"sha256-VKzMTMS9pIB/cwe17xPftCSK9Mf4Y6EuBEJlB4by5mE=\"\n  [mod.\"go.opentelemetry.io/auto/sdk\"]\n    version = \"v1.2.1\"\n    hash = \"sha256-73bFYhnxNf4SfeQ52ebnwOWywdQbqc9lWawCcSgofvE=\"\n  [mod.\"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\"]\n    version = \"v0.64.0\"\n    hash = \"sha256-xilqHTTNmvhxDFfTnE8ZytsrYq5gYFD77/glakljCy0=\"\n  [mod.\"go.opentelemetry.io/otel\"]\n    version = \"v1.42.0\"\n    hash = \"sha256-7dck3F+khfda+U8PLSJTVETFl/M/mjXboBOsDCtF5aQ=\"\n  [mod.\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"]\n    version = \"v1.39.0\"\n    hash = \"sha256-7pfSAaoIS1fbtVd9CCx6J4/DHBsmReon6r9Hocb2CCU=\"\n  [mod.\"go.opentelemetry.io/otel/metric\"]\n    version = \"v1.42.0\"\n    hash = \"sha256-OabGeuj05yLHN917j30E0QO8x8fHuZoNeotgfbzIWIA=\"\n  [mod.\"go.opentelemetry.io/otel/trace\"]\n    version = \"v1.42.0\"\n    hash = \"sha256-WowP4UqkV5BDYVlw4pivWf3DPANLgTlVbwzpED371zw=\"\n  [mod.\"go.uber.org/multierr\"]\n    version = \"v1.11.0\"\n    hash = \"sha256-Lb6rHHfR62Ozg2j2JZy3MKOMKdsfzd1IYTR57r3Mhp0=\"\n  [mod.\"go.uber.org/zap\"]\n    version = \"v1.27.1\"\n    hash = \"sha256-bn/MMu7X3GkUuW12Xwn9JYbOJeEu9+yoQtkmO+36xlQ=\"\n  [mod.\"go.yaml.in/yaml/v2\"]\n    version = \"v2.4.3\"\n    hash = \"sha256-WqfrOUQFvfuORgl1yyVOcsEXU/vwWQHkcVWx3vCxvaw=\"\n  [mod.\"go.yaml.in/yaml/v3\"]\n    version = \"v3.0.4\"\n    hash = \"sha256-NkGFiDPoCxbr3LFsI6OCygjjkY0rdmg5ggvVVwpyDQ4=\"\n  [mod.\"golang.org/x/crypto\"]\n    version = \"v0.50.0\"\n    hash = \"sha256-vC1BJT7+3UBWLyEE5n3to0NKhMo6m2HGow2HiFgpQLo=\"\n  [mod.\"golang.org/x/exp\"]\n    version = \"v0.0.0-20260112195511-716be5621a96\"\n    hash = \"sha256-rWqwXzLvvhcI/ZkOQMqCXMKI5FAuHd9YNoKTXujmboA=\"\n  [mod.\"golang.org/x/mod\"]\n    version = \"v0.35.0\"\n    hash = \"sha256-ICEQxokHywOFInDPqoP+go9l1tZSz3roknF5SXPtNV4=\"\n  [mod.\"golang.org/x/net\"]\n    version = \"v0.53.0\"\n    hash = \"sha256-G9gKLmyaf6lIV429NKX+YlL6oUPJwlv+BrG6qGhzvmU=\"\n  [mod.\"golang.org/x/oauth2\"]\n    version = \"v0.36.0\"\n    hash = \"sha256-evS7WkMrpgonmTcqtWFpC5rSKZN8O+vnAhNUs1MS9kw=\"\n  [mod.\"golang.org/x/sync\"]\n    version = \"v0.20.0\"\n    hash = \"sha256-ybcjhCfK6lroUM0yswUvWooW8MOQZBXyiSqoxG6Uy0Y=\"\n  [mod.\"golang.org/x/sys\"]\n    version = \"v0.43.0\"\n    hash = \"sha256-aDQXqSTZES2l/132PBxhZN4ywldpPyfm7LByYCHzzwM=\"\n  [mod.\"golang.org/x/term\"]\n    version = \"v0.42.0\"\n    hash = \"sha256-FCiDvAfq7dgBGQuiDYDFJbj/JPawhrmPF2qdUEftQ1c=\"\n  [mod.\"golang.org/x/text\"]\n    version = \"v0.36.0\"\n    hash = \"sha256-/0t9C6Mc8kYjxweFB0us2lGKo8GovHhBiq5nlMOppC0=\"\n  [mod.\"golang.org/x/time\"]\n    version = \"v0.15.0\"\n    hash = \"sha256-5D24A65wn7k93Jj3+918UKjB9ccmGHPBEqjD2XDB92E=\"\n  [mod.\"golang.org/x/tools\"]\n    version = \"v0.44.0\"\n    hash = \"sha256-xuj5FLtSJsAojLLTLXtPdLAIFNTKoVFbDMuqRXmj2W4=\"\n  [mod.\"gomodules.xyz/jsonpatch/v2\"]\n    version = \"v2.5.0\"\n    hash = \"sha256-L3Xy24GTtcDHmMgc9rlgUm3GrxFO7XQKJhfYIr3li1s=\"\n  [mod.\"google.golang.org/genproto/googleapis/api\"]\n    version = \"v0.0.0-20260316180232-0b37fe3546d5\"\n    hash = \"sha256-OmiDhqoKu+PLEn3hxnYqSmjPfCs6cgObHkky8ENgUOk=\"\n  [mod.\"google.golang.org/genproto/googleapis/rpc\"]\n    version = \"v0.0.0-20260316180232-0b37fe3546d5\"\n    hash = \"sha256-pnGJ+eFKWgDoT8oYp88FEOaafs+WJe3vsZHyZLSj5pU=\"\n  [mod.\"google.golang.org/grpc\"]\n    version = \"v1.79.3\"\n    hash = \"sha256-mO9ZI8ONBMEBTlrwIPeQMfb3C9ru5zPb26P+uG/pIzY=\"\n  [mod.\"google.golang.org/protobuf\"]\n    version = \"v1.36.11\"\n    hash = \"sha256-7W+6jntfI/awWL3JP6yQedxqP5S9o3XvPgJ2XxxsIeE=\"\n  [mod.\"gopkg.in/evanphx/json-patch.v4\"]\n    version = \"v4.13.0\"\n    hash = \"sha256-1iyZpBaeBLmNkJ3T4A9fAEXEYB9nk9V02ug4pwl5dy0=\"\n  [mod.\"gopkg.in/inf.v0\"]\n    version = \"v0.9.1\"\n    hash = \"sha256-z84XlyeWLcoYOvWLxPkPFgLkpjyb2Y4pdeGMyySOZQI=\"\n  [mod.\"gopkg.in/yaml.v2\"]\n    version = \"v2.4.0\"\n    hash = \"sha256-uVEGglIedjOIGZzHW4YwN1VoRSTK8o0eGZqzd+TNdd0=\"\n  [mod.\"gopkg.in/yaml.v3\"]\n    version = \"v3.0.1\"\n    hash = \"sha256-FqL9TKYJ0XkNwJFnq9j0VvJ5ZUU1RvH/52h/f5bkYAU=\"\n  [mod.\"k8s.io/api\"]\n    version = \"v0.35.1\"\n    hash = \"sha256-lOfx98TObjLpO9Xg+PcR7G567x5pY/iiChXFhbpRclQ=\"\n  [mod.\"k8s.io/apiextensions-apiserver\"]\n    version = \"v0.35.0\"\n    hash = \"sha256-RZdGkV4SoCTY022pIzQbeVwaxIYhIpXapLZrD593gh8=\"\n  [mod.\"k8s.io/apimachinery\"]\n    version = \"v0.35.1\"\n    hash = \"sha256-+dplbHUOfaCaD2E9IS4F3lnjSCr/a4LjTgdB9de92Pw=\"\n  [mod.\"k8s.io/client-go\"]\n    version = \"v0.35.1\"\n    hash = \"sha256-QEQ7TLUviAXDbvp2s6tT3HZtXy7pLjj5qSDu89iC9ek=\"\n  [mod.\"k8s.io/code-generator\"]\n    version = \"v0.35.0\"\n    hash = \"sha256-0F8vNVdF/quBPGxOpxBL/GhupnkMoTE8dcZYX57RZKk=\"\n  [mod.\"k8s.io/component-base\"]\n    version = \"v0.35.0\"\n    hash = \"sha256-fIAmKs3/T8oHrXBX3sMWr/D1DGhyCsgM+6ZFdaA8BXU=\"\n  [mod.\"k8s.io/gengo/v2\"]\n    version = \"v2.0.0-20251215205346-5ee0d033ba5b\"\n    hash = \"sha256-FxD4b+cOzKuXsGI4NpsaUK/YTOMxugMGAh2jY3od3p8=\"\n  [mod.\"k8s.io/klog/v2\"]\n    version = \"v2.130.1\"\n    hash = \"sha256-n5vls1o1a0V0KYv+3SULq4q3R2Is15K8iDHhFlsSH4o=\"\n  [mod.\"k8s.io/kube-openapi\"]\n    version = \"v0.0.0-20260127142750-a19766b6e2d4\"\n    hash = \"sha256-NS8NvGTX3Ycoc4JU/jwLgtNlD5OOQ5zk2hzvFFSD/jM=\"\n  [mod.\"k8s.io/utils\"]\n    version = \"v0.0.0-20260108192941-914a6e750570\"\n    hash = \"sha256-eFcd1fZT9M7wc/foeEAUj3jgHJfEY9U3lVqQTkIVJ44=\"\n  [mod.\"sigs.k8s.io/apiserver-network-proxy/konnectivity-client\"]\n    version = \"v0.34.0\"\n    hash = \"sha256-98ScvhhmVxEVAGv9rhk10ceYUadNOuBERtCcdaIb42s=\"\n  [mod.\"sigs.k8s.io/controller-runtime\"]\n    version = \"v0.23.1\"\n    hash = \"sha256-iOaYAJgy/Q1Hi6afs5mLtP8K5J8Cs/MlDoGp8wE1GOY=\"\n  [mod.\"sigs.k8s.io/controller-tools\"]\n    version = \"v0.20.0\"\n    hash = \"sha256-1/v8hCCykTDMjhx4jM84/v+WJlQ8M1whdgpGfgmSRRU=\"\n  [mod.\"sigs.k8s.io/json\"]\n    version = \"v0.0.0-20250730193827-2d320260d730\"\n    hash = \"sha256-y3vUPJYL6oxu/8c0j4vgX6fzqHtVPSCjfyuWkZYf6+I=\"\n  [mod.\"sigs.k8s.io/randfill\"]\n    version = \"v1.0.0\"\n    hash = \"sha256-xldQxDwW84hmlihdSOFfjXyauhxEWV9KmIDLZMTcYNo=\"\n  [mod.\"sigs.k8s.io/structured-merge-diff/v6\"]\n    version = \"v6.3.2-0.20260122202528-d9cc6641c482\"\n    hash = \"sha256-4ZUkeHKvdhsZmFSSZKAL/1GZdPO6735BY6FqRc+8tog=\"\n  [mod.\"sigs.k8s.io/yaml\"]\n    version = \"v1.6.0\"\n    hash = \"sha256-49hg7IVPzwxeovp+HTMiWa/10NMMTSTjAdCmIv6p9dw=\"\n"
  },
  {
    "path": "hack/boilerplate.go.txt",
    "content": "/*\nCopyright 2025 The Crossplane 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"
  },
  {
    "path": "hack/linter-violation.tmpl",
    "content": "`{{violation.rule}}`: {{violation.message}}\n\nRefer to Crossplane's [coding style documentation](https://github.com/crossplane/crossplane/blob/main/CONTRIBUTING.md#coding-style-and-linting) for more information."
  },
  {
    "path": "nix/apps.nix",
    "content": "# Interactive development commands for Crossplane Runtime.\n#\n# Apps run outside the Nix sandbox with full filesystem and network access.\n# They're designed for local development where Go modules are already available.\n#\n# All apps are builder functions that take an attrset of arguments and return a\n# complete app definition ({ type, meta.description, program }). Most use\n# writeShellApplication to create the program. The text block is preprocessed:\n#\n#   ${somePkg}/bin/foo   -> /nix/store/.../bin/foo  (Nix store path)\n#   ''${SOME_VAR}        -> ${SOME_VAR}             (shell variable, escaped)\n#\n# Each app declares its tool dependencies via runtimeInputs, with inheritPath\n# set to false. This ensures apps only use explicitly declared tools.\n{ pkgs }:\n{\n  # Run Go unit tests.\n  test = _: {\n    type = \"app\";\n    meta.description = \"Run unit tests\";\n    program = pkgs.lib.getExe (\n      pkgs.writeShellApplication {\n        name = \"crossplane-runtime-test\";\n        runtimeInputs = [ pkgs.go ];\n        inheritPath = false;\n        text = ''\n          export CGO_ENABLED=0\n          go test ./apis/... ./pkg/... \"$@\"\n        '';\n      }\n    );\n  };\n\n  # Run golangci-lint.\n  lint =\n    {\n      fix ? false,\n    }:\n    {\n      type = \"app\";\n      meta.description = \"Run golangci-lint\" + (if fix then \" with auto-fix\" else \"\");\n      program = pkgs.lib.getExe (\n        pkgs.writeShellApplication {\n          name = \"crossplane-runtime-lint\";\n          runtimeInputs = [\n            pkgs.go\n            pkgs.golangci-lint\n          ];\n          inheritPath = false;\n          text = ''\n            export CGO_ENABLED=0\n            export GOLANGCI_LINT_CACHE=\"''${XDG_CACHE_HOME:-$HOME/.cache}/golangci-lint\"\n            golangci-lint run ${if fix then \"--fix\" else \"\"} \"$@\"\n          '';\n        }\n      );\n    };\n\n  # Run code generation.\n  generate = _: {\n    type = \"app\";\n    meta.description = \"Run code generation\";\n    program = pkgs.lib.getExe (\n      pkgs.writeShellApplication {\n        name = \"crossplane-runtime-generate\";\n        runtimeInputs = [\n          pkgs.coreutils\n          pkgs.go\n          pkgs.buf\n          pkgs.protoc-gen-go\n          pkgs.protoc-gen-go-grpc\n          pkgs.kubernetes-controller-tools\n        ];\n        inheritPath = false;\n        text = ''\n          export CGO_ENABLED=0\n\n          echo \"Running go generate...\"\n          go generate -tags 'generate' ./...\n\n          echo \"Done\"\n        '';\n      }\n    );\n  };\n\n  # Run go mod tidy and regenerate gomod2nix.toml.\n  tidy = _: {\n    type = \"app\";\n    meta.description = \"Run go mod tidy and regenerate gomod2nix.toml\";\n    program = pkgs.lib.getExe (\n      pkgs.writeShellApplication {\n        name = \"crossplane-runtime-tidy\";\n        runtimeInputs = [\n          pkgs.go\n          pkgs.gomod2nix\n        ];\n        inheritPath = false;\n        text = ''\n          export CGO_ENABLED=0\n          echo \"Running go mod tidy...\"\n          go mod tidy\n          echo \"Running go mod verify...\"\n          go mod verify\n          echo \"Regenerating gomod2nix.toml...\"\n          gomod2nix generate\n          echo \"Done\"\n        '';\n      }\n    );\n  };\n}\n"
  },
  {
    "path": "nix/checks.nix",
    "content": "# CI check builders for Crossplane Runtime.\n#\n# Checks run inside the Nix sandbox without network or filesystem access. This\n# makes them fully reproducible but means Go modules must come from gomod2nix.\n#\n# Most checks use buildGoApplication, which sets up the Go environment with\n# modules from gomod2nix.toml. This is different from apps, which run outside\n# the sandbox and can access Go modules normally.\n#\n# All checks are builder functions that take an attrset of arguments and return\n# a derivation. The actual check definitions live in flake.nix.\n{ pkgs, self }:\n{\n  # Run Go unit tests with coverage.\n  test =\n    _:\n    pkgs.buildGoApplication {\n      pname = \"crossplane-runtime-test\";\n      version = \"0.0.0\";\n      src = self;\n      pwd = self;\n      modules = ../gomod2nix.toml;\n\n      CGO_ENABLED = \"0\";\n\n      dontBuild = true;\n\n      checkPhase = ''\n        runHook preCheck\n        export HOME=$TMPDIR\n        go test -covermode=count -coverprofile=coverage.txt ./apis/... ./pkg/...\n        runHook postCheck\n      '';\n\n      installPhase = ''\n        mkdir -p $out\n        cp coverage.txt $out/\n      '';\n    };\n\n  # Run golangci-lint (without --fix, since source is read-only).\n  goLint =\n    _:\n    pkgs.buildGoApplication {\n      pname = \"crossplane-runtime-go-lint\";\n      version = \"0.0.0\";\n      src = self;\n      pwd = self;\n      modules = ../gomod2nix.toml;\n\n      CGO_ENABLED = \"0\";\n\n      nativeBuildInputs = [ pkgs.golangci-lint ];\n\n      dontBuild = true;\n\n      checkPhase = ''\n        runHook preCheck\n        export HOME=$TMPDIR\n        export GOLANGCI_LINT_CACHE=$TMPDIR/.cache/golangci-lint\n        golangci-lint run\n        runHook postCheck\n      '';\n\n      installPhase = ''\n        mkdir -p $out\n        touch $out/.lint-passed\n      '';\n    };\n\n  # Verify generated code matches committed code.\n  generate =\n    _:\n    pkgs.buildGoApplication {\n      pname = \"crossplane-runtime-generate-check\";\n      version = \"0.0.0\";\n      src = self;\n      pwd = self;\n      modules = ../gomod2nix.toml;\n\n      CGO_ENABLED = \"0\";\n\n      nativeBuildInputs = [\n        pkgs.buf\n        pkgs.protoc-gen-go\n        pkgs.protoc-gen-go-grpc\n        pkgs.kubernetes-controller-tools\n      ];\n\n      dontBuild = true;\n\n      checkPhase = ''\n        runHook preCheck\n        export HOME=$TMPDIR\n\n        echo \"Running go generate...\"\n        go generate -tags generate ./...\n\n        echo \"Comparing against committed source...\"\n        if ! diff -rq . ${self} --exclude=vendor > /dev/null 2>&1; then\n          echo \"ERROR: Generated code is out of date. Run 'nix run .#generate' and commit.\"\n          diff -r . ${self} --exclude=vendor || true\n          exit 1\n        fi\n\n        runHook postCheck\n      '';\n\n      installPhase = ''\n        mkdir -p $out\n        touch $out/.generate-passed\n      '';\n    };\n\n  # Run Nix linters (statix, deadnix, nixfmt).\n  nixLint =\n    _:\n    pkgs.runCommand \"crossplane-runtime-nix-lint\"\n      {\n        nativeBuildInputs = [\n          pkgs.statix\n          pkgs.deadnix\n          pkgs.nixfmt-rfc-style\n        ];\n      }\n      ''\n        statix check ${self}\n        deadnix --fail ${self}/flake.nix ${self}/nix\n        nixfmt --check ${self}/flake.nix ${self}/nix/*.nix\n        mkdir -p $out\n        touch $out/.nix-lint-passed\n      '';\n}\n"
  },
  {
    "path": "nix.sh",
    "content": "#!/usr/bin/env bash\n# nix.sh - Run Nix commands via Docker without installing Nix locally.\n#\n# Usage: ./nix.sh <command>\n#\n# Run './nix.sh flake show' for available apps and packages, or see flake.nix.\n# Examples: ./nix.sh run .#test, ./nix.sh build, ./nix.sh develop\n#\n# The first run downloads dependencies into /nix/store (cached in a Docker\n# volume). Subsequent runs reuse the cache. To reset: docker volume rm crossplane-nix\n\nset -e\n\n# When NIX_SH_CONTAINER is set, we're running inside the Docker container.\n# This script re-executes itself inside the container to avoid sh -c quoting.\n\nif [ \"${NIX_SH_CONTAINER:-}\" = \"1\" ]; then\n  # Install tools this entrypoint script needs. It needs rsync to copy build\n  # the build result (cp doesn't work well on MacOS volumes). Installed\n  # packages persist across runs thanks to the crossplane-nix volume.\n  command -v rsync &>/dev/null || nix-env -iA nixpkgs.rsync\n\n  # The container runs as root, but the bind-mounted /crossplane-runtime is\n  # owned by the host user. Git refuses to operate in directories owned by\n  # other users.\n  git config --global --add safe.directory /crossplane-runtime\n\n  # Record the current time. After nix runs, we'll find files newer than this\n  # marker and chown them to the host user.\n  marker=$(mktemp)\n\n  # If result (i.e. the build output) is a directory, remove it so nix build can\n  # create its symlink. We only remove directories, not symlinks (which might be\n  # from a host Nix install).\n  if [ -d result ] && [ ! -L result ]; then\n    rm -rf result\n  fi\n\n  nix \"${@}\"\n\n  # Nix build makes result/ a symlink to a directory in the Nix store. That\n  # directory only exists inside the container, but it creates the symlink in\n  # /crossplane-runtime, which is shared with the host. We use this rsync trick\n  # to make result/ a directory of regular files.\n  if [ -L result ] && readlink result | grep -q '^/nix/store/' && [ -e result ]; then\n    rsync -rL --chmod=u+w result/ result.tmp\n    rm result\n    mv result.tmp result\n  fi\n\n  # Fix ownership of any files nix created or modified. The container runs as\n  # root, so without this, generated files would be root-owned on the host.\n  # Using -newer is surgical - we only chown files touched during this run.\n  find /crossplane-runtime -newer \"${marker}\" -exec chown \"${HOST_UID}:${HOST_GID}\" {} + 2>/dev/null || true\n  rm -f \"${marker}\"\n\n  exit 0\nfi\n\n# When running on the host, launch a Docker container and re-execute this\n# script inside it.\n\n# Nix configuration, equivalent to /etc/nix/nix.conf.\nNIX_CONFIG=\"\n# Flakes are Nix's modern project format - a flake.nix file plus a flake.lock\n# that pins all dependencies. This is still marked 'experimental' but is stable\n# and widely used.\nexperimental-features = nix-command flakes\n\n# Build multiple derivations in parallel. A derivation is Nix's build unit,\n# like a Makefile target. 'auto' uses one job per CPU core.\nmax-jobs = auto\n\n# Sandbox builds to prevent access to undeclared dependencies. Requires --privileged.\nsandbox = true\n\n# Cachix is a binary cache service. Our GitHub Actions CI pushes there, so if CI\n# has recently built the commit you're on Nix will download stuff instead of\n# rebuilding it locally.\nextra-substituters = https://crossplane.cachix.org\nextra-trusted-public-keys = crossplane.cachix.org-1:NJluVUN9TX0rY/zAxHYaT19Y5ik4ELH4uFuxje+62d4=\n\"\n\n# Only allocate a TTY if stdout is a terminal. TTY mode corrupts binary output\n# (e.g., when piping stream-image to docker load). The -i flag keeps stdin open\n# for interactive commands like 'nix develop'.\nINTERACTIVE_FLAGS=\"\"\nif [ -t 1 ]; then\n  INTERACTIVE_FLAGS=\"-it\"\nfi\n\n# Run with --privileged for sandboxed builds.\ndocker run --rm --privileged --cgroupns=host ${INTERACTIVE_FLAGS} \\\n  -v \"$(pwd):/crossplane-runtime\" \\\n  -v \"crossplane-nix:/nix\" \\\n  -w /crossplane-runtime \\\n  -e \"NIX_SH_CONTAINER=1\" \\\n  -e \"NIX_CONFIG=${NIX_CONFIG}\" \\\n  -e \"GOMODCACHE=/nix/go-mod-cache\" \\\n  -e \"GOCACHE=/nix/go-build-cache\" \\\n  -e \"HOST_UID=$(id -u)\" \\\n  -e \"HOST_GID=$(id -g)\" \\\n  -e \"TERM=${TERM:-xterm}\" \\\n  nixos/nix \\\n  /crossplane-runtime/nix.sh \"${@}\"\n"
  },
  {
    "path": "pkg/certificates/certificates.go",
    "content": "/*\nCopyright 2023 The Crossplane 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// Package certificates loads TLS certificates from a given folder.\npackage certificates\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\nconst (\n\terrLoadCert  = \"cannot load certificate\"\n\terrLoadCA    = \"cannot load CA certificate\"\n\terrInvalidCA = \"invalid CA certificate\"\n)\n\n// LoadMTLSConfig loads TLS certificates in the given folder using well-defined filenames for certificates in a Kubernetes environment.\nfunc LoadMTLSConfig(caPath, certPath, keyPath string, isServer bool) (*tls.Config, error) {\n\ttlsCertFilePath := filepath.Clean(certPath)\n\ttlsKeyFilePath := filepath.Clean(keyPath)\n\n\tcertificate, err := tls.LoadX509KeyPair(tlsCertFilePath, tlsKeyFilePath)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, errLoadCert)\n\t}\n\n\tcaCertFilePath := filepath.Clean(caPath)\n\n\tca, err := os.ReadFile(caCertFilePath)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, errLoadCA)\n\t}\n\n\tpool := x509.NewCertPool()\n\tif !pool.AppendCertsFromPEM(ca) {\n\t\treturn nil, errors.New(errInvalidCA)\n\t}\n\n\ttlsConfig := &tls.Config{\n\t\tMinVersion:   tls.VersionTLS12,\n\t\tCertificates: []tls.Certificate{certificate},\n\t}\n\n\tif isServer {\n\t\ttlsConfig.ClientCAs = pool\n\t\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\t} else {\n\t\ttlsConfig.RootCAs = pool\n\t}\n\n\treturn tlsConfig, nil\n}\n"
  },
  {
    "path": "pkg/certificates/certificates_test.go",
    "content": "package certificates\n\nimport (\n\t\"crypto/tls\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nvar (\n\terrNoSuchFile = errors.New(\"open invalid/path/tls.crt: no such file or directory\")\n\terrNoCAFile   = errors.New(\"open test-data/no-ca/ca.crt: no such file or directory\")\n)\n\nconst (\n\tcaCertFileName  = \"ca.crt\"\n\ttlsCertFileName = \"tls.crt\"\n\ttlsKeyFileName  = \"tls.key\"\n)\n\nfunc TestLoad(t *testing.T) {\n\ttype args struct {\n\t\tcertsFolderPath         string\n\t\trequireClientValidation bool\n\t}\n\n\ttype want struct {\n\t\terr error\n\t\tout *tls.Config\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs\n\t\twant\n\t}{\n\t\t\"LoadCertError\": {\n\t\t\treason: \"Should return a proper error if certificates do not exist.\",\n\t\t\targs: args{\n\t\t\t\tcertsFolderPath: \"invalid/path\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errNoSuchFile, errLoadCert),\n\t\t\t\tout: nil,\n\t\t\t},\n\t\t},\n\t\t\"LoadCAError\": {\n\t\t\treason: \"Should return a proper error if CA certificate does not exist.\",\n\t\t\targs: args{\n\t\t\t\tcertsFolderPath: \"test-data/no-ca\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errNoCAFile, errLoadCA),\n\t\t\t\tout: nil,\n\t\t\t},\n\t\t},\n\t\t\"InvalidCAError\": {\n\t\t\treason: \"Should return a proper error if CA certificate is not valid.\",\n\t\t\targs: args{\n\t\t\t\tcertsFolderPath: \"test-data/invalid-certs/\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.New(errInvalidCA),\n\t\t\t\tout: nil,\n\t\t\t},\n\t\t},\n\t\t\"NoError\": {\n\t\t\treason: \"Should not return an error after loading certificates.\",\n\t\t\targs: args{\n\t\t\t\tcertsFolderPath: \"test-data/certs/\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: nil,\n\t\t\t\tout: &tls.Config{},\n\t\t\t},\n\t\t},\n\t\t\"NoErrorWithClientValidation\": {\n\t\t\treason: \"Should not return an error after loading certificates.\",\n\t\t\targs: args{\n\t\t\t\tcertsFolderPath:         \"test-data/certs/\",\n\t\t\t\trequireClientValidation: true,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: nil,\n\t\t\t\tout: &tls.Config{\n\t\t\t\t\tClientAuth: tls.RequireAndVerifyClientCert,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tcertsFolderPath := tc.certsFolderPath\n\t\t\trequireClient := tc.requireClientValidation\n\n\t\t\tcfg, err := LoadMTLSConfig(filepath.Join(certsFolderPath, caCertFileName), filepath.Join(certsFolderPath, tlsCertFileName), filepath.Join(certsFolderPath, tlsKeyFileName), requireClient)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nLoad(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif requireClient {\n\t\t\t\tif diff := cmp.Diff(tc.out.ClientAuth, cfg.ClientAuth); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"\\n%s\\nLoad(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/certificates/test-data/certs/ca.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBejCCASGgAwIBAgIIOGozHYTTZu4wCgYIKoZIzj0EAwIwETEPMA0GA1UEAxMG\nUm9vdENBMCAXDTE5MTIyMzA4NTYzN1oYDzIxMTkxMTI5MDkwMTM3WjARMQ8wDQYD\nVQQDEwZSb290Q0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQmKXRMMLbjn8ur\nDaO/rNa8VXq32FHt7wr8+xXf0OhaCimQHxWmCHXmierP+UWs4TwZ5/NTyHZ8OOCj\nsSEGgA1ao2EwXzAOBgNVHQ8BAf8EBAMCAaYwHQYDVR0lBBYwFAYIKwYBBQUHAwEG\nCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNQ5LeIUMgDmha6m\nHlW5Yte2trnyMAoGCCqGSM49BAMCA0cAMEQCIACPtB0wO8CGBjdANqnHOnREgEqu\nKieHeY3sYL2H+7YfAiAmfLtMe3hPdI3+sDPVZTPDe8HYFher8yWb/DCBZCT1Ww==\n-----END CERTIFICATE-----"
  },
  {
    "path": "pkg/certificates/test-data/certs/tls.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBxDCCAWmgAwIBAgIUVkhaF0okPcEJaKYKJRyTHU+aQMwwCgYIKoZIzj0EAwIw\nETEPMA0GA1UEAxMGUm9vdENBMCAXDTE5MTIyMzA4NTkwMFoYDzIxMTkxMTI5MDg1\nOTAwWjARMQ8wDQYDVQQDEwZjbGllbnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC\nAASyDjp+6zyn0W2MWtX07u3iudcahyLtTD51DzTIdplcT/bezWBWxLnP0JzzGORS\nf/Uf59PjMCbE66fFSNCQpcdlo4GcMIGZMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUE\nFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU\nrRNJVmij3xwiQyNfzKuhcCKnAtAwHwYDVR0jBBgwFoAU1Dkt4hQyAOaFrqYeVbli\n17a2ufIwGgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMAoGCCqGSM49BAMCA0kA\nMEYCIQCpZppRb5t2kjyILMnLhJ/cHKsvXpAWcO8FrDx/VBoP1wIhALtw1B73X2bj\nEPps3Or2UzJNxNroBNRgqIo7XkaKQRe8\n-----END CERTIFICATE-----"
  },
  {
    "path": "pkg/certificates/test-data/certs/tls.key",
    "content": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIDcpnLnAoOvR+q7rEKEY4zEWTicMkPaHJ1iC8lHEy9v8oAoGCCqGSM49\nAwEHoUQDQgAEsg46fus8p9FtjFrV9O7t4rnXGoci7Uw+dQ80yHaZXE/23s1gVsS5\nz9Cc8xjkUn/1H+fT4zAmxOunxUjQkKXHZQ==\n-----END EC PRIVATE KEY-----"
  },
  {
    "path": "pkg/certificates/test-data/invalid-certs/ca.crt",
    "content": "MIIBejCCASGgAwIBAgIIOGozHYTTZu4wCgYIKoZIzj0EAwIwETEPMA0GA1UEAxMG\nUm9vdENBMCAXDTE5MTIyMzA4NTYzN1oYDzIxMTkxMTI5MDkwMTM3WjARMQ8wDQYD\nVQQDEwZSb290Q0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQmKXRMMLbjn8ur\nDaO/rNa8VXq32FHt7wr8+xXf0OhaCimQHxWmCHXmierP+UWs4TwZ5/NTyHZ8OOCj\nsSEGgA1ao2EwXzAOBgNVHQ8BAf8EBAMCAaYwHQYDVR0lBBYwFAYIKwYBBQUHAwEG\nCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNQ5LeIUMgDmha6m\nHlW5Yte2trnyMAoGCCqGSM49BAMCA0cAMEQCIACPtB0wO8CGBjdANqnHOnREgEqu\nKieHeY3sYL2H+7YfAiAmfLtMe3hPdI3+sDPVZTPDe8HYFher8yWb/DCBZCT1Ww==\n"
  },
  {
    "path": "pkg/certificates/test-data/invalid-certs/tls.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBxDCCAWmgAwIBAgIUVkhaF0okPcEJaKYKJRyTHU+aQMwwCgYIKoZIzj0EAwIw\nETEPMA0GA1UEAxMGUm9vdENBMCAXDTE5MTIyMzA4NTkwMFoYDzIxMTkxMTI5MDg1\nOTAwWjARMQ8wDQYDVQQDEwZjbGllbnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC\nAASyDjp+6zyn0W2MWtX07u3iudcahyLtTD51DzTIdplcT/bezWBWxLnP0JzzGORS\nf/Uf59PjMCbE66fFSNCQpcdlo4GcMIGZMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUE\nFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU\nrRNJVmij3xwiQyNfzKuhcCKnAtAwHwYDVR0jBBgwFoAU1Dkt4hQyAOaFrqYeVbli\n17a2ufIwGgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMAoGCCqGSM49BAMCA0kA\nMEYCIQCpZppRb5t2kjyILMnLhJ/cHKsvXpAWcO8FrDx/VBoP1wIhALtw1B73X2bj\nEPps3Or2UzJNxNroBNRgqIo7XkaKQRe8\n-----END CERTIFICATE-----"
  },
  {
    "path": "pkg/certificates/test-data/invalid-certs/tls.key",
    "content": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIDcpnLnAoOvR+q7rEKEY4zEWTicMkPaHJ1iC8lHEy9v8oAoGCCqGSM49\nAwEHoUQDQgAEsg46fus8p9FtjFrV9O7t4rnXGoci7Uw+dQ80yHaZXE/23s1gVsS5\nz9Cc8xjkUn/1H+fT4zAmxOunxUjQkKXHZQ==\n-----END EC PRIVATE KEY-----"
  },
  {
    "path": "pkg/certificates/test-data/no-ca/tls.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBxDCCAWmgAwIBAgIUVkhaF0okPcEJaKYKJRyTHU+aQMwwCgYIKoZIzj0EAwIw\nETEPMA0GA1UEAxMGUm9vdENBMCAXDTE5MTIyMzA4NTkwMFoYDzIxMTkxMTI5MDg1\nOTAwWjARMQ8wDQYDVQQDEwZjbGllbnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC\nAASyDjp+6zyn0W2MWtX07u3iudcahyLtTD51DzTIdplcT/bezWBWxLnP0JzzGORS\nf/Uf59PjMCbE66fFSNCQpcdlo4GcMIGZMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUE\nFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU\nrRNJVmij3xwiQyNfzKuhcCKnAtAwHwYDVR0jBBgwFoAU1Dkt4hQyAOaFrqYeVbli\n17a2ufIwGgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMAoGCCqGSM49BAMCA0kA\nMEYCIQCpZppRb5t2kjyILMnLhJ/cHKsvXpAWcO8FrDx/VBoP1wIhALtw1B73X2bj\nEPps3Or2UzJNxNroBNRgqIo7XkaKQRe8\n-----END CERTIFICATE-----"
  },
  {
    "path": "pkg/certificates/test-data/no-ca/tls.key",
    "content": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIDcpnLnAoOvR+q7rEKEY4zEWTicMkPaHJ1iC8lHEy9v8oAoGCCqGSM49\nAwEHoUQDQgAEsg46fus8p9FtjFrV9O7t4rnXGoci7Uw+dQ80yHaZXE/23s1gVsS5\nz9Cc8xjkUn/1H+fT4zAmxOunxUjQkKXHZQ==\n-----END EC PRIVATE KEY-----"
  },
  {
    "path": "pkg/conditions/manager.go",
    "content": "/*\nCopyright 2025 The Crossplane 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// Package conditions enables consistent interactions with an object's status conditions.\npackage conditions\n\nimport (\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n)\n\n// ObjectWithConditions is the interface definition that allows.\ntype ObjectWithConditions interface {\n\tresource.Object\n\tresource.Conditioned\n}\n\n// Manager is an interface for a stateless factory-like object that produces ConditionSet objects.\ntype Manager interface {\n\t// For returns an implementation of a ConditionSet to operate on a specific ObjectWithConditions.\n\tFor(o ObjectWithConditions) ConditionSet\n}\n\n// ConditionSet holds operations for interacting with an object's conditions.\ntype ConditionSet interface {\n\t// MarkConditions adds or updates the conditions onto the managed resource object. Unlike a \"Set\" method, this also\n\t// can add contextual updates to the condition such as propagating the correct observedGeneration to the conditions\n\t// being changed.\n\tMarkConditions(condition ...xpv2.Condition)\n}\n\n// ObservedGenerationPropagationManager is the top level factor for producing a ConditionSet\n// on behalf of a ObjectWithConditions resource, the ConditionSet is only currently concerned with\n// propagating observedGeneration to conditions that are being updated.\n// observedGenerationPropagationManager implements Manager.\ntype ObservedGenerationPropagationManager struct{}\n\n// For implements Manager.For.\nfunc (m ObservedGenerationPropagationManager) For(o ObjectWithConditions) ConditionSet {\n\treturn &observedGenerationPropagationConditionSet{o: o}\n}\n\n// observedGenerationPropagationConditionSet propagates the meta.generation of the given object\n// to the observedGeneration of any condition being set via the `MarkConditions` method.\ntype observedGenerationPropagationConditionSet struct {\n\to ObjectWithConditions\n}\n\n// MarkConditions implements ConditionSet.MarkConditions.\nfunc (c *observedGenerationPropagationConditionSet) MarkConditions(condition ...xpv2.Condition) {\n\tif c == nil || c.o == nil {\n\t\treturn\n\t}\n\t// Foreach condition we have been sent to mark, update the observed generation.\n\tfor i := range condition {\n\t\tcondition[i].ObservedGeneration = c.o.GetGeneration()\n\t}\n\n\tc.o.SetConditions(condition...)\n}\n"
  },
  {
    "path": "pkg/conditions/manager_test.go",
    "content": "/*\nCopyright 2025 The Crossplane 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\thttp://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 conditions\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\n// Check that conditionsImpl implements ConditionManager.\nvar _ Manager = (*ObservedGenerationPropagationManager)(nil)\n\n// Check that conditionSet implements ConditionSet.\nvar _ ConditionSet = (*observedGenerationPropagationConditionSet)(nil)\n\nfunc TestOGConditionSetMark(t *testing.T) {\n\tmanager := new(ObservedGenerationPropagationManager)\n\n\ttests := map[string]struct {\n\t\treason string\n\t\tstart  []xpv2.Condition\n\t\tmark   []xpv2.Condition\n\t\twant   []xpv2.Condition\n\t}{\n\t\t\"ProvideNoConditions\": {\n\t\t\treason: \"If updating a resource without conditions with no new conditions, conditions should remain empty.\",\n\t\t\tstart:  nil,\n\t\t\tmark:   nil,\n\t\t\twant:   nil,\n\t\t},\n\t\t\"EmptyAppendCondition\": {\n\t\t\treason: \"If starting with a resource without conditions, and we mark a condition, it should propagate to conditions with the correct generation.\",\n\t\t\tstart:  nil,\n\t\t\tmark:   []xpv2.Condition{xpv2.ReconcileSuccess()},\n\t\t\twant:   []xpv2.Condition{xpv2.ReconcileSuccess().WithObservedGeneration(42)},\n\t\t},\n\t\t\"ExistingMarkNothing\": {\n\t\t\treason: \"If the resource has a condition and we update nothing, nothing should change.\",\n\t\t\tstart:  []xpv2.Condition{xpv2.Available().WithObservedGeneration(1)},\n\t\t\tmark:   nil,\n\t\t\twant:   []xpv2.Condition{xpv2.Available().WithObservedGeneration(1)},\n\t\t},\n\t\t\"ExistingUpdated\": {\n\t\t\treason: \"If a resource starts with a condition, and we update it, we should see the observedGeneration be updated\",\n\t\t\tstart:  []xpv2.Condition{xpv2.ReconcileSuccess().WithObservedGeneration(1)},\n\t\t\tmark:   []xpv2.Condition{xpv2.ReconcileSuccess()},\n\t\t\twant:   []xpv2.Condition{xpv2.ReconcileSuccess().WithObservedGeneration(42)},\n\t\t},\n\t\t\"ExistingAppended\": {\n\t\t\treason: \"If a resource has an existing condition and we make another condition, the new condition should merge into the conditions list.\",\n\t\t\tstart:  []xpv2.Condition{xpv2.Available().WithObservedGeneration(1)},\n\t\t\tmark:   []xpv2.Condition{xpv2.ReconcileSuccess()},\n\t\t\twant:   []xpv2.Condition{xpv2.Available().WithObservedGeneration(1), xpv2.ReconcileSuccess().WithObservedGeneration(42)},\n\t\t},\n\t}\n\tfor name, tt := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tut := newManaged(42, tt.start...)\n\t\t\tc := manager.For(ut)\n\t\t\tc.MarkConditions(tt.mark...)\n\n\t\t\tif diff := cmp.Diff(tt.want, ut.Conditions, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", tt.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"ManageNilObject\", func(t *testing.T) {\n\t\tc := manager.For(nil)\n\t\tif c == nil {\n\t\t\tt.Errorf(\"manager.For(nil) = %v, want non-nil\", c)\n\t\t}\n\t\t// Test that Marking on a Manager that has a nil object does not end up panicking.\n\t\tc.MarkConditions(xpv2.ReconcileSuccess())\n\t\t// Success!\n\t})\n}\n\nfunc TestOGManagerFor(t *testing.T) {\n\ttests := map[string]struct {\n\t\treason string\n\t\to      ObjectWithConditions\n\t\twant   ConditionSet\n\t}{\n\t\t\"NilObject\": {\n\t\t\treason: \"Even if an object is nil, the manager should return a non-nil ConditionSet\",\n\t\t\twant:   &observedGenerationPropagationConditionSet{},\n\t\t},\n\t\t\"Object\": {\n\t\t\treason: \"Object propagates into manager.\",\n\t\t\to:      &fake.Managed{},\n\t\t\twant: &observedGenerationPropagationConditionSet{\n\t\t\t\to: &fake.Managed{},\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tt := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tm := &ObservedGenerationPropagationManager{}\n\t\t\tif got := m.For(tt.o); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nFor() = %v, want %v\", tt.reason, got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc newManaged(generation int64, conditions ...xpv2.Condition) *fake.Managed {\n\tmg := &fake.Managed{}\n\tmg.Generation = generation\n\tmg.SetConditions(conditions...)\n\n\treturn mg\n}\n"
  },
  {
    "path": "pkg/controller/gate.go",
    "content": "package controller\n\nimport (\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// A Gate is an interface to allow reconcilers to delay a callback until a set of GVKs are set to true inside the gate.\ntype Gate interface {\n\t// Register to call a callback function when all given GVKs are marked true. If the callback is unblocked, the\n\t// registration is removed.\n\tRegister(callback func(), gvks ...schema.GroupVersionKind)\n\t// Set marks the associated condition to the given value. If the condition is already set as\n\t// that value, then this is a no-op. Returns true if there was an update detected.\n\tSet(gvk schema.GroupVersionKind, ready bool) bool\n}\n"
  },
  {
    "path": "pkg/controller/options.go",
    "content": "/*\nCopyright 2021 The Crossplane 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// Package controller configures controller options.\npackage controller\n\nimport (\n\t\"crypto/tls\"\n\t\"time\"\n\n\t\"sigs.k8s.io/controller-runtime/pkg/controller\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/event\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/feature\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/logging\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/ratelimiter\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/statemetrics\"\n)\n\n// DefaultOptions returns a functional set of options with conservative\n// defaults.\nfunc DefaultOptions() Options {\n\treturn Options{\n\t\tLogger:                  logging.NewNopLogger(),\n\t\tGlobalRateLimiter:       ratelimiter.NewGlobal(1),\n\t\tPollInterval:            1 * time.Minute,\n\t\tMaxConcurrentReconciles: 1,\n\t\tFeatures:                &feature.Flags{},\n\t\tEventFilterFunctions:    []event.FilterFn{},\n\t}\n}\n\n// Options frequently used by most Crossplane controllers.\ntype Options struct {\n\t// The Logger controllers should use.\n\tLogger logging.Logger\n\n\t// The GlobalRateLimiter used by this controller manager. The rate of\n\t// reconciles across all controllers will be subject to this limit.\n\tGlobalRateLimiter ratelimiter.RateLimiter\n\n\t// PollInterval at which each controller should speculatively poll to\n\t// determine whether it has work to do.\n\tPollInterval time.Duration\n\n\t// MaxConcurrentReconciles for each controller.\n\tMaxConcurrentReconciles int\n\n\t// Features that should be enabled.\n\tFeatures *feature.Flags\n\n\t// ESSOptions for External Secret Stores.\n\tESSOptions *ESSOptions\n\n\t// MetricOptions for recording metrics.\n\tMetricOptions *MetricOptions\n\n\t// ChangeLogOptions for recording change logs.\n\tChangeLogOptions *ChangeLogOptions\n\n\t// Gate implements a gated function callback pattern.\n\tGate Gate\n\n\t// EventFilterFunctions used to filter events emitted by the controllers.\n\tEventFilterFunctions []event.FilterFn\n}\n\n// ForControllerRuntime extracts options for controller-runtime.\nfunc (o Options) ForControllerRuntime() controller.Options {\n\trecoverPanic := true\n\n\treturn controller.Options{\n\t\tMaxConcurrentReconciles: o.MaxConcurrentReconciles,\n\t\tRateLimiter:             ratelimiter.NewController(),\n\t\tRecoverPanic:            &recoverPanic,\n\t}\n}\n\n// ESSOptions for External Secret Stores.\ntype ESSOptions struct {\n\tTLSConfig     *tls.Config\n\tTLSSecretName *string\n}\n\n// MetricOptions for recording metrics.\ntype MetricOptions struct {\n\t// PollStateMetricInterval at which each controller should record state\n\tPollStateMetricInterval time.Duration\n\n\t// MetricsRecorder to use for recording metrics.\n\tMRMetrics managed.MetricRecorder\n\n\t// MRStateMetrics to use for recording state metrics.\n\tMRStateMetrics *statemetrics.MRStateMetrics\n}\n\n// ChangeLogOptions for recording changes to managed resources into the change\n// logs.\ntype ChangeLogOptions struct {\n\tChangeLogger managed.ChangeLogger\n}\n"
  },
  {
    "path": "pkg/errors/errors.go",
    "content": "/*\nCopyright 2021 The Crossplane 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// Package errors is a github.com/pkg/errors compatible API for native errors.\n// It includes only the subset of the github.com/pkg/errors API that is used by\n// the Crossplane project.\npackage errors\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\tkerrors \"k8s.io/apimachinery/pkg/util/errors\"\n)\n\n// New returns an error that formats as the given text. Each call to New returns\n// a distinct error value even if the text is identical.\nfunc New(text string) error { return errors.New(text) }\n\n// Is reports whether any error in err's chain matches target.\n//\n// The chain consists of err itself followed by the sequence of errors obtained\n// by repeatedly calling Unwrap.\n//\n// An error is considered to match a target if it is equal to that target or if\n// it implements a method Is(error) bool such that Is(target) returns true.\n//\n// An error type might provide an Is method so it can be treated as equivalent\n// to an existing error. For example, if MyError defines\n//\n//\tfunc (m MyError) Is(target error) bool { return target == fs.ErrExist }\n//\n// then Is(MyError{}, fs.ErrExist) returns true. See syscall.Errno.Is for\n// an example in the standard library.\nfunc Is(err, target error) bool { return errors.Is(err, target) }\n\n// As finds the first error in err's chain that matches target, and if so, sets\n// target to that error value and returns true. Otherwise, it returns false.\n//\n// The chain consists of err itself followed by the sequence of errors obtained\n// by repeatedly calling Unwrap.\n//\n// An error matches target if the error's concrete value is assignable to the\n// value pointed to by target, or if the error has a method As(any) bool\n// such that As(target) returns true. In the latter case, the As method is\n// responsible for setting target.\n//\n// An error type might provide an As method so it can be treated as if it were a\n// different error type.\n//\n// As panics if target is not a non-nil pointer to either a type that implements\n// error, or to any interface type.\nfunc As(err error, target any) bool { return errors.As(err, target) }\n\n// Unwrap returns the result of calling the Unwrap method on err, if err's type\n// contains an Unwrap method returning error. Otherwise, Unwrap returns nil.\nfunc Unwrap(err error) error { return errors.Unwrap(err) }\n\n// Errorf formats according to a format specifier and returns the string as a\n// value that satisfies error.\n//\n// If the format specifier includes a %w verb with an error operand, the\n// returned error will implement an Unwrap method returning the operand. It is\n// invalid to include more than one %w verb or to supply it with an operand that\n// does not implement the error interface. The %w verb is otherwise a synonym\n// for %v.\nfunc Errorf(format string, a ...any) error { return fmt.Errorf(format, a...) }\n\n// WithMessage annotates err with a new message. If err is nil, WithMessage\n// returns nil.\nfunc WithMessage(err error, message string) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"%s: %w\", message, err)\n}\n\n// WithMessagef annotates err with the format specifier. If err is nil,\n// WithMessagef returns nil.\nfunc WithMessagef(err error, format string, args ...any) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"%s: %w\", fmt.Sprintf(format, args...), err)\n}\n\n// Wrap is an alias for WithMessage.\nfunc Wrap(err error, message string) error {\n\treturn WithMessage(err, message)\n}\n\n// Wrapf is an alias for WithMessagef.\nfunc Wrapf(err error, format string, args ...any) error {\n\treturn WithMessagef(err, format, args...)\n}\n\n// Cause calls Unwrap on each error it finds. It returns the first error it\n// finds that does not have an Unwrap method - i.e. the first error that was not\n// the result of a Wrap call, a Wrapf call, or an Errorf call with %w wrapping.\nfunc Cause(err error) error {\n\ttype wrapped interface {\n\t\tUnwrap() error\n\t}\n\n\tfor err != nil {\n\t\tw, ok := err.(wrapped)\n\t\tif !ok {\n\t\t\treturn err\n\t\t}\n\n\t\terr = w.Unwrap()\n\t}\n\n\treturn err\n}\n\n// MultiError is an error that wraps multiple errors.\ntype MultiError interface {\n\terror\n\tUnwrap() []error\n}\n\n// Join returns an error that wraps the given errors. Any nil error values are\n// discarded. Join returns nil if errs contains no non-nil values. The error\n// formats as the concatenation of the strings obtained by calling the Error\n// method of each element of errs and formatting like:\n//\n//\t[first error, second error, third error]\n//\n// Note: aggregating errors should not be the default. Usually, return only the\n// first error, and only aggregate if there is clear value to the user.\nfunc Join(errs ...error) MultiError {\n\terr := kerrors.NewAggregate(errs)\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\treturn multiError{aggregate: err}\n}\n\ntype multiError struct {\n\taggregate kerrors.Aggregate\n}\n\nfunc (m multiError) Error() string {\n\treturn m.aggregate.Error()\n}\n\nfunc (m multiError) Unwrap() []error {\n\treturn m.aggregate.Errors()\n}\n"
  },
  {
    "path": "pkg/errors/errors_test.go",
    "content": "/*\nCopyright 2021 The Crossplane 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 errors\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nfunc TestWrap(t *testing.T) {\n\ttype args struct {\n\t\terr     error\n\t\tmessage string\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"NilError\": {\n\t\t\targs: args{\n\t\t\t\terr:     nil,\n\t\t\t\tmessage: \"very useful context\",\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"NonNilError\": {\n\t\t\targs: args{\n\t\t\t\terr:     New(\"boom\"),\n\t\t\t\tmessage: \"very useful context\",\n\t\t\t},\n\t\t\twant: Errorf(\"very useful context: %w\", New(\"boom\")),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := Wrap(tc.args.err, tc.args.message)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"Wrap(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestWrapf(t *testing.T) {\n\ttype args struct {\n\t\terr     error\n\t\tmessage string\n\t\targs    []any\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"NilError\": {\n\t\t\targs: args{\n\t\t\t\terr:     nil,\n\t\t\t\tmessage: \"very useful context\",\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"NonNilError\": {\n\t\t\targs: args{\n\t\t\t\terr:     New(\"boom\"),\n\t\t\t\tmessage: \"very useful context about %s\",\n\t\t\t\targs:    []any{\"ducks\"},\n\t\t\t},\n\t\t\twant: Errorf(\"very useful context about %s: %w\", \"ducks\", New(\"boom\")),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := Wrapf(tc.args.err, tc.args.message, tc.args.args...)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"Wrapf(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCause(t *testing.T) {\n\tcases := map[string]struct {\n\t\terr  error\n\t\twant error\n\t}{\n\t\t\"NilError\": {\n\t\t\terr:  nil,\n\t\t\twant: nil,\n\t\t},\n\t\t\"BareError\": {\n\t\t\terr:  New(\"boom\"),\n\t\t\twant: New(\"boom\"),\n\t\t},\n\t\t\"WrappedError\": {\n\t\t\terr:  Wrap(Wrap(New(\"boom\"), \"interstitial context\"), \"very important context\"),\n\t\t\twant: New(\"boom\"),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := Cause(tc.err)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"Cause(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/errors/reconcile.go",
    "content": "/*\nCopyright 2023 The Crossplane 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 errors\n\nimport (\n\t\"context\"\n\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n)\n\n// SilentlyRequeueOnConflict returns a requeue result and silently drops the\n// error if it is a Kubernetes conflict error from the optimistic concurrency\n// protocol.\nfunc SilentlyRequeueOnConflict(result reconcile.Result, err error) (reconcile.Result, error) {\n\tif kerrors.IsConflict(Cause(err)) {\n\t\treturn reconcile.Result{Requeue: true}, nil\n\t}\n\n\treturn result, err\n}\n\n// WithSilentRequeueOnConflict wraps a Reconciler and silently drops conflict\n// errors and requeues instead.\nfunc WithSilentRequeueOnConflict(r reconcile.Reconciler) reconcile.Reconciler {\n\treturn &silentlyRequeueOnConflict{Reconciler: r}\n}\n\ntype silentlyRequeueOnConflict struct {\n\treconcile.Reconciler\n}\n\nfunc (r *silentlyRequeueOnConflict) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {\n\tresult, err := r.Reconciler.Reconcile(ctx, req)\n\treturn SilentlyRequeueOnConflict(result, err)\n}\n"
  },
  {
    "path": "pkg/errors/reconcile_test.go",
    "content": "/*\nCopyright 2023 The Crossplane 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 errors\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nfunc TestSilentlyRequeueOnConflict(t *testing.T) {\n\ttype args struct {\n\t\tresult reconcile.Result\n\t\terr    error\n\t}\n\n\ttype want struct {\n\t\tresult reconcile.Result\n\t\terr    error\n\t}\n\n\ttests := []struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t{\n\t\t\treason: \"nil error\",\n\t\t\targs: args{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: time.Second},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: time.Second},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\treason: \"other error\",\n\t\t\targs: args{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: time.Second},\n\t\t\t\terr:    New(\"some other error\"),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: time.Second},\n\t\t\t\terr:    New(\"some other error\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\treason: \"conflict error\",\n\t\t\targs: args{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: time.Second},\n\t\t\t\terr:    kerrors.NewConflict(schema.GroupResource{Group: \"nature\", Resource: \"stones\"}, \"foo\", New(\"nested error\")),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{Requeue: true},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\treason: \"nested conflict error\",\n\t\t\targs: args{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: time.Second},\n\t\t\t\terr: Wrap(\n\t\t\t\t\tkerrors.NewConflict(schema.GroupResource{Group: \"nature\", Resource: \"stones\"}, \"foo\", New(\"nested error\")),\n\t\t\t\t\t\"outer error\"),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{Requeue: true},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.reason, func(t *testing.T) {\n\t\t\tgot, err := SilentlyRequeueOnConflict(tt.args.result, tt.args.err)\n\t\t\tif diff := cmp.Diff(tt.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIgnoreConflict(...): -want error, +got error:\\n%s\", tt.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tt.want.result, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIgnoreConflict(...): -want result, +got result:\\n%s\", tt.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/event/event.go",
    "content": "/*\nCopyright 2019 The Crossplane 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// Package event records Kubernetes events.\npackage event\n\nimport (\n\t\"maps\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/client-go/tools/record\"\n)\n\n// A Type of event.\ntype Type string\n\n// Event types. See below for valid types.\n// https://godoc.org/k8s.io/client-go/tools/record#EventRecorder\nconst (\n\tTypeNormal  Type = \"Normal\"\n\tTypeWarning Type = \"Warning\"\n)\n\n// Reason an event occurred.\ntype Reason string\n\n// An Event relating to a Crossplane resource.\ntype Event struct {\n\tType        Type\n\tReason      Reason\n\tMessage     string\n\tAnnotations map[string]string\n}\n\n// Normal returns a normal, informational event.\nfunc Normal(r Reason, message string, keysAndValues ...string) Event {\n\te := Event{\n\t\tType:        TypeNormal,\n\t\tReason:      r,\n\t\tMessage:     message,\n\t\tAnnotations: map[string]string{},\n\t}\n\tsliceMap(keysAndValues, e.Annotations)\n\n\treturn e\n}\n\n// Warning returns a warning event, typically due to an error.\nfunc Warning(r Reason, err error, keysAndValues ...string) Event {\n\te := Event{\n\t\tType:        TypeWarning,\n\t\tReason:      r,\n\t\tMessage:     err.Error(),\n\t\tAnnotations: map[string]string{},\n\t}\n\tsliceMap(keysAndValues, e.Annotations)\n\n\treturn e\n}\n\n// A Recorder records Kubernetes events.\ntype Recorder interface {\n\tEvent(obj runtime.Object, e Event)\n\tWithAnnotations(keysAndValues ...string) Recorder\n}\n\n// An APIRecorder records Kubernetes events to an API server.\ntype APIRecorder struct {\n\tkube        record.EventRecorder\n\tannotations map[string]string\n\tfilterFns   []FilterFn\n}\n\n// FilterFn is a function used to filter events.\n// It should return false when events should not be sent.\ntype FilterFn func(obj runtime.Object, e Event) bool\n\n// NewAPIRecorder returns an APIRecorder that records Kubernetes events to an\n// APIServer using the supplied EventRecorder.\nfunc NewAPIRecorder(r record.EventRecorder, fns ...FilterFn) *APIRecorder {\n\treturn &APIRecorder{kube: r, annotations: map[string]string{}, filterFns: fns}\n}\n\n// Event records the supplied event.\nfunc (r *APIRecorder) Event(obj runtime.Object, e Event) {\n\tfor _, filter := range r.filterFns {\n\t\tif filter(obj, e) {\n\t\t\treturn\n\t\t}\n\t}\n\n\tr.kube.AnnotatedEventf(obj, r.annotations, string(e.Type), string(e.Reason), \"%s\", e.Message)\n}\n\n// WithAnnotations returns a new *APIRecorder that includes the supplied\n// annotations with all recorded events.\nfunc (r *APIRecorder) WithAnnotations(keysAndValues ...string) Recorder {\n\tar := NewAPIRecorder(r.kube)\n\tmaps.Copy(ar.annotations, r.annotations)\n\n\tsliceMap(keysAndValues, ar.annotations)\n\n\treturn ar\n}\n\nfunc sliceMap(from []string, to map[string]string) {\n\tfor i := 0; i+1 < len(from); i += 2 {\n\t\tk, v := from[i], from[i+1]\n\t\tto[k] = v\n\t}\n}\n\n// A NopRecorder does nothing.\ntype NopRecorder struct{}\n\n// NewNopRecorder returns a Recorder that does nothing.\nfunc NewNopRecorder() *NopRecorder {\n\treturn &NopRecorder{}\n}\n\n// Event does nothing.\nfunc (r *NopRecorder) Event(_ runtime.Object, _ Event) {}\n\n// WithAnnotations does nothing.\nfunc (r *NopRecorder) WithAnnotations(_ ...string) Recorder { return r }\n"
  },
  {
    "path": "pkg/event/event_test.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 event\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nfunc TestSliceMap(t *testing.T) {\n\ttype args struct {\n\t\tfrom []string\n\t\tto   map[string]string\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   map[string]string\n\t}{\n\t\t\"OnePair\": {\n\t\t\treason: \"One key value pair should be added.\",\n\t\t\targs: args{\n\t\t\t\tfrom: []string{\"key\", \"val\"},\n\t\t\t\tto:   map[string]string{},\n\t\t\t},\n\t\t\twant: map[string]string{\"key\": \"val\"},\n\t\t},\n\t\t\"TwoPairs\": {\n\t\t\treason: \"Two key value pairs should be added.\",\n\t\t\targs: args{\n\t\t\t\tfrom: []string{\n\t\t\t\t\t\"key\", \"val\",\n\t\t\t\t\t\"another\", \"value\",\n\t\t\t\t},\n\t\t\t\tto: map[string]string{},\n\t\t\t},\n\t\t\twant: map[string]string{\n\t\t\t\t\"key\":     \"val\",\n\t\t\t\t\"another\": \"value\",\n\t\t\t},\n\t\t},\n\t\t\"NoValue\": {\n\t\t\treason: \"Two key value pairs should be added.\",\n\t\t\targs: args{\n\t\t\t\tfrom: []string{\"key\"},\n\t\t\t\tto:   map[string]string{},\n\t\t\t},\n\t\t\twant: map[string]string{},\n\t\t},\n\t\t\"ExtraneousKey\": {\n\t\t\treason: \"One key value pair should be added.\",\n\t\t\targs: args{\n\t\t\t\tfrom: []string{\n\t\t\t\t\t\"key\", \"val\",\n\t\t\t\t\t\"extraneous\",\n\t\t\t\t},\n\t\t\t\tto: map[string]string{},\n\t\t\t},\n\t\t\twant: map[string]string{\"key\": \"val\"},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tsliceMap(tc.args.from, tc.args.to)\n\n\t\t\tif diff := cmp.Diff(tc.want, tc.args.to); diff != \"\" {\n\t\t\t\tt.Errorf(\"%s\\nsliceMap(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/feature/feature.go",
    "content": "/*\nCopyright 2021 The Crossplane 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// Package feature contains utilities for managing Crossplane features.\npackage feature\n\nimport (\n\t\"sync\"\n)\n\n// A Flag enables a particular feature.\ntype Flag string\n\n// Flags that are enabled. The zero value - i.e. &feature.Flags{} - is usable.\ntype Flags struct {\n\tm       sync.RWMutex\n\tenabled map[Flag]bool\n}\n\n// Enable a feature flag.\nfunc (fs *Flags) Enable(f Flag) {\n\tfs.m.Lock()\n\n\tif fs.enabled == nil {\n\t\tfs.enabled = make(map[Flag]bool)\n\t}\n\n\tfs.enabled[f] = true\n\tfs.m.Unlock()\n}\n\n// Enabled returns true if the supplied feature flag is enabled.\nfunc (fs *Flags) Enabled(f Flag) bool {\n\tif fs == nil {\n\t\treturn false\n\t}\n\n\tfs.m.RLock()\n\tdefer fs.m.RUnlock()\n\n\treturn fs.enabled[f]\n}\n"
  },
  {
    "path": "pkg/feature/feature_test.go",
    "content": "/*\nCopyright 2021 The Crossplane 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 feature\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nfunc TestEnable(t *testing.T) {\n\tvar cool Flag = \"cool\"\n\n\tt.Run(\"EnableMutatesZeroValue\", func(t *testing.T) {\n\t\tf := &Flags{}\n\t\tf.Enable(cool)\n\n\t\twant := true\n\t\tgot := f.Enabled(cool)\n\n\t\tif diff := cmp.Diff(want, got); diff != \"\" {\n\t\t\tt.Errorf(\"f.Enabled(...): -want, +got:\\n%s\", diff)\n\t\t}\n\t})\n\n\tt.Run(\"EnabledOnEmptyFlagsReturnsFalse\", func(t *testing.T) {\n\t\tf := &Flags{}\n\n\t\twant := false\n\t\tgot := f.Enabled(cool)\n\n\t\tif diff := cmp.Diff(want, got); diff != \"\" {\n\t\t\tt.Errorf(\"f.Enabled(...): -want, +got:\\n%s\", diff)\n\t\t}\n\t})\n\n\tt.Run(\"EnabledOnNilReturnsFalse\", func(t *testing.T) {\n\t\tvar f *Flags\n\n\t\twant := false\n\t\tgot := f.Enabled(cool)\n\n\t\tif diff := cmp.Diff(want, got); diff != \"\" {\n\t\t\tt.Errorf(\"f.Enabled(...): -want, +got:\\n%s\", diff)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pkg/feature/features.go",
    "content": "/*\nCopyright 2023 The Crossplane 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 feature\n\n// EnableBetaManagementPolicies enables beta support for\n// Management Policies. See the below design for more details.\n// https://github.com/crossplane/crossplane/pull/3531\nconst EnableBetaManagementPolicies Flag = \"EnableBetaManagementPolicies\"\n\n// EnableAlphaChangeLogs enables alpha support for capturing change logs during\n// reconciliation. See the following design for more details:\n// https://github.com/crossplane/crossplane/pull/5822\nconst EnableAlphaChangeLogs Flag = \"EnableAlphaChangeLogs\"\n"
  },
  {
    "path": "pkg/fieldpath/fieldpath.go",
    "content": "/*\nCopyright 2019 The Crossplane 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// Package fieldpath provides utilities for working with field paths.\n//\n// Field paths reference a field within a Kubernetes object via a simple string.\n// API conventions describe the syntax as \"standard JavaScript syntax for\n// accessing that field, assuming the JSON object was transformed into a\n// JavaScript object, without the leading dot, such as metadata.name\".\n//\n// Valid examples:\n//\n// * metadata.name\n// * spec.containers[0].name\n// * data[.config.yml]\n// * metadata.annotations['crossplane.io/external-name']\n// * spec.items[0][8]\n// * apiVersion\n// * [42]\n//\n// Invalid examples:\n//\n// * .metadata.name - Leading period.\n// * metadata..name - Double period.\n// * metadata.name. - Trailing period.\n// * spec.containers[] - Empty brackets.\n// * spec.containers.[0].name - Period before open bracket.\n//\n// https://github.com/kubernetes/community/blob/61f3d0/contributors/devel/sig-architecture/api-conventions.md#selecting-fields\npackage fieldpath\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode/utf8\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\n// A SegmentType within a field path; either a field within an object, or an\n// index within an array.\ntype SegmentType int\n\n// Segment types.\nconst (\n\t_ SegmentType = iota\n\tSegmentField\n\tSegmentIndex\n)\n\n// A Segment of a field path.\ntype Segment struct {\n\tType  SegmentType\n\tField string\n\tIndex uint\n}\n\n// Segments of a field path.\ntype Segments []Segment\n\nfunc (sg Segments) String() string {\n\tvar b strings.Builder\n\n\tfor _, s := range sg {\n\t\tswitch s.Type {\n\t\tcase SegmentField:\n\t\t\tif s.Field == wildcard || strings.ContainsRune(s.Field, period) {\n\t\t\t\tb.WriteString(fmt.Sprintf(\"[%s]\", s.Field))\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tb.WriteString(fmt.Sprintf(\".%s\", s.Field))\n\t\tcase SegmentIndex:\n\t\t\tb.WriteString(fmt.Sprintf(\"[%d]\", s.Index))\n\t\t}\n\t}\n\n\treturn strings.TrimPrefix(b.String(), \".\")\n}\n\n// FieldOrIndex produces a new segment from the supplied string. The segment is\n// considered to be an array index if the string can be interpreted as an\n// unsigned 32 bit integer. Anything else is interpreted as an object field\n// name.\nfunc FieldOrIndex(s string) Segment {\n\t// Attempt to parse the segment as an unsigned integer. If the integer is\n\t// larger than 2^32 (the limit for most JSON arrays) we presume it's too big\n\t// to be an array index, and is thus a field name.\n\tif i, err := strconv.ParseUint(s, 10, 32); err == nil {\n\t\treturn Segment{Type: SegmentIndex, Index: uint(i)}\n\t}\n\n\t// If the segment is not a valid unsigned integer we presume it's\n\t// a string field name.\n\treturn Field(s)\n}\n\n// Field produces a new segment from the supplied string. The segment is always\n// considered to be an object field name.\nfunc Field(s string) Segment {\n\treturn Segment{Type: SegmentField, Field: strings.Trim(s, \"'\\\"\")}\n}\n\n// Parse the supplied path into a slice of Segments.\nfunc Parse(path string) (Segments, error) {\n\tl := &lexer{input: path, items: make(chan item)}\n\tgo l.run()\n\n\tsegments := make(Segments, 0, 1)\n\n\tfor i := range l.items {\n\t\tswitch i.typ { //nolint:exhaustive // We're only worried about names, not separators.\n\t\tcase itemField:\n\t\t\tsegments = append(segments, Field(i.val))\n\t\tcase itemFieldOrIndex:\n\t\t\tsegments = append(segments, FieldOrIndex(i.val))\n\t\tcase itemError:\n\t\t\treturn nil, errors.Errorf(\"%s at position %d\", i.val, i.pos)\n\t\t}\n\t}\n\n\treturn segments, nil\n}\n\nconst (\n\tperiod       = '.'\n\tleftBracket  = '['\n\trightBracket = ']'\n\n\twildcard = \"*\"\n)\n\ntype itemType int\n\nconst (\n\titemError itemType = iota\n\titemPeriod\n\titemLeftBracket\n\titemRightBracket\n\titemField\n\titemFieldOrIndex\n\titemEOL\n)\n\ntype item struct {\n\ttyp itemType\n\tpos int\n\tval string\n}\n\ntype stateFn func(*lexer) stateFn\n\n// A simplified version of the text/template lexer.\n// https://github.com/golang/go/blob/6396bc9d/src/text/template/parse/lex.go#L108\ntype lexer struct {\n\tinput string\n\tpos   int\n\tstart int\n\titems chan item\n}\n\nfunc (l *lexer) run() {\n\tfor state := lexField; state != nil; {\n\t\tstate = state(l)\n\t}\n\n\tclose(l.items)\n}\n\nfunc (l *lexer) emit(t itemType) {\n\t// Don't emit empty values.\n\tif l.pos <= l.start {\n\t\treturn\n\t}\n\n\tl.items <- item{typ: t, pos: l.start, val: l.input[l.start:l.pos]}\n\n\tl.start = l.pos\n}\n\nfunc (l *lexer) errorf(pos int, format string, args ...any) stateFn {\n\tl.items <- item{typ: itemError, pos: pos, val: fmt.Sprintf(format, args...)}\n\treturn nil\n}\n\nfunc lexField(l *lexer) stateFn {\n\tfor i, r := range l.input[l.pos:] {\n\t\tswitch r {\n\t\t// A right bracket may not appear in an object field name.\n\t\tcase rightBracket:\n\t\t\treturn l.errorf(l.pos+i, \"unexpected %q\", rightBracket)\n\n\t\t// A left bracket indicates the end of the field name.\n\t\tcase leftBracket:\n\t\t\tl.pos += i\n\t\t\tl.emit(itemField)\n\n\t\t\treturn lexLeftBracket\n\n\t\t// A period indicates the end of the field name.\n\t\tcase period:\n\t\t\tl.pos += i\n\t\t\tl.emit(itemField)\n\n\t\t\treturn lexPeriod\n\t\t}\n\t}\n\n\t// The end of the input indicates the end of the field name.\n\tl.pos = len(l.input)\n\tl.emit(itemField)\n\tl.emit(itemEOL)\n\n\treturn nil\n}\n\nfunc lexPeriod(l *lexer) stateFn {\n\t// A period may not appear at the beginning or the end of the input.\n\tif l.pos == 0 || l.pos == len(l.input)-1 {\n\t\treturn l.errorf(l.pos, \"unexpected %q\", period)\n\t}\n\n\tl.pos += utf8.RuneLen(period)\n\tl.emit(itemPeriod)\n\n\t// A period may only be followed by a field name. We defer checking for\n\t// right brackets to lexField, where they are invalid.\n\tr, _ := utf8.DecodeRuneInString(l.input[l.pos:])\n\tif r == period {\n\t\treturn l.errorf(l.pos, \"unexpected %q\", period)\n\t}\n\n\tif r == leftBracket {\n\t\treturn l.errorf(l.pos, \"unexpected %q\", leftBracket)\n\t}\n\n\treturn lexField\n}\n\nfunc lexLeftBracket(l *lexer) stateFn {\n\t// A right bracket must appear before the input ends.\n\tif !strings.ContainsRune(l.input[l.pos:], rightBracket) {\n\t\treturn l.errorf(l.pos, \"unterminated %q\", leftBracket)\n\t}\n\n\tl.pos += utf8.RuneLen(leftBracket)\n\tl.emit(itemLeftBracket)\n\n\treturn lexFieldOrIndex\n}\n\n// Strings between brackets may be either a field name or an array index.\n// Periods have no special meaning in this context.\nfunc lexFieldOrIndex(l *lexer) stateFn {\n\t// We know a right bracket exists before EOL thanks to the preceding\n\t// lexLeftBracket.\n\trbi := strings.IndexRune(l.input[l.pos:], rightBracket)\n\n\t// A right bracket may not immediately follow a left bracket.\n\tif rbi == 0 {\n\t\treturn l.errorf(l.pos, \"unexpected %q\", rightBracket)\n\t}\n\n\t// A left bracket may not appear before the next right bracket.\n\tif lbi := strings.IndexRune(l.input[l.pos:l.pos+rbi], leftBracket); lbi > -1 {\n\t\treturn l.errorf(l.pos+lbi, \"unexpected %q\", leftBracket)\n\t}\n\n\t// Periods are not considered field separators when we're inside brackets.\n\tl.pos += rbi\n\tl.emit(itemFieldOrIndex)\n\n\treturn lexRightBracket\n}\n\nfunc lexRightBracket(l *lexer) stateFn {\n\tl.pos += utf8.RuneLen(rightBracket)\n\tl.emit(itemRightBracket)\n\n\treturn lexField\n}\n"
  },
  {
    "path": "pkg/fieldpath/fieldpath_test.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 fieldpath\n\nimport (\n\t\"math\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nfunc TestSegments(t *testing.T) {\n\tcases := map[string]struct {\n\t\ts    Segments\n\t\twant string\n\t}{\n\t\t\"SingleField\": {\n\t\t\ts:    Segments{Field(\"spec\")},\n\t\t\twant: \"spec\",\n\t\t},\n\t\t\"SingleIndex\": {\n\t\t\ts:    Segments{FieldOrIndex(\"0\")},\n\t\t\twant: \"[0]\",\n\t\t},\n\t\t\"FieldsAndIndex\": {\n\t\t\ts: Segments{\n\t\t\t\tField(\"spec\"),\n\t\t\t\tField(\"containers\"),\n\t\t\t\tFieldOrIndex(\"0\"),\n\t\t\t\tField(\"name\"),\n\t\t\t},\n\t\t\twant: \"spec.containers[0].name\",\n\t\t},\n\t\t\"PeriodsInField\": {\n\t\t\ts: Segments{\n\t\t\t\tField(\"data\"),\n\t\t\t\tField(\".config.yml\"),\n\t\t\t},\n\t\t\twant: \"data[.config.yml]\",\n\t\t},\n\t\t\"Wildcard\": {\n\t\t\ts: Segments{\n\t\t\t\tField(\"spec\"),\n\t\t\t\tField(\"containers\"),\n\t\t\t\tFieldOrIndex(\"*\"),\n\t\t\t\tField(\"name\"),\n\t\t\t},\n\t\t\twant: \"spec.containers[*].name\",\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tif diff := cmp.Diff(tc.want, tc.s.String()); diff != \"\" {\n\t\t\t\tt.Errorf(\"s.String(): -want, +got:\\n %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFieldOrIndex(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\ts      string\n\t\twant   Segment\n\t}{\n\t\t\"Field\": {\n\t\t\treason: \"An unambiguous string should be interpreted as a field segment\",\n\t\t\ts:      \"coolField\",\n\t\t\twant:   Segment{Type: SegmentField, Field: \"coolField\"},\n\t\t},\n\t\t\"QuotedField\": {\n\t\t\treason: \"A quoted string should be interpreted as a field segment with the quotes removed\",\n\t\t\ts:      \"'coolField'\",\n\t\t\twant:   Segment{Type: SegmentField, Field: \"coolField\"},\n\t\t},\n\t\t\"QuotedFieldWithPeriods\": {\n\t\t\treason: \"A quoted string with periods should be interpreted as a field segment with the quotes removed\",\n\t\t\ts:      \"'cool.Field'\",\n\t\t\twant:   Segment{Type: SegmentField, Field: \"cool.Field\"},\n\t\t},\n\t\t\"Index\": {\n\t\t\treason: \"An unambiguous integer should be interpreted as an index segment\",\n\t\t\ts:      \"3\",\n\t\t\twant:   Segment{Type: SegmentIndex, Index: 3},\n\t\t},\n\t\t\"Negative\": {\n\t\t\treason: \"A negative integer should be interpreted as an field segment\",\n\t\t\ts:      \"-3\",\n\t\t\twant:   Segment{Type: SegmentField, Field: \"-3\"},\n\t\t},\n\t\t\"Float\": {\n\t\t\treason: \"A float should be interpreted as an field segment\",\n\t\t\ts:      \"3.0\",\n\t\t\twant:   Segment{Type: SegmentField, Field: \"3.0\"},\n\t\t},\n\t\t\"Overflow\": {\n\t\t\treason: \"A very big integer will be interpreted as a field segment\",\n\t\t\ts:      strconv.Itoa(math.MaxUint32 + 1),\n\t\t\twant:   Segment{Type: SegmentField, Field: strconv.Itoa(math.MaxUint32 + 1)},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := FieldOrIndex(tc.s)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nFieldOrIndex(...): %s: -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParse(t *testing.T) {\n\ttype want struct {\n\t\ts   Segments\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tpath   string\n\t\twant   want\n\t}{\n\t\t\"SingleField\": {\n\t\t\treason: \"A path with no brackets or periods should be interpreted as a single field segment\",\n\t\t\tpath:   \"spec\",\n\t\t\twant: want{\n\t\t\t\ts: Segments{Field(\"spec\")},\n\t\t\t},\n\t\t},\n\t\t\"SingleIndex\": {\n\t\t\treason: \"An integer surrounded by brackets should be interpreted as an index\",\n\t\t\tpath:   \"[0]\",\n\t\t\twant: want{\n\t\t\t\ts: Segments{FieldOrIndex(\"0\")},\n\t\t\t},\n\t\t},\n\t\t\"TwoFields\": {\n\t\t\treason: \"A path with one period should be interpreted as two field segments\",\n\t\t\tpath:   \"metadata.name\",\n\t\t\twant: want{\n\t\t\t\ts: Segments{Field(\"metadata\"), Field(\"name\")},\n\t\t\t},\n\t\t},\n\t\t\"APIConventionsExample\": {\n\t\t\treason: \"The example given by the Kubernetes API convention should be parse correctly\",\n\t\t\tpath:   \"fields[1].state.current\",\n\t\t\twant: want{\n\t\t\t\ts: Segments{\n\t\t\t\t\tField(\"fields\"),\n\t\t\t\t\tFieldOrIndex(\"1\"),\n\t\t\t\t\tField(\"state\"),\n\t\t\t\t\tField(\"current\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SimpleIndex\": {\n\t\t\treason: \"Indexing an object field that is an array should result in a field and an index\",\n\t\t\tpath:   \"items[0]\",\n\t\t\twant: want{\n\t\t\t\ts: Segments{Field(\"items\"), FieldOrIndex(\"0\")},\n\t\t\t},\n\t\t},\n\t\t\"FieldsAndIndex\": {\n\t\t\treason: \"A path with periods and braces should be interpreted as fields and indices\",\n\t\t\tpath:   \"spec.containers[0].name\",\n\t\t\twant: want{\n\t\t\t\ts: Segments{\n\t\t\t\t\tField(\"spec\"),\n\t\t\t\t\tField(\"containers\"),\n\t\t\t\t\tFieldOrIndex(\"0\"),\n\t\t\t\t\tField(\"name\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"NestedArray\": {\n\t\t\treason: \"A nested array should result in two consecutive index fields\",\n\t\t\tpath:   \"nested[0][1].name\",\n\t\t\twant: want{\n\t\t\t\ts: Segments{\n\t\t\t\t\tField(\"nested\"),\n\t\t\t\t\tFieldOrIndex(\"0\"),\n\t\t\t\t\tFieldOrIndex(\"1\"),\n\t\t\t\t\tField(\"name\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"BracketStyleField\": {\n\t\t\treason: \"A field name can be specified using brackets rather than a period\",\n\t\t\tpath:   \"spec[containers][0].name\",\n\t\t\twant: want{\n\t\t\t\ts: Segments{\n\t\t\t\t\tField(\"spec\"),\n\t\t\t\t\tField(\"containers\"),\n\t\t\t\t\tFieldOrIndex(\"0\"),\n\t\t\t\t\tField(\"name\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"BracketFieldWithPeriod\": {\n\t\t\treason: \"A field name specified using brackets can include a period\",\n\t\t\tpath:   \"data[.config.yml]\",\n\t\t\twant: want{\n\t\t\t\ts: Segments{\n\t\t\t\t\tField(\"data\"),\n\t\t\t\t\tFieldOrIndex(\".config.yml\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"QuotedFieldWithPeriodInBracket\": {\n\t\t\treason: \"A field name specified using quote and in bracket can include a period\",\n\t\t\tpath:   \"metadata.labels['app.hash']\",\n\t\t\twant: want{\n\t\t\t\ts: Segments{\n\t\t\t\t\tField(\"metadata\"),\n\t\t\t\t\tField(\"labels\"),\n\t\t\t\t\tFieldOrIndex(\"app.hash\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"LeadingPeriod\": {\n\t\t\treason: \"A path may not start with a period (unlike a JSON path)\",\n\t\t\tpath:   \".metadata.name\",\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"unexpected '.' at position 0\"),\n\t\t\t},\n\t\t},\n\t\t\"TrailingPeriod\": {\n\t\t\treason: \"A path may not end with a period\",\n\t\t\tpath:   \"metadata.name.\",\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"unexpected '.' at position 13\"),\n\t\t\t},\n\t\t},\n\t\t\"BracketsFollowingPeriod\": {\n\t\t\treason: \"Brackets may not follow a period\",\n\t\t\tpath:   \"spec.containers.[0].name\",\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"unexpected '[' at position 16\"),\n\t\t\t},\n\t\t},\n\t\t\"DoublePeriod\": {\n\t\t\treason: \"A path may not include two consecutive periods\",\n\t\t\tpath:   \"metadata..name\",\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"unexpected '.' at position 9\"),\n\t\t\t},\n\t\t},\n\t\t\"DanglingRightBracket\": {\n\t\t\treason: \"A right bracket may not appear in a field name\",\n\t\t\tpath:   \"metadata.]name\",\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"unexpected ']' at position 9\"),\n\t\t\t},\n\t\t},\n\t\t\"DoubleOpenBracket\": {\n\t\t\treason: \"Brackets may not be nested\",\n\t\t\tpath:   \"spec[bracketed[name]]\",\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"unexpected '[' at position 14\"),\n\t\t\t},\n\t\t},\n\t\t\"DanglingLeftBracket\": {\n\t\t\treason: \"A left bracket must be closed\",\n\t\t\tpath:   \"spec[name\",\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"unterminated '[' at position 4\"),\n\t\t\t},\n\t\t},\n\t\t\"EmptyBracket\": {\n\t\t\treason: \"Brackets may not be empty\",\n\t\t\tpath:   \"spec[]\",\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"unexpected ']' at position 5\"),\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot, err := Parse(tc.path)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\nParse(%s): %s: -want error, +got error:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.s, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nParse(%s): %s: -want, +got:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/fieldpath/merge.go",
    "content": "/*\nCopyright 2021 The Crossplane 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 fieldpath\n\nimport (\n\t\"reflect\"\n\n\t\"dario.cat/mergo\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\nconst (\n\terrInvalidMerge = \"failed to merge values\"\n)\n\n// MergeValue of the receiver p at the specified field path with the supplied\n// value according to supplied merge options.\nfunc (p *Paved) MergeValue(path string, value any, mo *MergeOptions) error {\n\tdst, err := p.GetValue(path)\n\tif IsNotFound(err) || mo == nil {\n\t\tdst = nil\n\t} else if err != nil {\n\t\treturn err\n\t}\n\n\tdst, err = merge(dst, value, mo)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn p.SetValue(path, dst)\n}\n\n// merges the given src onto the given dst.\n// dst and src must have the same type.\n// If a nil merge options is supplied, the default behavior is MergeOptions'\n// default behavior. If dst or src is nil, src is returned\n// (i.e., dst replaced by src).\nfunc merge(dst, src any, mergeOptions *MergeOptions) (any, error) {\n\t// because we are merging values of a field, which can be a slice, and\n\t// because mergo currently supports merging only maps or structs,\n\t// we wrap the argument to be passed to mergo.Merge in a map.\n\tconst keyArg = \"arg\"\n\n\targWrap := func(arg any) map[string]any {\n\t\treturn map[string]any{\n\t\t\tkeyArg: arg,\n\t\t}\n\t}\n\n\tif dst == nil || src == nil {\n\t\treturn src, nil // no merge, replace\n\t}\n\t// TODO(aru): we may provide an extra MergeOption to also append duplicates of slice elements\n\t// but, by default, do not append duplicate slice items if MergeOptions.AppendSlice is set\n\tif mergeOptions.isAppendSlice() {\n\t\tsrc = removeSourceDuplicates(dst, src)\n\t}\n\n\tmDst := argWrap(dst)\n\t// use merge semantics with the configured merge options to obtain the target dst value\n\tif err := mergo.Merge(&mDst, argWrap(src), mergeOptions.mergoConfiguration()...); err != nil {\n\t\treturn nil, errors.Wrap(err, errInvalidMerge)\n\t}\n\n\treturn mDst[keyArg], nil\n}\n\nfunc removeSourceDuplicates(dst, src any) any {\n\tsliceDst, sliceSrc := reflect.ValueOf(dst), reflect.ValueOf(src)\n\tif sliceDst.Kind() == reflect.Ptr {\n\t\tsliceDst = sliceDst.Elem()\n\t}\n\n\tif sliceSrc.Kind() == reflect.Ptr {\n\t\tsliceSrc = sliceSrc.Elem()\n\t}\n\n\tif sliceDst.Kind() != reflect.Slice || sliceSrc.Kind() != reflect.Slice {\n\t\treturn src\n\t}\n\n\tresult := reflect.New(sliceSrc.Type()).Elem() // we will not modify src\n\tfor i := range sliceSrc.Len() {\n\t\titemSrc := sliceSrc.Index(i)\n\n\t\tfound := false\n\t\tfor j := 0; j < sliceDst.Len() && !found; j++ {\n\t\t\t// if src item is found in the dst array\n\t\t\tif reflect.DeepEqual(itemSrc.Interface(), sliceDst.Index(j).Interface()) {\n\t\t\t\tfound = true\n\t\t\t}\n\t\t}\n\n\t\tif !found {\n\t\t\t// then put src item into result\n\t\t\tresult = reflect.Append(result, itemSrc)\n\t\t}\n\t}\n\n\treturn result.Interface()\n}\n\n// MergeOptions Specifies merge options on a field path.\ntype MergeOptions struct { // TODO(aru): add more options that control merging behavior\n\t// Specifies that already existing values in a merged map should be preserved\n\t// +optional\n\tKeepMapValues *bool `json:\"keepMapValues,omitempty\"`\n\t// Specifies that already existing elements in a merged slice should be preserved\n\t// +optional\n\tAppendSlice *bool `json:\"appendSlice,omitempty\"`\n}\n\n// mergoConfiguration the default behavior is to replace maps and slices.\nfunc (mo *MergeOptions) mergoConfiguration() []func(*mergo.Config) {\n\tconfig := []func(*mergo.Config){mergo.WithOverride}\n\tif mo == nil {\n\t\treturn config\n\t}\n\n\tif mo.KeepMapValues != nil && *mo.KeepMapValues {\n\t\tconfig = config[:0]\n\t}\n\n\tif mo.AppendSlice != nil && *mo.AppendSlice {\n\t\tconfig = append(config, mergo.WithAppendSlice)\n\t}\n\n\treturn config\n}\n\n// isAppendSlice returns true if mo.AppendSlice is set to true.\nfunc (mo *MergeOptions) isAppendSlice() bool {\n\treturn mo != nil && mo.AppendSlice != nil && *mo.AppendSlice\n}\n"
  },
  {
    "path": "pkg/fieldpath/merge_test.go",
    "content": "/*\nCopyright 2021 The Crossplane 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 fieldpath\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"k8s.io/apimachinery/pkg/util/json\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nfunc TestMergeValue(t *testing.T) {\n\tconst (\n\t\tpathTest = \"a\"\n\t\tvalSrc   = \"e1-from-source\"\n\t\tvalSrc2  = \"e1-from-source-2\"\n\t\tvalDst   = \"e1-from-destination\"\n\t)\n\n\tformatArr := func(arr []string) string {\n\t\treturn fmt.Sprintf(`{\"%s\": [\"%s\"]}`, pathTest, strings.Join(arr, `\", \"`))\n\t}\n\tformatMap := func(val string) string {\n\t\treturn fmt.Sprintf(`{\"%s\": {\"%s\": \"%s\"}}`, pathTest, pathTest, val)\n\t}\n\tappendArr := func(dst, src []string) []string {\n\t\treturn reflect.AppendSlice(reflect.ValueOf(dst), reflect.ValueOf(src)).Interface().([]string)\n\t}\n\n\tarrSrc := []string{valSrc}\n\tfnMapSrc := func() map[string]any {\n\t\treturn map[string]any{pathTest: valSrc}\n\t}\n\tarrDst := []string{valDst}\n\tfnMapDst := func() map[string]any {\n\t\treturn map[string]any{pathTest: map[string]any{pathTest: valDst}}\n\t}\n\tvalFalse, valTrue := false, true\n\n\ttype fields struct {\n\t\tobject map[string]any\n\t}\n\n\ttype args struct {\n\t\tpath  string\n\t\tvalue any\n\t\tmo    *MergeOptions\n\t}\n\n\ttype want struct {\n\t\tserialized string\n\t\terr        error\n\t}\n\n\ttests := map[string]struct {\n\t\treason string\n\t\tfields fields\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"MergeArrayNoMergeOptions\": {\n\t\t\treason: \"If no merge options are given, default is to override an array\",\n\t\t\tfields: fields{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\tpathTest: valDst,\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tpath:  pathTest,\n\t\t\t\tvalue: arrSrc,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tserialized: formatArr(arrSrc),\n\t\t\t},\n\t\t},\n\t\t\"MergeArrayNoAppend\": {\n\t\t\treason: \"If MergeOptions.AppendSlice is false, an array should be overridden when merging\",\n\t\t\tfields: fields{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\tpathTest: arrDst,\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tpath:  pathTest,\n\t\t\t\tvalue: arrSrc,\n\t\t\t\tmo: &MergeOptions{\n\t\t\t\t\tAppendSlice: &valFalse,\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tserialized: formatArr(arrSrc),\n\t\t\t},\n\t\t},\n\t\t\"MergeArrayAppend\": {\n\t\t\treason: \"If MergeOptions.AppendSlice is true, dst array should be merged with the src array\",\n\t\t\tfields: fields{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\tpathTest: arrDst,\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tpath:  pathTest,\n\t\t\t\tvalue: arrSrc,\n\t\t\t\tmo: &MergeOptions{\n\t\t\t\t\tAppendSlice: &valTrue,\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tserialized: formatArr(appendArr(arrDst, arrSrc)),\n\t\t\t},\n\t\t},\n\t\t\"MergeArrayAppendDuplicate\": {\n\t\t\treason: \"If MergeOptions.AppendSlice is true, dst array should be merged with the src array not allowing duplicates\",\n\t\t\tfields: fields{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\tpathTest: []string{valDst, valSrc},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tpath:  pathTest,\n\t\t\t\tvalue: []string{valSrc, valSrc2},\n\t\t\t\tmo: &MergeOptions{\n\t\t\t\t\tAppendSlice: &valTrue,\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tserialized: formatArr([]string{valDst, valSrc, valSrc2}),\n\t\t\t},\n\t\t},\n\t\t\"MergeMapNoMergeOptions\": {\n\t\t\treason: \"If no merge options are given, default is to override a map key\",\n\t\t\tfields: fields{\n\t\t\t\tobject: fnMapDst(),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tpath:  pathTest,\n\t\t\t\tvalue: fnMapSrc(),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tserialized: formatMap(valSrc),\n\t\t\t},\n\t\t},\n\t\t\"MergeMapNoKeep\": {\n\t\t\treason: \"If MergeOptions.KeepMapValues is false, a map key should be overridden\",\n\t\t\tfields: fields{\n\t\t\t\tobject: fnMapDst(),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tpath:  pathTest,\n\t\t\t\tvalue: fnMapSrc(),\n\t\t\t\tmo: &MergeOptions{\n\t\t\t\t\tKeepMapValues: &valFalse,\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tserialized: formatMap(valSrc),\n\t\t\t},\n\t\t},\n\t\t\"MergeMapKeep\": {\n\t\t\treason: \"If MergeOptions.KeepMapValues is true, a dst map key should preserve its value\",\n\t\t\tfields: fields{\n\t\t\t\tobject: fnMapDst(),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tpath:  pathTest,\n\t\t\t\tvalue: fnMapSrc(),\n\t\t\t\tmo: &MergeOptions{\n\t\t\t\t\tKeepMapValues: &valTrue,\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tserialized: formatMap(valDst),\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\twant := make(map[string]any)\n\t\t\tif err := json.Unmarshal([]byte(tc.want.serialized), &want); err != nil {\n\t\t\t\tt.Fatalf(\"Test case error: Unable to unmarshall JSON doc: %v\", err)\n\t\t\t}\n\n\t\t\tp := &Paved{\n\t\t\t\tobject: tc.fields.object,\n\t\t\t}\n\n\t\t\terr := p.MergeValue(tc.args.path, tc.args.value, tc.args.mo)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.MergeValue(%s, %v): %s: -want error, +got error:\\n%s\",\n\t\t\t\t\ttc.args.path, tc.args.value, tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(want, p.object); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.MergeValue(%s, %v): %s: -want, +got:\\n%s\",\n\t\t\t\t\ttc.args.path, tc.args.value, tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/fieldpath/paved.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 fieldpath\n\nimport (\n\t\"strconv\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/util/json\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\n// DefaultMaxFieldPathIndex is the max allowed index in a field path.\nconst DefaultMaxFieldPathIndex = 1024\n\ntype notFoundError struct {\n\terror\n}\n\nfunc (e notFoundError) IsNotFound() bool {\n\treturn true\n}\n\n// IsNotFound returns true if the supplied error indicates a field path was not\n// found, for example because a field did not exist within an object or an\n// index was out of bounds in an array.\nfunc IsNotFound(err error) bool {\n\tcause := errors.Cause(err)\n\t_, ok := cause.(interface {\n\t\tIsNotFound() bool\n\t})\n\n\treturn ok\n}\n\n// A Paved JSON object supports getting and setting values by their field path.\ntype Paved struct {\n\tobject            map[string]any\n\tmaxFieldPathIndex uint\n}\n\n// PavedOption can be used to configure a Paved behavior.\ntype PavedOption func(paved *Paved)\n\n// PaveObject paves a runtime.Object, making it possible to get and set values\n// by field path. o must be a non-nil pointer to an object.\nfunc PaveObject(o runtime.Object, opts ...PavedOption) (*Paved, error) {\n\tu, err := runtime.DefaultUnstructuredConverter.ToUnstructured(o)\n\treturn Pave(u, opts...), errors.Wrap(err, \"cannot convert object to unstructured data\")\n}\n\n// Pave a JSON object, making it possible to get and set values by field path.\nfunc Pave(object map[string]any, opts ...PavedOption) *Paved {\n\tp := &Paved{object: object, maxFieldPathIndex: DefaultMaxFieldPathIndex}\n\n\tfor _, opt := range opts {\n\t\topt(p)\n\t}\n\n\treturn p\n}\n\n// WithMaxFieldPathIndex returns a PavedOption that sets the max allowed index for field paths, 0 means no limit.\nfunc WithMaxFieldPathIndex(maxIndex uint) PavedOption {\n\treturn func(paved *Paved) {\n\t\tpaved.maxFieldPathIndex = maxIndex\n\t}\n}\n\nfunc (p *Paved) maxFieldPathIndexEnabled() bool {\n\treturn p.maxFieldPathIndex > 0\n}\n\n// MarshalJSON to the underlying object.\nfunc (p Paved) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(p.object)\n}\n\n// UnmarshalJSON from the underlying object.\nfunc (p *Paved) UnmarshalJSON(data []byte) error {\n\treturn json.Unmarshal(data, &p.object)\n}\n\n// UnstructuredContent returns the JSON serialisable content of this Paved.\nfunc (p *Paved) UnstructuredContent() map[string]any {\n\tif p.object == nil {\n\t\treturn make(map[string]any)\n\t}\n\n\treturn p.object\n}\n\n// SetUnstructuredContent sets the JSON serialisable content of this Paved.\nfunc (p *Paved) SetUnstructuredContent(content map[string]any) {\n\tp.object = content\n}\n\nfunc (p *Paved) getValue(s Segments) (any, error) {\n\treturn getValueFromInterface(p.object, s)\n}\n\nfunc getValueFromInterface(it any, s Segments) (any, error) {\n\tfor i, current := range s {\n\t\tfinal := i == len(s)-1\n\n\t\tswitch current.Type {\n\t\tcase SegmentIndex:\n\t\t\tarray, ok := it.([]any)\n\t\t\tif !ok {\n\t\t\t\treturn nil, errors.Errorf(\"%s: not an array\", s[:i])\n\t\t\t}\n\n\t\t\tif current.Index >= uint(len(array)) {\n\t\t\t\treturn nil, notFoundError{errors.Errorf(\"%s: no such element\", s[:i+1])}\n\t\t\t}\n\n\t\t\tif final {\n\t\t\t\treturn array[current.Index], nil\n\t\t\t}\n\n\t\t\tit = array[current.Index]\n\t\tcase SegmentField:\n\t\t\tswitch object := it.(type) {\n\t\t\tcase map[string]any:\n\t\t\t\tv, ok := object[current.Field]\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, notFoundError{errors.Errorf(\"%s: no such field\", s[:i+1])}\n\t\t\t\t}\n\n\t\t\t\tif final {\n\t\t\t\t\treturn v, nil\n\t\t\t\t}\n\n\t\t\t\tit = object[current.Field]\n\t\t\tcase nil:\n\t\t\t\treturn nil, notFoundError{errors.Errorf(\"%s: expected map, got nil\", s[:i])}\n\t\t\tdefault:\n\t\t\t\treturn nil, errors.Errorf(\"%s: not an object\", s[:i])\n\t\t\t}\n\t\t}\n\t}\n\n\t// This should be unreachable.\n\treturn nil, nil\n}\n\n// ExpandWildcards expands wildcards for a given field path. It returns an\n// array of field paths with expanded values. Please note that expanded paths\n// depend on the input data which is paved.object.\n//\n// Example:\n//\n// For a Paved object with the following data: []byte(`{\"spec\":{\"containers\":[{\"name\":\"cool\", \"image\": \"latest\", \"args\": [\"start\", \"now\", \"debug\"]}]}}`),\n// ExpandWildcards(\"spec.containers[*].args[*]\") returns:\n// []string{\"spec.containers[0].args[0]\", \"spec.containers[0].args[1]\", \"spec.containers[0].args[2]\"},.\nfunc (p *Paved) ExpandWildcards(path string) ([]string, error) {\n\tsegments, err := Parse(path)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"cannot parse path %q\", path)\n\t}\n\n\tsegmentsArray, err := expandWildcards(p.object, segments)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"cannot expand wildcards for segments: %q\", segments)\n\t}\n\n\tpaths := make([]string, len(segmentsArray))\n\tfor i, s := range segmentsArray {\n\t\tpaths[i] = s.String()\n\t}\n\n\treturn paths, nil\n}\n\nfunc expandWildcards(data any, segments Segments) ([]Segments, error) { //nolint:gocognit // See note below.\n\t// Even complexity turns out to be high, it is mostly because we have duplicate\n\t// logic for arrays and maps and a couple of error handling.\n\tvar res []Segments\n\n\tit := data\n\n\tfor i, current := range segments {\n\t\t// wildcards are regular fields with \"*\" as string\n\t\tif current.Type == SegmentField && current.Field == wildcard {\n\t\t\tswitch mapOrArray := it.(type) {\n\t\t\tcase []any:\n\t\t\t\tfor ix := range mapOrArray {\n\t\t\t\t\texpanded := make(Segments, len(segments))\n\t\t\t\t\tcopy(expanded, segments)\n\t\t\t\t\texpanded = append(append(expanded[:i], FieldOrIndex(strconv.Itoa(ix))), expanded[i+1:]...)\n\n\t\t\t\t\tr, err := expandWildcards(data, expanded)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, errors.Wrapf(err, \"%q: cannot expand wildcards\", expanded)\n\t\t\t\t\t}\n\n\t\t\t\t\tres = append(res, r...)\n\t\t\t\t}\n\t\t\tcase map[string]any:\n\t\t\t\tfor k := range mapOrArray {\n\t\t\t\t\texpanded := make(Segments, len(segments))\n\t\t\t\t\tcopy(expanded, segments)\n\t\t\t\t\texpanded = append(append(expanded[:i], Field(k)), expanded[i+1:]...)\n\n\t\t\t\t\tr, err := expandWildcards(data, expanded)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, errors.Wrapf(err, \"%q: cannot expand wildcards\", expanded)\n\t\t\t\t\t}\n\n\t\t\t\t\tres = append(res, r...)\n\t\t\t\t}\n\t\t\tcase nil:\n\t\t\t\treturn nil, notFoundError{errors.Errorf(\"wildcard field %q is not found in the path\", segments[:i])}\n\t\t\tdefault:\n\t\t\t\treturn nil, errors.Errorf(\"%q: unexpected wildcard usage\", segments[:i])\n\t\t\t}\n\n\t\t\treturn res, nil\n\t\t}\n\n\t\tvar err error\n\n\t\tit, err = getValueFromInterface(data, segments[:i+1])\n\t\tif IsNotFound(err) {\n\t\t\treturn nil, nil\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn append(res, segments), nil\n}\n\n// GetValue of the supplied field path.\nfunc (p *Paved) GetValue(path string) (any, error) {\n\tsegments, err := Parse(path)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"cannot parse path %q\", path)\n\t}\n\n\treturn p.getValue(segments)\n}\n\n// GetValueInto the supplied type.\nfunc (p *Paved) GetValueInto(path string, out any) error {\n\tval, err := p.GetValue(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tjs, err := json.Marshal(val)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"cannot marshal value to JSON\")\n\t}\n\n\treturn errors.Wrap(json.Unmarshal(js, out), \"cannot unmarshal value from JSON\")\n}\n\n// GetString value of the supplied field path.\nfunc (p *Paved) GetString(path string) (string, error) {\n\tv, err := p.GetValue(path)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\ts, ok := v.(string)\n\tif !ok {\n\t\treturn \"\", errors.Errorf(\"%s: not a string\", path)\n\t}\n\n\treturn s, nil\n}\n\n// GetStringArray value of the supplied field path.\nfunc (p *Paved) GetStringArray(path string) ([]string, error) {\n\tv, err := p.GetValue(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ta, ok := v.([]any)\n\tif !ok {\n\t\treturn nil, errors.Errorf(\"%s: not an array\", path)\n\t}\n\n\tsa := make([]string, len(a))\n\tfor i := range a {\n\t\ts, ok := a[i].(string)\n\t\tif !ok {\n\t\t\treturn nil, errors.Errorf(\"%s: not an array of strings\", path)\n\t\t}\n\n\t\tsa[i] = s\n\t}\n\n\treturn sa, nil\n}\n\n// GetStringObject value of the supplied field path.\nfunc (p *Paved) GetStringObject(path string) (map[string]string, error) {\n\tv, err := p.GetValue(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\to, ok := v.(map[string]any)\n\tif !ok {\n\t\treturn nil, errors.Errorf(\"%s: not an object\", path)\n\t}\n\n\tso := make(map[string]string)\n\n\tfor k, in := range o {\n\t\ts, ok := in.(string)\n\t\tif !ok {\n\t\t\treturn nil, errors.Errorf(\"%s: not an object with string field values\", path)\n\t\t}\n\n\t\tso[k] = s\n\t}\n\n\treturn so, nil\n}\n\n// GetBool value of the supplied field path.\nfunc (p *Paved) GetBool(path string) (bool, error) {\n\tv, err := p.GetValue(path)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tb, ok := v.(bool)\n\tif !ok {\n\t\treturn false, errors.Errorf(\"%s: not a bool\", path)\n\t}\n\n\treturn b, nil\n}\n\n// GetInteger value of the supplied field path.\nfunc (p *Paved) GetInteger(path string) (int64, error) {\n\tv, err := p.GetValue(path)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tf, ok := v.(int64)\n\tif !ok {\n\t\treturn 0, errors.Errorf(\"%s: not a (int64) number\", path)\n\t}\n\n\treturn f, nil\n}\n\nfunc (p *Paved) setValue(s Segments, value any) error {\n\t// We expect p.object to look like JSON data that was unmarshalled into an\n\t// any per https://golang.org/pkg/encoding/json/#Unmarshal. We\n\t// marshal our value to JSON and unmarshal it into an any to ensure\n\t// it meets these criteria before setting it within p.object.\n\tv, err := toValidJSON(value)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := p.validateSegments(s); err != nil {\n\t\treturn err\n\t}\n\n\tvar in any = p.object\n\n\tfor i, current := range s {\n\t\tfinal := i == len(s)-1\n\n\t\tswitch current.Type {\n\t\tcase SegmentIndex:\n\t\t\tarray, ok := in.([]any)\n\t\t\tif !ok {\n\t\t\t\treturn errors.Errorf(\"%s is not an array\", s[:i])\n\t\t\t}\n\n\t\t\tif final {\n\t\t\t\tarray[current.Index] = v\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tprepareElement(array, current, s[i+1])\n\t\t\tin = array[current.Index]\n\n\t\tcase SegmentField:\n\t\t\tobject, ok := in.(map[string]any)\n\t\t\tif !ok {\n\t\t\t\treturn errors.Errorf(\"%s is not an object\", s[:i])\n\t\t\t}\n\n\t\t\tif final {\n\t\t\t\tobject[current.Field] = v\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tprepareField(object, current, s[i+1])\n\t\t\tin = object[current.Field]\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc toValidJSON(value any) (any, error) {\n\tvar v any\n\n\tj, err := json.Marshal(value)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"cannot marshal value to JSON\")\n\t}\n\n\tif err := json.Unmarshal(j, &v); err != nil {\n\t\treturn nil, errors.Wrap(err, \"cannot unmarshal value from JSON\")\n\t}\n\n\treturn v, nil\n}\n\nfunc prepareElement(array []any, current, next Segment) {\n\t// If this segment is not the final one and doesn't exist we need to\n\t// create it for our next segment.\n\tif array[current.Index] == nil {\n\t\tswitch next.Type {\n\t\tcase SegmentIndex:\n\t\t\tarray[current.Index] = make([]any, next.Index+1)\n\t\tcase SegmentField:\n\t\t\tarray[current.Index] = make(map[string]any)\n\t\t}\n\n\t\treturn\n\t}\n\n\t// If our next segment indexes an array that exists in our current segment's\n\t// element we must ensure the array is long enough to set the next segment.\n\tif next.Type != SegmentIndex {\n\t\treturn\n\t}\n\n\tna, ok := array[current.Index].([]any)\n\tif !ok {\n\t\treturn\n\t}\n\n\tif next.Index < uint(len(na)) {\n\t\treturn\n\t}\n\n\tarray[current.Index] = append(na, make([]any, next.Index-uint(len(na))+1)...)\n}\n\nfunc prepareField(object map[string]any, current, next Segment) {\n\t// If this segment is not the final one and doesn't exist we need to\n\t// create it for our next segment.\n\tif _, ok := object[current.Field]; !ok {\n\t\tswitch next.Type {\n\t\tcase SegmentIndex:\n\t\t\tobject[current.Field] = make([]any, next.Index+1)\n\t\tcase SegmentField:\n\t\t\tobject[current.Field] = make(map[string]any)\n\t\t}\n\n\t\treturn\n\t}\n\n\t// If our next segment indexes an array that exists in our current segment's\n\t// field we must ensure the array is long enough to set the next segment.\n\tif next.Type != SegmentIndex {\n\t\treturn\n\t}\n\n\tna, ok := object[current.Field].([]any)\n\tif !ok {\n\t\treturn\n\t}\n\n\tif next.Index < uint(len(na)) {\n\t\treturn\n\t}\n\n\tobject[current.Field] = append(na, make([]any, next.Index-uint(len(na))+1)...)\n}\n\n// SetValue at the supplied field path.\nfunc (p *Paved) SetValue(path string, value any) error {\n\tsegments, err := Parse(path)\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"cannot parse path %q\", path)\n\t}\n\n\treturn p.setValue(segments, value)\n}\n\nfunc (p *Paved) validateSegments(s Segments) error {\n\tif !p.maxFieldPathIndexEnabled() {\n\t\treturn nil\n\t}\n\n\tfor _, segment := range s {\n\t\tif segment.Type == SegmentIndex && segment.Index > p.maxFieldPathIndex {\n\t\t\treturn errors.Errorf(\"index %v is greater than max allowed index %d\", segment.Index, p.maxFieldPathIndex)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// SetString value at the supplied field path.\nfunc (p *Paved) SetString(path, value string) error {\n\treturn p.SetValue(path, value)\n}\n\n// SetBool value at the supplied field path.\nfunc (p *Paved) SetBool(path string, value bool) error {\n\treturn p.SetValue(path, value)\n}\n\n// SetNumber value at the supplied field path.\nfunc (p *Paved) SetNumber(path string, value float64) error {\n\treturn p.SetValue(path, value)\n}\n\n// DeleteField deletes the field from the object.\n// If the path points to an entry in an array, the element\n// on that index is removed and the next ones are pulled\n// back. If it is a field on a map, the field is\n// removed from the map.\nfunc (p *Paved) DeleteField(path string) error {\n\tsegments, err := Parse(path)\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"cannot parse path %q\", path)\n\t}\n\n\treturn p.delete(segments)\n}\n\nfunc (p *Paved) delete(segments Segments) error { //nolint:gocognit // See note below.\n\t// NOTE(muvaf): I could not reduce the cyclomatic complexity\n\t// more than that without disturbing the reading flow.\n\tif len(segments) == 1 {\n\t\to, err := deleteField(p.object, segments[0])\n\t\tif err != nil {\n\t\t\treturn errors.Wrapf(err, \"cannot delete %s\", segments)\n\t\t}\n\n\t\tp.object = o.(map[string]any) //nolint:forcetypeassert // We're deleting from the root of the paved object, which is always a map[string]any.\n\n\t\treturn nil\n\t}\n\n\tvar in any = p.object\n\n\tfor i, current := range segments {\n\t\t// beforeLast is true for the element before the last one because\n\t\t// slices cannot be changed in place and Go does not allow\n\t\t// taking address of map elements which prevents us from\n\t\t// assigning a new array for that entry unless we have the\n\t\t// map available in the context, which is achieved by iterating\n\t\t// until the element before the last one as opposed to\n\t\t// Set/Get functions in this file.\n\t\tbeforeLast := i == len(segments)-2\n\n\t\tswitch current.Type {\n\t\tcase SegmentIndex:\n\t\t\tarray, ok := in.([]any)\n\t\t\tif !ok {\n\t\t\t\treturn errors.Errorf(\"%s is not an array\", segments[:i])\n\t\t\t}\n\n\t\t\t// It doesn't exist anyway.\n\t\t\tif uint(len(array)) <= current.Index {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tif beforeLast {\n\t\t\t\to, err := deleteField(array[current.Index], segments[len(segments)-1])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.Wrapf(err, \"cannot delete %s\", segments)\n\t\t\t\t}\n\n\t\t\t\tarray[current.Index] = o\n\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tin = array[current.Index]\n\t\tcase SegmentField:\n\t\t\tobject, ok := in.(map[string]any)\n\t\t\tif !ok {\n\t\t\t\treturn errors.Errorf(\"%s is not an object\", segments[:i])\n\t\t\t}\n\n\t\t\t// It doesn't exist anyway.\n\t\t\tif _, ok := object[current.Field]; !ok {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tif beforeLast {\n\t\t\t\to, err := deleteField(object[current.Field], segments[len(segments)-1])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.Wrapf(err, \"cannot delete %s\", segments)\n\t\t\t\t}\n\n\t\t\t\tobject[current.Field] = o\n\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tin = object[current.Field]\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// deleteField deletes the object in obj pointed by\n// the given Segment and returns it. Returned object\n// may or may not have the same address in memory.\nfunc deleteField(obj any, s Segment) (any, error) {\n\tswitch s.Type {\n\tcase SegmentIndex:\n\t\tarray, ok := obj.([]any)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"not an array\")\n\t\t}\n\n\t\tif len(array) == 0 || uint(len(array)) <= s.Index {\n\t\t\treturn array, nil\n\t\t}\n\n\t\tfor i := s.Index; i < uint(len(array))-1; i++ {\n\t\t\tarray[i] = array[i+1]\n\t\t}\n\n\t\treturn array[:len(array)-1], nil\n\tcase SegmentField:\n\t\tobject, ok := obj.(map[string]any)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"not an object\")\n\t\t}\n\n\t\tdelete(object, s.Field)\n\n\t\treturn object, nil\n\t}\n\n\treturn nil, nil\n}\n"
  },
  {
    "path": "pkg/fieldpath/paved_test.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 fieldpath\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"k8s.io/apimachinery/pkg/util/json\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nfunc TestIsNotFound(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\terr    error\n\t\twant   bool\n\t}{\n\t\t\"NotFound\": {\n\t\t\treason: \"An error with method `IsNotFound() bool` should be considered a not found error.\",\n\t\t\terr:    notFoundError{errors.New(\"boom\")},\n\t\t\twant:   true,\n\t\t},\n\t\t\"WrapsNotFound\": {\n\t\t\treason: \"An error that wraps an error with method `IsNotFound() bool` should be considered a not found error.\",\n\t\t\terr:    errors.Wrap(notFoundError{errors.New(\"boom\")}, \"because reasons\"),\n\t\t\twant:   true,\n\t\t},\n\t\t\"SomethingElse\": {\n\t\t\treason: \"An error without method `IsNotFound() bool` should not be considered a not found error.\",\n\t\t\terr:    errors.New(\"boom\"),\n\t\t\twant:   false,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := IsNotFound(tc.err)\n\t\t\tif got != tc.want {\n\t\t\t\tt.Errorf(\"IsNotFound(...): Want %t, got %t\", tc.want, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetValue(t *testing.T) {\n\ttype want struct {\n\t\tvalue any\n\t\terr   error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tpath   string\n\t\tdata   []byte\n\t\twant   want\n\t}{\n\t\t\"MetadataName\": {\n\t\t\treason: \"It should be possible to get a field from a nested object\",\n\t\t\tpath:   \"metadata.name\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"name\":\"cool\"}}`),\n\t\t\twant: want{\n\t\t\t\tvalue: \"cool\",\n\t\t\t},\n\t\t},\n\t\t\"ContainerName\": {\n\t\t\treason: \"It should be possible to get a field from an object array element\",\n\t\t\tpath:   \"spec.containers[0].name\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\":[{\"name\":\"cool\"}]}}`),\n\t\t\twant: want{\n\t\t\t\tvalue: \"cool\",\n\t\t\t},\n\t\t},\n\t\t\"NestedArray\": {\n\t\t\treason: \"It should be possible to get a field from a nested array\",\n\t\t\tpath:   \"items[0][1]\",\n\t\t\tdata:   []byte(`{\"items\":[[\"a\", \"b\"]]}`),\n\t\t\twant: want{\n\t\t\t\tvalue: \"b\",\n\t\t\t},\n\t\t},\n\t\t\"OwnerRefController\": {\n\t\t\treason: \"Requesting a boolean field path should work.\",\n\t\t\tpath:   \"metadata.ownerRefs[0].controller\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"ownerRefs\":[{\"controller\": true}]}}`),\n\t\t\twant: want{\n\t\t\t\tvalue: true,\n\t\t\t},\n\t\t},\n\t\t\"MetadataVersion\": {\n\t\t\treason: \"Requesting an integer field should work\",\n\t\t\tpath:   \"metadata.version\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"version\":2}}`),\n\t\t\twant: want{\n\t\t\t\tvalue: int64(2),\n\t\t\t},\n\t\t},\n\t\t\"SomeFloat\": {\n\t\t\treason: \"Requesting a float field should work\",\n\t\t\tpath:   \"metadata.version\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"version\":2.0}}`),\n\t\t\twant: want{\n\t\t\t\tvalue: float64(2),\n\t\t\t},\n\t\t},\n\t\t\"MetadataNope\": {\n\t\t\treason: \"Requesting a non-existent object field should fail\",\n\t\t\tpath:   \"metadata.name\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"nope\":\"cool\"}}`),\n\t\t\twant: want{\n\t\t\t\terr: notFoundError{errors.New(\"metadata.name: no such field\")},\n\t\t\t},\n\t\t},\n\t\t\"InsufficientContainers\": {\n\t\t\treason: \"Requesting a non-existent array element should fail\",\n\t\t\tpath:   \"spec.containers[1].name\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\":[{\"name\":\"cool\"}]}}`),\n\t\t\twant: want{\n\t\t\t\terr: notFoundError{errors.New(\"spec.containers[1]: no such element\")},\n\t\t\t},\n\t\t},\n\t\t\"NotAnArray\": {\n\t\t\treason: \"Indexing an object should fail\",\n\t\t\tpath:   \"metadata[1]\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"nope\":\"cool\"}}`),\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"metadata: not an array\"),\n\t\t\t},\n\t\t},\n\t\t\"NotAnObject\": {\n\t\t\treason: \"Requesting a field in an array should fail\",\n\t\t\tpath:   \"spec.containers[nope].name\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\":[{\"name\":\"cool\"}]}}`),\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"spec.containers: not an object\"),\n\t\t\t},\n\t\t},\n\t\t\"MalformedPath\": {\n\t\t\treason: \"Requesting an invalid field path should fail\",\n\t\t\tpath:   \"spec[]\",\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errors.New(\"unexpected ']' at position 5\"), \"cannot parse path \\\"spec[]\\\"\"),\n\t\t\t},\n\t\t},\n\t\t\"NilParent\": {\n\t\t\treason: \"Request for a path with a nil parent value\",\n\t\t\tpath:   \"spec.containers[*].name\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\": null}}`),\n\t\t\twant: want{\n\t\t\t\terr: notFoundError{errors.Errorf(\"%s: expected map, got nil\", \"spec.containers\")},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tin := make(map[string]any)\n\t\t\t_ = json.Unmarshal(tc.data, &in)\n\t\t\tp := Pave(in)\n\n\t\t\tgot, err := p.GetValue(tc.path)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.GetValue(%s): %s: -want error, +got error:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.value, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\np.GetValue(%s): %s: -want, +got:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetValueInto(t *testing.T) {\n\ttype Struct struct {\n\t\tSlice       []string `json:\"slice\"`\n\t\tStringField string   `json:\"string\"`\n\t\tIntField    int      `json:\"int\"`\n\t}\n\n\ttype Slice []string\n\n\ttype args struct {\n\t\tpath string\n\t\tout  any\n\t}\n\n\ttype want struct {\n\t\tout any\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tdata   []byte\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"Struct\": {\n\t\t\treason: \"It should be possible to get a value into a struct.\",\n\t\t\tdata:   []byte(`{\"s\":{\"slice\":[\"a\"],\"string\":\"b\",\"int\":1}}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"s\",\n\t\t\t\tout:  &Struct{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tout: &Struct{Slice: []string{\"a\"}, StringField: \"b\", IntField: 1},\n\t\t\t},\n\t\t},\n\t\t\"Slice\": {\n\t\t\treason: \"It should be possible to get a value into a slice.\",\n\t\t\tdata:   []byte(`{\"s\": [\"a\", \"b\"]}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"s\",\n\t\t\t\tout:  &Slice{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tout: &Slice{\"a\", \"b\"},\n\t\t\t},\n\t\t},\n\t\t\"MissingPath\": {\n\t\t\treason: \"Getting a value from a fieldpath that doesn't exist should return an error.\",\n\t\t\tdata:   []byte(`{}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"s\",\n\t\t\t\tout:  &Struct{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tout: &Struct{},\n\t\t\t\terr: notFoundError{errors.New(\"s: no such field\")},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tin := make(map[string]any)\n\t\t\t_ = json.Unmarshal(tc.data, &in)\n\t\t\tp := Pave(in)\n\n\t\t\terr := p.GetValueInto(tc.args.path, tc.args.out)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.GetValueInto(%s): %s: -want error, +got error:\\n%s\", tc.args.path, tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.out, tc.args.out); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\np.GetValueInto(%s): %s: -want, +got:\\n%s\", tc.args.path, tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetString(t *testing.T) {\n\ttype want struct {\n\t\tvalue string\n\t\terr   error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tpath   string\n\t\tdata   []byte\n\t\twant   want\n\t}{\n\t\t\"MetadataName\": {\n\t\t\treason: \"It should be possible to get a field from a nested object\",\n\t\t\tpath:   \"metadata.name\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"name\":\"cool\"}}`),\n\t\t\twant: want{\n\t\t\t\tvalue: \"cool\",\n\t\t\t},\n\t\t},\n\t\t\"MalformedPath\": {\n\t\t\treason: \"Requesting an invalid field path should fail\",\n\t\t\tpath:   \"spec[]\",\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errors.New(\"unexpected ']' at position 5\"), \"cannot parse path \\\"spec[]\\\"\"),\n\t\t\t},\n\t\t},\n\t\t\"NotAString\": {\n\t\t\treason: \"Requesting an non-string field path should fail\",\n\t\t\tpath:   \"metadata.version\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"version\":2}}`),\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"metadata.version: not a string\"),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tin := make(map[string]any)\n\t\t\t_ = json.Unmarshal(tc.data, &in)\n\t\t\tp := Pave(in)\n\n\t\t\tgot, err := p.GetString(tc.path)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.GetString(%s): %s: -want error, +got error:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.value, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\np.GetString(%s): %s: -want, +got:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetStringArray(t *testing.T) {\n\ttype want struct {\n\t\tvalue []string\n\t\terr   error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tpath   string\n\t\tdata   []byte\n\t\twant   want\n\t}{\n\t\t\"MetadataLabels\": {\n\t\t\treason: \"It should be possible to get a field from a nested object\",\n\t\t\tpath:   \"spec.containers[0].command\",\n\t\t\tdata:   []byte(`{\"spec\": {\"containers\": [{\"command\": [\"/bin/bash\"]}]}}`),\n\t\t\twant: want{\n\t\t\t\tvalue: []string{\"/bin/bash\"},\n\t\t\t},\n\t\t},\n\t\t\"MalformedPath\": {\n\t\t\treason: \"Requesting an invalid field path should fail\",\n\t\t\tpath:   \"spec[]\",\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errors.New(\"unexpected ']' at position 5\"), \"cannot parse path \\\"spec[]\\\"\"),\n\t\t\t},\n\t\t},\n\t\t\"NotAnArray\": {\n\t\t\treason: \"Requesting an non-object field path should fail\",\n\t\t\tpath:   \"metadata.version\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"version\":2}}`),\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"metadata.version: not an array\"),\n\t\t\t},\n\t\t},\n\t\t\"NotAStringArray\": {\n\t\t\treason: \"Requesting an non-string-object field path should fail\",\n\t\t\tpath:   \"metadata.versions\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"versions\":[1,2]}}`),\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"metadata.versions: not an array of strings\"),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tin := make(map[string]any)\n\t\t\t_ = json.Unmarshal(tc.data, &in)\n\t\t\tp := Pave(in)\n\n\t\t\tgot, err := p.GetStringArray(tc.path)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.GetStringArray(%s): %s: -want error, +got error:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.value, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\np.GetStringArray(%s): %s: -want, +got:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetStringObject(t *testing.T) {\n\ttype want struct {\n\t\tvalue map[string]string\n\t\terr   error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tpath   string\n\t\tdata   []byte\n\t\twant   want\n\t}{\n\t\t\"MetadataLabels\": {\n\t\t\treason: \"It should be possible to get a field from a nested object\",\n\t\t\tpath:   \"metadata.labels\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"labels\":{\"cool\":\"true\"}}}`),\n\t\t\twant: want{\n\t\t\t\tvalue: map[string]string{\"cool\": \"true\"},\n\t\t\t},\n\t\t},\n\t\t\"MalformedPath\": {\n\t\t\treason: \"Requesting an invalid field path should fail\",\n\t\t\tpath:   \"spec[]\",\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errors.New(\"unexpected ']' at position 5\"), \"cannot parse path \\\"spec[]\\\"\"),\n\t\t\t},\n\t\t},\n\t\t\"NotAnObject\": {\n\t\t\treason: \"Requesting an non-object field path should fail\",\n\t\t\tpath:   \"metadata.version\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"version\":2}}`),\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"metadata.version: not an object\"),\n\t\t\t},\n\t\t},\n\t\t\"NotAStringObject\": {\n\t\t\treason: \"Requesting an non-string-object field path should fail\",\n\t\t\tpath:   \"metadata.versions\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"versions\":{\"a\": 2}}}`),\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"metadata.versions: not an object with string field values\"),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tin := make(map[string]any)\n\t\t\t_ = json.Unmarshal(tc.data, &in)\n\t\t\tp := Pave(in)\n\n\t\t\tgot, err := p.GetStringObject(tc.path)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.GetStringObject(%s): %s: -want error, +got error:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.value, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\np.GetStringObject(%s): %s: -want, +got:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetBool(t *testing.T) {\n\ttype want struct {\n\t\tvalue bool\n\t\terr   error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tpath   string\n\t\tdata   []byte\n\t\twant   want\n\t}{\n\t\t\"OwnerRefController\": {\n\t\t\treason: \"Requesting a boolean field path should work.\",\n\t\t\tpath:   \"metadata.ownerRefs[0].controller\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"ownerRefs\":[{\"controller\": true}]}}`),\n\t\t\twant: want{\n\t\t\t\tvalue: true,\n\t\t\t},\n\t\t},\n\t\t\"MalformedPath\": {\n\t\t\treason: \"Requesting an invalid field path should fail\",\n\t\t\tpath:   \"spec[]\",\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errors.New(\"unexpected ']' at position 5\"), \"cannot parse path \\\"spec[]\\\"\"),\n\t\t\t},\n\t\t},\n\t\t\"NotABool\": {\n\t\t\treason: \"Requesting an non-boolean field path should fail\",\n\t\t\tpath:   \"metadata.name\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"name\":\"cool\"}}`),\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"metadata.name: not a bool\"),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tin := make(map[string]any)\n\t\t\t_ = json.Unmarshal(tc.data, &in)\n\t\t\tp := Pave(in)\n\n\t\t\tgot, err := p.GetBool(tc.path)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.GetBool(%s): %s: -want error, +got error:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.value, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\np.GetBool(%s): %s: -want, +got:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetInteger(t *testing.T) {\n\ttype want struct {\n\t\tvalue int64\n\t\terr   error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tpath   string\n\t\tdata   []byte\n\t\twant   want\n\t}{\n\t\t\"MetadataVersion\": {\n\t\t\treason: \"Requesting a number field should work\",\n\t\t\tpath:   \"metadata.version\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"version\":2}}`),\n\t\t\twant: want{\n\t\t\t\tvalue: 2,\n\t\t\t},\n\t\t},\n\t\t\"MalformedPath\": {\n\t\t\treason: \"Requesting an invalid field path should fail\",\n\t\t\tpath:   \"spec[]\",\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errors.New(\"unexpected ']' at position 5\"), \"cannot parse path \\\"spec[]\\\"\"),\n\t\t\t},\n\t\t},\n\t\t\"NotANumber\": {\n\t\t\treason: \"Requesting an non-number field path should fail\",\n\t\t\tpath:   \"metadata.name\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"name\":\"cool\"}}`),\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"metadata.name: not a (int64) number\"),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tin := make(map[string]any)\n\t\t\t_ = json.Unmarshal(tc.data, &in)\n\t\t\tp := Pave(in)\n\n\t\t\tgot, err := p.GetInteger(tc.path)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.GetNumber(%s): %s: -want error, +got error:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.value, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\np.GetNumber(%s): %s: -want, +got:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSetValue(t *testing.T) {\n\ttype args struct {\n\t\tpath  string\n\t\tvalue any\n\t\topts  []PavedOption\n\t}\n\n\ttype want struct {\n\t\tobject map[string]any\n\t\terr    error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tdata   []byte\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"MetadataName\": {\n\t\t\treason: \"Setting an object field should work\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"name\":\"lame\"}}`),\n\t\t\targs: args{\n\t\t\t\tpath:  \"metadata.name\",\n\t\t\t\tvalue: \"cool\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"metadata\": map[string]any{\n\t\t\t\t\t\t\"name\": \"cool\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"NonExistentMetadataName\": {\n\t\t\treason: \"Setting a non-existent object field should work\",\n\t\t\tdata:   []byte(`{}`),\n\t\t\targs: args{\n\t\t\t\tpath:  \"metadata.name\",\n\t\t\t\tvalue: \"cool\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"metadata\": map[string]any{\n\t\t\t\t\t\t\"name\": \"cool\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"ContainerName\": {\n\t\t\treason: \"Setting a field of an object that is an array element should work\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\":[{\"name\":\"lame\"}]}}`),\n\t\t\targs: args{\n\t\t\t\tpath:  \"spec.containers[0].name\",\n\t\t\t\tvalue: \"cool\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"spec\": map[string]any{\n\t\t\t\t\t\t\"containers\": []any{\n\t\t\t\t\t\t\tmap[string]any{\n\t\t\t\t\t\t\t\t\"name\": \"cool\",\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\"NonExistentContainerName\": {\n\t\t\treason: \"Setting a field of a non-existent object that is an array element should work\",\n\t\t\tdata:   []byte(`{}`),\n\t\t\targs: args{\n\t\t\t\tpath:  \"spec.containers[0].name\",\n\t\t\t\tvalue: \"cool\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"spec\": map[string]any{\n\t\t\t\t\t\t\"containers\": []any{\n\t\t\t\t\t\t\tmap[string]any{\n\t\t\t\t\t\t\t\t\"name\": \"cool\",\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\"NewContainer\": {\n\t\t\treason: \"Growing an array object field should work\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\":[{\"name\":\"cool\"}]}}`),\n\t\t\targs: args{\n\t\t\t\tpath:  \"spec.containers[1].name\",\n\t\t\t\tvalue: \"cooler\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"spec\": map[string]any{\n\t\t\t\t\t\t\"containers\": []any{\n\t\t\t\t\t\t\tmap[string]any{\n\t\t\t\t\t\t\t\t\"name\": \"cool\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tmap[string]any{\n\t\t\t\t\t\t\t\t\"name\": \"cooler\",\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\"NestedArray\": {\n\t\t\treason: \"Setting a value in a nested array should work\",\n\t\t\tdata:   []byte(`{}`),\n\t\t\targs: args{\n\t\t\t\tpath:  \"data[0][0]\",\n\t\t\t\tvalue: \"a\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"data\": []any{\n\t\t\t\t\t\t[]any{\"a\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"GrowNestedArray\": {\n\t\t\treason: \"Growing then setting a value in a nested array should work\",\n\t\t\tdata:   []byte(`{\"data\":[[\"a\"]]}`),\n\t\t\targs: args{\n\t\t\t\tpath:  \"data[0][1]\",\n\t\t\t\tvalue: \"b\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"data\": []any{\n\t\t\t\t\t\t[]any{\"a\", \"b\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"GrowArrayField\": {\n\t\t\treason: \"Growing then setting a value in an array field should work\",\n\t\t\tdata:   []byte(`{\"data\":[\"a\"]}`),\n\t\t\targs: args{\n\t\t\t\tpath:  \"data[2]\",\n\t\t\t\tvalue: \"c\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"data\": []any{\"a\", nil, \"c\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"RejectsHighIndexes\": {\n\t\t\treason: \"Paths having indexes above the maximum default value are rejected\",\n\t\t\tdata:   []byte(`{\"data\":[\"a\"]}`),\n\t\t\targs: args{\n\t\t\t\tpath:  fmt.Sprintf(\"data[%v]\", DefaultMaxFieldPathIndex+1),\n\t\t\t\tvalue: \"c\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"data\": []any{\"a\"},\n\t\t\t\t},\n\t\t\t\terr: errors.Errorf(\"index %v is greater than max allowed index %v\",\n\t\t\t\t\tDefaultMaxFieldPathIndex+1, DefaultMaxFieldPathIndex),\n\t\t\t},\n\t\t},\n\t\t\"NotRejectsHighIndexesIfNoDefaultOptions\": {\n\t\t\treason: \"Paths having indexes above the maximum default value are not rejected if default disabled\",\n\t\t\tdata:   []byte(`{\"data\":[\"a\"]}`),\n\t\t\targs: args{\n\t\t\t\tpath:  fmt.Sprintf(\"data[%v]\", DefaultMaxFieldPathIndex+1),\n\t\t\t\tvalue: \"c\",\n\t\t\t\topts:  []PavedOption{WithMaxFieldPathIndex(0)},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"data\": func() []any {\n\t\t\t\t\t\tres := make([]any, DefaultMaxFieldPathIndex+2)\n\t\t\t\t\t\tres[0] = \"a\"\n\t\t\t\t\t\tres[DefaultMaxFieldPathIndex+1] = \"c\"\n\n\t\t\t\t\t\treturn res\n\t\t\t\t\t}(),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"MapStringString\": {\n\t\t\treason: \"A map of string to string should be converted to a map of string to any\",\n\t\t\tdata:   []byte(`{\"metadata\":{}}`),\n\t\t\targs: args{\n\t\t\t\tpath:  \"metadata.labels\",\n\t\t\t\tvalue: map[string]string{\"cool\": \"very\"},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"metadata\": map[string]any{\n\t\t\t\t\t\t\"labels\": map[string]any{\"cool\": \"very\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"OwnerReference\": {\n\t\t\treason: \"An ObjectReference (i.e. struct) should be converted to a map of string to any\",\n\t\t\tdata:   []byte(`{\"metadata\":{}}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"metadata.ownerRefs[0]\",\n\t\t\t\tvalue: metav1.OwnerReference{\n\t\t\t\t\tAPIVersion: \"v\",\n\t\t\t\t\tKind:       \"k\",\n\t\t\t\t\tName:       \"n\",\n\t\t\t\t\tUID:        types.UID(\"u\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"metadata\": map[string]any{\n\t\t\t\t\t\t\"ownerRefs\": []any{\n\t\t\t\t\t\t\tmap[string]any{\n\t\t\t\t\t\t\t\t\"apiVersion\": \"v\",\n\t\t\t\t\t\t\t\t\"kind\":       \"k\",\n\t\t\t\t\t\t\t\t\"name\":       \"n\",\n\t\t\t\t\t\t\t\t\"uid\":        \"u\",\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\"NotAnArray\": {\n\t\t\treason: \"Indexing an object field should fail\",\n\t\t\tdata:   []byte(`{\"data\":{}}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"data[0]\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\"data\": map[string]any{}},\n\t\t\t\terr:    errors.New(\"data is not an array\"),\n\t\t\t},\n\t\t},\n\t\t\"NotAnObject\": {\n\t\t\treason: \"Requesting a field in an array should fail\",\n\t\t\tdata:   []byte(`{\"data\":[]}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"data.name\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\"data\": []any{}},\n\t\t\t\terr:    errors.New(\"data is not an object\"),\n\t\t\t},\n\t\t},\n\t\t\"MalformedPath\": {\n\t\t\treason: \"Requesting an invalid field path should fail\",\n\t\t\targs: args{\n\t\t\t\tpath: \"spec[]\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{},\n\t\t\t\terr:    errors.Wrap(errors.New(\"unexpected ']' at position 5\"), \"cannot parse path \\\"spec[]\\\"\"),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tin := make(map[string]any)\n\t\t\t_ = json.Unmarshal(tc.data, &in)\n\t\t\tp := Pave(in, tc.args.opts...)\n\n\t\t\terr := p.SetValue(tc.args.path, tc.args.value)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.SetValue(%s, %v): %s: -want error, +got error:\\n%s\", tc.args.path, tc.args.value, tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.object, p.object); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.SetValue(%s, %v): %s: -want, +got:\\n%s\", tc.args.path, tc.args.value, tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExpandWildcards(t *testing.T) {\n\ttype want struct {\n\t\texpanded []string\n\t\terr      error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tpath   string\n\t\tdata   []byte\n\t\twant   want\n\t}{\n\t\t\"NoWildcardExisting\": {\n\t\t\treason: \"It should return same path if no wildcard in an existing path\",\n\t\t\tpath:   \"password\",\n\t\t\tdata:   []byte(`{\"password\":\"top-secret\"}`),\n\t\t\twant: want{\n\t\t\t\texpanded: []string{\"password\"},\n\t\t\t},\n\t\t},\n\t\t\"NoWildcardNonExisting\": {\n\t\t\treason: \"It should return no results if no wildcard in a non-existing path\",\n\t\t\tpath:   \"username\",\n\t\t\tdata:   []byte(`{\"password\":\"top-secret\"}`),\n\t\t\twant: want{\n\t\t\t\texpanded: []string{},\n\t\t\t},\n\t\t},\n\t\t\"NestedNoWildcardExisting\": {\n\t\t\treason: \"It should return same path if no wildcard in an existing path\",\n\t\t\tpath:   \"items[0][1]\",\n\t\t\tdata:   []byte(`{\"items\":[[\"a\", \"b\"]]}`),\n\t\t\twant: want{\n\t\t\t\texpanded: []string{\"items[0][1]\"},\n\t\t\t},\n\t\t},\n\t\t\"NestedNoWildcardNonExisting\": {\n\t\t\treason: \"It should return no results if no wildcard in a non-existing path\",\n\t\t\tpath:   \"items[0][5]\",\n\t\t\tdata:   []byte(`{\"items\":[[\"a\", \"b\"]]}`),\n\t\t\twant: want{\n\t\t\t\texpanded: []string{},\n\t\t\t},\n\t\t},\n\t\t\"NestedArray\": {\n\t\t\treason: \"It should return all possible paths for an array\",\n\t\t\tpath:   \"items[*][*]\",\n\t\t\tdata:   []byte(`{\"items\":[[\"a\", \"b\", \"c\"], [\"d\"]]}`),\n\t\t\twant: want{\n\t\t\t\texpanded: []string{\"items[0][0]\", \"items[0][1]\", \"items[0][2]\", \"items[1][0]\"},\n\t\t\t},\n\t\t},\n\t\t\"KeysOfMap\": {\n\t\t\treason: \"It should return all possible paths for a map in proper syntax\",\n\t\t\tpath:   \"items[*]\",\n\t\t\tdata:   []byte(`{\"items\":{ \"key1\": \"val1\", \"key2.as.annotation\": \"val2\"}}`),\n\t\t\twant: want{\n\t\t\t\texpanded: []string{\"items.key1\", \"items[key2.as.annotation]\"},\n\t\t\t},\n\t\t},\n\t\t\"ArrayOfObjects\": {\n\t\t\treason: \"It should return all possible paths for an array of objects\",\n\t\t\tpath:   \"spec.containers[*][*]\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\":[{\"name\":\"cool\", \"image\": \"latest\", \"args\": [\"start\", \"now\"]}]}}`),\n\t\t\twant: want{\n\t\t\t\texpanded: []string{\"spec.containers[0].name\", \"spec.containers[0].image\", \"spec.containers[0].args\"},\n\t\t\t},\n\t\t},\n\t\t\"MultiLayer\": {\n\t\t\treason: \"It should return all possible paths for a multilayer input\",\n\t\t\tpath:   \"spec.containers[*].args[*]\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\":[{\"name\":\"cool\", \"image\": \"latest\", \"args\": [\"start\", \"now\", \"debug\"]}]}}`),\n\t\t\twant: want{\n\t\t\t\texpanded: []string{\"spec.containers[0].args[0]\", \"spec.containers[0].args[1]\", \"spec.containers[0].args[2]\"},\n\t\t\t},\n\t\t},\n\t\t\"WildcardInTheBeginning\": {\n\t\t\treason: \"It should return all possible paths for a multilayer input with wildcard in the beginning\",\n\t\t\tpath:   \"spec.containers[*].args[1]\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\":[{\"name\":\"cool\", \"image\": \"latest\", \"args\": [\"start\", \"now\", \"debug\"]}]}}`),\n\t\t\twant: want{\n\t\t\t\texpanded: []string{\"spec.containers[0].args[1]\"},\n\t\t\t},\n\t\t},\n\t\t\"WildcardAtTheEnd\": {\n\t\t\treason: \"It should return all possible paths for a multilayer input with wildcard at the end\",\n\t\t\tpath:   \"spec.containers[0].args[*]\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\":[{\"name\":\"cool\", \"image\": \"latest\", \"args\": [\"start\", \"now\", \"debug\"]}]}}`),\n\t\t\twant: want{\n\t\t\t\texpanded: []string{\"spec.containers[0].args[0]\", \"spec.containers[0].args[1]\", \"spec.containers[0].args[2]\"},\n\t\t\t},\n\t\t},\n\t\t\"NoData\": {\n\t\t\treason: \"If there is no input data, no expanded fields could be found\",\n\t\t\tpath:   \"metadata[*]\",\n\t\t\tdata:   nil,\n\t\t\twant: want{\n\t\t\t\texpanded: []string{},\n\t\t\t},\n\t\t},\n\t\t\"InsufficientContainers\": {\n\t\t\treason: \"Requesting a non-existent array element should return nothing\",\n\t\t\tpath:   \"spec.containers[1].args[*]\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\":[{\"name\":\"cool\"}]}}`),\n\t\t\twant: want{\n\t\t\t\texpanded: []string{},\n\t\t\t},\n\t\t},\n\t\t\"UnexpectedWildcard\": {\n\t\t\treason: \"Requesting a wildcard for an object should fail\",\n\t\t\tpath:   \"spec.containers[0].name[*]\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\":[{\"name\":\"cool\"}]}}`),\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrapf(errors.Errorf(\"%q: unexpected wildcard usage\", \"spec.containers[0].name\"), \"cannot expand wildcards for segments: %q\", \"spec.containers[0].name[*]\"),\n\t\t\t},\n\t\t},\n\t\t\"NotAnArray\": {\n\t\t\treason: \"Indexing an object should fail\",\n\t\t\tpath:   \"metadata[1]\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"nope\":\"cool\"}}`),\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrapf(errors.New(\"metadata: not an array\"), \"cannot expand wildcards for segments: %q\", \"metadata[1]\"),\n\t\t\t},\n\t\t},\n\t\t\"NotAnObject\": {\n\t\t\treason: \"Requesting a field in an array should fail\",\n\t\t\tpath:   \"spec.containers[nope].name\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\":[{\"name\":\"cool\"}]}}`),\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrapf(errors.New(\"spec.containers: not an object\"), \"cannot expand wildcards for segments: %q\", \"spec.containers.nope.name\"),\n\t\t\t},\n\t\t},\n\t\t\"MalformedPath\": {\n\t\t\treason: \"Requesting an invalid field path should fail\",\n\t\t\tpath:   \"spec[]\",\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errors.New(\"unexpected ']' at position 5\"), \"cannot parse path \\\"spec[]\\\"\"),\n\t\t\t},\n\t\t},\n\t\t\"NilValue\": {\n\t\t\treason: \"Requesting a wildcard for an object that has nil value\",\n\t\t\tpath:   \"spec.containers[*].name\",\n\t\t\tdata:   []byte(`{\"spec\":{\"containers\": null}}`),\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrapf(notFoundError{errors.Errorf(\"wildcard field %q is not found in the path\", \"spec.containers\")}, \"cannot expand wildcards for segments: %q\", \"spec.containers[*].name\"),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tin := make(map[string]any)\n\t\t\t_ = json.Unmarshal(tc.data, &in)\n\t\t\tp := Pave(in)\n\n\t\t\tgot, err := p.ExpandWildcards(tc.path)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.ExpandWildcards(%s): %s: -want error, +got error:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.expanded, got, cmpopts.SortSlices(func(x, y string) bool {\n\t\t\t\treturn x < y\n\t\t\t})); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\np.ExpandWildcards(%s): %s: -want, +got:\\n%s\", tc.path, tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDeleteField(t *testing.T) {\n\ttype args struct {\n\t\tpath string\n\t}\n\n\ttype want struct {\n\t\tobject map[string]any\n\t\terr    error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tdata   []byte\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"MalformedPath\": {\n\t\t\treason: \"Requesting an invalid field path should fail\",\n\t\t\targs: args{\n\t\t\t\tpath: \"spec[]\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{},\n\t\t\t\terr:    errors.Wrap(errors.New(\"unexpected ']' at position 5\"), \"cannot parse path \\\"spec[]\\\"\"),\n\t\t\t},\n\t\t},\n\t\t\"IndexGivenForNonArray\": {\n\t\t\treason: \"Trying to delete a numbered index from a map should fail.\",\n\t\t\tdata:   []byte(`{\"data\":{}}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"data[0]\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\"data\": map[string]any{}},\n\t\t\t\terr:    errors.Wrap(errors.New(\"not an array\"), \"cannot delete data[0]\"),\n\t\t\t},\n\t\t},\n\t\t\"KeyGivenForNonMap\": {\n\t\t\treason: \"Trying to delete a key from an array should fail.\",\n\t\t\tdata:   []byte(`{\"data\":[[\"a\"]]}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"data[0].a\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\"data\": []any{[]any{\"a\"}}},\n\t\t\t\terr:    errors.Wrap(errors.New(\"not an object\"), \"cannot delete data[0].a\"),\n\t\t\t},\n\t\t},\n\t\t\"KeyGivenForNonMapInMiddle\": {\n\t\t\treason: \"If one of the segments that is a field corresponds to array, it should fail.\",\n\t\t\tdata:   []byte(`{\"data\":[{\"another\": \"field\"}]}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"data.some.another\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\"data\": []any{\n\t\t\t\t\tmap[string]any{\n\t\t\t\t\t\t\"another\": \"field\",\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t\terr: errors.New(\"data is not an object\"),\n\t\t\t},\n\t\t},\n\t\t\"IndexGivenForNonArrayInMiddle\": {\n\t\t\treason: \"If one of the segments that is an index corresponds to map, it should fail.\",\n\t\t\tdata:   []byte(`{\"data\":{\"another\": [\"field\"]}}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"data[0].another\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\"data\": map[string]any{\n\t\t\t\t\t\"another\": []any{\n\t\t\t\t\t\t\"field\",\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t\terr: errors.New(\"data is not an array\"),\n\t\t\t},\n\t\t},\n\t\t\"ObjectField\": {\n\t\t\treason: \"Deleting a field from a map should work.\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"name\":\"lame\"}}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"metadata.name\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"metadata\": map[string]any{},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"ObjectSingleField\": {\n\t\t\treason: \"Deleting a field from a map should work.\",\n\t\t\tdata:   []byte(`{\"metadata\":{\"name\":\"lame\"}, \"olala\": {\"omama\": \"koala\"}}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"metadata\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"olala\": map[string]any{\n\t\t\t\t\t\t\"omama\": \"koala\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"ObjectLeafField\": {\n\t\t\treason: \"Deleting a field that is deep in the tree from a map should work.\",\n\t\t\tdata:   []byte(`{\"spec\":{\"some\": {\"more\": \"delete-me\"}}}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"spec.some.more\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"spec\": map[string]any{\n\t\t\t\t\t\t\"some\": map[string]any{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"ObjectMidField\": {\n\t\t\treason: \"Deleting a field that is in the middle of the tree from a map should work.\",\n\t\t\tdata:   []byte(`{\"spec\":{\"some\": {\"more\": \"delete-me\"}}}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"spec.some\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"spec\": map[string]any{},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"ObjectInArray\": {\n\t\t\treason: \"Deleting a field that is in the middle of the tree from a map should work.\",\n\t\t\tdata:   []byte(`{\"spec\":[{\"some\": {\"more\": \"delete-me\"}}]}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"spec[0].some.more\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"spec\": []any{\n\t\t\t\t\t\tmap[string]any{\n\t\t\t\t\t\t\t\"some\": map[string]any{},\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\"ArrayFirstElement\": {\n\t\t\treason: \"Deleting the first element from an array should work\",\n\t\t\tdata:   []byte(`{\"items\":[\"a\", \"b\"]}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"items[0]\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"items\": []any{\n\t\t\t\t\t\t\"b\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"ArrayLastElement\": {\n\t\t\treason: \"Deleting the last element from an array should work\",\n\t\t\tdata:   []byte(`{\"items\":[\"a\", \"b\"]}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"items[1]\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"items\": []any{\n\t\t\t\t\t\t\"a\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"ArrayMidElement\": {\n\t\t\treason: \"Deleting an element that is neither first nor last from an array should work\",\n\t\t\tdata:   []byte(`{\"items\":[\"a\", \"b\", \"c\"]}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"items[1]\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"items\": []any{\n\t\t\t\t\t\t\"a\",\n\t\t\t\t\t\t\"c\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"ArrayOnlyElements\": {\n\t\t\treason: \"Deleting the only element from an array should work\",\n\t\t\tdata:   []byte(`{\"items\":[\"a\"]}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"items[0]\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"items\": []any{},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"ArrayMultipleIndex\": {\n\t\t\treason: \"Deleting an element from an array of array should work\",\n\t\t\tdata:   []byte(`{\"items\":[[\"a\", \"b\"]]}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"items[0][1]\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"items\": []any{\n\t\t\t\t\t\t[]any{\n\t\t\t\t\t\t\t\"a\",\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\"ArrayNoElement\": {\n\t\t\treason: \"Deleting an element from an empty array should work\",\n\t\t\tdata:   []byte(`{\"items\":[]}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"items[0]\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"items\": []any{},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"NonExistentPathInMap\": {\n\t\t\treason: \"It should be no-op if the field does not exist already.\",\n\t\t\tdata:   []byte(`{\"items\":[]}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"items[0].metadata\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"items\": []any{},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"NonExistentPathInArray\": {\n\t\t\treason: \"It should be no-op if the field does not exist already.\",\n\t\t\tdata:   []byte(`{\"items\":{\"some\": \"other\"}}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"items.metadata[0]\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"items\": map[string]any{\n\t\t\t\t\t\t\"some\": \"other\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"NonExistentElementInArray\": {\n\t\t\treason: \"It should be no-op if the field does not exist already.\",\n\t\t\tdata:   []byte(`{\"items\":[\"some\", \"other\"]}`),\n\t\t\targs: args{\n\t\t\t\tpath: \"items[5]\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobject: map[string]any{\n\t\t\t\t\t\"items\": []any{\n\t\t\t\t\t\t\"some\", \"other\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tin := make(map[string]any)\n\t\t\t_ = json.Unmarshal(tc.data, &in)\n\t\t\tp := Pave(in)\n\n\t\t\terr := p.DeleteField(tc.args.path)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.DeleteField(%s): %s: -want error, +got error:\\n%s\", tc.args.path, tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.object, p.object); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\np.DeleteField(%s): %s: -want, +got:\\n%s\", tc.args.path, tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/gate/gate.go",
    "content": "/*\nCopyright 2025 The Crossplane 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// Package gate contains a gated function callback registration implementation.\npackage gate\n\nimport (\n\t\"slices\"\n\t\"sync\"\n)\n\n// Gate implements a gated function callback registration with comparable conditions.\ntype Gate[T comparable] struct {\n\tmux       sync.RWMutex\n\tsatisfied map[T]bool\n\tfns       []gated[T]\n}\n\n// gated is an internal tracking resource.\ntype gated[T comparable] struct {\n\t// fn is the function callback we will invoke when all the dependent conditions are true.\n\tfn func()\n\t// depends is the list of conditions this gated function is waiting on. This is an AND.\n\tdepends []T\n\t// released means the gated function has been invoked and we can garbage collect this gated function.\n\treleased bool\n}\n\n// Register a callback function that will be called when all the provided dependent conditions are true.\n// After all conditions are true, the callback function is removed from the registration and will not be called again.\n// Thread Safe.\nfunc (g *Gate[T]) Register(fn func(), depends ...T) {\n\tg.mux.Lock()\n\tg.fns = append(g.fns, gated[T]{fn: fn, depends: depends})\n\tg.mux.Unlock()\n\n\tg.process()\n}\n\n// Set marks the associated condition to the given value. If the condition is already set as that value, then this is a\n// no-op. Returns true if there was an update detected. Thread safe.\nfunc (g *Gate[T]) Set(condition T, value bool) bool {\n\tg.mux.Lock()\n\n\tif g.satisfied == nil {\n\t\tg.satisfied = make(map[T]bool)\n\t}\n\n\told, found := g.satisfied[condition]\n\n\tupdated := false\n\tif !found || old != value {\n\t\tupdated = true\n\t\tg.satisfied[condition] = value\n\t}\n\t// process() would also like to lock the mux, so we must unlock here directly and not use defer.\n\tg.mux.Unlock()\n\n\tif updated {\n\t\tg.process()\n\t}\n\n\treturn updated\n}\n\nfunc (g *Gate[T]) process() {\n\tg.mux.Lock()\n\tdefer g.mux.Unlock()\n\n\tfor i := range g.fns {\n\t\t// release controls if we should release the function.\n\t\trelease := true\n\n\t\tfor _, dep := range g.fns[i].depends {\n\t\t\tif !g.satisfied[dep] {\n\t\t\t\trelease = false\n\t\t\t}\n\t\t}\n\n\t\tif release {\n\t\t\tfn := g.fns[i].fn\n\t\t\t// mark the function released so we can garbage collect after we are done with the loop.\n\t\t\tg.fns[i].released = true\n\t\t\t// Need to capture a copy of fn or else we would be accessing a deleted member when the go routine runs.\n\t\t\tgo fn()\n\t\t}\n\t}\n\n\t// garbage collect released functions.\n\tg.fns = slices.DeleteFunc(g.fns, func(a gated[T]) bool {\n\t\treturn a.released\n\t})\n}\n"
  },
  {
    "path": "pkg/gate/gate_test.go",
    "content": "/*\nCopyright 2025 The Crossplane 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 gate_test\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/gate\"\n)\n\nfunc TestGateRegister(t *testing.T) {\n\ttype args struct {\n\t\tdepends []string\n\t}\n\n\ttype want struct {\n\t\tcalled bool\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"NoDependencies\": {\n\t\t\treason: \"Should immediately call function when no dependencies are required\",\n\t\t\targs: args{\n\t\t\t\tdepends: []string{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcalled: true,\n\t\t\t},\n\t\t},\n\t\t\"SingleDependency\": {\n\t\t\treason: \"Should not call function when dependency is not met\",\n\t\t\targs: args{\n\t\t\t\tdepends: []string{\"condition1\"},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcalled: false,\n\t\t\t},\n\t\t},\n\t\t\"MultipleDependencies\": {\n\t\t\treason: \"Should not call function when multiple dependencies are not met\",\n\t\t\targs: args{\n\t\t\t\tdepends: []string{\"condition1\", \"condition2\"},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcalled: false,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tg := new(gate.Gate[string])\n\n\t\t\tcalled := false\n\n\t\t\tg.Register(func() {\n\t\t\t\tcalled = true\n\t\t\t}, tc.args.depends...)\n\n\t\t\t// Give some time for goroutine to execute\n\t\t\ttime.Sleep(10 * time.Millisecond)\n\n\t\t\tif diff := cmp.Diff(tc.want.called, called); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nRegister(...): -want called, +got called:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGateIntegration(t *testing.T) {\n\ttype want struct {\n\t\tcalled bool\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tsetup  func(g *gate.Gate[string]) chan bool\n\t\twant   want\n\t}{\n\t\t\"SingleDependencyMet\": {\n\t\t\treason: \"Should call function when single dependency is met\",\n\t\t\tsetup: func(g *gate.Gate[string]) chan bool {\n\t\t\t\tcalled := make(chan bool, 1)\n\n\t\t\t\tg.Register(func() {\n\t\t\t\t\tcalled <- true\n\t\t\t\t}, \"condition1\")\n\n\t\t\t\t// Set condition to true (will be initialized as false first)\n\t\t\t\tg.Set(\"condition1\", true)\n\n\t\t\t\treturn called\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcalled: true,\n\t\t\t},\n\t\t},\n\t\t\"MultipleDependenciesMet\": {\n\t\t\treason: \"Should call function when all dependencies are met\",\n\t\t\tsetup: func(g *gate.Gate[string]) chan bool {\n\t\t\t\tcalled := make(chan bool, 1)\n\n\t\t\t\tg.Register(func() {\n\t\t\t\t\tcalled <- true\n\t\t\t\t}, \"condition1\", \"condition2\")\n\n\t\t\t\t// Set both conditions to true\n\t\t\t\tg.Set(\"condition1\", true)\n\t\t\t\tg.Set(\"condition2\", true)\n\n\t\t\t\treturn called\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcalled: true,\n\t\t\t},\n\t\t},\n\t\t\"PartialDependenciesMet\": {\n\t\t\treason: \"Should not call function when only some dependencies are met\",\n\t\t\tsetup: func(g *gate.Gate[string]) chan bool {\n\t\t\t\tcalled := make(chan bool, 1)\n\n\t\t\t\tg.Register(func() {\n\t\t\t\t\tcalled <- true\n\t\t\t\t}, \"condition1\", \"condition2\")\n\n\t\t\t\t// Set only one condition to true\n\t\t\t\tg.Set(\"condition1\", true)\n\n\t\t\t\treturn called\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcalled: false,\n\t\t\t},\n\t\t},\n\t\t\"DependenciesAlreadyMet\": {\n\t\t\treason: \"Should call function when dependencies are already met\",\n\t\t\tsetup: func(g *gate.Gate[string]) chan bool {\n\t\t\t\tcalled := make(chan bool, 1)\n\n\t\t\t\tg.Set(\"condition1\", true)\n\t\t\t\tg.Set(\"condition2\", true)\n\n\t\t\t\tg.Register(func() {\n\t\t\t\t\tcalled <- true\n\t\t\t\t}, \"condition1\", \"condition2\")\n\n\t\t\t\treturn called\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcalled: true,\n\t\t\t},\n\t\t},\n\t\t\"DependencySetThenUnset\": {\n\t\t\treason: \"Should call function when dependency is met, even if unset later\",\n\t\t\tsetup: func(g *gate.Gate[string]) chan bool {\n\t\t\t\tcalled := make(chan bool, 1)\n\n\t\t\t\tg.Register(func() {\n\t\t\t\t\tcalled <- true\n\t\t\t\t}, \"condition1\")\n\n\t\t\t\t// Set condition to true then false (function already called when true)\n\t\t\t\tg.Set(\"condition1\", true)\n\t\t\t\tg.Set(\"condition1\", false)\n\n\t\t\t\treturn called\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcalled: true,\n\t\t\t},\n\t\t},\n\t\t\"FunctionCalledOnlyOnce\": {\n\t\t\treason: \"Should call function only once even if conditions change after\",\n\t\t\tsetup: func(g *gate.Gate[string]) chan bool {\n\t\t\t\tcalled := make(chan bool, 2) // Buffer for potential multiple calls\n\n\t\t\t\tg.Register(func() {\n\t\t\t\t\tcalled <- true\n\t\t\t\t}, \"condition1\")\n\n\t\t\t\t// Set condition multiple times\n\t\t\t\tg.Set(\"condition1\", true)\n\t\t\t\tg.Set(\"condition1\", false)\n\t\t\t\tg.Set(\"condition1\", true)\n\n\t\t\t\treturn called\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcalled: true,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tg := new(gate.Gate[string])\n\n\t\t\tcallChannel := tc.setup(g)\n\n\t\t\tvar got bool\n\t\t\tselect {\n\t\t\tcase got = <-callChannel:\n\t\t\tcase <-time.After(100 * time.Millisecond):\n\t\t\t\tgot = false\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.called, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIntegration test: -want called, +got called:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\t// For the \"only once\" test, ensure no additional calls\n\t\t\tif name == \"FunctionCalledOnlyOnce\" && tc.want.called {\n\t\t\t\tselect {\n\t\t\t\tcase <-callChannel:\n\t\t\t\t\tt.Errorf(\"\\n%s\\nFunction was called more than once\", tc.reason)\n\t\t\t\tcase <-time.After(50 * time.Millisecond):\n\t\t\t\t\t// Good - no additional calls\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGateConcurrency(t *testing.T) {\n\tg := new(gate.Gate[string])\n\n\tconst numGoroutines = 100\n\n\tvar wg sync.WaitGroup\n\n\tcallCount := make(chan struct{}, numGoroutines)\n\n\t// Register functions concurrently\n\tfor range numGoroutines {\n\t\twg.Go(func() {\n\t\t\tg.Register(func() {\n\t\t\t\tcallCount <- struct{}{}\n\t\t\t}, \"shared-condition\")\n\t\t})\n\t}\n\n\t// Wait for all registrations\n\twg.Wait()\n\n\t// Set condition to true once\n\tg.Set(\"shared-condition\", true)\n\n\t// Give some time for goroutines to execute\n\ttime.Sleep(100 * time.Millisecond)\n\n\t// Count how many functions were called\n\tclose(callCount)\n\n\tcount := 0\n\tfor range callCount {\n\t\tcount++\n\t}\n\n\tif count != numGoroutines {\n\t\tt.Errorf(\"Expected %d function calls, got %d\", numGoroutines, count)\n\t}\n}\n\nfunc TestGateTypeSafety(t *testing.T) {\n\tintGate := new(gate.Gate[int])\n\n\tcalled := false\n\n\tintGate.Register(func() {\n\t\tcalled = true\n\t}, 1, 2, 3)\n\n\tintGate.Set(1, true)\n\tintGate.Set(2, true)\n\tintGate.Set(3, true)\n\n\t// Give some time for goroutine to execute\n\ttime.Sleep(10 * time.Millisecond)\n\n\tif !called {\n\t\tt.Error(\"Function should have been called when all int conditions were met\")\n\t}\n}\n"
  },
  {
    "path": "pkg/logging/klog.go",
    "content": "// Copyright 2024 Upbound Inc.\n// All rights reserved\n\npackage logging\n\nimport (\n\t\"flag\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/go-logr/logr\"\n\t\"k8s.io/klog/v2\"\n)\n\n// SetFilteredKlogLogger sets log as the logger backend of klog, but filtering\n// aggressively to avoid noise.\nfunc SetFilteredKlogLogger(log logr.Logger) {\n\t// initialize klog at verbosity level 3, dropping everything higher.\n\tfs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)\n\tklog.InitFlags(fs)\n\tfs.Parse([]string{\"--v=3\"}) //nolint:errcheck // we couldn't do anything here anyway\n\n\tklogr := logr.New(&requestThrottlingFilter{log.GetSink()})\n\tklog.SetLogger(klogr)\n}\n\n// requestThrottlingFilter drops everything that is not a client-go throttling\n// message, compare:\n// https://github.com/kubernetes/client-go/blob/8c4efe8d079e405329f314fb789a41ac6af101dc/rest/request.go#L621\ntype requestThrottlingFilter struct {\n\tlogr.LogSink\n}\n\nfunc (l *requestThrottlingFilter) Info(level int, msg string, keysAndValues ...any) {\n\tif !strings.Contains(msg, \"Waited for \") || !strings.Contains(msg, \"  request: \") {\n\t\treturn\n\t}\n\n\tl.LogSink.Info(l.klogToLogrLevel(level), msg, keysAndValues...)\n}\n\nfunc (l *requestThrottlingFilter) Enabled(level int) bool {\n\treturn l.LogSink.Enabled(l.klogToLogrLevel(level))\n}\n\nfunc (l *requestThrottlingFilter) klogToLogrLevel(klogLvl int) int {\n\t// we want a default klog level of 3 for info, 4 for debug, corresponding to\n\t// logr levels of 0 and 1.\n\tif klogLvl >= 3 {\n\t\treturn klogLvl - 3\n\t}\n\n\treturn 0\n}\n\nfunc (l *requestThrottlingFilter) WithCallDepth(depth int) logr.LogSink {\n\tif delegate, ok := l.LogSink.(logr.CallDepthLogSink); ok {\n\t\treturn &requestThrottlingFilter{LogSink: delegate.WithCallDepth(depth)}\n\t}\n\n\treturn l\n}\n"
  },
  {
    "path": "pkg/logging/logging.go",
    "content": "/*\nCopyright 2019 The Crossplane 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// Package logging provides Crossplane's recommended logging interface.\n//\n// The logging interface defined by this package is inspired by the following:\n//\n// * https://peter.bourgon.org/go-best-practices-2016/#logging-and-instrumentation\n// * https://dave.cheney.net/2015/11/05/lets-talk-about-logging\n// * https://dave.cheney.net/2017/01/23/the-package-level-logger-anti-pattern\n// * https://github.com/crossplane/crossplane/blob/c06433/design/one-pager-error-and-event-reporting.md\n//\n// It is similar to other logging interfaces inspired by said article, namely:\n//\n// * https://github.com/go-logr/logr\n// * https://github.com/go-log/log\n//\n// Crossplane prefers not to use go-logr because it desires a simpler API with\n// only two levels (per Dave's article); Info and Debug. Crossplane prefers not\n// to use go-log because it does not support structured logging. This package\n// *is* however a subset of go-logr's functionality, and is intended to wrap\n// go-logr (interfaces all the way down!), in order to maintain compatibility\n// with the https://github.com/kubernetes-sigs/controller-runtime/ log plumbing.\npackage logging\n\nimport (\n\t\"github.com/go-logr/logr\"\n)\n\n// A Logger logs messages. Messages may be supplemented by structured data.\ntype Logger interface {\n\t// Info logs a message with optional structured data. Structured data must\n\t// be supplied as an array that alternates between string keys and values of\n\t// an arbitrary type. Use Info for messages that Crossplane operators are\n\t// very likely to be concerned with when running Crossplane.\n\tInfo(msg string, keysAndValues ...any)\n\n\t// Debug logs a message with optional structured data. Structured data must\n\t// be supplied as an array that alternates between string keys and values of\n\t// an arbitrary type. Use Debug for messages that Crossplane operators or\n\t// developers may be concerned with when debugging Crossplane.\n\tDebug(msg string, keysAndValues ...any)\n\n\t// WithValues returns a Logger that will include the supplied structured\n\t// data with any subsequent messages it logs. Structured data must\n\t// be supplied as an array that alternates between string keys and values of\n\t// an arbitrary type.\n\tWithValues(keysAndValues ...any) Logger\n}\n\n// NewNopLogger returns a Logger that does nothing.\nfunc NewNopLogger() Logger { return nopLogger{} }\n\ntype nopLogger struct{}\n\nfunc (l nopLogger) Info(_ string, _ ...any)    {}\nfunc (l nopLogger) Debug(_ string, _ ...any)   {}\nfunc (l nopLogger) WithValues(_ ...any) Logger { return nopLogger{} }\n\n// NewLogrLogger returns a Logger that is satisfied by the supplied logr.Logger,\n// which may be satisfied in turn by various logging implementations (Zap, klog,\n// etc). Debug messages are logged at V(1).\nfunc NewLogrLogger(l logr.Logger) Logger {\n\treturn logrLogger{log: l}\n}\n\ntype logrLogger struct {\n\tlog logr.Logger\n}\n\nfunc (l logrLogger) Info(msg string, keysAndValues ...any) {\n\tl.log.Info(msg, keysAndValues...) //nolint:logrlint // False positive - logrlint thinks there's an odd number of args.\n}\n\nfunc (l logrLogger) Debug(msg string, keysAndValues ...any) {\n\tl.log.V(1).Info(msg, keysAndValues...) //nolint:logrlint // False positive - logrlint thinks there's an odd number of args.\n}\n\nfunc (l logrLogger) WithValues(keysAndValues ...any) Logger {\n\treturn logrLogger{log: l.log.WithValues(keysAndValues...)} //nolint:logrlint // False positive - logrlint thinks there's an odd number of args.\n}\n"
  },
  {
    "path": "pkg/meta/meta.go",
    "content": "/*\nCopyright 2019 The Crossplane 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// Package meta contains functions for dealing with Kubernetes object metadata.\npackage meta\n\nimport (\n\t\"maps\"\n\t\"slices\"\n\t\"time\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\nconst (\n\t// AnnotationKeyExternalName is the key in the annotations map of a\n\t// resource for the name of the resource as it appears on provider's\n\t// systems.\n\tAnnotationKeyExternalName = \"crossplane.io/external-name\"\n\n\t// AnnotationKeyExternalCreatePending is the key in the annotations map\n\t// of a resource that indicates the last time creation of the external\n\t// resource was pending (i.e. about to happen). Its value must be an\n\t// RFC3999 timestamp.\n\tAnnotationKeyExternalCreatePending = \"crossplane.io/external-create-pending\"\n\n\t// AnnotationKeyExternalCreateSucceeded is the key in the annotations\n\t// map of a resource that represents the last time the external resource\n\t// was created successfully. Its value must be an RFC3339 timestamp,\n\t// which can be used to determine how long ago a resource was created.\n\t// This is useful for eventually consistent APIs that may take some time\n\t// before the API called by Observe will report that a recently created\n\t// external resource exists.\n\tAnnotationKeyExternalCreateSucceeded = \"crossplane.io/external-create-succeeded\"\n\n\t// AnnotationKeyExternalCreateFailed is the key in the annotations map\n\t// of a resource that indicates the last time creation of the external\n\t// resource failed. Its value must be an RFC3999 timestamp.\n\tAnnotationKeyExternalCreateFailed = \"crossplane.io/external-create-failed\"\n\n\t// AnnotationKeyReconciliationPaused is the key in the annotations map\n\t// of a resource that indicates that further reconciliations on the\n\t// resource are paused. All create/update/delete/generic events on\n\t// the resource will be filtered and thus no further reconcile requests\n\t// will be queued for the resource.\n\tAnnotationKeyReconciliationPaused = \"crossplane.io/paused\"\n\n\t// AnnotationKeyPollInterval overrides the controller-level poll\n\t// interval for a specific resource. The value must be a valid Go\n\t// duration string (e.g. \"1h\", \"30m\", \"24h\").\n\tAnnotationKeyPollInterval = \"crossplane.io/poll-interval\"\n\n\t// AnnotationKeyReconcileRequestedAt triggers an immediate\n\t// reconciliation when its value changes. The value is an opaque\n\t// token, typically a timestamp. After handling, the reconciler\n\t// records the token in status.lastHandledReconcileAt.\n\tAnnotationKeyReconcileRequestedAt = \"crossplane.io/reconcile-requested-at\"\n)\n\n// ReferenceTo returns an object reference to the supplied object, presumed to\n// be of the supplied group, version, and kind.\n//\n// Deprecated: use a more specific reference type, such as TypedReference or\n// Reference instead of the overly verbose ObjectReference.\n// See https://github.com/crossplane/crossplane-runtime/issues/49\nfunc ReferenceTo(o metav1.Object, of schema.GroupVersionKind) *corev1.ObjectReference {\n\tv, k := of.ToAPIVersionAndKind()\n\n\treturn &corev1.ObjectReference{\n\t\tAPIVersion: v,\n\t\tKind:       k,\n\t\tNamespace:  o.GetNamespace(),\n\t\tName:       o.GetName(),\n\t\tUID:        o.GetUID(),\n\t}\n}\n\n// TypedReferenceTo returns a typed object reference to the supplied object,\n// presumed to be of the supplied group, version, and kind.\nfunc TypedReferenceTo(o metav1.Object, of schema.GroupVersionKind) *xpv2.TypedReference {\n\tv, k := of.ToAPIVersionAndKind()\n\n\treturn &xpv2.TypedReference{\n\t\tAPIVersion: v,\n\t\tKind:       k,\n\t\tName:       o.GetName(),\n\t\tUID:        o.GetUID(),\n\t}\n}\n\n// AsOwner converts the supplied object reference to an owner reference.\nfunc AsOwner(r *xpv2.TypedReference) metav1.OwnerReference {\n\treturn metav1.OwnerReference{\n\t\tAPIVersion: r.APIVersion,\n\t\tKind:       r.Kind,\n\t\tName:       r.Name,\n\t\tUID:        r.UID,\n\t}\n}\n\n// AsController converts the supplied object reference to a controller\n// reference. You may also consider using metav1.NewControllerRef.\nfunc AsController(r *xpv2.TypedReference) metav1.OwnerReference {\n\tt := true\n\tref := AsOwner(r)\n\tref.Controller = &t\n\tref.BlockOwnerDeletion = &t\n\n\treturn ref\n}\n\n// HaveSameController returns true if both supplied objects are controlled by\n// the same object.\nfunc HaveSameController(a, b metav1.Object) bool {\n\tac := metav1.GetControllerOf(a)\n\tbc := metav1.GetControllerOf(b)\n\n\t// We do not consider two objects without any controller to have\n\t// the same controller.\n\tif ac == nil || bc == nil {\n\t\treturn false\n\t}\n\n\treturn ac.UID == bc.UID\n}\n\n// NamespacedNameOf returns the referenced object's namespaced name.\nfunc NamespacedNameOf(r *corev1.ObjectReference) types.NamespacedName {\n\treturn types.NamespacedName{Namespace: r.Namespace, Name: r.Name}\n}\n\n// AddOwnerReference to the supplied object' metadata. Any existing owner with\n// the same UID as the supplied reference will be replaced.\nfunc AddOwnerReference(o metav1.Object, r metav1.OwnerReference) {\n\trefs := o.GetOwnerReferences()\n\tfor i := range refs {\n\t\tif refs[i].UID == r.UID {\n\t\t\trefs[i] = r\n\t\t\to.SetOwnerReferences(refs)\n\n\t\t\treturn\n\t\t}\n\t}\n\n\to.SetOwnerReferences(append(refs, r))\n}\n\n// AddControllerReference to the supplied object's metadata. Any existing owner\n// with the same UID as the supplied reference will be replaced. Returns an\n// error if the supplied object is already controlled by a different owner.\nfunc AddControllerReference(o metav1.Object, r metav1.OwnerReference) error {\n\tif c := metav1.GetControllerOf(o); c != nil && c.UID != r.UID {\n\t\treturn errors.Errorf(\"%s is already controlled by %s %s (UID %s)\", o.GetName(), c.Kind, c.Name, c.UID)\n\t}\n\n\tAddOwnerReference(o, r)\n\n\treturn nil\n}\n\n// AddFinalizer to the supplied Kubernetes object's metadata.\nfunc AddFinalizer(o metav1.Object, finalizer string) {\n\tf := o.GetFinalizers()\n\tif slices.Contains(f, finalizer) {\n\t\treturn\n\t}\n\n\to.SetFinalizers(append(f, finalizer))\n}\n\n// RemoveFinalizer from the supplied Kubernetes object's metadata.\nfunc RemoveFinalizer(o metav1.Object, finalizer string) {\n\tf := o.GetFinalizers()\n\tfor i, e := range f {\n\t\tif e == finalizer {\n\t\t\tf = append(f[:i], f[i+1:]...)\n\t\t}\n\t}\n\n\to.SetFinalizers(f)\n}\n\n// FinalizerExists checks whether given finalizer is already set.\nfunc FinalizerExists(o metav1.Object, finalizer string) bool {\n\tf := o.GetFinalizers()\n\treturn slices.Contains(f, finalizer)\n}\n\n// AddLabels to the supplied object.\nfunc AddLabels(o metav1.Object, labels map[string]string) {\n\tl := o.GetLabels()\n\tif l == nil {\n\t\to.SetLabels(labels)\n\t\treturn\n\t}\n\n\tmaps.Copy(l, labels)\n\n\to.SetLabels(l)\n}\n\n// RemoveLabels with the supplied keys from the supplied object.\nfunc RemoveLabels(o metav1.Object, labels ...string) {\n\tl := o.GetLabels()\n\tif l == nil {\n\t\treturn\n\t}\n\n\tfor _, k := range labels {\n\t\tdelete(l, k)\n\t}\n\n\to.SetLabels(l)\n}\n\n// AddAnnotations to the supplied object.\nfunc AddAnnotations(o metav1.Object, annotations map[string]string) {\n\ta := o.GetAnnotations()\n\tif a == nil {\n\t\to.SetAnnotations(annotations)\n\t\treturn\n\t}\n\n\tmaps.Copy(a, annotations)\n\n\to.SetAnnotations(a)\n}\n\n// RemoveAnnotations with the supplied keys from the supplied object.\nfunc RemoveAnnotations(o metav1.Object, annotations ...string) {\n\ta := o.GetAnnotations()\n\tif a == nil {\n\t\treturn\n\t}\n\n\tfor _, k := range annotations {\n\t\tdelete(a, k)\n\t}\n\n\to.SetAnnotations(a)\n}\n\n// WasDeleted returns true if the supplied object was deleted from the API server.\nfunc WasDeleted(o metav1.Object) bool {\n\treturn !o.GetDeletionTimestamp().IsZero()\n}\n\n// WasCreated returns true if the supplied object was created in the API server.\nfunc WasCreated(o metav1.Object) bool {\n\t// This looks a little different from WasDeleted because DeletionTimestamp\n\t// returns a reference while CreationTimestamp returns a value.\n\tt := o.GetCreationTimestamp()\n\treturn !t.IsZero()\n}\n\n// GetExternalName returns the external name annotation value on the resource.\nfunc GetExternalName(o metav1.Object) string {\n\treturn o.GetAnnotations()[AnnotationKeyExternalName]\n}\n\n// SetExternalName sets the external name annotation of the resource.\nfunc SetExternalName(o metav1.Object, name string) {\n\tAddAnnotations(o, map[string]string{AnnotationKeyExternalName: name})\n}\n\n// GetExternalCreatePending returns the time at which the external resource\n// was most recently pending creation.\nfunc GetExternalCreatePending(o metav1.Object) time.Time {\n\ta := o.GetAnnotations()[AnnotationKeyExternalCreatePending]\n\n\tt, err := time.Parse(time.RFC3339, a)\n\tif err != nil {\n\t\treturn time.Time{}\n\t}\n\n\treturn t\n}\n\n// SetExternalCreatePending sets the time at which the external resource was\n// most recently pending creation to the supplied time.\nfunc SetExternalCreatePending(o metav1.Object, t time.Time) {\n\tAddAnnotations(o, map[string]string{AnnotationKeyExternalCreatePending: t.Format(time.RFC3339)})\n}\n\n// GetExternalCreateSucceeded returns the time at which the external resource\n// was most recently created.\nfunc GetExternalCreateSucceeded(o metav1.Object) time.Time {\n\ta := o.GetAnnotations()[AnnotationKeyExternalCreateSucceeded]\n\n\tt, err := time.Parse(time.RFC3339, a)\n\tif err != nil {\n\t\treturn time.Time{}\n\t}\n\n\treturn t\n}\n\n// SetExternalCreateSucceeded sets the time at which the external resource was\n// most recently created to the supplied time.\nfunc SetExternalCreateSucceeded(o metav1.Object, t time.Time) {\n\tAddAnnotations(o, map[string]string{AnnotationKeyExternalCreateSucceeded: t.Format(time.RFC3339)})\n}\n\n// GetExternalCreateFailed returns the time at which the external resource\n// recently failed to create.\nfunc GetExternalCreateFailed(o metav1.Object) time.Time {\n\ta := o.GetAnnotations()[AnnotationKeyExternalCreateFailed]\n\n\tt, err := time.Parse(time.RFC3339, a)\n\tif err != nil {\n\t\treturn time.Time{}\n\t}\n\n\treturn t\n}\n\n// SetExternalCreateFailed sets the time at which the external resource most\n// recently failed to create.\nfunc SetExternalCreateFailed(o metav1.Object, t time.Time) {\n\tAddAnnotations(o, map[string]string{AnnotationKeyExternalCreateFailed: t.Format(time.RFC3339)})\n}\n\n// ExternalCreateIncomplete returns true if creation of the external resource\n// appears to be incomplete. We deem creation to be incomplete if the 'external\n// create pending' annotation is the newest of all tracking annotations that are\n// set (i.e. pending, succeeded, and failed).\nfunc ExternalCreateIncomplete(o metav1.Object) bool {\n\tpending := GetExternalCreatePending(o)\n\tsucceeded := GetExternalCreateSucceeded(o)\n\tfailed := GetExternalCreateFailed(o)\n\n\t// If creation never started it can't be incomplete.\n\tif pending.IsZero() {\n\t\treturn false\n\t}\n\n\tlatest := succeeded\n\tif failed.After(succeeded) {\n\t\tlatest = failed\n\t}\n\n\treturn pending.After(latest)\n}\n\n// ExternalCreateSucceededDuring returns true if creation of the external\n// resource that corresponds to the supplied managed resource succeeded within\n// the supplied duration.\nfunc ExternalCreateSucceededDuring(o metav1.Object, d time.Duration) bool {\n\tt := GetExternalCreateSucceeded(o)\n\tif t.IsZero() {\n\t\treturn false\n\t}\n\n\treturn time.Since(t) < d\n}\n\n// IsPaused returns true if the object has the AnnotationKeyReconciliationPaused\n// annotation set to `true`.\nfunc IsPaused(o metav1.Object) bool {\n\treturn o.GetAnnotations()[AnnotationKeyReconciliationPaused] == \"true\"\n}\n\n// GetPollInterval returns the poll interval override for the given resource,\n// if set via the AnnotationKeyPollInterval annotation.\nfunc GetPollInterval(o metav1.Object) (time.Duration, bool) {\n\tv, ok := o.GetAnnotations()[AnnotationKeyPollInterval]\n\tif !ok || v == \"\" {\n\t\treturn 0, false\n\t}\n\td, err := time.ParseDuration(v)\n\tif err != nil || d <= 0 {\n\t\treturn 0, false\n\t}\n\treturn d, true\n}\n\n// GetReconcileRequest returns the reconcile-requested-at annotation token\n// and true if present and non-empty, or empty string and false otherwise.\nfunc GetReconcileRequest(o metav1.Object) (string, bool) {\n\tv, ok := o.GetAnnotations()[AnnotationKeyReconcileRequestedAt]\n\tif !ok || v == \"\" {\n\t\treturn \"\", false\n\t}\n\treturn v, true\n}\n\n// SetReconcileRequest sets the reconcile-requested-at annotation to the\n// supplied token value.\nfunc SetReconcileRequest(o metav1.Object, token string) {\n\tAddAnnotations(o, map[string]string{AnnotationKeyReconcileRequestedAt: token})\n}\n"
  },
  {
    "path": "pkg/meta/meta_test.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 meta\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nconst (\n\tgroup        = \"coolstuff\"\n\tversion      = \"v1\"\n\tgroupVersion = group + \"/\" + version\n\tkind         = \"coolresource\"\n\tnamespace    = \"coolns\"\n\tname         = \"cool\"\n\tuid          = types.UID(\"definitely-a-uuid\")\n)\n\nfunc TestReferenceTo(t *testing.T) {\n\ttype args struct {\n\t\to  metav1.Object\n\t\tof schema.GroupVersionKind\n\t}\n\n\ttests := map[string]struct {\n\t\targs\n\t\twant *corev1.ObjectReference\n\t}{\n\t\t\"WithTypeMeta\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tNamespace: namespace,\n\t\t\t\t\t\tName:      name,\n\t\t\t\t\t\tUID:       uid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tof: schema.GroupVersionKind{\n\t\t\t\t\tGroup:   group,\n\t\t\t\t\tVersion: version,\n\t\t\t\t\tKind:    kind,\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: &corev1.ObjectReference{\n\t\t\t\tAPIVersion: groupVersion,\n\t\t\t\tKind:       kind,\n\t\t\t\tNamespace:  namespace,\n\t\t\t\tName:       name,\n\t\t\t\tUID:        uid,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := ReferenceTo(tc.o, tc.of)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"ReferenceTo(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestTypedReferenceTo(t *testing.T) {\n\ttype args struct {\n\t\to  metav1.Object\n\t\tof schema.GroupVersionKind\n\t}\n\n\ttests := map[string]struct {\n\t\targs\n\t\twant *xpv2.TypedReference\n\t}{\n\t\t\"WithTypeMeta\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tNamespace: namespace,\n\t\t\t\t\t\tName:      name,\n\t\t\t\t\t\tUID:       uid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tof: schema.GroupVersionKind{\n\t\t\t\t\tGroup:   group,\n\t\t\t\t\tVersion: version,\n\t\t\t\t\tKind:    kind,\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: &xpv2.TypedReference{\n\t\t\t\tAPIVersion: groupVersion,\n\t\t\t\tKind:       kind,\n\t\t\t\tName:       name,\n\t\t\t\tUID:        uid,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := TypedReferenceTo(tc.o, tc.of)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"TypedReferenceTo(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAsOwner(t *testing.T) {\n\ttests := map[string]struct {\n\t\tr    *xpv2.TypedReference\n\t\twant metav1.OwnerReference\n\t}{\n\t\t\"Successful\": {\n\t\t\tr: &xpv2.TypedReference{\n\t\t\t\tAPIVersion: groupVersion,\n\t\t\t\tKind:       kind,\n\t\t\t\tName:       name,\n\t\t\t\tUID:        uid,\n\t\t\t},\n\t\t\twant: metav1.OwnerReference{\n\t\t\t\tAPIVersion: groupVersion,\n\t\t\t\tKind:       kind,\n\t\t\t\tName:       name,\n\t\t\t\tUID:        uid,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := AsOwner(tc.r)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"AsOwner(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAsController(t *testing.T) {\n\tflag := true\n\n\ttests := map[string]struct {\n\t\tr    *xpv2.TypedReference\n\t\twant metav1.OwnerReference\n\t}{\n\t\t\"Successful\": {\n\t\t\tr: &xpv2.TypedReference{\n\t\t\t\tAPIVersion: groupVersion,\n\t\t\t\tKind:       kind,\n\t\t\t\tName:       name,\n\t\t\t\tUID:        uid,\n\t\t\t},\n\t\t\twant: metav1.OwnerReference{\n\t\t\t\tAPIVersion:         groupVersion,\n\t\t\t\tKind:               kind,\n\t\t\t\tName:               name,\n\t\t\t\tUID:                uid,\n\t\t\t\tController:         &flag,\n\t\t\t\tBlockOwnerDeletion: &flag,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := AsController(tc.r)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"AsController(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHaveSameController(t *testing.T) {\n\tcontroller := true\n\n\tcontrollerA := metav1.OwnerReference{\n\t\tUID:        uid,\n\t\tController: &controller,\n\t}\n\n\tcontrollerB := metav1.OwnerReference{\n\t\tUID:        types.UID(\"a-different-uuid\"),\n\t\tController: &controller,\n\t}\n\n\tcases := map[string]struct {\n\t\ta    metav1.Object\n\t\tb    metav1.Object\n\t\twant bool\n\t}{\n\t\t\"SameController\": {\n\t\t\ta: &corev1.Pod{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{controllerA},\n\t\t\t\t},\n\t\t\t},\n\t\t\tb: &corev1.Pod{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{controllerA},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"AHasNoController\": {\n\t\t\ta: &corev1.Pod{},\n\t\t\tb: &corev1.Pod{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{controllerB},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t\t\"BHasNoController\": {\n\t\t\ta: &corev1.Pod{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{controllerA},\n\t\t\t\t},\n\t\t\t},\n\t\t\tb:    &corev1.Pod{},\n\t\t\twant: false,\n\t\t},\n\t\t\"ControllersDiffer\": {\n\t\t\ta: &corev1.Pod{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{controllerA},\n\t\t\t\t},\n\t\t\t},\n\t\t\tb: &corev1.Pod{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{controllerB},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := HaveSameController(tc.a, tc.b)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"HaveSameController(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNamespacedNameOf(t *testing.T) {\n\tcases := map[string]struct {\n\t\tr    *corev1.ObjectReference\n\t\twant types.NamespacedName\n\t}{\n\t\t\"Success\": {\n\t\t\tr:    &corev1.ObjectReference{Namespace: namespace, Name: name},\n\t\t\twant: types.NamespacedName{Namespace: namespace, Name: name},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := NamespacedNameOf(tc.r)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"NamespacedNameOf(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAddOwnerReference(t *testing.T) {\n\towner := metav1.OwnerReference{UID: uid}\n\tother := metav1.OwnerReference{UID: \"a-different-uuid\"}\n\tctrlr := metav1.OwnerReference{UID: uid, Controller: func() *bool { c := true; return &c }()}\n\n\ttype args struct {\n\t\to metav1.Object\n\t\tr metav1.OwnerReference\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant []metav1.OwnerReference\n\t}{\n\t\t\"NoExistingOwners\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{},\n\t\t\t\tr: owner,\n\t\t\t},\n\t\t\twant: []metav1.OwnerReference{owner},\n\t\t},\n\t\t\"UpdateExistingOwner\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{ctrlr},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tr: owner,\n\t\t\t},\n\t\t\twant: []metav1.OwnerReference{owner},\n\t\t},\n\t\t\"OwnedByAnotherObject\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{other},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tr: owner,\n\t\t\t},\n\t\t\twant: []metav1.OwnerReference{other, owner},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tAddOwnerReference(tc.args.o, tc.args.r)\n\n\t\t\tgot := tc.args.o.GetOwnerReferences()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"tc.args.o.GetOwnerReferences(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAddControllerReference(t *testing.T) {\n\towner := metav1.OwnerReference{UID: uid}\n\tother := metav1.OwnerReference{UID: \"a-different-uuid\"}\n\tctrlr := metav1.OwnerReference{UID: uid, Controller: func() *bool { c := true; return &c }()}\n\totrlr := metav1.OwnerReference{\n\t\tKind:       \"lame\",\n\t\tName:       \"othercontroller\",\n\t\tUID:        \"a-different-uuid\",\n\t\tController: func() *bool { c := true; return &c }(),\n\t}\n\n\ttype args struct {\n\t\to metav1.Object\n\t\tr metav1.OwnerReference\n\t}\n\n\ttype want struct {\n\t\towners []metav1.OwnerReference\n\t\terr    error\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant want\n\t}{\n\t\t\"NoExistingOwners\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{},\n\t\t\t\tr: owner,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\towners: []metav1.OwnerReference{owner},\n\t\t\t},\n\t\t},\n\t\t\"UpdateExistingOwner\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{ctrlr},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tr: owner,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\towners: []metav1.OwnerReference{owner},\n\t\t\t},\n\t\t},\n\t\t\"OwnedByAnotherObject\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{other},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tr: owner,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\towners: []metav1.OwnerReference{other, owner},\n\t\t\t},\n\t\t},\n\t\t\"ControlledByAnotherObject\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:            name,\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{otrlr},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tr: owner,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\towners: []metav1.OwnerReference{otrlr},\n\t\t\t\terr:    errors.Errorf(\"%s is already controlled by %s %s (UID %s)\", name, otrlr.Kind, otrlr.Name, otrlr.UID),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := AddControllerReference(tc.args.o, tc.args.r)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"AddControllerReference(...): -want error, +got error:\\n%s\", diff)\n\t\t\t}\n\n\t\t\tgot := tc.args.o.GetOwnerReferences()\n\t\t\tif diff := cmp.Diff(tc.want.owners, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"tc.args.o.GetOwnerReferences(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAddFinalizer(t *testing.T) {\n\tfinalizer := \"fin\"\n\tfunalizer := \"fun\"\n\n\ttype args struct {\n\t\to         metav1.Object\n\t\tfinalizer string\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant []string\n\t}{\n\t\t\"NoExistingFinalizers\": {\n\t\t\targs: args{\n\t\t\t\to:         &corev1.Pod{},\n\t\t\t\tfinalizer: finalizer,\n\t\t\t},\n\t\t\twant: []string{finalizer},\n\t\t},\n\t\t\"FinalizerAlreadyExists\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tFinalizers: []string{finalizer},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tfinalizer: finalizer,\n\t\t\t},\n\t\t\twant: []string{finalizer},\n\t\t},\n\t\t\"AnotherFinalizerExists\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tFinalizers: []string{funalizer},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tfinalizer: finalizer,\n\t\t\t},\n\t\t\twant: []string{funalizer, finalizer},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tAddFinalizer(tc.args.o, tc.args.finalizer)\n\n\t\t\tgot := tc.args.o.GetFinalizers()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"tc.args.o.GetFinalizers(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRemoveFinalizer(t *testing.T) {\n\tfinalizer := \"fin\"\n\tfunalizer := \"fun\"\n\n\ttype args struct {\n\t\to         metav1.Object\n\t\tfinalizer string\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant []string\n\t}{\n\t\t\"NoExistingFinalizers\": {\n\t\t\targs: args{\n\t\t\t\to:         &corev1.Pod{},\n\t\t\t\tfinalizer: finalizer,\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"FinalizerExists\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tFinalizers: []string{finalizer},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tfinalizer: finalizer,\n\t\t\t},\n\t\t\twant: []string{},\n\t\t},\n\t\t\"AnotherFinalizerExists\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tFinalizers: []string{finalizer, funalizer},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tfinalizer: finalizer,\n\t\t\t},\n\t\t\twant: []string{funalizer},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tRemoveFinalizer(tc.args.o, tc.args.finalizer)\n\n\t\t\tgot := tc.args.o.GetFinalizers()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"tc.args.o.GetFinalizers(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFinalizerExists(t *testing.T) {\n\tfinalizer := \"fin\"\n\tfunalizer := \"fun\"\n\n\ttype args struct {\n\t\to         metav1.Object\n\t\tfinalizer string\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant bool\n\t}{\n\t\t\"NoExistingFinalizers\": {\n\t\t\targs: args{\n\t\t\t\to:         &corev1.Pod{},\n\t\t\t\tfinalizer: finalizer,\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t\t\"FinalizerExists\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tFinalizers: []string{finalizer},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tfinalizer: finalizer,\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"AnotherFinalizerExists\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tFinalizers: []string{funalizer},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tfinalizer: finalizer,\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tif diff := cmp.Diff(tc.want, FinalizerExists(tc.args.o, tc.args.finalizer)); diff != \"\" {\n\t\t\t\tt.Errorf(\"tc.args.o.GetFinalizers(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAddLabels(t *testing.T) {\n\tkey, value := \"key\", \"value\"\n\texistingKey, existingValue := \"ekey\", \"evalue\"\n\n\ttype args struct {\n\t\to      metav1.Object\n\t\tlabels map[string]string\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant map[string]string\n\t}{\n\t\t\"ExistingLabels\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\t\texistingKey: existingValue,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tlabels: map[string]string{key: value},\n\t\t\t},\n\t\t\twant: map[string]string{\n\t\t\t\texistingKey: existingValue,\n\t\t\t\tkey:         value,\n\t\t\t},\n\t\t},\n\t\t\"NoExistingLabels\": {\n\t\t\targs: args{\n\t\t\t\to:      &corev1.Pod{},\n\t\t\t\tlabels: map[string]string{key: value},\n\t\t\t},\n\t\t\twant: map[string]string{key: value},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tAddLabels(tc.args.o, tc.args.labels)\n\n\t\t\tgot := tc.args.o.GetLabels()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"tc.args.o.GetLabels(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRemoveLabels(t *testing.T) {\n\tkeyA, valueA := \"keyA\", \"valueA\"\n\tkeyB, valueB := \"keyB\", \"valueB\"\n\n\ttype args struct {\n\t\to      metav1.Object\n\t\tlabels []string\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant map[string]string\n\t}{\n\t\t\"ExistingLabels\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\t\tkeyA: valueA,\n\t\t\t\t\t\t\tkeyB: valueB,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tlabels: []string{keyA},\n\t\t\t},\n\t\t\twant: map[string]string{keyB: valueB},\n\t\t},\n\t\t\"NoExistingLabels\": {\n\t\t\targs: args{\n\t\t\t\to:      &corev1.Pod{},\n\t\t\t\tlabels: []string{keyA},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tRemoveLabels(tc.args.o, tc.args.labels...)\n\n\t\t\tgot := tc.args.o.GetLabels()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"tc.args.o.GetLabels(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAddAnnotations(t *testing.T) {\n\tkey, value := \"key\", \"value\"\n\texistingKey, existingValue := \"ekey\", \"evalue\"\n\n\ttype args struct {\n\t\to           metav1.Object\n\t\tannotations map[string]string\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant map[string]string\n\t}{\n\t\t\"ExistingAnnotations\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\texistingKey: existingValue,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tannotations: map[string]string{key: value},\n\t\t\t},\n\t\t\twant: map[string]string{\n\t\t\t\texistingKey: existingValue,\n\t\t\t\tkey:         value,\n\t\t\t},\n\t\t},\n\t\t\"NoExistingAnnotations\": {\n\t\t\targs: args{\n\t\t\t\to:           &corev1.Pod{},\n\t\t\t\tannotations: map[string]string{key: value},\n\t\t\t},\n\t\t\twant: map[string]string{key: value},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tAddAnnotations(tc.args.o, tc.args.annotations)\n\n\t\t\tgot := tc.args.o.GetAnnotations()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"tc.args.o.GetAnnotations(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRemoveAnnotations(t *testing.T) {\n\tkeyA, valueA := \"keyA\", \"valueA\"\n\tkeyB, valueB := \"keyB\", \"valueB\"\n\n\ttype args struct {\n\t\to           metav1.Object\n\t\tannotations []string\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant map[string]string\n\t}{\n\t\t\"ExistingAnnotations\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\tkeyA: valueA,\n\t\t\t\t\t\t\tkeyB: valueB,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tannotations: []string{keyA},\n\t\t\t},\n\t\t\twant: map[string]string{keyB: valueB},\n\t\t},\n\t\t\"NoExistingAnnotations\": {\n\t\t\targs: args{\n\t\t\t\to:           &corev1.Pod{},\n\t\t\t\tannotations: []string{keyA},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tRemoveAnnotations(tc.args.o, tc.args.annotations...)\n\n\t\t\tgot := tc.args.o.GetAnnotations()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"tc.args.o.GetAnnotations(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestWasDeleted(t *testing.T) {\n\tnow := metav1.Now()\n\n\tcases := map[string]struct {\n\t\to    metav1.Object\n\t\twant bool\n\t}{\n\t\t\"ObjectWasDeleted\": {\n\t\t\to:    &corev1.Pod{ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: &now}},\n\t\t\twant: true,\n\t\t},\n\t\t\"ObjectWasNotDeleted\": {\n\t\t\to:    &corev1.Pod{ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: nil}},\n\t\t\twant: false,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := WasDeleted(tc.o)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"WasDeleted(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestWasCreated(t *testing.T) {\n\tnow := metav1.Now()\n\tzero := metav1.Time{}\n\n\tcases := map[string]struct {\n\t\to    metav1.Object\n\t\twant bool\n\t}{\n\t\t\"ObjectWasCreated\": {\n\t\t\to:    &corev1.Pod{ObjectMeta: metav1.ObjectMeta{CreationTimestamp: now}},\n\t\t\twant: true,\n\t\t},\n\t\t\"ObjectWasNotCreated\": {\n\t\t\to:    &corev1.Pod{ObjectMeta: metav1.ObjectMeta{CreationTimestamp: zero}},\n\t\t\twant: false,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := WasCreated(tc.o)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"WasCreated(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetExternalName(t *testing.T) {\n\tcases := map[string]struct {\n\t\to    metav1.Object\n\t\twant string\n\t}{\n\t\t\"ExternalNameExists\": {\n\t\t\to:    &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalName: name}}},\n\t\t\twant: name,\n\t\t},\n\t\t\"NoExternalName\": {\n\t\t\to:    &corev1.Pod{},\n\t\t\twant: \"\",\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := GetExternalName(tc.o)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"GetExternalName(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSetExternalName(t *testing.T) {\n\tcases := map[string]struct {\n\t\to    metav1.Object\n\t\tname string\n\t\twant metav1.Object\n\t}{\n\t\t\"SetsTheCorrectKey\": {\n\t\t\to:    &corev1.Pod{},\n\t\t\tname: name,\n\t\t\twant: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalName: name}}},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tSetExternalName(tc.o, tc.name)\n\n\t\t\tif diff := cmp.Diff(tc.want, tc.o); diff != \"\" {\n\t\t\t\tt.Errorf(\"SetExternalName(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetExternalCreatePending(t *testing.T) {\n\tnow := time.Now().Round(time.Second)\n\n\tcases := map[string]struct {\n\t\to    metav1.Object\n\t\twant time.Time\n\t}{\n\t\t\"ExternalCreatePendingExists\": {\n\t\t\to:    &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalCreatePending: now.Format(time.RFC3339)}}},\n\t\t\twant: now,\n\t\t},\n\t\t\"NoExternalCreatePending\": {\n\t\t\to:    &corev1.Pod{},\n\t\t\twant: time.Time{},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := GetExternalCreatePending(tc.o)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"GetExternalCreatePending(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSetExternalCreatePending(t *testing.T) {\n\tnow := time.Now()\n\n\tcases := map[string]struct {\n\t\to    metav1.Object\n\t\tt    time.Time\n\t\twant metav1.Object\n\t}{\n\t\t\"SetsTheCorrectKey\": {\n\t\t\to:    &corev1.Pod{},\n\t\t\tt:    now,\n\t\t\twant: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalCreatePending: now.Format(time.RFC3339)}}},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tSetExternalCreatePending(tc.o, tc.t)\n\n\t\t\tif diff := cmp.Diff(tc.want, tc.o); diff != \"\" {\n\t\t\t\tt.Errorf(\"SetExternalCreatePending(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetExternalCreateSucceeded(t *testing.T) {\n\tnow := time.Now().Round(time.Second)\n\n\tcases := map[string]struct {\n\t\to    metav1.Object\n\t\twant time.Time\n\t}{\n\t\t\"ExternalCreateTimeExists\": {\n\t\t\to:    &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalCreateSucceeded: now.Format(time.RFC3339)}}},\n\t\t\twant: now,\n\t\t},\n\t\t\"NoExternalCreateTime\": {\n\t\t\to:    &corev1.Pod{},\n\t\t\twant: time.Time{},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := GetExternalCreateSucceeded(tc.o)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"GetExternalCreateSucceeded(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSetExternalCreateSucceeded(t *testing.T) {\n\tnow := time.Now()\n\n\tcases := map[string]struct {\n\t\to    metav1.Object\n\t\tt    time.Time\n\t\twant metav1.Object\n\t}{\n\t\t\"SetsTheCorrectKey\": {\n\t\t\to:    &corev1.Pod{},\n\t\t\tt:    now,\n\t\t\twant: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalCreateSucceeded: now.Format(time.RFC3339)}}},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tSetExternalCreateSucceeded(tc.o, tc.t)\n\n\t\t\tif diff := cmp.Diff(tc.want, tc.o); diff != \"\" {\n\t\t\t\tt.Errorf(\"SetExternalCreateSucceeded(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetExternalCreateFailed(t *testing.T) {\n\tnow := time.Now().Round(time.Second)\n\n\tcases := map[string]struct {\n\t\to    metav1.Object\n\t\twant time.Time\n\t}{\n\t\t\"ExternalCreateFailedExists\": {\n\t\t\to:    &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalCreateFailed: now.Format(time.RFC3339)}}},\n\t\t\twant: now,\n\t\t},\n\t\t\"NoExternalCreateFailed\": {\n\t\t\to:    &corev1.Pod{},\n\t\t\twant: time.Time{},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := GetExternalCreateFailed(tc.o)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"GetExternalCreateFailed(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSetExternalCreateFailed(t *testing.T) {\n\tnow := time.Now()\n\n\tcases := map[string]struct {\n\t\to    metav1.Object\n\t\tt    time.Time\n\t\twant metav1.Object\n\t}{\n\t\t\"SetsTheCorrectKey\": {\n\t\t\to:    &corev1.Pod{},\n\t\t\tt:    now,\n\t\t\twant: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalCreateFailed: now.Format(time.RFC3339)}}},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tSetExternalCreateFailed(tc.o, tc.t)\n\n\t\t\tif diff := cmp.Diff(tc.want, tc.o); diff != \"\" {\n\t\t\t\tt.Errorf(\"SetExternalCreateFailed(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExternalCreateSucceededDuring(t *testing.T) {\n\ttype args struct {\n\t\to metav1.Object\n\t\td time.Duration\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant bool\n\t}{\n\t\t\"NotYetSuccessfullyCreated\": {\n\t\t\targs: args{\n\t\t\t\to: &corev1.Pod{},\n\t\t\t\td: 1 * time.Minute,\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t\t\"SuccessfullyCreatedTooLongAgo\": {\n\t\t\targs: args{\n\t\t\t\to: func() metav1.Object {\n\t\t\t\t\to := &corev1.Pod{}\n\t\t\t\t\tt := time.Now().Add(-2 * time.Minute)\n\t\t\t\t\tSetExternalCreateSucceeded(o, t)\n\n\t\t\t\t\treturn o\n\t\t\t\t}(),\n\t\t\t\td: 1 * time.Minute,\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t\t\"SuccessfullyCreatedWithinDuration\": {\n\t\t\targs: args{\n\t\t\t\to: func() metav1.Object {\n\t\t\t\t\to := &corev1.Pod{}\n\t\t\t\t\tt := time.Now().Add(-30 * time.Second)\n\t\t\t\t\tSetExternalCreateSucceeded(o, t)\n\n\t\t\t\t\treturn o\n\t\t\t\t}(),\n\t\t\t\td: 1 * time.Minute,\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := ExternalCreateSucceededDuring(tc.args.o, tc.args.d)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"ExternalCreateSucceededDuring(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExternalCreateIncomplete(t *testing.T) {\n\tnow := time.Now().Format(time.RFC3339)\n\tearlier := time.Now().Add(-1 * time.Second).Format(time.RFC3339)\n\tevenEarlier := time.Now().Add(-1 * time.Minute).Format(time.RFC3339)\n\n\tcases := map[string]struct {\n\t\treason string\n\t\to      metav1.Object\n\t\twant   bool\n\t}{\n\t\t\"CreateNeverPending\": {\n\t\t\treason: \"If we've never called Create it can't be incomplete.\",\n\t\t\to:      &corev1.Pod{},\n\t\t\twant:   false,\n\t\t},\n\t\t\"CreateSucceeded\": {\n\t\t\treason: \"If Create succeeded since it was pending, it's complete.\",\n\t\t\to: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{\n\t\t\t\tAnnotationKeyExternalCreateFailed:    evenEarlier,\n\t\t\t\tAnnotationKeyExternalCreatePending:   earlier,\n\t\t\t\tAnnotationKeyExternalCreateSucceeded: now,\n\t\t\t}}},\n\t\t\twant: false,\n\t\t},\n\t\t\"CreateFailed\": {\n\t\t\treason: \"If Create failed since it was pending, it's complete.\",\n\t\t\to: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{\n\t\t\t\tAnnotationKeyExternalCreateSucceeded: evenEarlier,\n\t\t\t\tAnnotationKeyExternalCreatePending:   earlier,\n\t\t\t\tAnnotationKeyExternalCreateFailed:    now,\n\t\t\t}}},\n\t\t\twant: false,\n\t\t},\n\t\t\"CreateNeverCompleted\": {\n\t\t\treason: \"If Create was pending but never succeeded or failed, it's incomplete.\",\n\t\t\to: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{\n\t\t\t\tAnnotationKeyExternalCreatePending: earlier,\n\t\t\t}}},\n\t\t\twant: true,\n\t\t},\n\t\t\"RecreateNeverCompleted\": {\n\t\t\treason: \"If Create is pending and there's an older success we're probably trying to recreate a deleted external resource, and it's incomplete.\",\n\t\t\to: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{\n\t\t\t\tAnnotationKeyExternalCreateSucceeded: earlier,\n\t\t\t\tAnnotationKeyExternalCreatePending:   now,\n\t\t\t}}},\n\t\t\twant: true,\n\t\t},\n\t\t\"RetryNeverCompleted\": {\n\t\t\treason: \"If Create is pending and there's an older failure we're probably trying to recreate a deleted external resource, and it's incomplete.\",\n\t\t\to: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{\n\t\t\t\tAnnotationKeyExternalCreateFailed:  earlier,\n\t\t\t\tAnnotationKeyExternalCreatePending: now,\n\t\t\t}}},\n\t\t\twant: true,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := ExternalCreateIncomplete(tc.o)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"ExternalCreateIncomplete(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsPaused(t *testing.T) {\n\tcases := map[string]struct {\n\t\to    metav1.Object\n\t\twant bool\n\t}{\n\t\t\"HasPauseAnnotationSetTrue\": {\n\t\t\to: func() metav1.Object {\n\t\t\t\tp := &corev1.Pod{}\n\t\t\t\tp.SetAnnotations(map[string]string{\n\t\t\t\t\tAnnotationKeyReconciliationPaused: \"true\",\n\t\t\t\t})\n\n\t\t\t\treturn p\n\t\t\t}(),\n\t\t\twant: true,\n\t\t},\n\t\t\"NoPauseAnnotation\": {\n\t\t\to:    &corev1.Pod{},\n\t\t\twant: false,\n\t\t},\n\t\t\"HasEmptyPauseAnnotation\": {\n\t\t\to: func() metav1.Object {\n\t\t\t\tp := &corev1.Pod{}\n\t\t\t\tp.SetAnnotations(map[string]string{\n\t\t\t\t\tAnnotationKeyReconciliationPaused: \"\",\n\t\t\t\t})\n\n\t\t\t\treturn p\n\t\t\t}(),\n\t\t\twant: false,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := IsPaused(tc.o)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"IsPaused(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetPollInterval(t *testing.T) {\n\tcases := map[string]struct {\n\t\to        metav1.Object\n\t\twantDur  time.Duration\n\t\twantBool bool\n\t}{\n\t\t\"NoAnnotation\": {\n\t\t\to: &corev1.Pod{},\n\t\t},\n\t\t\"EmptyAnnotation\": {\n\t\t\to: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{\n\t\t\t\tAnnotationKeyPollInterval: \"\",\n\t\t\t}}},\n\t\t},\n\t\t\"InvalidDuration\": {\n\t\t\to: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{\n\t\t\t\tAnnotationKeyPollInterval: \"not-a-duration\",\n\t\t\t}}},\n\t\t},\n\t\t\"NegativeDuration\": {\n\t\t\to: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{\n\t\t\t\tAnnotationKeyPollInterval: \"-5m\",\n\t\t\t}}},\n\t\t},\n\t\t\"ZeroDuration\": {\n\t\t\to: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{\n\t\t\t\tAnnotationKeyPollInterval: \"0s\",\n\t\t\t}}},\n\t\t},\n\t\t\"ValidDuration\": {\n\t\t\to: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{\n\t\t\t\tAnnotationKeyPollInterval: \"24h\",\n\t\t\t}}},\n\t\t\twantDur:  24 * time.Hour,\n\t\t\twantBool: true,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\td, ok := GetPollInterval(tc.o)\n\t\t\tif diff := cmp.Diff(tc.wantBool, ok); diff != \"\" {\n\t\t\t\tt.Errorf(\"GetPollInterval(...) ok: -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t\tif diff := cmp.Diff(tc.wantDur, d); diff != \"\" {\n\t\t\t\tt.Errorf(\"GetPollInterval(...) duration: -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetReconcileRequest(t *testing.T) {\n\tcases := map[string]struct {\n\t\to         metav1.Object\n\t\twantToken string\n\t\twantBool  bool\n\t}{\n\t\t\"NoAnnotation\": {\n\t\t\to: &corev1.Pod{},\n\t\t},\n\t\t\"EmptyAnnotation\": {\n\t\t\to: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{\n\t\t\t\tAnnotationKeyReconcileRequestedAt: \"\",\n\t\t\t}}},\n\t\t},\n\t\t\"HasToken\": {\n\t\t\to: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{\n\t\t\t\tAnnotationKeyReconcileRequestedAt: \"2024-01-15T10:30:00Z\",\n\t\t\t}}},\n\t\t\twantToken: \"2024-01-15T10:30:00Z\",\n\t\t\twantBool:  true,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttoken, ok := GetReconcileRequest(tc.o)\n\t\t\tif diff := cmp.Diff(tc.wantBool, ok); diff != \"\" {\n\t\t\t\tt.Errorf(\"GetReconcileRequest(...) ok: -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t\tif diff := cmp.Diff(tc.wantToken, token); diff != \"\" {\n\t\t\t\tt.Errorf(\"GetReconcileRequest(...) token: -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSetReconcileRequest(t *testing.T) {\n\to := &corev1.Pod{}\n\tSetReconcileRequest(o, \"my-token\")\n\tgot := o.GetAnnotations()[AnnotationKeyReconcileRequestedAt]\n\tif diff := cmp.Diff(\"my-token\", got); diff != \"\" {\n\t\tt.Errorf(\"SetReconcileRequest(...): -want, +got:\\n%s\", diff)\n\t}\n}\n"
  },
  {
    "path": "pkg/password/password.go",
    "content": "/*\nCopyright 2019 The Crossplane 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// Package password contains a simple password generator.\npackage password\n\nimport (\n\t\"crypto/rand\"\n\t\"math/big\"\n)\n\n// Settings for password generation.\ntype Settings struct {\n\t// CharacterSet of allowed password characters.\n\tCharacterSet string\n\n\t// Length of generated passwords.\n\tLength int\n}\n\n// Default password generation settings.\n//\n//nolint:gochecknoglobals // We treat this as a constant.\nvar Default = Settings{\n\tCharacterSet: \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\",\n\tLength:       27,\n}\n\n// Generate a random, 27 character password that may consist of lowercase\n// letters, uppercase letters, or numbers.\nfunc Generate() (string, error) {\n\treturn Default.Generate()\n}\n\n// Generate a password.\nfunc (s Settings) Generate() (string, error) {\n\tpw := make([]byte, s.Length)\n\tfor i := range s.Length {\n\t\tn, err := rand.Int(rand.Reader, big.NewInt(int64(len(s.CharacterSet))))\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tpw[i] = s.CharacterSet[n.Int64()]\n\t}\n\n\treturn string(pw), nil\n}\n"
  },
  {
    "path": "pkg/password/password_test.go",
    "content": "package password\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nfunc TestGenerate(t *testing.T) {\n\t// ¯\\_(ツ)_/¯\n\twant := \"aaa\"\n\n\tgot, err := Settings{CharacterSet: \"a\", Length: 3}.Generate()\n\tif diff := cmp.Diff(want, got); diff != \"\" {\n\t\tt.Errorf(\"Generate(): -want, +got:\\n%s\", diff)\n\t}\n\n\tif err != nil {\n\t\tt.Errorf(\"Generate: %s\\n\", err)\n\t}\n}\n"
  },
  {
    "path": "pkg/ratelimiter/default.go",
    "content": "/*\nCopyright 2021 The Crossplane 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// Package ratelimiter contains suggested default ratelimiters for Crossplane.\npackage ratelimiter\n\nimport (\n\t\"time\"\n\n\t\"golang.org/x/time/rate\"\n\t\"k8s.io/client-go/rest\"\n\t\"k8s.io/client-go/util/workqueue\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n)\n\n// NewGlobal returns a token bucket rate limiter meant for limiting the number\n// of average total requeues per second for all controllers registered with a\n// controller manager. The bucket size (i.e. allowed burst) is rps * 10.\nfunc NewGlobal(rps int) *BucketRateLimiter {\n\treturn &workqueue.TypedBucketRateLimiter[string]{Limiter: rate.NewLimiter(rate.Limit(rps), rps*10)}\n}\n\n// ControllerRateLimiter to work with [sigs.k8s.io/controller-runtime/pkg/controller.Options].\ntype ControllerRateLimiter = workqueue.TypedRateLimiter[reconcile.Request]\n\n// NewController returns a rate limiter that takes the maximum delay between the\n// passed rate limiter and a per-item exponential backoff limiter. The\n// exponential backoff limiter has a base delay of 1s and a maximum of 60s.\nfunc NewController() ControllerRateLimiter {\n\treturn workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](1*time.Second, 60*time.Second)\n}\n\n// LimitRESTConfig returns a copy of the supplied REST config with rate limits\n// derived from the supplied rate of reconciles per second.\nfunc LimitRESTConfig(cfg *rest.Config, rps int) *rest.Config {\n\t// The Kubernetes controller manager and controller-runtime controller\n\t// managers use 20qps with 30 burst. We default to 10 reconciles per\n\t// second so our defaults are designed to accommodate that.\n\tout := rest.CopyConfig(cfg)\n\tout.QPS = float32(rps * 5)\n\tout.Burst = rps * 10\n\n\treturn out\n}\n"
  },
  {
    "path": "pkg/ratelimiter/reconciler.go",
    "content": "/*\nCopyright 2021 The Crossplane 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 ratelimiter\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"k8s.io/client-go/util/workqueue\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n)\n\n// BucketRateLimiter for a standard crossplane reconciler.\ntype BucketRateLimiter = workqueue.TypedBucketRateLimiter[string]\n\n// RateLimiter for a standard crossplane reconciler.\ntype RateLimiter = workqueue.TypedRateLimiter[string]\n\n// A Reconciler rate limits an inner, wrapped Reconciler. Requests that are rate\n// limited immediately return RequeueAfter: d without calling the wrapped\n// Reconciler, where d is imposed by the rate limiter.\ntype Reconciler struct {\n\tname  string\n\tinner reconcile.Reconciler\n\tlimit RateLimiter\n\n\tlimited  map[string]struct{}\n\tlimitedL sync.RWMutex\n}\n\n// NewReconciler wraps the supplied Reconciler, ensuring requests are passed to\n// it no more frequently than the supplied RateLimiter allows. Multiple uniquely\n// named Reconcilers can share the same RateLimiter.\nfunc NewReconciler(name string, r reconcile.Reconciler, l RateLimiter) *Reconciler {\n\treturn &Reconciler{name: name, inner: r, limit: l, limited: make(map[string]struct{})}\n}\n\n// Reconcile the supplied request subject to rate limiting.\nfunc (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {\n\titem := r.name + req.String()\n\tif d := r.when(req); d > 0 {\n\t\treturn reconcile.Result{RequeueAfter: d}, nil\n\t}\n\n\tr.limit.Forget(item)\n\n\treturn r.inner.Reconcile(ctx, req)\n}\n\n// when adapts the upstream rate limiter's 'When' method such that rate limited\n// requests can call it again when they return and will be allowed to proceed\n// immediately without being subject to further rate limiting. It is optimised\n// for handling requests that have not been and will not be rate limited without\n// blocking.\nfunc (r *Reconciler) when(req reconcile.Request) time.Duration {\n\titem := r.name + req.String()\n\n\tr.limitedL.RLock()\n\t_, limited := r.limited[item]\n\tr.limitedL.RUnlock()\n\n\t// If we already rate limited this request we trust that it complied and\n\t// let it pass immediately.\n\tif limited {\n\t\tr.limitedL.Lock()\n\t\tdelete(r.limited, item)\n\t\tr.limitedL.Unlock()\n\n\t\treturn 0\n\t}\n\n\td := r.limit.When(item)\n\n\t// Record that this request was rate limited so that we can let it\n\t// through immediately when it requeues after the supplied duration.\n\tif d != 0 {\n\t\tr.limitedL.Lock()\n\t\tr.limited[item] = struct{}{}\n\t\tr.limitedL.Unlock()\n\t}\n\n\treturn d\n}\n"
  },
  {
    "path": "pkg/ratelimiter/reconciler_test.go",
    "content": "/*\nCopyright 2021 The Crossplane 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 ratelimiter\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nvar _ RateLimiter = &predictableRateLimiter{}\n\ntype predictableRateLimiter struct{ d time.Duration }\n\nfunc (r *predictableRateLimiter) When(_ string) time.Duration { return r.d }\nfunc (r *predictableRateLimiter) Forget(_ string)             {}\nfunc (r *predictableRateLimiter) NumRequeues(_ string) int    { return 0 }\n\nfunc TestReconcile(t *testing.T) {\n\ttype args struct {\n\t\tctx context.Context\n\t\treq reconcile.Request\n\t}\n\n\ttype want struct {\n\t\tres reconcile.Result\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tr      reconcile.Reconciler\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"NotRateLimited\": {\n\t\t\treason: \"Requests that are not rate limited should be passed to the inner Reconciler.\",\n\t\t\tr: NewReconciler(\"test\",\n\t\t\t\treconcile.Func(func(_ context.Context, _ reconcile.Request) (reconcile.Result, error) {\n\t\t\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t\t\t}),\n\t\t\t\t&predictableRateLimiter{}),\n\t\t\twant: want{\n\t\t\t\tres: reconcile.Result{Requeue: true},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"RateLimited\": {\n\t\t\treason: \"Requests that are rate limited should be requeued after the duration specified by the RateLimiter.\",\n\t\t\tr:      NewReconciler(\"test\", nil, &predictableRateLimiter{d: 8 * time.Second}),\n\t\t\twant: want{\n\t\t\t\tres: reconcile.Result{RequeueAfter: 8 * time.Second},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"Returning\": {\n\t\t\treason: \"Returning requests that were previously rate limited should be allowed through without further rate limiting.\",\n\t\t\tr: func() reconcile.Reconciler {\n\t\t\t\tinner := reconcile.Func(func(_ context.Context, _ reconcile.Request) (reconcile.Result, error) {\n\t\t\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t\t\t})\n\n\t\t\t\t// Rate limit the request once.\n\t\t\t\tr := NewReconciler(\"test\", inner, &predictableRateLimiter{d: 8 * time.Second})\n\t\t\t\tr.Reconcile(context.Background(), reconcile.Request{NamespacedName: types.NamespacedName{Name: \"limited\"}})\n\n\t\t\t\treturn r\n\t\t\t}(),\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\treq: reconcile.Request{NamespacedName: types.NamespacedName{Name: \"limited\"}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tres: reconcile.Result{Requeue: true},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot, err := tc.r.Reconcile(tc.args.ctx, tc.args.req)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"%s\\nr.Reconcile(...): -want, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.res, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"%s\\nr.Reconcile(...): -want, +got result:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/reconciler/customresourcesgate/reconciler.go",
    "content": "/*\nCopyright 2025 The Crossplane 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// Package customresourcesgate implements a CustomResourceReconciler to report GKVs status to a Gate.\n// This reconciler requires cluster scoped GET,LIST,WATCH on customresourcedefinitions[apiextensions.k8s.io]\npackage customresourcesgate\n\nimport (\n\t\"context\"\n\n\tapiextensionsv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/controller\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/logging\"\n)\n\n// Reconciler reconciles a CustomResourceDefinitions in order to gate and wait\n// on CRD readiness to start downstream controllers.\ntype Reconciler struct {\n\tlog  logging.Logger\n\tgate controller.Gate\n}\n\n// Reconcile reconciles CustomResourceDefinitions and reports ready and unready GVKs to the gate.\nfunc (r *Reconciler) Reconcile(_ context.Context, crd *apiextensionsv1.CustomResourceDefinition) (ctrl.Result, error) {\n\testablished := isEstablished(crd)\n\tgkvs := toGVKs(crd)\n\n\tswitch {\n\t// CRD is not ready or being deleted.\n\tcase !established || !crd.GetDeletionTimestamp().IsZero():\n\t\tfor gvk := range gkvs {\n\t\t\tr.log.Debug(\"gvk is not ready\", \"gvk\", gvk)\n\t\t\tr.gate.Set(gvk, false)\n\t\t}\n\n\t\treturn ctrl.Result{}, nil\n\n\t// CRD is ready.\n\tdefault:\n\t\tfor gvk, served := range gkvs {\n\t\t\tif served {\n\t\t\t\tr.log.Debug(\"gvk is ready\", \"gvk\", gvk)\n\t\t\t\tr.gate.Set(gvk, true)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ctrl.Result{}, nil\n}\n\nfunc toGVKs(crd *apiextensionsv1.CustomResourceDefinition) map[schema.GroupVersionKind]bool {\n\tgvks := make(map[schema.GroupVersionKind]bool, len(crd.Spec.Versions))\n\tfor _, version := range crd.Spec.Versions {\n\t\tgvks[schema.GroupVersionKind{Group: crd.Spec.Group, Version: version.Name, Kind: crd.Spec.Names.Kind}] = version.Served\n\t}\n\n\treturn gvks\n}\n\nfunc isEstablished(crd *apiextensionsv1.CustomResourceDefinition) bool {\n\tif len(crd.Status.Conditions) > 0 {\n\t\tfor _, cond := range crd.Status.Conditions {\n\t\t\tif cond.Type == apiextensionsv1.Established {\n\t\t\t\treturn cond.Status == apiextensionsv1.ConditionTrue\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "pkg/reconciler/customresourcesgate/reconciler_test.go",
    "content": "/*\nCopyright 2025 The Crossplane 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 customresourcesgate\n\nimport (\n\t\"context\"\n\t\"slices\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\tapiextensionsv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/logging\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nfunc TestToGVKs(t *testing.T) {\n\ttype args struct {\n\t\tcrd *apiextensionsv1.CustomResourceDefinition\n\t}\n\n\ttype want struct {\n\t\tgvks map[schema.GroupVersionKind]bool\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"SingleVersionServed\": {\n\t\t\treason: \"Should return single GVK for CRD with one served version\",\n\t\t\targs: args{\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tSpec: apiextensionsv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: \"example.com\",\n\t\t\t\t\t\tNames: apiextensionsv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tKind: \"TestResource\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVersions: []apiextensionsv1.CustomResourceDefinitionVersion{\n\t\t\t\t\t\t\t{Name: \"v1\", Served: 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\twant: want{\n\t\t\t\tgvks: map[schema.GroupVersionKind]bool{\n\t\t\t\t\t{Group: \"example.com\", Version: \"v1\", Kind: \"TestResource\"}: true,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"MultipleVersionsWithServedStatus\": {\n\t\t\treason: \"Should return GVKs with correct served status for multiple versions\",\n\t\t\targs: args{\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tSpec: apiextensionsv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: \"example.com\",\n\t\t\t\t\t\tNames: apiextensionsv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tKind: \"TestResource\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVersions: []apiextensionsv1.CustomResourceDefinitionVersion{\n\t\t\t\t\t\t\t{Name: \"v1alpha1\", Served: false},\n\t\t\t\t\t\t\t{Name: \"v1beta1\", Served: true},\n\t\t\t\t\t\t\t{Name: \"v1\", Served: 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\twant: want{\n\t\t\t\tgvks: map[schema.GroupVersionKind]bool{\n\t\t\t\t\t{Group: \"example.com\", Version: \"v1alpha1\", Kind: \"TestResource\"}: false,\n\t\t\t\t\t{Group: \"example.com\", Version: \"v1beta1\", Kind: \"TestResource\"}:  true,\n\t\t\t\t\t{Group: \"example.com\", Version: \"v1\", Kind: \"TestResource\"}:       true,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"NoVersions\": {\n\t\t\treason: \"Should return empty map for CRD with no versions\",\n\t\t\targs: args{\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tSpec: apiextensionsv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: \"example.com\",\n\t\t\t\t\t\tNames: apiextensionsv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tKind: \"TestResource\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVersions: []apiextensionsv1.CustomResourceDefinitionVersion{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tgvks: map[schema.GroupVersionKind]bool{},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := toGVKs(tc.args.crd)\n\n\t\t\tif diff := cmp.Diff(tc.want.gvks, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\ntoGVKs(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsEstablished(t *testing.T) {\n\ttype args struct {\n\t\tcrd *apiextensionsv1.CustomResourceDefinition\n\t}\n\n\ttype want struct {\n\t\testablished bool\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"EstablishedTrue\": {\n\t\t\treason: \"Should return true when CRD has Established condition with True status\",\n\t\t\targs: args{\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tStatus: apiextensionsv1.CustomResourceDefinitionStatus{\n\t\t\t\t\t\tConditions: []apiextensionsv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType:   apiextensionsv1.Established,\n\t\t\t\t\t\t\t\tStatus: apiextensionsv1.ConditionTrue,\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\twant: want{\n\t\t\t\testablished: true,\n\t\t\t},\n\t\t},\n\t\t\"EstablishedFalse\": {\n\t\t\treason: \"Should return false when CRD has Established condition with False status\",\n\t\t\targs: args{\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tStatus: apiextensionsv1.CustomResourceDefinitionStatus{\n\t\t\t\t\t\tConditions: []apiextensionsv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType:   apiextensionsv1.Established,\n\t\t\t\t\t\t\t\tStatus: apiextensionsv1.ConditionFalse,\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\twant: want{\n\t\t\t\testablished: false,\n\t\t\t},\n\t\t},\n\t\t\"EstablishedUnknown\": {\n\t\t\treason: \"Should return false when CRD has Established condition with Unknown status\",\n\t\t\targs: args{\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tStatus: apiextensionsv1.CustomResourceDefinitionStatus{\n\t\t\t\t\t\tConditions: []apiextensionsv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType:   apiextensionsv1.Established,\n\t\t\t\t\t\t\t\tStatus: apiextensionsv1.ConditionUnknown,\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\twant: want{\n\t\t\t\testablished: false,\n\t\t\t},\n\t\t},\n\t\t\"NoEstablishedCondition\": {\n\t\t\treason: \"Should return false when CRD has no Established condition\",\n\t\t\targs: args{\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tStatus: apiextensionsv1.CustomResourceDefinitionStatus{\n\t\t\t\t\t\tConditions: []apiextensionsv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType:   apiextensionsv1.NamesAccepted,\n\t\t\t\t\t\t\t\tStatus: apiextensionsv1.ConditionTrue,\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\twant: want{\n\t\t\t\testablished: false,\n\t\t\t},\n\t\t},\n\t\t\"NoConditions\": {\n\t\t\treason: \"Should return false when CRD has no conditions\",\n\t\t\targs: args{\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tStatus: apiextensionsv1.CustomResourceDefinitionStatus{\n\t\t\t\t\t\tConditions: []apiextensionsv1.CustomResourceDefinitionCondition{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\testablished: false,\n\t\t\t},\n\t\t},\n\t\t\"MultipleConditions\": {\n\t\t\treason: \"Should return true when CRD has multiple conditions including Established=True\",\n\t\t\targs: args{\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tStatus: apiextensionsv1.CustomResourceDefinitionStatus{\n\t\t\t\t\t\tConditions: []apiextensionsv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType:   apiextensionsv1.NamesAccepted,\n\t\t\t\t\t\t\t\tStatus: apiextensionsv1.ConditionTrue,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType:   apiextensionsv1.Established,\n\t\t\t\t\t\t\t\tStatus: apiextensionsv1.ConditionTrue,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType:   apiextensionsv1.Terminating,\n\t\t\t\t\t\t\t\tStatus: apiextensionsv1.ConditionFalse,\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\twant: want{\n\t\t\t\testablished: true,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := isEstablished(tc.args.crd)\n\n\t\t\tif diff := cmp.Diff(tc.want.established, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nisEstablished(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// MockGate implements the controller.Gate interface for testing.\ntype MockGate struct {\n\tTrueCalls  []schema.GroupVersionKind\n\tFalseCalls []schema.GroupVersionKind\n}\n\nfunc NewMockGate() *MockGate {\n\treturn &MockGate{\n\t\tTrueCalls:  make([]schema.GroupVersionKind, 0),\n\t\tFalseCalls: make([]schema.GroupVersionKind, 0),\n\t}\n}\n\nfunc (m *MockGate) Set(gvk schema.GroupVersionKind, value bool) bool {\n\tif value {\n\t\tif m.TrueCalls == nil {\n\t\t\tm.TrueCalls = make([]schema.GroupVersionKind, 0)\n\t\t}\n\n\t\tm.TrueCalls = append(m.TrueCalls, gvk)\n\t} else {\n\t\tif m.FalseCalls == nil {\n\t\t\tm.FalseCalls = make([]schema.GroupVersionKind, 0)\n\t\t}\n\n\t\tm.FalseCalls = append(m.FalseCalls, gvk)\n\t}\n\n\treturn true\n}\n\nfunc (m *MockGate) Register(func(), ...schema.GroupVersionKind) {}\n\nfunc TestReconcile(t *testing.T) {\n\tnow := metav1.Now()\n\n\ttype fields struct {\n\t\tgate *MockGate\n\t}\n\n\ttype args struct {\n\t\tctx context.Context\n\t\tcrd *apiextensionsv1.CustomResourceDefinition\n\t}\n\n\ttype want struct {\n\t\tresult     ctrl.Result\n\t\terr        error\n\t\ttrueCalls  []schema.GroupVersionKind\n\t\tfalseCalls []schema.GroupVersionKind\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tfields fields\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"EstablishedCRDCallsGateTrue\": {\n\t\t\treason: \"Should call gate.True for all GVKs when CRD is established\",\n\t\t\tfields: fields{\n\t\t\t\tgate: NewMockGate(),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"testresources.example.com\",\n\t\t\t\t\t},\n\t\t\t\t\tSpec: apiextensionsv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: \"example.com\",\n\t\t\t\t\t\tNames: apiextensionsv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tKind: \"TestResource\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVersions: []apiextensionsv1.CustomResourceDefinitionVersion{\n\t\t\t\t\t\t\t{Name: \"v1alpha1\", Served: true},\n\t\t\t\t\t\t\t{Name: \"v1\", Served: true},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tStatus: apiextensionsv1.CustomResourceDefinitionStatus{\n\t\t\t\t\t\tConditions: []apiextensionsv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType:   apiextensionsv1.Established,\n\t\t\t\t\t\t\t\tStatus: apiextensionsv1.ConditionTrue,\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\twant: want{\n\t\t\t\tresult: ctrl.Result{},\n\t\t\t\terr:    nil,\n\t\t\t\ttrueCalls: []schema.GroupVersionKind{\n\t\t\t\t\t{Group: \"example.com\", Version: \"v1alpha1\", Kind: \"TestResource\"},\n\t\t\t\t\t{Group: \"example.com\", Version: \"v1\", Kind: \"TestResource\"},\n\t\t\t\t},\n\t\t\t\tfalseCalls: []schema.GroupVersionKind{},\n\t\t\t},\n\t\t},\n\t\t\"NotEstablishedCRDCallsGateFalse\": {\n\t\t\treason: \"Should call gate.False for all GVKs when CRD is not established\",\n\t\t\tfields: fields{\n\t\t\t\tgate: NewMockGate(),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"testresources.example.com\",\n\t\t\t\t\t},\n\t\t\t\t\tSpec: apiextensionsv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: \"example.com\",\n\t\t\t\t\t\tNames: apiextensionsv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tKind: \"TestResource\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVersions: []apiextensionsv1.CustomResourceDefinitionVersion{\n\t\t\t\t\t\t\t{Name: \"v1\", Served: true},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tStatus: apiextensionsv1.CustomResourceDefinitionStatus{\n\t\t\t\t\t\tConditions: []apiextensionsv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType:   apiextensionsv1.Established,\n\t\t\t\t\t\t\t\tStatus: apiextensionsv1.ConditionFalse,\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\twant: want{\n\t\t\t\tresult:    ctrl.Result{},\n\t\t\t\terr:       nil,\n\t\t\t\ttrueCalls: []schema.GroupVersionKind{},\n\t\t\t\tfalseCalls: []schema.GroupVersionKind{\n\t\t\t\t\t{Group: \"example.com\", Version: \"v1\", Kind: \"TestResource\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"DeletingCRDCallsGateFalse\": {\n\t\t\treason: \"Should call gate.False for all GVKs when CRD is being deleted\",\n\t\t\tfields: fields{\n\t\t\t\tgate: NewMockGate(),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:              \"testresources.example.com\",\n\t\t\t\t\t\tDeletionTimestamp: &now,\n\t\t\t\t\t},\n\t\t\t\t\tSpec: apiextensionsv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: \"example.com\",\n\t\t\t\t\t\tNames: apiextensionsv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tKind: \"TestResource\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVersions: []apiextensionsv1.CustomResourceDefinitionVersion{\n\t\t\t\t\t\t\t{Name: \"v1\", Served: true},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tStatus: apiextensionsv1.CustomResourceDefinitionStatus{\n\t\t\t\t\t\tConditions: []apiextensionsv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType:   apiextensionsv1.Established,\n\t\t\t\t\t\t\t\tStatus: apiextensionsv1.ConditionTrue,\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\twant: want{\n\t\t\t\tresult:    ctrl.Result{},\n\t\t\t\terr:       nil,\n\t\t\t\ttrueCalls: []schema.GroupVersionKind{},\n\t\t\t\tfalseCalls: []schema.GroupVersionKind{\n\t\t\t\t\t{Group: \"example.com\", Version: \"v1\", Kind: \"TestResource\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"MixedServedVersions\": {\n\t\t\treason: \"Should only call gate.True for served versions\",\n\t\t\tfields: fields{\n\t\t\t\tgate: NewMockGate(),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"testresources.example.com\",\n\t\t\t\t\t},\n\t\t\t\t\tSpec: apiextensionsv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: \"example.com\",\n\t\t\t\t\t\tNames: apiextensionsv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tKind: \"TestResource\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVersions: []apiextensionsv1.CustomResourceDefinitionVersion{\n\t\t\t\t\t\t\t{Name: \"v1alpha1\", Served: false},\n\t\t\t\t\t\t\t{Name: \"v1\", Served: true},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tStatus: apiextensionsv1.CustomResourceDefinitionStatus{\n\t\t\t\t\t\tConditions: []apiextensionsv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType:   apiextensionsv1.Established,\n\t\t\t\t\t\t\t\tStatus: apiextensionsv1.ConditionTrue,\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\twant: want{\n\t\t\t\tresult: ctrl.Result{},\n\t\t\t\terr:    nil,\n\t\t\t\ttrueCalls: []schema.GroupVersionKind{\n\t\t\t\t\t{Group: \"example.com\", Version: \"v1\", Kind: \"TestResource\"},\n\t\t\t\t},\n\t\t\t\tfalseCalls: []schema.GroupVersionKind{},\n\t\t\t},\n\t\t},\n\t\t\"NoVersionsCRD\": {\n\t\t\treason: \"Should handle CRD with no versions gracefully\",\n\t\t\tfields: fields{\n\t\t\t\tgate: NewMockGate(),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tcrd: &apiextensionsv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"testresources.example.com\",\n\t\t\t\t\t},\n\t\t\t\t\tSpec: apiextensionsv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: \"example.com\",\n\t\t\t\t\t\tNames: apiextensionsv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tKind: \"TestResource\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVersions: []apiextensionsv1.CustomResourceDefinitionVersion{},\n\t\t\t\t\t},\n\t\t\t\t\tStatus: apiextensionsv1.CustomResourceDefinitionStatus{\n\t\t\t\t\t\tConditions: []apiextensionsv1.CustomResourceDefinitionCondition{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType:   apiextensionsv1.Established,\n\t\t\t\t\t\t\t\tStatus: apiextensionsv1.ConditionTrue,\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\twant: want{\n\t\t\t\tresult:     ctrl.Result{},\n\t\t\t\terr:        nil,\n\t\t\t\ttrueCalls:  []schema.GroupVersionKind{},\n\t\t\t\tfalseCalls: []schema.GroupVersionKind{},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := &Reconciler{\n\t\t\t\tlog:  logging.NewNopLogger(),\n\t\t\t\tgate: tc.fields.gate,\n\t\t\t}\n\n\t\t\tgot, err := r.Reconcile(tc.args.ctx, tc.args.crd)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nr.Reconcile(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.result, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nr.Reconcile(...): -want result, +got result:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\t// Only check gate calls if gate is not nil\n\t\t\tif tc.fields.gate != nil {\n\t\t\t\tsortGVK := func(a, b schema.GroupVersionKind) int {\n\t\t\t\t\tif c := strings.Compare(a.Group, b.Group); c != 0 {\n\t\t\t\t\t\treturn c\n\t\t\t\t\t}\n\t\t\t\t\tif c := strings.Compare(a.Version, b.Version); c != 0 {\n\t\t\t\t\t\treturn c\n\t\t\t\t\t}\n\t\t\t\t\treturn strings.Compare(a.Kind, b.Kind)\n\t\t\t\t}\n\n\t\t\t\tslices.SortFunc(tc.want.trueCalls, sortGVK)\n\t\t\t\tslices.SortFunc(tc.fields.gate.TrueCalls, sortGVK)\n\n\t\t\t\tif diff := cmp.Diff(tc.want.trueCalls, tc.fields.gate.TrueCalls); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"\\n%s\\ngate.True calls: -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t\t}\n\n\t\t\t\tslices.SortFunc(tc.want.falseCalls, sortGVK)\n\t\t\t\tslices.SortFunc(tc.fields.gate.FalseCalls, sortGVK)\n\n\t\t\t\tif diff := cmp.Diff(tc.want.falseCalls, tc.fields.gate.FalseCalls); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"\\n%s\\ngate.False calls: -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/reconciler/customresourcesgate/setup.go",
    "content": "/*\nCopyright 2025 The Crossplane 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\thttp://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 customresourcesgate\n\nimport (\n\t\"errors\"\n\t\"reflect\"\n\n\tapiextensionsv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/controller\"\n)\n\n// Setup adds a controller that reconciles CustomResourceDefinitions to support delayed start of controllers.\n// o.Gate is expected to be something like *gate.Gate[schema.GroupVersionKind].\nfunc Setup(mgr ctrl.Manager, o controller.Options) error {\n\tif o.Gate == nil || reflect.ValueOf(o.Gate).IsNil() {\n\t\treturn errors.New(\"gate is required\")\n\t}\n\n\tr := &Reconciler{\n\t\tlog:  o.Logger,\n\t\tgate: o.Gate,\n\t}\n\n\treturn ctrl.NewControllerManagedBy(mgr).\n\t\tFor(&apiextensionsv1.CustomResourceDefinition{}).\n\t\tNamed(\"crd-gate\").\n\t\tComplete(reconcile.AsReconciler[*apiextensionsv1.CustomResourceDefinition](mgr.GetClient(), r))\n}\n"
  },
  {
    "path": "pkg/reconciler/doc.go",
    "content": "/*\nCopyright 2020 The Crossplane 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// Package reconciler provides reconcilers for Crossplane resources.\npackage reconciler\n"
  },
  {
    "path": "pkg/reconciler/managed/api.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 managed\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\n\tjsonpatch \"github.com/evanphx/json-patch\"\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\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/util/retry\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n)\n\nconst (\n\t// fieldOwnerAPISimpleRefResolver owns the reference fields\n\t// the managed reconciler resolves.\n\tfieldOwnerAPISimpleRefResolver = \"managed.crossplane.io/api-simple-reference-resolver\"\n)\n\n// Error strings.\nconst (\n\terrCreateOrUpdateSecret      = \"cannot create or update connection secret\"\n\terrUpdateManaged             = \"cannot update managed resource\"\n\terrPatchManaged              = \"cannot patch the managed resource via server-side apply\"\n\terrMarshalExisting           = \"cannot marshal the existing object into JSON\"\n\terrMarshalResolved           = \"cannot marshal the object with the resolved references into JSON\"\n\terrPreparePatch              = \"cannot prepare the JSON merge patch for the resolved object\"\n\terrUpdateManagedStatus       = \"cannot update managed resource status\"\n\terrResolveReferences         = \"cannot resolve references\"\n\terrUpdateCriticalAnnotations = \"cannot update critical annotations\"\n)\n\n// NameAsExternalName writes the name of the managed resource to\n// the external name annotation field in order to be used as name of\n// the external resource in provider.\ntype NameAsExternalName struct{ client client.Client }\n\n// NewNameAsExternalName returns a new NameAsExternalName.\nfunc NewNameAsExternalName(c client.Client) *NameAsExternalName {\n\treturn &NameAsExternalName{client: c}\n}\n\n// Initialize the given managed resource.\nfunc (a *NameAsExternalName) Initialize(ctx context.Context, mg resource.Managed) error {\n\tif meta.GetExternalName(mg) != \"\" {\n\t\treturn nil\n\t}\n\n\tmeta.SetExternalName(mg, mg.GetName())\n\n\treturn errors.Wrap(a.client.Update(ctx, mg), errUpdateManaged)\n}\n\n// An APISecretPublisher publishes ConnectionDetails by submitting a Secret to a\n// Kubernetes API server.\ntype APISecretPublisher struct {\n\tsecret resource.Applicator\n\ttyper  runtime.ObjectTyper\n}\n\n// NewAPISecretPublisher returns a new APISecretPublisher.\nfunc NewAPISecretPublisher(c client.Client, ot runtime.ObjectTyper) *APISecretPublisher {\n\t// NOTE(negz): We transparently inject an APIPatchingApplicator in order to maintain\n\t// backward compatibility with the original API of this function.\n\treturn &APISecretPublisher{\n\t\tsecret: resource.NewApplicatorWithRetry(resource.NewAPIPatchingApplicator(c),\n\t\t\tresource.IsAPIErrorWrapped, nil),\n\t\ttyper: ot,\n\t}\n}\n\n// PublishConnection publishes the supplied ConnectionDetails to a Secret in the\n// same namespace as the supplied Managed resource. It is a no-op if the secret\n// already exists with the supplied ConnectionDetails.\nfunc (a *APISecretPublisher) PublishConnection(ctx context.Context, o resource.ConnectionSecretOwner, c ConnectionDetails) (bool, error) {\n\t// This resource does not want to expose a connection secret.\n\tif o.GetWriteConnectionSecretToReference() == nil {\n\t\treturn false, nil\n\t}\n\n\ts := resource.ConnectionSecretFor(o, resource.MustGetKind(o, a.typer))\n\ts.Data = c\n\n\terr := a.secret.Apply(ctx, s,\n\t\tresource.ConnectionSecretMustBeControllableBy(o.GetUID()),\n\t\tresource.AllowUpdateIf(func(current, desired runtime.Object) bool {\n\t\t\t// We consider the update to be a no-op and don't allow it if the\n\t\t\t// current and existing secret data are identical.\n\t\t\t//nolint:forcetypeassert // Will always be a secret.\n\t\t\treturn !cmp.Equal(current.(*corev1.Secret).Data, desired.(*corev1.Secret).Data, cmpopts.EquateEmpty())\n\t\t}),\n\t)\n\tif resource.IsNotAllowed(err) {\n\t\t// The update was not allowed because it was a no-op.\n\t\treturn false, nil\n\t}\n\n\tif err != nil {\n\t\treturn false, errors.Wrap(err, errCreateOrUpdateSecret)\n\t}\n\n\treturn true, nil\n}\n\n// UnpublishConnection is no-op since PublishConnection only creates resources\n// that will be garbage collected by Kubernetes when the managed resource is\n// deleted.\nfunc (a *APISecretPublisher) UnpublishConnection(_ context.Context, _ resource.ConnectionSecretOwner, _ ConnectionDetails) error {\n\treturn nil\n}\n\n// An APILocalSecretPublisher publishes ConnectionDetails by submitting a Secret to a\n// Kubernetes API server.\ntype APILocalSecretPublisher struct {\n\tsecret resource.Applicator\n\ttyper  runtime.ObjectTyper\n}\n\n// NewAPILocalSecretPublisher returns a new APILocalSecretPublisher.\nfunc NewAPILocalSecretPublisher(c client.Client, ot runtime.ObjectTyper) *APILocalSecretPublisher {\n\t// NOTE(negz): We transparently inject an APIPatchingApplicator in order to maintain\n\t// backward compatibility with the original API of this function.\n\treturn &APILocalSecretPublisher{\n\t\tsecret: resource.NewApplicatorWithRetry(resource.NewAPIPatchingApplicator(c),\n\t\t\tresource.IsAPIErrorWrapped, nil),\n\t\ttyper: ot,\n\t}\n}\n\n// PublishConnection publishes the supplied ConnectionDetails to a Secret in the\n// same namespace as the supplied Managed resource. It is a no-op if the secret\n// already exists with the supplied ConnectionDetails.\nfunc (a *APILocalSecretPublisher) PublishConnection(ctx context.Context, o resource.LocalConnectionSecretOwner, c ConnectionDetails) (bool, error) {\n\t// This resource does not want to expose a connection secret.\n\tif o.GetWriteConnectionSecretToReference() == nil {\n\t\treturn false, nil\n\t}\n\n\ts := resource.LocalConnectionSecretFor(o, resource.MustGetKind(o, a.typer))\n\ts.Data = c\n\n\terr := a.secret.Apply(ctx, s,\n\t\tresource.ConnectionSecretMustBeControllableBy(o.GetUID()),\n\t\tresource.AllowUpdateIf(func(current, desired runtime.Object) bool {\n\t\t\t// We consider the update to be a no-op and don't allow it if the\n\t\t\t// current and existing secret data are identical.\n\t\t\t//nolint:forcetypeassert // Will always be a secret.\n\t\t\t// NOTE(erhancagirici): cmp package is not recommended for production use\n\t\t\treturn !cmp.Equal(current.(*corev1.Secret).Data, desired.(*corev1.Secret).Data, cmpopts.EquateEmpty())\n\t\t}),\n\t)\n\tif resource.IsNotAllowed(err) {\n\t\t// The update was not allowed because it was a no-op.\n\t\treturn false, nil\n\t}\n\n\tif err != nil {\n\t\treturn false, errors.Wrap(err, errCreateOrUpdateSecret)\n\t}\n\n\treturn true, nil\n}\n\n// UnpublishConnection is no-op since PublishConnection only creates resources\n// that will be garbage collected by Kubernetes when the managed resource is\n// deleted.\nfunc (a *APILocalSecretPublisher) UnpublishConnection(_ context.Context, _ resource.LocalConnectionSecretOwner, _ ConnectionDetails) error {\n\treturn nil\n}\n\n// An APISimpleReferenceResolver resolves references from one managed resource\n// to others by calling the referencing resource's ResolveReferences method, if\n// any.\ntype APISimpleReferenceResolver struct {\n\tclient client.Client\n}\n\n// NewAPISimpleReferenceResolver returns a ReferenceResolver that resolves\n// references from one managed resource to others by calling the referencing\n// resource's ResolveReferences method, if any.\nfunc NewAPISimpleReferenceResolver(c client.Client) *APISimpleReferenceResolver {\n\treturn &APISimpleReferenceResolver{client: c}\n}\n\nfunc prepareJSONMerge(existing, resolved runtime.Object) ([]byte, error) {\n\t// restore the to be replaced GVK so that the existing object is\n\t// not modified by this function.\n\tdefer existing.GetObjectKind().SetGroupVersionKind(existing.GetObjectKind().GroupVersionKind())\n\t// we need the apiVersion and kind in the patch document so we set them\n\t// to their zero values and make them available in the calculated patch\n\t// in the first place, instead of an unmarshal/marshal from the prepared\n\t// patch []byte later.\n\texisting.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})\n\n\teBuff, err := json.Marshal(existing)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, errMarshalExisting)\n\t}\n\n\trBuff, err := json.Marshal(resolved)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, errMarshalResolved)\n\t}\n\n\tpatch, err := jsonpatch.CreateMergePatch(eBuff, rBuff)\n\n\treturn patch, errors.Wrap(err, errPreparePatch)\n}\n\n// ResolveReferences of the supplied managed resource by calling its\n// ResolveReferences method, if any.\nfunc (a *APISimpleReferenceResolver) ResolveReferences(ctx context.Context, mg resource.Managed) error {\n\trr, ok := mg.(interface {\n\t\tResolveReferences(ctx context.Context, r client.Reader) error\n\t})\n\tif !ok {\n\t\t// This managed resource doesn't have any references to resolve.\n\t\treturn nil\n\t}\n\n\texisting := mg.DeepCopyObject()\n\n\tif err := rr.ResolveReferences(ctx, a.client); err != nil {\n\t\treturn errors.Wrap(err, errResolveReferences)\n\t}\n\n\tif cmp.Equal(existing, mg, cmpopts.EquateEmpty()) {\n\t\t// The resource didn't change during reference resolution.\n\t\treturn nil\n\t}\n\n\tpatch, err := prepareJSONMerge(existing, mg)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn errors.Wrap(a.client.Patch(ctx, mg, client.RawPatch(types.ApplyPatchType, patch), client.FieldOwner(fieldOwnerAPISimpleRefResolver), client.ForceOwnership), errPatchManaged)\n}\n\n// A RetryingCriticalAnnotationUpdater is a CriticalAnnotationUpdater that\n// retries annotation updates in the face of API server errors.\ntype RetryingCriticalAnnotationUpdater struct {\n\tclient client.Client\n}\n\n// NewRetryingCriticalAnnotationUpdater returns a CriticalAnnotationUpdater that\n// retries annotation updates in the face of API server errors.\nfunc NewRetryingCriticalAnnotationUpdater(c client.Client) *RetryingCriticalAnnotationUpdater {\n\treturn &RetryingCriticalAnnotationUpdater{client: c}\n}\n\n// UpdateCriticalAnnotations updates (i.e. persists) the annotations of the\n// supplied Object. It retries in the face of any API server error several times\n// in order to ensure annotations that contain critical state are persisted.\n// Pending changes to the supplied Object's spec, status, or other metadata\n// might get reset to their current state according to the API server, e.g. in\n// case of a conflict error.\nfunc (u *RetryingCriticalAnnotationUpdater) UpdateCriticalAnnotations(ctx context.Context, o client.Object) error {\n\ta := o.GetAnnotations()\n\terr := retry.OnError(retry.DefaultRetry, func(err error) bool {\n\t\treturn !errors.Is(err, context.Canceled)\n\t}, func() error {\n\t\terr := u.client.Update(ctx, o)\n\t\tif kerrors.IsConflict(err) {\n\t\t\tif getErr := u.client.Get(ctx, client.ObjectKeyFromObject(o), o); getErr != nil {\n\t\t\t\treturn getErr\n\t\t\t}\n\n\t\t\tmeta.AddAnnotations(o, a)\n\t\t}\n\n\t\treturn err\n\t})\n\n\treturn errors.Wrap(err, errUpdateCriticalAnnotations)\n}\n"
  },
  {
    "path": "pkg/reconciler/managed/api_test.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 managed\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\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\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nvar (\n\t_ Initializer              = &NameAsExternalName{}\n\t_ ConnectionPublisher      = &APISecretPublisher{}\n\t_ LocalConnectionPublisher = &APILocalSecretPublisher{}\n)\n\nfunc TestNameAsExternalName(t *testing.T) {\n\ttype args struct {\n\t\tctx context.Context\n\t\tmg  resource.Managed\n\t}\n\n\ttype want struct {\n\t\terr error\n\t\tmg  resource.Managed\n\t}\n\n\terrBoom := errors.New(\"boom\")\n\ttestExternalName := \"my-\" +\n\t\t\"external-name\"\n\n\tcases := map[string]struct {\n\t\tclient client.Client\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"UpdateManagedError\": {\n\t\t\tclient: &test.MockClient{MockUpdate: test.NewMockUpdateFn(errBoom)},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg:  &fake.LegacyManaged{ObjectMeta: metav1.ObjectMeta{Name: testExternalName}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errUpdateManaged),\n\t\t\t\tmg: &fake.LegacyManaged{ObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:        testExternalName,\n\t\t\t\t\tAnnotations: map[string]string{meta.AnnotationKeyExternalName: testExternalName},\n\t\t\t\t}},\n\t\t\t},\n\t\t},\n\t\t\"UpdateSuccessful\": {\n\t\t\tclient: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil)},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg:  &fake.LegacyManaged{ObjectMeta: metav1.ObjectMeta{Name: testExternalName}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: nil,\n\t\t\t\tmg: &fake.LegacyManaged{ObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:        testExternalName,\n\t\t\t\t\tAnnotations: map[string]string{meta.AnnotationKeyExternalName: testExternalName},\n\t\t\t\t}},\n\t\t\t},\n\t\t},\n\t\t\"UpdateNotNeeded\": {\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg: &fake.LegacyManaged{ObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:        testExternalName,\n\t\t\t\t\tAnnotations: map[string]string{meta.AnnotationKeyExternalName: \"some-name\"},\n\t\t\t\t}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: nil,\n\t\t\t\tmg: &fake.LegacyManaged{ObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:        testExternalName,\n\t\t\t\t\tAnnotations: map[string]string{meta.AnnotationKeyExternalName: \"some-name\"},\n\t\t\t\t}},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tapi := NewNameAsExternalName(tc.client)\n\n\t\t\terr := api.Initialize(tc.args.ctx, tc.args.mg)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"api.Initialize(...): -want error, +got error:\\n%s\", diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.mg, tc.args.mg, test.EquateConditions()); diff != \"\" {\n\t\t\t\tt.Errorf(\"api.Initialize(...) Managed: -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAPISecretPublisher(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\n\tmg := &fake.LegacyManaged{\n\t\tConnectionSecretWriterTo: fake.ConnectionSecretWriterTo{Ref: &xpv2.SecretReference{\n\t\t\tNamespace: \"coolnamespace\",\n\t\t\tName:      \"coolsecret\",\n\t\t}},\n\t}\n\n\tcd := ConnectionDetails{\"cool\": {42}}\n\n\ttype fields struct {\n\t\tsecret resource.Applicator\n\t\ttyper  runtime.ObjectTyper\n\t}\n\n\ttype args struct {\n\t\tctx context.Context\n\t\tmg  resource.LegacyManaged\n\t\tc   ConnectionDetails\n\t}\n\n\ttype want struct {\n\t\terr       error\n\t\tpublished bool\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tfields fields\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"ResourceDoesNotPublishSecret\": {\n\t\t\treason: \"A managed resource with a nil GetWriteConnectionSecretToReference should not publish a secret\",\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg:  &fake.LegacyManaged{},\n\t\t\t},\n\t\t},\n\t\t\"ApplyError\": {\n\t\t\treason: \"An error applying the connection secret should be returned\",\n\t\t\tfields: fields{\n\t\t\t\tsecret: resource.ApplyFn(func(_ context.Context, _ client.Object, _ ...resource.ApplyOption) error { return errBoom }),\n\t\t\t\ttyper:  fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg:  mg,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errCreateOrUpdateSecret),\n\t\t\t},\n\t\t},\n\t\t\"AlreadyPublished\": {\n\t\t\treason: \"An up to date connection secret should result in no error and not being published\",\n\t\t\tfields: fields{\n\t\t\t\tsecret: resource.ApplyFn(func(ctx context.Context, o client.Object, ao ...resource.ApplyOption) error {\n\t\t\t\t\twant := resource.ConnectionSecretFor(mg, fake.GVK(mg))\n\n\t\t\t\t\twant.Data = cd\n\t\t\t\t\tfor _, fn := range ao {\n\t\t\t\t\t\tif err := fn(ctx, o, want); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t\ttyper: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg:  mg,\n\t\t\t\tc:   cd,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpublished: false,\n\t\t\t\terr:       nil,\n\t\t\t},\n\t\t},\n\t\t\"Success\": {\n\t\t\treason: \"A successful application of the connection secret should result in no error\",\n\t\t\tfields: fields{\n\t\t\t\tsecret: resource.ApplyFn(func(_ context.Context, o client.Object, _ ...resource.ApplyOption) error {\n\t\t\t\t\twant := resource.ConnectionSecretFor(mg, fake.GVK(mg))\n\n\t\t\t\t\twant.Data = cd\n\t\t\t\t\tif diff := cmp.Diff(want, o); diff != \"\" {\n\t\t\t\t\t\tt.Errorf(\"-want, +got:\\n%s\", diff)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t\ttyper: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg:  mg,\n\t\t\t\tc:   cd,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpublished: true,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ta := &APISecretPublisher{tc.fields.secret, tc.fields.typer}\n\n\t\t\tgot, gotErr := a.PublishConnection(tc.args.ctx, tc.args.mg, tc.args.c)\n\t\t\tif diff := cmp.Diff(tc.want.err, gotErr, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nPublish(...): -wantErr, +gotErr:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.published, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nPublish(...): -wantPublished, +gotPublished:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAPILocalSecretPublisher(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\n\tmg := &fake.ModernManaged{\n\t\tLocalConnectionSecretWriterTo: fake.LocalConnectionSecretWriterTo{Ref: &xpv2.LocalSecretReference{\n\t\t\tName: \"coolsecret\",\n\t\t}},\n\t}\n\n\tcd := ConnectionDetails{\"cool\": {42}}\n\n\ttype fields struct {\n\t\tsecret resource.Applicator\n\t\ttyper  runtime.ObjectTyper\n\t}\n\n\ttype args struct {\n\t\tctx context.Context\n\t\tmg  resource.ModernManaged\n\t\tc   ConnectionDetails\n\t}\n\n\ttype want struct {\n\t\terr       error\n\t\tpublished bool\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tfields fields\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"ResourceDoesNotPublishSecret\": {\n\t\t\treason: \"A managed resource with a nil GetWriteConnectionSecretToReference should not publish a secret\",\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg:  &fake.ModernManaged{},\n\t\t\t},\n\t\t},\n\t\t\"ApplyError\": {\n\t\t\treason: \"An error applying the connection secret should be returned\",\n\t\t\tfields: fields{\n\t\t\t\tsecret: resource.ApplyFn(func(_ context.Context, _ client.Object, _ ...resource.ApplyOption) error { return errBoom }),\n\t\t\t\ttyper:  fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg:  mg,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errCreateOrUpdateSecret),\n\t\t\t},\n\t\t},\n\t\t\"AlreadyPublished\": {\n\t\t\treason: \"An up to date connection secret should result in no error and not being published\",\n\t\t\tfields: fields{\n\t\t\t\tsecret: resource.ApplyFn(func(ctx context.Context, o client.Object, ao ...resource.ApplyOption) error {\n\t\t\t\t\twant := resource.LocalConnectionSecretFor(mg, fake.GVK(mg))\n\n\t\t\t\t\twant.Data = cd\n\t\t\t\t\tfor _, fn := range ao {\n\t\t\t\t\t\tif err := fn(ctx, o, want); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t\ttyper: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg:  mg,\n\t\t\t\tc:   cd,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpublished: false,\n\t\t\t\terr:       nil,\n\t\t\t},\n\t\t},\n\t\t\"Success\": {\n\t\t\treason: \"A successful application of the connection secret should result in no error\",\n\t\t\tfields: fields{\n\t\t\t\tsecret: resource.ApplyFn(func(_ context.Context, o client.Object, _ ...resource.ApplyOption) error {\n\t\t\t\t\twant := resource.LocalConnectionSecretFor(mg, fake.GVK(mg))\n\n\t\t\t\t\twant.Data = cd\n\t\t\t\t\tif diff := cmp.Diff(want, o); diff != \"\" {\n\t\t\t\t\t\tt.Errorf(\"-want, +got:\\n%s\", diff)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t\ttyper: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg:  mg,\n\t\t\t\tc:   cd,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpublished: true,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ta := &APILocalSecretPublisher{tc.fields.secret, tc.fields.typer}\n\n\t\t\tgot, gotErr := a.PublishConnection(tc.args.ctx, tc.args.mg, tc.args.c)\n\t\t\tif diff := cmp.Diff(tc.want.err, gotErr, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nPublish(...): -wantErr, +gotErr:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.published, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nPublish(...): -wantPublished, +gotPublished:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype mockSimpleReferencer struct {\n\tresource.Managed\n\n\tMockResolveReferences func(context.Context, client.Reader) error `json:\"-\"`\n}\n\nfunc (r *mockSimpleReferencer) ResolveReferences(ctx context.Context, c client.Reader) error {\n\treturn r.MockResolveReferences(ctx, c)\n}\n\nfunc (r *mockSimpleReferencer) DeepCopyObject() runtime.Object {\n\treturn &mockSimpleReferencer{Managed: r.Managed.DeepCopyObject().(resource.Managed)}\n}\n\nfunc (r *mockSimpleReferencer) Equal(s *mockSimpleReferencer) bool {\n\treturn cmp.Equal(r.Managed, s.Managed)\n}\n\nfunc TestResolveReferences(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\n\tdifferent := &fake.LegacyManaged{}\n\n\ttype args struct {\n\t\tctx context.Context\n\t\tmg  resource.Managed\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tc      client.Client\n\t\targs   args\n\t\twant   error\n\t}{\n\t\t\"NoReferencersFound\": {\n\t\t\treason: \"Should return early without error when the managed resource has no references.\",\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg:  &fake.LegacyManaged{},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"ResolveReferencesError\": {\n\t\t\treason: \"Should return errors encountered while resolving references.\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg: &mockSimpleReferencer{\n\t\t\t\t\tManaged: &fake.LegacyManaged{},\n\t\t\t\t\tMockResolveReferences: func(context.Context, client.Reader) error {\n\t\t\t\t\t\treturn errBoom\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: errors.Wrap(errBoom, errResolveReferences),\n\t\t},\n\t\t\"SuccessfulNoop\": {\n\t\t\treason: \"Should return without error when resolution does not change the managed resource.\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg: &mockSimpleReferencer{\n\t\t\t\t\tManaged: &fake.LegacyManaged{},\n\t\t\t\t\tMockResolveReferences: func(context.Context, client.Reader) error {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"SuccessfulUpdate\": {\n\t\t\treason: \"Should return without error when a value is successfully resolved.\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockPatch: test.NewMockPatchFn(nil),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg: &mockSimpleReferencer{\n\t\t\t\t\tManaged: different,\n\t\t\t\t\tMockResolveReferences: func(context.Context, client.Reader) error {\n\t\t\t\t\t\tdifferent.SetName(\"I'm different!\")\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"PatchError\": {\n\t\t\treason: \"Should return an error when the managed resource cannot be updated.\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockPatch: test.NewMockPatchFn(errBoom),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tmg: &mockSimpleReferencer{\n\t\t\t\t\tManaged: different,\n\t\t\t\t\tMockResolveReferences: func(context.Context, client.Reader) error {\n\t\t\t\t\t\tdifferent.SetName(\"I'm different-er!\")\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: errors.Wrap(errBoom, errPatchManaged),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewAPISimpleReferenceResolver(tc.c)\n\n\t\t\tgot := r.ResolveReferences(tc.args.ctx, tc.args.mg)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nr.ResolveReferences(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestPrepareJSONMerge(t *testing.T) {\n\ttype args struct {\n\t\texisting runtime.Object\n\t\tresolved runtime.Object\n\t}\n\n\ttype want struct {\n\t\tpatch string\n\t\terr   error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"SuccessfulPatch\": {\n\t\t\treason: \"Should successfully compute the JSON merge patch document.\",\n\t\t\targs: args{\n\t\t\t\texisting: &fake.LegacyManaged{},\n\t\t\t\tresolved: &fake.LegacyManaged{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"resolved\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpatch: `{\"name\":\"resolved\"}`,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tpatch, err := prepareJSONMerge(tc.args.existing, tc.args.resolved)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nprepareJSONMerge(...): -wantErr, +gotErr:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.patch, string(patch)); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nprepareJSONMerge(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRetryingCriticalAnnotationUpdater(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\n\ttype args struct {\n\t\tctx context.Context\n\t\to   client.Object\n\t}\n\n\ttype want struct {\n\t\terr error\n\t\to   client.Object\n\t}\n\n\tsetLabels := func(obj client.Object) error {\n\t\tobj.SetLabels(map[string]string{\"getcalled\": \"true\"})\n\t\treturn nil\n\t}\n\tobjectReturnedByGet := &fake.LegacyManaged{}\n\tsetLabels(objectReturnedByGet)\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tc      *test.MockClient\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"UpdateConflictGetError\": {\n\t\t\treason: \"We should return any error we encounter getting the supplied object\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(errBoom, setLabels),\n\t\t\t\tMockUpdate: test.NewMockUpdateFn(kerrors.NewConflict(schema.GroupResource{\n\t\t\t\t\tGroup:    \"foo.com\",\n\t\t\t\t\tResource: \"bars\",\n\t\t\t\t}, \"abc\", errBoom)),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\to: &fake.LegacyManaged{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errUpdateCriticalAnnotations),\n\t\t\t\to:   objectReturnedByGet,\n\t\t\t},\n\t\t},\n\t\t\"UpdateError\": {\n\t\t\treason: \"We should return any error we encounter updating the supplied object\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet:    test.NewMockGetFn(nil, setLabels),\n\t\t\t\tMockUpdate: test.NewMockUpdateFn(errBoom),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\to: &fake.LegacyManaged{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errUpdateCriticalAnnotations),\n\t\t\t\to:   &fake.LegacyManaged{},\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulGetAfterAConflict\": {\n\t\t\treason: \"A successful get after a conflict should not hide the conflict error and prevent retries\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, setLabels),\n\t\t\t\tMockUpdate: test.NewMockUpdateFn(kerrors.NewConflict(schema.GroupResource{\n\t\t\t\t\tGroup:    \"foo.com\",\n\t\t\t\t\tResource: \"bars\",\n\t\t\t\t}, \"abc\", errBoom)),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\to: &fake.LegacyManaged{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(kerrors.NewConflict(schema.GroupResource{\n\t\t\t\t\tGroup:    \"foo.com\",\n\t\t\t\t\tResource: \"bars\",\n\t\t\t\t}, \"abc\", errBoom), errUpdateCriticalAnnotations),\n\t\t\t\to: objectReturnedByGet,\n\t\t\t},\n\t\t},\n\t\t\"Success\": {\n\t\t\treason: \"We should return without error if we successfully update our annotations\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet:    test.NewMockGetFn(nil, setLabels),\n\t\t\t\tMockUpdate: test.NewMockUpdateFn(errBoom),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\to: &fake.LegacyManaged{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errUpdateCriticalAnnotations),\n\t\t\t\to:   &fake.LegacyManaged{},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tu := NewRetryingCriticalAnnotationUpdater(tc.c)\n\n\t\t\tgot := u.UpdateCriticalAnnotations(tc.args.ctx, tc.args.o)\n\t\t\tif diff := cmp.Diff(tc.want.err, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nu.UpdateCriticalAnnotations(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.o, tc.args.o); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nu.UpdateCriticalAnnotations(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/reconciler/managed/changelogger.go",
    "content": "/*\nCopyright 2024 The Crossplane 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 managed\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/protobuf/types/known/timestamppb\"\n\t\"k8s.io/utils/ptr\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/apis/changelogs/proto/v1alpha1\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n)\n\nconst (\n\tdefaultSendTimeout = 10 * time.Second\n)\n\n// ChangeLogger is an interface for recording changes made to resources to the\n// change logs.\ntype ChangeLogger interface {\n\tLog(ctx context.Context, managed resource.Managed, opType v1alpha1.OperationType, changeErr error, ad AdditionalDetails) error\n}\n\n// GRPCChangeLogger processes changes to resources and helps to send them to the\n// change log gRPC service.\ntype GRPCChangeLogger struct {\n\tclient          v1alpha1.ChangeLogServiceClient\n\tproviderVersion string\n\tsendTimeout     time.Duration\n}\n\n// NewGRPCChangeLogger creates a new gRPC based ChangeLogger initialized with\n// the given client.\nfunc NewGRPCChangeLogger(client v1alpha1.ChangeLogServiceClient, o ...GRPCChangeLoggerOption) *GRPCChangeLogger {\n\tg := &GRPCChangeLogger{\n\t\tclient:      client,\n\t\tsendTimeout: defaultSendTimeout,\n\t}\n\n\tfor _, clo := range o {\n\t\tclo(g)\n\t}\n\n\treturn g\n}\n\n// A GRPCChangeLoggerOption configures a GRPCChangeLoggerOption.\ntype GRPCChangeLoggerOption func(*GRPCChangeLogger)\n\n// WithProviderVersion sets the provider version to be included in the change\n// log entry.\nfunc WithProviderVersion(version string) GRPCChangeLoggerOption {\n\treturn func(g *GRPCChangeLogger) {\n\t\tg.providerVersion = version\n\t}\n}\n\n// WithSendTimeout sets the timeout for sending and/or waiting for change log\n// entries to the change log service.\nfunc WithSendTimeout(timeout time.Duration) GRPCChangeLoggerOption {\n\treturn func(g *GRPCChangeLogger) {\n\t\tg.sendTimeout = timeout\n\t}\n}\n\n// Log sends the given change log entry to the change log service.\nfunc (g *GRPCChangeLogger) Log(ctx context.Context, managed resource.Managed, opType v1alpha1.OperationType, changeErr error, ad AdditionalDetails) error {\n\t// get an error message from the error if it exists\n\tvar changeErrMessage *string\n\tif changeErr != nil {\n\t\tchangeErrMessage = ptr.To(changeErr.Error())\n\t}\n\n\t// capture the full state of the managed resource from before we performed the change\n\tsnapshot, err := resource.AsProtobufStruct(managed)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"cannot snapshot managed resource\")\n\t}\n\n\tgvk := managed.GetObjectKind().GroupVersionKind()\n\n\tentry := &v1alpha1.ChangeLogEntry{\n\t\tTimestamp:         timestamppb.Now(),\n\t\tProvider:          g.providerVersion,\n\t\tApiVersion:        gvk.GroupVersion().String(),\n\t\tKind:              gvk.Kind,\n\t\tName:              managed.GetName(),\n\t\tExternalName:      meta.GetExternalName(managed),\n\t\tOperation:         opType,\n\t\tSnapshot:          snapshot,\n\t\tErrorMessage:      changeErrMessage,\n\t\tAdditionalDetails: ad,\n\t}\n\n\t// create a specific context and timeout for sending the change log entry\n\t// that is different than the parent context that is for the entire\n\t// reconciliation\n\tsendCtx, sendCancel := context.WithTimeout(ctx, g.sendTimeout)\n\tdefer sendCancel()\n\n\t// send everything we've got to the change log service\n\t_, err = g.client.SendChangeLog(sendCtx, &v1alpha1.SendChangeLogRequest{Entry: entry}, grpc.WaitForReady(true))\n\n\treturn errors.Wrap(err, \"cannot send change log entry\")\n}\n\n// nopChangeLogger does nothing for recording change logs, this is the default\n// implementation if a provider has not enabled the change logs feature.\ntype nopChangeLogger struct{}\n\nfunc newNopChangeLogger() *nopChangeLogger {\n\treturn &nopChangeLogger{}\n}\n\nfunc (n *nopChangeLogger) Log(_ context.Context, _ resource.Managed, _ v1alpha1.OperationType, _ error, _ AdditionalDetails) error {\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/reconciler/managed/changelogger_test.go",
    "content": "/*\nCopyright 2024 The Crossplane 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 managed\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/protobuf/testing/protocmp\"\n\t\"google.golang.org/protobuf/types/known/structpb\"\n\t\"google.golang.org/protobuf/types/known/timestamppb\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/utils/ptr\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/apis/changelogs/proto/v1alpha1\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\n// A mock implementation of the ChangeLogServiceClient interface to help with\n// testing and verifying change log entries.\ntype changeLogServiceClient struct {\n\trequests []*v1alpha1.SendChangeLogRequest\n\tsendFn   func(ctx context.Context, in *v1alpha1.SendChangeLogRequest, opts ...grpc.CallOption) (*v1alpha1.SendChangeLogResponse, error)\n}\n\nfunc (c *changeLogServiceClient) SendChangeLog(ctx context.Context, in *v1alpha1.SendChangeLogRequest, opts ...grpc.CallOption) (*v1alpha1.SendChangeLogResponse, error) {\n\tc.requests = append(c.requests, in)\n\tif c.sendFn != nil {\n\t\treturn c.sendFn(ctx, in, opts...)\n\t}\n\n\treturn nil, nil\n}\n\nfunc TestChangeLogger(t *testing.T) {\n\ttype args struct {\n\t\tmr  resource.Managed\n\t\tad  AdditionalDetails\n\t\terr error\n\t\tc   *changeLogServiceClient\n\t}\n\n\ttype want struct {\n\t\trequests []*v1alpha1.SendChangeLogRequest\n\t\terr      error\n\t}\n\n\terrBoom := errors.New(\"boom\")\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"ChangeLogsSuccess\": {\n\t\t\treason: \"Change log entry should be recorded successfully.\",\n\t\t\targs: args{\n\t\t\t\tmr: &fake.Managed{ObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:        \"cool-managed\",\n\t\t\t\t\tAnnotations: map[string]string{meta.AnnotationKeyExternalName: \"cool-managed\"},\n\t\t\t\t}},\n\t\t\t\terr: errBoom,\n\t\t\t\tad:  AdditionalDetails{\"key\": \"value\", \"key2\": \"value2\"},\n\t\t\t\tc:   &changeLogServiceClient{requests: []*v1alpha1.SendChangeLogRequest{}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\t// a well fleshed out change log entry should be sent\n\t\t\t\trequests: []*v1alpha1.SendChangeLogRequest{\n\t\t\t\t\t{\n\t\t\t\t\t\tEntry: &v1alpha1.ChangeLogEntry{\n\t\t\t\t\t\t\tTimestamp:    timestamppb.Now(),\n\t\t\t\t\t\t\tProvider:     \"provider-cool:v9.99.999\",\n\t\t\t\t\t\t\tApiVersion:   (&fake.Managed{}).GetObjectKind().GroupVersionKind().GroupVersion().String(),\n\t\t\t\t\t\t\tKind:         (&fake.Managed{}).GetObjectKind().GroupVersionKind().Kind,\n\t\t\t\t\t\t\tName:         \"cool-managed\",\n\t\t\t\t\t\t\tExternalName: \"cool-managed\",\n\t\t\t\t\t\t\tOperation:    v1alpha1.OperationType_OPERATION_TYPE_CREATE,\n\t\t\t\t\t\t\tSnapshot: mustObjectAsProtobufStruct(&fake.Managed{ObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\t\t\tName:        \"cool-managed\",\n\t\t\t\t\t\t\t\tAnnotations: map[string]string{meta.AnnotationKeyExternalName: \"cool-managed\"},\n\t\t\t\t\t\t\t}}),\n\t\t\t\t\t\t\tErrorMessage:      ptr.To(\"boom\"),\n\t\t\t\t\t\t\tAdditionalDetails: AdditionalDetails{\"key\": \"value\", \"key2\": \"value2\"},\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\"SendChangeLogsFailure\": {\n\t\t\treason: \"Error from sending change log entry should be handled and recorded.\",\n\t\t\targs: args{\n\t\t\t\tmr: &fake.Managed{},\n\t\t\t\tc: &changeLogServiceClient{\n\t\t\t\t\trequests: []*v1alpha1.SendChangeLogRequest{},\n\t\t\t\t\t// make the send change log function return an error\n\t\t\t\t\tsendFn: func(_ context.Context, _ *v1alpha1.SendChangeLogRequest, _ ...grpc.CallOption) (*v1alpha1.SendChangeLogResponse, error) {\n\t\t\t\t\t\treturn &v1alpha1.SendChangeLogResponse{}, errBoom\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\t// we'll still see a change log entry, but it won't make it all\n\t\t\t\t// the way to its destination and we should see an event for\n\t\t\t\t// that failure\n\t\t\t\trequests: []*v1alpha1.SendChangeLogRequest{\n\t\t\t\t\t{\n\t\t\t\t\t\tEntry: &v1alpha1.ChangeLogEntry{\n\t\t\t\t\t\t\t// we expect less fields to be set on the change log\n\t\t\t\t\t\t\t// entry because we're not initializing the managed\n\t\t\t\t\t\t\t// resource with much data in this simulated failure\n\t\t\t\t\t\t\t// test case\n\t\t\t\t\t\t\tTimestamp:  timestamppb.Now(),\n\t\t\t\t\t\t\tProvider:   \"provider-cool:v9.99.999\",\n\t\t\t\t\t\t\tApiVersion: (&fake.Managed{}).GetObjectKind().GroupVersionKind().GroupVersion().String(),\n\t\t\t\t\t\t\tKind:       (&fake.Managed{}).GetObjectKind().GroupVersionKind().Kind,\n\t\t\t\t\t\t\tOperation:  v1alpha1.OperationType_OPERATION_TYPE_CREATE,\n\t\t\t\t\t\t\tSnapshot:   mustObjectAsProtobufStruct(&fake.Managed{}),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\terr: errors.Wrap(errBoom, \"cannot send change log entry\"),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tchange := NewGRPCChangeLogger(tc.args.c, WithProviderVersion(\"provider-cool:v9.99.999\"))\n\t\t\terr := change.Log(context.Background(), tc.args.mr, v1alpha1.OperationType_OPERATION_TYPE_CREATE, tc.args.err, tc.args.ad)\n\n\t\t\tif diff := cmp.Diff(tc.want.requests, tc.args.c.requests, equateApproxTimepb(time.Second)...); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nr.RecordChangeLog(...): -want requests, +got requests:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nr.RecordChangeLog(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc mustObjectAsProtobufStruct(o runtime.Object) *structpb.Struct {\n\ts, err := resource.AsProtobufStruct(o)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn s\n}\n\n// A set of cmp.Option that enables usage of cmpopts.EquateApproxTime for\n// timestamppb.Timestamp types.\n// Source: https://github.com/golang/protobuf/issues/1347\nfunc equateApproxTimepb(margin time.Duration) []cmp.Option {\n\treturn cmp.Options{\n\t\tcmpopts.EquateApproxTime(margin),\n\t\tprotocmp.Transform(),\n\t\tcmp.FilterPath(\n\t\t\tfunc(p cmp.Path) bool {\n\t\t\t\tif p.Last().Type() == reflect.TypeFor[protocmp.Message]() {\n\t\t\t\t\ta, b := p.Last().Values()\n\t\t\t\t\treturn msgIsTimestamp(a) && msgIsTimestamp(b)\n\t\t\t\t}\n\n\t\t\t\treturn false\n\t\t\t},\n\t\t\tcmp.Transformer(\"timestamppb\", func(t protocmp.Message) time.Time {\n\t\t\t\treturn time.Unix(t[\"seconds\"].(int64), int64(t[\"nanos\"].(int32))).UTC()\n\t\t\t}),\n\t\t),\n\t}\n}\n\nfunc msgIsTimestamp(x reflect.Value) bool {\n\treturn x.Interface().(protocmp.Message).Descriptor().FullName() == \"google.protobuf.Timestamp\"\n}\n"
  },
  {
    "path": "pkg/reconciler/managed/doc.go",
    "content": "/*\nCopyright 2020 The Crossplane 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// Package managed provides a reconciler that manages the lifecycle of a\n// resource in an external system.\npackage managed\n"
  },
  {
    "path": "pkg/reconciler/managed/metrics.go",
    "content": "/*\nCopyright 2024 The Crossplane 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 managed\n\nimport (\n\t\"sync\"\n\t\"time\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tkmetrics \"k8s.io/component-base/metrics\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n)\n\nconst subSystem = \"crossplane\"\n\n// MetricRecorder records the managed resource metrics.\ntype MetricRecorder interface { //nolint:interfacebloat // The first two methods are coming from Prometheus\n\tDescribe(ch chan<- *prometheus.Desc)\n\tCollect(ch chan<- prometheus.Metric)\n\n\trecordUnchanged(name string)\n\trecordFirstTimeReconciled(managed resource.Managed)\n\trecordFirstTimeReady(managed resource.Managed)\n\trecordDrift(managed resource.Managed)\n\trecordDeleted(managed resource.Managed)\n}\n\n// MRMetricRecorder records the lifecycle metrics of managed resources.\ntype MRMetricRecorder struct {\n\tfirstObservation sync.Map\n\tlastObservation  sync.Map\n\n\tmrDetected       *prometheus.HistogramVec\n\tmrFirstTimeReady *prometheus.HistogramVec\n\tmrDeletion       *prometheus.HistogramVec\n\tmrDrift          *prometheus.HistogramVec\n}\n\n// NewMRMetricRecorder returns a new MRMetricRecorder which records metrics for managed resources.\nfunc NewMRMetricRecorder() *MRMetricRecorder {\n\treturn &MRMetricRecorder{\n\t\tmrDetected: prometheus.NewHistogramVec(prometheus.HistogramOpts{\n\t\t\tSubsystem: subSystem,\n\t\t\tName:      \"managed_resource_first_time_to_reconcile_seconds\",\n\t\t\tHelp:      \"The time it took for a managed resource to be detected by the controller\",\n\t\t\tBuckets:   kmetrics.ExponentialBuckets(10e-9, 10, 10),\n\t\t}, []string{\"gvk\"}),\n\t\tmrFirstTimeReady: prometheus.NewHistogramVec(prometheus.HistogramOpts{\n\t\t\tSubsystem: subSystem,\n\t\t\tName:      \"managed_resource_first_time_to_readiness_seconds\",\n\t\t\tHelp:      \"The time it took for a managed resource to become ready first time after creation\",\n\t\t\tBuckets:   []float64{1, 5, 10, 15, 30, 60, 120, 300, 600, 1800, 3600},\n\t\t}, []string{\"gvk\"}),\n\t\tmrDeletion: prometheus.NewHistogramVec(prometheus.HistogramOpts{\n\t\t\tSubsystem: subSystem,\n\t\t\tName:      \"managed_resource_deletion_seconds\",\n\t\t\tHelp:      \"The time it took for a managed resource to be deleted\",\n\t\t\tBuckets:   []float64{1, 5, 10, 15, 30, 60, 120, 300, 600, 1800, 3600},\n\t\t}, []string{\"gvk\"}),\n\t\tmrDrift: prometheus.NewHistogramVec(prometheus.HistogramOpts{\n\t\t\tSubsystem: subSystem,\n\t\t\tName:      \"managed_resource_drift_seconds\",\n\t\t\tHelp:      \"ALPHA: How long since the previous successful reconcile when a resource was found to be out of sync; excludes restart of the provider\",\n\t\t\tBuckets:   kmetrics.ExponentialBuckets(10e-9, 10, 10),\n\t\t}, []string{\"gvk\"}),\n\t}\n}\n\n// Describe sends the super-set of all possible descriptors of metrics\n// collected by this Collector to the provided channel and returns once\n// the last descriptor has been sent.\nfunc (r *MRMetricRecorder) Describe(ch chan<- *prometheus.Desc) {\n\tr.mrDetected.Describe(ch)\n\tr.mrFirstTimeReady.Describe(ch)\n\tr.mrDeletion.Describe(ch)\n\tr.mrDrift.Describe(ch)\n}\n\n// Collect is called by the Prometheus registry when collecting\n// metrics. The implementation sends each collected metric via the\n// provided channel and returns once the last metric has been sent.\nfunc (r *MRMetricRecorder) Collect(ch chan<- prometheus.Metric) {\n\tr.mrDetected.Collect(ch)\n\tr.mrFirstTimeReady.Collect(ch)\n\tr.mrDeletion.Collect(ch)\n\tr.mrDrift.Collect(ch)\n}\n\nfunc (r *MRMetricRecorder) recordUnchanged(name string) {\n\tr.lastObservation.Store(name, time.Now())\n}\n\nfunc (r *MRMetricRecorder) recordFirstTimeReconciled(managed resource.Managed) {\n\tif managed.GetCondition(xpv2.TypeSynced).Status == corev1.ConditionUnknown {\n\t\tr.mrDetected.With(getLabels(managed)).Observe(time.Since(managed.GetCreationTimestamp().Time).Seconds())\n\t\tr.firstObservation.Store(managed.GetName(), time.Now()) // this is the first time we reconciled on this resource\n\t}\n}\n\nfunc (r *MRMetricRecorder) recordDrift(managed resource.Managed) {\n\tname := managed.GetName()\n\n\tlast, ok := r.lastObservation.Load(name)\n\tif !ok {\n\t\treturn\n\t}\n\n\tlt, ok := last.(time.Time)\n\tif !ok {\n\t\treturn\n\t}\n\n\tr.mrDrift.With(getLabels(managed)).Observe(time.Since(lt).Seconds())\n\n\tr.lastObservation.Store(name, time.Now())\n}\n\nfunc (r *MRMetricRecorder) recordDeleted(managed resource.Managed) {\n\tr.mrDeletion.With(getLabels(managed)).Observe(time.Since(managed.GetDeletionTimestamp().Time).Seconds())\n}\n\nfunc (r *MRMetricRecorder) recordFirstTimeReady(managed resource.Managed) {\n\t// Note that providers may set the ready condition to \"True\", so we need\n\t// to check the value here to send the ready metric\n\tif managed.GetCondition(xpv2.TypeReady).Status == corev1.ConditionTrue {\n\t\t_, ok := r.firstObservation.Load(managed.GetName()) // This map is used to identify the first time to readiness\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tr.mrFirstTimeReady.With(getLabels(managed)).Observe(time.Since(managed.GetCreationTimestamp().Time).Seconds())\n\t\tr.firstObservation.Delete(managed.GetName())\n\t}\n}\n\n// A NopMetricRecorder does nothing.\ntype NopMetricRecorder struct{}\n\n// NewNopMetricRecorder returns a MRMetricRecorder that does nothing.\nfunc NewNopMetricRecorder() *NopMetricRecorder {\n\treturn &NopMetricRecorder{}\n}\n\n// Describe does nothing.\nfunc (r *NopMetricRecorder) Describe(_ chan<- *prometheus.Desc) {}\n\n// Collect does nothing.\nfunc (r *NopMetricRecorder) Collect(_ chan<- prometheus.Metric) {}\n\nfunc (r *NopMetricRecorder) recordUnchanged(_ string) {}\n\nfunc (r *NopMetricRecorder) recordFirstTimeReconciled(_ resource.Managed) {}\n\nfunc (r *NopMetricRecorder) recordDrift(_ resource.Managed) {}\n\nfunc (r *NopMetricRecorder) recordDeleted(_ resource.Managed) {}\n\nfunc (r *NopMetricRecorder) recordFirstTimeReady(_ resource.Managed) {}\n\nfunc getLabels(r resource.Managed) prometheus.Labels {\n\treturn prometheus.Labels{\n\t\t\"gvk\": r.GetObjectKind().GroupVersionKind().String(),\n\t}\n}\n"
  },
  {
    "path": "pkg/reconciler/managed/policies.go",
    "content": "/*\nCopyright 2023 The Crossplane 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 managed\n\nimport (\n\t\"fmt\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"k8s.io/apimachinery/pkg/util/sets\"\n)\n\n// ManagementPoliciesResolver is used to perform management policy checks\n// based on the management policy and if the management policy feature is enabled.\ntype ManagementPoliciesResolver struct {\n\tenabled            bool\n\tsupportedPolicies  []sets.Set[xpv2.ManagementAction]\n\tmanagementPolicies sets.Set[xpv2.ManagementAction]\n}\n\n// LegacyManagementPoliciesResolver is used to perform management policy checks\n// based on the management policy and if the management policy feature is enabled.\n//\n// Deprecated: this is for LegacyManaged types, that had deletion policy.\n// ModernManaged resources should use ManagementPoliciesResolver.\ntype LegacyManagementPoliciesResolver struct {\n\t*ManagementPoliciesResolver\n\n\tdeletionPolicy xpv2.DeletionPolicy\n}\n\n// A ManagementPoliciesResolverOption configures a ManagementPoliciesResolver.\ntype ManagementPoliciesResolverOption func(*ManagementPoliciesResolver)\n\n// WithSupportedManagementPolicies sets the supported management policies.\nfunc WithSupportedManagementPolicies(supportedManagementPolicies []sets.Set[xpv2.ManagementAction]) ManagementPoliciesResolverOption {\n\treturn func(r *ManagementPoliciesResolver) {\n\t\tr.supportedPolicies = supportedManagementPolicies\n\t}\n}\n\nfunc defaultSupportedManagementPolicies() []sets.Set[xpv2.ManagementAction] {\n\treturn []sets.Set[xpv2.ManagementAction]{\n\t\t// Default (all), the standard behaviour of crossplane in which all\n\t\t// reconciler actions are done.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionAll),\n\t\t// All actions explicitly set, the same as default.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve, xpv2.ManagementActionCreate, xpv2.ManagementActionUpdate, xpv2.ManagementActionLateInitialize, xpv2.ManagementActionDelete),\n\t\t// ObserveOnly, just observe action is done, the external resource is\n\t\t// considered as read-only.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve),\n\t\t// Pause, no action is being done. Alternative to setting the pause\n\t\t// annotation.\n\t\tsets.New[xpv2.ManagementAction](),\n\t\t// No LateInitialize filling in the spec.forProvider, allowing some\n\t\t// external resource fields to be managed externally.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve, xpv2.ManagementActionCreate, xpv2.ManagementActionUpdate, xpv2.ManagementActionDelete),\n\t\t// No Delete, the external resource is not deleted when the managed\n\t\t// resource is deleted.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve, xpv2.ManagementActionCreate, xpv2.ManagementActionUpdate, xpv2.ManagementActionLateInitialize),\n\t\t// No Delete and no LateInitialize, the external resource is not deleted\n\t\t// when the managed resource is deleted and the spec.forProvider is not\n\t\t// late initialized.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve, xpv2.ManagementActionCreate, xpv2.ManagementActionUpdate),\n\t\t// No Update, the external resource is not updated when the managed\n\t\t// resource is updated. Useful for immutable external resources.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve, xpv2.ManagementActionCreate, xpv2.ManagementActionDelete, xpv2.ManagementActionLateInitialize),\n\t\t// No Update and no Delete, the external resource is not updated\n\t\t// when the managed resource is updated and the external resource\n\t\t// is not deleted when the managed resource is deleted.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve, xpv2.ManagementActionCreate, xpv2.ManagementActionLateInitialize),\n\t\t// No Update and no LateInitialize, the external resource is not updated\n\t\t// when the managed resource is updated and the spec.forProvider is not\n\t\t// late initialized.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve, xpv2.ManagementActionCreate, xpv2.ManagementActionDelete),\n\t\t// No Update, no Delete and no LateInitialize, the external resource is\n\t\t// not updated when the managed resource is updated, the external resource\n\t\t// is not deleted when the managed resource is deleted and the\n\t\t// spec.forProvider is not late initialized.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve, xpv2.ManagementActionCreate),\n\t\t// Like ObserveOnly, but the external resource is deleted when the\n\t\t// managed resource is deleted.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve, xpv2.ManagementActionDelete),\n\t\t// No Crate and no Delete. Just update/patch the external resource.\n\t\t// Useful when the same external resource is managed by multiple\n\t\t// managed resources.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve, xpv2.ManagementActionUpdate),\n\t\t// Import mode: Allows observation of existing resources and populates spec.forProvider\n\t\t// through late initialization, without making any changes to the external resource.\n\t\t// Useful for safely importing existing resources to discover their current state.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve, xpv2.ManagementActionLateInitialize),\n\t\t// No Create, no Delete. Just Observe, Update and LateInitialize.\n\t\t// Useful when external resource lifecycle is managed elsewhere but you want\n\t\t// to allow Crossplane to make updates and discover state changes.\n\t\tsets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve, xpv2.ManagementActionUpdate, xpv2.ManagementActionLateInitialize),\n\t}\n}\n\n// NewManagementPoliciesResolver returns an ManagementPolicyChecker based\n// on the management policies and if the management policies feature\n// is enabled.\nfunc NewManagementPoliciesResolver(managementPolicyEnabled bool, managementPolicy xpv2.ManagementPolicies, o ...ManagementPoliciesResolverOption) ManagementPoliciesChecker {\n\tr := &ManagementPoliciesResolver{\n\t\tenabled:            managementPolicyEnabled,\n\t\tsupportedPolicies:  defaultSupportedManagementPolicies(),\n\t\tmanagementPolicies: sets.New[xpv2.ManagementAction](managementPolicy...),\n\t}\n\n\tfor _, ro := range o {\n\t\tro(r)\n\t}\n\n\treturn r\n}\n\n// NewLegacyManagementPoliciesResolver returns an ManagementPolicyChecker based\n// on the management policies and if the management policies feature\n// is enabled.\n//\n// Deprecated: this is intended for LegacyManaged resources that had deletionPolicy\n// ModernManaged resources should use NewManagementPoliciesResolver.\nfunc NewLegacyManagementPoliciesResolver(managementPolicyEnabled bool, managementPolicy xpv2.ManagementPolicies, deletionPolicy xpv2.DeletionPolicy, o ...ManagementPoliciesResolverOption) ManagementPoliciesChecker {\n\tr := &ManagementPoliciesResolver{\n\t\tenabled:            managementPolicyEnabled,\n\t\tsupportedPolicies:  defaultSupportedManagementPolicies(),\n\t\tmanagementPolicies: sets.New[xpv2.ManagementAction](managementPolicy...),\n\t}\n\n\tfor _, ro := range o {\n\t\tro(r)\n\t}\n\n\treturn &LegacyManagementPoliciesResolver{r, deletionPolicy}\n}\n\n// Validate checks if the management policy is valid.\n// If the management policy feature is disabled, but uses a non-default value,\n// it returns an error.\n// If the management policy feature is enabled, but uses a non-supported value,\n// it returns an error.\nfunc (m *ManagementPoliciesResolver) Validate() error {\n\t// check if its disabled, but uses a non-default value.\n\tif !m.enabled {\n\t\tif !m.managementPolicies.Equal(sets.New[xpv2.ManagementAction](xpv2.ManagementActionAll)) && m.managementPolicies.Len() != 0 {\n\t\t\treturn fmt.Errorf(errFmtManagementPolicyNonDefault, m.managementPolicies.UnsortedList())\n\t\t}\n\t\t// if its just disabled we don't care about supported policies\n\t\treturn nil\n\t}\n\n\t// check if the policy is a non-supported combination\n\tfor _, p := range m.supportedPolicies {\n\t\tif p.Equal(m.managementPolicies) {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(errFmtManagementPolicyNotSupported, m.managementPolicies.UnsortedList())\n}\n\n// IsPaused returns true if the management policy is empty and the\n// management policies feature is enabled.\nfunc (m *ManagementPoliciesResolver) IsPaused() bool {\n\tif !m.enabled {\n\t\treturn false\n\t}\n\n\treturn m.managementPolicies.Len() == 0\n}\n\n// ShouldCreate returns true if the Create action is allowed.\n// If the management policy feature is disabled, it returns true.\nfunc (m *ManagementPoliciesResolver) ShouldCreate() bool {\n\tif !m.enabled {\n\t\treturn true\n\t}\n\n\treturn m.managementPolicies.HasAny(xpv2.ManagementActionCreate, xpv2.ManagementActionAll)\n}\n\n// ShouldUpdate returns true if the Update action is allowed.\n// If the management policy feature is disabled, it returns true.\nfunc (m *ManagementPoliciesResolver) ShouldUpdate() bool {\n\tif !m.enabled {\n\t\treturn true\n\t}\n\n\treturn m.managementPolicies.HasAny(xpv2.ManagementActionUpdate, xpv2.ManagementActionAll)\n}\n\n// ShouldLateInitialize returns true if the LateInitialize action is allowed.\n// If the management policy feature is disabled, it returns true.\nfunc (m *ManagementPoliciesResolver) ShouldLateInitialize() bool {\n\tif !m.enabled {\n\t\treturn true\n\t}\n\n\treturn m.managementPolicies.HasAny(xpv2.ManagementActionLateInitialize, xpv2.ManagementActionAll)\n}\n\n// ShouldOnlyObserve returns true if the Observe action is allowed and all\n// other actions are not allowed. If the management policy feature is disabled,\n// it returns false.\nfunc (m *ManagementPoliciesResolver) ShouldOnlyObserve() bool {\n\tif !m.enabled {\n\t\treturn false\n\t}\n\n\treturn m.managementPolicies.Equal(sets.New[xpv2.ManagementAction](xpv2.ManagementActionObserve))\n}\n\n// ShouldDelete returns true based only on the managementPolicies.\n// If the management policy feature is disabled, returns true.\n// Otherwise, it checks whether the managementPolicies explicitly\n// include Delete or * (all).\nfunc (m *ManagementPoliciesResolver) ShouldDelete() bool {\n\tif !m.enabled {\n\t\treturn true\n\t}\n\n\treturn m.managementPolicies.HasAny(xpv2.ManagementActionDelete, xpv2.ManagementActionAll)\n}\n\n// ShouldDelete returns true based on the combination of the deletionPolicy and\n// the managementPolicies. If the management policy feature is disabled, it\n// returns true if the deletionPolicy is set to \"Delete\". Otherwise, it checks\n// which field is set to a non-default value and makes a decision based on that.\n// We need to be careful until we completely remove the deletionPolicy in favor\n// of managementPolicies which conflict with the deletionPolicy regarding\n// deleting of the external resource. This function implements the proposal in\n// the Ignore Changes design doc under the \"Deprecation of `deletionPolicy`\".\nfunc (m *LegacyManagementPoliciesResolver) ShouldDelete() bool {\n\tif !m.enabled {\n\t\treturn m.deletionPolicy != xpv2.DeletionOrphan\n\t}\n\n\t// delete external resource if both the deletionPolicy and the\n\t// managementPolicies are set to delete\n\tif m.deletionPolicy == xpv2.DeletionDelete && m.managementPolicies.HasAny(xpv2.ManagementActionDelete, xpv2.ManagementActionAll) {\n\t\treturn true\n\t}\n\t// if the managementPolicies is not default, and it contains the deletion\n\t// action, we should delete the external resource\n\tif !m.managementPolicies.Equal(sets.New[xpv2.ManagementAction](xpv2.ManagementActionAll)) && m.managementPolicies.Has(xpv2.ManagementActionDelete) {\n\t\treturn true\n\t}\n\n\t// For all other cases, we should orphan the external resource.\n\t// Obvious cases:\n\t// DeletionOrphan && ManagementPolicies without Delete Action\n\t// Conflicting cases:\n\t// DeletionOrphan && Management Policy [\"*\"] (obeys non-default configuration)\n\t// DeletionDelete && ManagementPolicies that does not include the Delete\n\t// Action (obeys non-default configuration)\n\treturn false\n}\n"
  },
  {
    "path": "pkg/reconciler/managed/reconciler.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 managed\n\nimport (\n\t\"context\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"time\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\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\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/manager\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/apis/changelogs/proto/v1alpha1\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/conditions\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/event\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/feature\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/logging\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n)\n\nconst (\n\t// FinalizerName is the string that is used as finalizer on managed resource\n\t// objects.\n\tFinalizerName = \"finalizer.managedresource.crossplane.io\"\n\n\treconcileGracePeriod = 30 * time.Second\n\treconcileTimeout     = 1 * time.Minute\n\n\tdefaultPollInterval = 1 * time.Minute\n\tdefaultGracePeriod  = 30 * time.Second\n)\n\n// Error strings.\nconst (\n\terrFmtManagementPolicyNonDefault   = \"`spec.managementPolicies` is set to a non-default value but the feature is not enabled: %s\"\n\terrFmtManagementPolicyNotSupported = \"`spec.managementPolicies` is set to a value(%s) which is not supported. Check docs for supported policies\"\n\n\terrGetManaged               = \"cannot get managed resource\"\n\terrUpdateManagedAnnotations = \"cannot update managed resource annotations\"\n\terrCreateIncomplete         = \"cannot determine creation result - remove the \" + meta.AnnotationKeyExternalCreatePending + \" annotation if it is safe to proceed\"\n\terrReconcileConnect         = \"connect failed\"\n\terrReconcileObserve         = \"observe failed\"\n\terrReconcileCreate          = \"create failed\"\n\terrReconcileUpdate          = \"update failed\"\n\terrReconcileDelete          = \"delete failed\"\n\terrRecordChangeLog          = \"cannot record change log entry\"\n\n\terrExternalResourceNotExist = \"external resource does not exist\"\n\n\terrManagedNotImplemented = \"managed resource does not implement connection details\"\n)\n\n// Event reasons.\nconst (\n\treasonCannotConnect           event.Reason = \"CannotConnectToProvider\"\n\treasonCannotDisconnect        event.Reason = \"CannotDisconnectFromProvider\"\n\treasonCannotInitialize        event.Reason = \"CannotInitializeManagedResource\"\n\treasonCannotResolveRefs       event.Reason = \"CannotResolveResourceReferences\"\n\treasonCannotObserve           event.Reason = \"CannotObserveExternalResource\"\n\treasonCannotCreate            event.Reason = \"CannotCreateExternalResource\"\n\treasonCannotDelete            event.Reason = \"CannotDeleteExternalResource\"\n\treasonCannotPublish           event.Reason = \"CannotPublishConnectionDetails\"\n\treasonCannotUnpublish         event.Reason = \"CannotUnpublishConnectionDetails\"\n\treasonCannotUpdate            event.Reason = \"CannotUpdateExternalResource\"\n\treasonCannotUpdateManaged     event.Reason = \"CannotUpdateManagedResource\"\n\treasonManagementPolicyInvalid event.Reason = \"CannotUseInvalidManagementPolicy\"\n\n\treasonDeleted event.Reason = \"DeletedExternalResource\"\n\treasonCreated event.Reason = \"CreatedExternalResource\"\n\treasonUpdated event.Reason = \"UpdatedExternalResource\"\n\treasonPending event.Reason = \"PendingExternalResource\"\n\n\treasonReconciliationPaused    event.Reason = \"ReconciliationPaused\"\n\treasonReconcileRequestHandled event.Reason = \"ReconcileRequestHandled\"\n)\n\n// ControllerName returns the recommended name for controllers that use this\n// package to reconcile a particular kind of managed resource.\nfunc ControllerName(kind string) string {\n\treturn \"managed/\" + strings.ToLower(kind)\n}\n\n// ManagementPoliciesChecker is used to perform checks on management policies\n// to determine specific actions are allowed, or if they are the only allowed\n// action.\ntype ManagementPoliciesChecker interface { //nolint:interfacebloat // This has to be big.\n\t// Validate validates the management policies.\n\tValidate() error\n\t// IsPaused returns true if the resource is paused based\n\t// on the management policy.\n\tIsPaused() bool\n\n\t// ShouldOnlyObserve returns true if only the Observe action is allowed.\n\tShouldOnlyObserve() bool\n\t// ShouldCreate returns true if the Create action is allowed.\n\tShouldCreate() bool\n\t// ShouldLateInitialize returns true if the LateInitialize action is\n\t// allowed.\n\tShouldLateInitialize() bool\n\t// ShouldUpdate returns true if the Update action is allowed.\n\tShouldUpdate() bool\n\t// ShouldDelete returns true if the Delete action is allowed.\n\tShouldDelete() bool\n}\n\n// A reconcileRequestTracker can record which reconcile-request token was last\n// handled. Managed resources that embed ObservedStatus implement this\n// interface automatically.\ntype reconcileRequestTracker interface {\n\tGetLastHandledReconcileAt() string\n\tSetLastHandledReconcileAt(token string)\n}\n\n// A CriticalAnnotationUpdater is used when it is critical that annotations must\n// be updated before returning from the Reconcile loop.\ntype CriticalAnnotationUpdater interface {\n\tUpdateCriticalAnnotations(ctx context.Context, o client.Object) error\n}\n\n// A CriticalAnnotationUpdateFn may be used when it is critical that annotations\n// must be updated before returning from the Reconcile loop.\ntype CriticalAnnotationUpdateFn func(ctx context.Context, o client.Object) error\n\n// UpdateCriticalAnnotations of the supplied object.\nfunc (fn CriticalAnnotationUpdateFn) UpdateCriticalAnnotations(ctx context.Context, o client.Object) error {\n\treturn fn(ctx, o)\n}\n\n// ConnectionDetails created or updated during an operation on an external\n// resource, for example usernames, passwords, endpoints, ports, etc.\ntype ConnectionDetails map[string][]byte\n\n// AdditionalDetails represent any additional details the external client wants\n// to return about an operation that has been performed. These details will be\n// included in the change logs.\ntype AdditionalDetails map[string]string\n\n// A ConnectionPublisher manages the supplied ConnectionDetails for the\n// supplied Managed resource. ManagedPublishers must handle the case in which\n// the supplied ConnectionDetails are empty.\ntype ConnectionPublisher interface {\n\t// PublishConnection details for the supplied Managed resource. Publishing\n\t// must be additive; i.e. if details (a, b, c) are published, subsequently\n\t// publicing details (b, c, d) should update (b, c) but not remove a.\n\tPublishConnection(ctx context.Context, so resource.ConnectionSecretOwner, c ConnectionDetails) (published bool, err error)\n\n\t// UnpublishConnection details for the supplied Managed resource.\n\tUnpublishConnection(ctx context.Context, so resource.ConnectionSecretOwner, c ConnectionDetails) error\n}\n\n// ConnectionPublisherFns is the pluggable struct to produce objects with ConnectionPublisher interface.\ntype ConnectionPublisherFns struct {\n\tPublishConnectionFn   func(ctx context.Context, o resource.ConnectionSecretOwner, c ConnectionDetails) (bool, error)\n\tUnpublishConnectionFn func(ctx context.Context, o resource.ConnectionSecretOwner, c ConnectionDetails) error\n}\n\n// PublishConnection details for the supplied Managed resource.\nfunc (fn ConnectionPublisherFns) PublishConnection(ctx context.Context, o resource.ConnectionSecretOwner, c ConnectionDetails) (bool, error) {\n\treturn fn.PublishConnectionFn(ctx, o, c)\n}\n\n// UnpublishConnection details for the supplied Managed resource.\nfunc (fn ConnectionPublisherFns) UnpublishConnection(ctx context.Context, o resource.ConnectionSecretOwner, c ConnectionDetails) error {\n\treturn fn.UnpublishConnectionFn(ctx, o, c)\n}\n\n// A LocalConnectionPublisher manages the supplied ConnectionDetails for the\n// supplied Managed resource. ManagedPublishers must handle the case in which\n// the supplied ConnectionDetails are empty.\ntype LocalConnectionPublisher interface {\n\t// PublishConnection details for the supplied Managed resource. Publishing\n\t// must be additive; i.e. if details (a, b, c) are published, subsequently\n\t// publicing details (b, c, d) should update (b, c) but not remove a.\n\tPublishConnection(ctx context.Context, lso resource.LocalConnectionSecretOwner, c ConnectionDetails) (published bool, err error)\n\n\t// UnpublishConnection details for the supplied Managed resource.\n\tUnpublishConnection(ctx context.Context, lso resource.LocalConnectionSecretOwner, c ConnectionDetails) error\n}\n\n// LocalConnectionPublisherFns is the pluggable struct to produce objects with LocalConnectionPublisher interface.\ntype LocalConnectionPublisherFns struct {\n\tPublishConnectionFn   func(ctx context.Context, o resource.LocalConnectionSecretOwner, c ConnectionDetails) (bool, error)\n\tUnpublishConnectionFn func(ctx context.Context, o resource.LocalConnectionSecretOwner, c ConnectionDetails) error\n}\n\n// PublishConnection details for the supplied Managed resource.\nfunc (fn LocalConnectionPublisherFns) PublishConnection(ctx context.Context, o resource.LocalConnectionSecretOwner, c ConnectionDetails) (bool, error) {\n\treturn fn.PublishConnectionFn(ctx, o, c)\n}\n\n// UnpublishConnection details for the supplied Managed resource.\nfunc (fn LocalConnectionPublisherFns) UnpublishConnection(ctx context.Context, o resource.LocalConnectionSecretOwner, c ConnectionDetails) error {\n\treturn fn.UnpublishConnectionFn(ctx, o, c)\n}\n\n// A ConnectionDetailsFetcher fetches connection details for the supplied\n// Connection Secret owner.\ntype ConnectionDetailsFetcher interface {\n\tFetchConnection(ctx context.Context, so resource.ConnectionSecretOwner) (ConnectionDetails, error)\n}\n\n// Initializer establishes ownership of the supplied Managed resource.\n// This typically involves the operations that are run before calling any\n// ExternalClient methods.\ntype Initializer interface {\n\tInitialize(ctx context.Context, mg resource.Managed) error\n}\n\n// A InitializerChain chains multiple managed initializers.\ntype InitializerChain []Initializer\n\n// Initialize calls each Initializer serially. It returns the first\n// error it encounters, if any.\nfunc (cc InitializerChain) Initialize(ctx context.Context, mg resource.Managed) error {\n\tfor _, c := range cc {\n\t\tif err := c.Initialize(ctx, mg); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// A InitializerFn is a function that satisfies the Initializer\n// interface.\ntype InitializerFn func(ctx context.Context, mg resource.Managed) error\n\n// Initialize calls InitializerFn function.\nfunc (m InitializerFn) Initialize(ctx context.Context, mg resource.Managed) error {\n\treturn m(ctx, mg)\n}\n\n// A ReferenceResolver resolves references to other managed resources.\ntype ReferenceResolver interface {\n\t// ResolveReferences resolves all fields in the supplied managed resource\n\t// that are references to other managed resources by updating corresponding\n\t// fields, for example setting spec.network to the Network resource\n\t// specified by spec.networkRef.name.\n\tResolveReferences(ctx context.Context, mg resource.Managed) error\n}\n\n// A ReferenceResolverFn is a function that satisfies the\n// ReferenceResolver interface.\ntype ReferenceResolverFn func(context.Context, resource.Managed) error\n\n// ResolveReferences calls ReferenceResolverFn function.\nfunc (m ReferenceResolverFn) ResolveReferences(ctx context.Context, mg resource.Managed) error {\n\treturn m(ctx, mg)\n}\n\n// An ExternalConnector produces a new ExternalClient given the supplied\n// Managed resource.\ntype ExternalConnector = TypedExternalConnector[resource.Managed]\n\n// A TypedExternalConnector produces a new ExternalClient given the supplied\n// Managed resource.\ntype TypedExternalConnector[managed resource.Managed] interface {\n\t// Connect to the provider specified by the supplied managed resource and\n\t// produce an ExternalClient.\n\tConnect(ctx context.Context, mg managed) (TypedExternalClient[managed], error)\n}\n\n// A NopDisconnector converts an ExternalConnector into an\n// ExternalConnectDisconnector with a no-op Disconnect method.\ntype NopDisconnector = TypedNopDisconnector[resource.Managed]\n\n// A TypedNopDisconnector converts an ExternalConnector into an\n// ExternalConnectDisconnector with a no-op Disconnect method.\ntype TypedNopDisconnector[managed resource.Managed] struct {\n\tc TypedExternalConnector[managed]\n}\n\n// Connect calls the underlying ExternalConnector's Connect method.\nfunc (c *TypedNopDisconnector[managed]) Connect(ctx context.Context, mg managed) (TypedExternalClient[managed], error) {\n\treturn c.c.Connect(ctx, mg)\n}\n\n// Disconnect does nothing. It never returns an error.\nfunc (c *TypedNopDisconnector[managed]) Disconnect(_ context.Context) error {\n\treturn nil\n}\n\n// NewNopDisconnector converts an ExternalConnector into an\n// ExternalConnectDisconnector with a no-op Disconnect method.\nfunc NewNopDisconnector(c ExternalConnector) ExternalConnectDisconnector {\n\treturn NewTypedNopDisconnector(c)\n}\n\n// NewTypedNopDisconnector converts an TypedExternalConnector into an\n// ExternalConnectDisconnector with a no-op Disconnect method.\nfunc NewTypedNopDisconnector[managed resource.Managed](c TypedExternalConnector[managed]) TypedExternalConnectDisconnector[managed] {\n\treturn &TypedNopDisconnector[managed]{c}\n}\n\n// An ExternalConnectDisconnector produces a new ExternalClient given the supplied\n// Managed resource.\ntype ExternalConnectDisconnector = TypedExternalConnectDisconnector[resource.Managed]\n\n// A TypedExternalConnectDisconnector produces a new ExternalClient given the supplied\n// Managed resource.\ntype TypedExternalConnectDisconnector[managed resource.Managed] interface {\n\tTypedExternalConnector[managed]\n\tExternalDisconnector\n}\n\n// An ExternalConnectorFn is a function that satisfies the ExternalConnector\n// interface.\ntype ExternalConnectorFn = TypedExternalConnectorFn[resource.Managed]\n\n// An TypedExternalConnectorFn is a function that satisfies the\n// TypedExternalConnector interface.\ntype TypedExternalConnectorFn[managed resource.Managed] func(ctx context.Context, mg managed) (TypedExternalClient[managed], error)\n\n// Connect to the provider specified by the supplied managed resource and\n// produce an ExternalClient.\nfunc (ec TypedExternalConnectorFn[managed]) Connect(ctx context.Context, mg managed) (TypedExternalClient[managed], error) {\n\treturn ec(ctx, mg)\n}\n\n// An ExternalDisconnectorFn is a function that satisfies the ExternalConnector\n// interface.\ntype ExternalDisconnectorFn func(ctx context.Context) error\n\n// Disconnect from provider and close the ExternalClient.\nfunc (ed ExternalDisconnectorFn) Disconnect(ctx context.Context) error {\n\treturn ed(ctx)\n}\n\n// ExternalConnectDisconnectorFns are functions that satisfy the\n// ExternalConnectDisconnector interface.\ntype ExternalConnectDisconnectorFns = TypedExternalConnectDisconnectorFns[resource.Managed]\n\n// TypedExternalConnectDisconnectorFns are functions that satisfy the\n// TypedExternalConnectDisconnector interface.\ntype TypedExternalConnectDisconnectorFns[managed resource.Managed] struct {\n\tConnectFn    func(ctx context.Context, mg managed) (TypedExternalClient[managed], error)\n\tDisconnectFn func(ctx context.Context) error\n}\n\n// Connect to the provider specified by the supplied managed resource and\n// produce an ExternalClient.\nfunc (fns TypedExternalConnectDisconnectorFns[managed]) Connect(ctx context.Context, mg managed) (TypedExternalClient[managed], error) {\n\treturn fns.ConnectFn(ctx, mg)\n}\n\n// Disconnect from the provider and close the ExternalClient.\nfunc (fns TypedExternalConnectDisconnectorFns[managed]) Disconnect(ctx context.Context) error {\n\treturn fns.DisconnectFn(ctx)\n}\n\n// An ExternalClient manages the lifecycle of an external resource.\n// None of the calls here should be blocking. All of the calls should be\n// idempotent. For example, Create call should not return AlreadyExists error\n// if it's called again with the same parameters or Delete call should not\n// return error if there is an ongoing deletion or resource does not exist.\ntype ExternalClient = TypedExternalClient[resource.Managed]\n\n// A TypedExternalClient manages the lifecycle of an external resource.\n// None of the calls here should be blocking. All of the calls should be\n// idempotent. For example, Create call should not return AlreadyExists error\n// if it's called again with the same parameters or Delete call should not\n// return error if there is an ongoing deletion or resource does not exist.\ntype TypedExternalClient[managedType resource.Managed] interface {\n\t// Observe the external resource the supplied Managed resource\n\t// represents, if any. Observe implementations must not modify the\n\t// external resource, but may update the supplied Managed resource to\n\t// reflect the state of the external resource. Status modifications are\n\t// automatically persisted unless ResourceLateInitialized is true - see\n\t// ResourceLateInitialized for more detail.\n\tObserve(ctx context.Context, mg managedType) (ExternalObservation, error)\n\n\t// Create an external resource per the specifications of the supplied\n\t// Managed resource. Called when Observe reports that the associated\n\t// external resource does not exist. Create implementations may update\n\t// managed resource annotations, and those updates will be persisted.\n\t// All other updates will be discarded.\n\tCreate(ctx context.Context, mg managedType) (ExternalCreation, error)\n\n\t// Update the external resource represented by the supplied Managed\n\t// resource, if necessary. Called unless Observe reports that the\n\t// associated external resource is up to date.\n\tUpdate(ctx context.Context, mg managedType) (ExternalUpdate, error)\n\n\t// Delete the external resource upon deletion of its associated Managed\n\t// resource. Called when the managed resource has been deleted.\n\tDelete(ctx context.Context, mg managedType) (ExternalDelete, error)\n\n\t// Disconnect from the provider and close the ExternalClient.\n\t// Called at the end of reconcile loop. An ExternalClient not requiring\n\t// to explicitly disconnect to cleanup it resources, can provide a no-op\n\t// implementation which just return nil.\n\tDisconnect(ctx context.Context) error\n}\n\n// ExternalClientFns are a series of functions that satisfy the ExternalClient\n// interface.\ntype ExternalClientFns = TypedExternalClientFns[resource.Managed]\n\n// TypedExternalClientFns are a series of functions that satisfy the\n// ExternalClient interface.\ntype TypedExternalClientFns[managed resource.Managed] struct {\n\tObserveFn    func(ctx context.Context, mg managed) (ExternalObservation, error)\n\tCreateFn     func(ctx context.Context, mg managed) (ExternalCreation, error)\n\tUpdateFn     func(ctx context.Context, mg managed) (ExternalUpdate, error)\n\tDeleteFn     func(ctx context.Context, mg managed) (ExternalDelete, error)\n\tDisconnectFn func(ctx context.Context) error\n}\n\n// Observe the external resource the supplied Managed resource represents, if\n// any.\nfunc (e TypedExternalClientFns[managed]) Observe(ctx context.Context, mg managed) (ExternalObservation, error) {\n\treturn e.ObserveFn(ctx, mg)\n}\n\n// Create an external resource per the specifications of the supplied Managed\n// resource.\nfunc (e TypedExternalClientFns[managed]) Create(ctx context.Context, mg managed) (ExternalCreation, error) {\n\treturn e.CreateFn(ctx, mg)\n}\n\n// Update the external resource represented by the supplied Managed resource, if\n// necessary.\nfunc (e TypedExternalClientFns[managed]) Update(ctx context.Context, mg managed) (ExternalUpdate, error) {\n\treturn e.UpdateFn(ctx, mg)\n}\n\n// Delete the external resource upon deletion of its associated Managed\n// resource.\nfunc (e TypedExternalClientFns[managed]) Delete(ctx context.Context, mg managed) (ExternalDelete, error) {\n\treturn e.DeleteFn(ctx, mg)\n}\n\n// Disconnect the external client.\nfunc (e TypedExternalClientFns[managed]) Disconnect(ctx context.Context) error {\n\treturn e.DisconnectFn(ctx)\n}\n\n// A NopConnector does nothing.\ntype NopConnector struct{}\n\n// Connect returns a NopClient. It never returns an error.\nfunc (c *NopConnector) Connect(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\treturn &NopClient{}, nil\n}\n\n// A NopClient does nothing.\ntype NopClient struct{}\n\n// Observe does nothing. It returns an empty ExternalObservation and no error.\nfunc (c *NopClient) Observe(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\treturn ExternalObservation{}, nil\n}\n\n// Create does nothing. It returns an empty ExternalCreation and no error.\nfunc (c *NopClient) Create(_ context.Context, _ resource.Managed) (ExternalCreation, error) {\n\treturn ExternalCreation{}, nil\n}\n\n// Update does nothing. It returns an empty ExternalUpdate and no error.\nfunc (c *NopClient) Update(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\treturn ExternalUpdate{}, nil\n}\n\n// Delete does nothing. It never returns an error.\nfunc (c *NopClient) Delete(_ context.Context, _ resource.Managed) (ExternalDelete, error) {\n\treturn ExternalDelete{}, nil\n}\n\n// Disconnect does nothing. It never returns an error.\nfunc (c *NopClient) Disconnect(_ context.Context) error { return nil }\n\n// An ExternalObservation is the result of an observation of an external\n// resource.\ntype ExternalObservation struct {\n\t// ResourceExists must be true if a corresponding external resource exists\n\t// for the managed resource. Typically this is proven by the presence of an\n\t// external resource of the expected kind whose unique identifier matches\n\t// the managed resource's external name. Crossplane uses this information to\n\t// determine whether it needs to create or delete the external resource.\n\tResourceExists bool\n\n\t// ResourceUpToDate should be true if the corresponding external resource\n\t// appears to be up-to-date - i.e. updating the external resource to match\n\t// the desired state of the managed resource would be a no-op. Keep in mind\n\t// that often only a subset of external resource fields can be updated.\n\t// Crossplane uses this information to determine whether it needs to update\n\t// the external resource.\n\tResourceUpToDate bool\n\n\t// ResourceLateInitialized should be true if the managed resource's spec was\n\t// updated during its observation. A Crossplane provider may update a\n\t// managed resource's spec fields after it is created or updated, as long as\n\t// the updates are limited to setting previously unset fields, and adding\n\t// keys to maps. Crossplane uses this information to determine whether\n\t// changes to the spec were made during observation that must be persisted.\n\t// Note that changes to the spec will be persisted before changes to the\n\t// status, and that pending changes to the status may be lost when the spec\n\t// is persisted. Status changes will be persisted by the first subsequent\n\t// observation that _does not_ late initialize the managed resource, so it\n\t// is important that Observe implementations do not late initialize the\n\t// resource every time they are called.\n\tResourceLateInitialized bool\n\n\t// ConnectionDetails required to connect to this resource. These details\n\t// are a set that is collated throughout the managed resource's lifecycle -\n\t// i.e. returning new connection details will have no affect on old details\n\t// unless an existing key is overwritten. Crossplane may publish these\n\t// credentials to a store (e.g. a Secret).\n\tConnectionDetails ConnectionDetails\n\n\t// Diff is a Debug level message that is sent to the reconciler when\n\t// there is a change in the observed Managed Resource. It is useful for\n\t// finding where the observed diverges from the desired state.\n\t// The string should be a cmp.Diff that details the difference.\n\tDiff string\n}\n\n// An ExternalCreation is the result of the creation of an external resource.\ntype ExternalCreation struct {\n\t// ConnectionDetails required to connect to this resource. These details\n\t// are a set that is collated throughout the managed resource's lifecycle -\n\t// i.e. returning new connection details will have no affect on old details\n\t// unless an existing key is overwritten. Crossplane may publish these\n\t// credentials to a store (e.g. a Secret).\n\tConnectionDetails ConnectionDetails\n\n\t// AdditionalDetails represent any additional details the external client\n\t// wants to return about the creation operation that was performed.\n\tAdditionalDetails AdditionalDetails\n}\n\n// An ExternalUpdate is the result of an update to an external resource.\ntype ExternalUpdate struct {\n\t// ConnectionDetails required to connect to this resource. These details\n\t// are a set that is collated throughout the managed resource's lifecycle -\n\t// i.e. returning new connection details will have no affect on old details\n\t// unless an existing key is overwritten. Crossplane may publish these\n\t// credentials to a store (e.g. a Secret).\n\tConnectionDetails ConnectionDetails\n\n\t// AdditionalDetails represent any additional details the external client\n\t// wants to return about the update operation that was performed.\n\tAdditionalDetails AdditionalDetails\n}\n\n// An ExternalDelete is the result of a deletion of an external resource.\ntype ExternalDelete struct {\n\t// AdditionalDetails represent any additional details the external client\n\t// wants to return about the delete operation that was performed.\n\tAdditionalDetails AdditionalDetails\n}\n\n// A Reconciler reconciles managed resources by creating and managing the\n// lifecycle of an external resource, i.e. a resource in an external system such\n// as a cloud provider API. Each controller must watch the managed resource kind\n// for which it is responsible.\ntype Reconciler struct {\n\tclient     client.Client\n\tnewManaged func() resource.Managed\n\n\tpollInterval     time.Duration\n\tminPollInterval  time.Duration\n\tpollIntervalHook PollIntervalHook\n\n\ttimeout             time.Duration\n\tcreationGracePeriod time.Duration\n\n\tfeatures feature.Flags\n\n\t// The below structs embed the set of interfaces used to implement the\n\t// managed resource reconciler. We do this primarily for readability, so\n\t// that the reconciler logic reads r.external.Connect(),\n\t// r.managed.Delete(), etc.\n\texternal mrExternal\n\tmanaged  mrManaged\n\n\tconditions conditions.Manager\n\n\tsupportedManagementPolicies []sets.Set[xpv2.ManagementAction]\n\n\tlog                       logging.Logger\n\trecord                    event.Recorder\n\tmetricRecorder            MetricRecorder\n\tchange                    ChangeLogger\n\tdeterministicExternalName bool\n}\n\ntype mrManaged struct {\n\tCriticalAnnotationUpdater\n\tConnectionPublisher\n\tresource.Finalizer\n\tInitializer\n\tReferenceResolver\n\tLocalConnectionPublisher\n}\n\nfunc defaultMRManaged(m manager.Manager) mrManaged {\n\treturn mrManaged{\n\t\tCriticalAnnotationUpdater: NewRetryingCriticalAnnotationUpdater(m.GetClient()),\n\t\tFinalizer:                 resource.NewAPIFinalizer(m.GetClient(), FinalizerName),\n\t\tInitializer:               NewNameAsExternalName(m.GetClient()),\n\t\tReferenceResolver:         NewAPISimpleReferenceResolver(m.GetClient()),\n\t\tConnectionPublisher:       NewAPISecretPublisher(m.GetClient(), m.GetScheme()),\n\t\tLocalConnectionPublisher:  NewAPILocalSecretPublisher(m.GetClient(), m.GetScheme()),\n\t}\n}\n\nfunc (m mrManaged) PublishConnection(ctx context.Context, managed resource.Managed, c ConnectionDetails) (bool, error) {\n\tswitch so := managed.(type) {\n\tcase resource.LocalConnectionSecretOwner:\n\t\treturn m.LocalConnectionPublisher.PublishConnection(ctx, so, c)\n\tcase resource.ConnectionSecretOwner:\n\t\treturn m.ConnectionPublisher.PublishConnection(ctx, so, c)\n\tdefault:\n\t\treturn false, errors.New(errManagedNotImplemented)\n\t}\n}\n\nfunc (m mrManaged) UnpublishConnection(ctx context.Context, managed resource.Managed, c ConnectionDetails) error {\n\tswitch so := managed.(type) {\n\tcase resource.LocalConnectionSecretOwner:\n\t\treturn m.LocalConnectionPublisher.UnpublishConnection(ctx, so, c)\n\tcase resource.ConnectionSecretOwner:\n\t\treturn m.ConnectionPublisher.UnpublishConnection(ctx, so, c)\n\tdefault:\n\t\treturn errors.New(errManagedNotImplemented)\n\t}\n}\n\ntype mrExternal struct {\n\tExternalConnectDisconnector\n}\n\nfunc defaultMRExternal() mrExternal {\n\treturn mrExternal{\n\t\tExternalConnectDisconnector: NewNopDisconnector(&NopConnector{}),\n\t}\n}\n\n// A ReconcilerOption configures a Reconciler.\ntype ReconcilerOption func(*Reconciler)\n\n// WithTimeout specifies the timeout duration cumulatively for all the calls happen\n// in the reconciliation function. In case the deadline exceeds, reconciler will\n// still have some time to make the necessary calls to report the error such as\n// status update.\nfunc WithTimeout(duration time.Duration) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.timeout = duration\n\t}\n}\n\n// WithPollInterval specifies how long the Reconciler should wait before queueing\n// a new reconciliation after a successful reconcile. The Reconciler requeues\n// after a specified duration when it is not actively waiting for an external\n// operation, but wishes to check whether an existing external resource needs to\n// be synced to its Crossplane Managed resource.\nfunc WithPollInterval(after time.Duration) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.pollInterval = after\n\t}\n}\n\n// WithMinPollInterval specifies the shortest poll interval a resource may\n// request via annotation. Annotation values below this floor are clamped to\n// the minimum.\nfunc WithMinPollInterval(d time.Duration) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.minPollInterval = d\n\t}\n}\n\n// WithMetricRecorder configures the Reconciler to use the supplied MetricRecorder.\nfunc WithMetricRecorder(recorder MetricRecorder) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.metricRecorder = recorder\n\t}\n}\n\n// PollIntervalHook represents the function type passed to the\n// WithPollIntervalHook option to support dynamic computation of the poll\n// interval.\ntype PollIntervalHook func(managed resource.Managed, pollInterval time.Duration) time.Duration\n\nfunc defaultPollIntervalHook(_ resource.Managed, pollInterval time.Duration) time.Duration {\n\treturn pollInterval\n}\n\n// WithPollIntervalHook adds a hook that can be used to configure the\n// delay before an up-to-date resource is reconciled again after a successful\n// reconcile. If this option is passed multiple times, only the latest hook\n// will be used.\nfunc WithPollIntervalHook(hook PollIntervalHook) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.pollIntervalHook = hook\n\t}\n}\n\n// WithPollJitterHook adds a simple PollIntervalHook to add jitter to the poll\n// interval used when queuing a new reconciliation after a successful\n// reconcile. The added jitter will be a random duration between -jitter and\n// +jitter. This option wraps WithPollIntervalHook, and is subject to the same\n// constraint that only the latest hook will be used.\nfunc WithPollJitterHook(jitter time.Duration) ReconcilerOption {\n\treturn WithPollIntervalHook(func(_ resource.Managed, pollInterval time.Duration) time.Duration {\n\t\treturn pollInterval + time.Duration((rand.Float64()-0.5)*2*float64(jitter)) //nolint:gosec // No need for secure randomness.\n\t})\n}\n\n// WithCreationGracePeriod configures an optional period during which we will\n// wait for the external API to report that a newly created external resource\n// exists. This allows us to tolerate eventually consistent APIs that do not\n// immediately report that newly created resources exist when queried. All\n// resources have a 30 second grace period by default.\nfunc WithCreationGracePeriod(d time.Duration) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.creationGracePeriod = d\n\t}\n}\n\n// WithExternalConnector specifies how the Reconciler should connect to the API\n// used to sync and delete external resources.\nfunc WithExternalConnector(c ExternalConnector) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.external.ExternalConnectDisconnector = NewNopDisconnector(c)\n\t}\n}\n\n// WithTypedExternalConnector specifies how the Reconciler should connect to the API\n// used to sync and delete external resources.\nfunc WithTypedExternalConnector[managed resource.Managed](c TypedExternalConnector[managed]) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.external.ExternalConnectDisconnector = &typedExternalConnectDisconnectorWrapper[managed]{\n\t\t\tc: NewTypedNopDisconnector(c),\n\t\t}\n\t}\n}\n\n// WithCriticalAnnotationUpdater specifies how the Reconciler should update a\n// managed resource's critical annotations. Implementations typically contain\n// some kind of retry logic to increase the likelihood that critical annotations\n// (like non-deterministic external names) will be persisted.\nfunc WithCriticalAnnotationUpdater(u CriticalAnnotationUpdater) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.managed.CriticalAnnotationUpdater = u\n\t}\n}\n\n// withConnectionPublishers specifies how the Reconciler should publish\n// its connection details such as credentials and endpoints.\n// for unit testing only.\nfunc withConnectionPublishers(p ConnectionPublisher) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.managed.ConnectionPublisher = p\n\t}\n}\n\n// withLocalConnectionPublishers specifies how the Reconciler should publish\n// its connection details such as credentials and endpoints.\n// for unit testing only.\nfunc withLocalConnectionPublishers(p LocalConnectionPublisher) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.managed.LocalConnectionPublisher = p\n\t}\n}\n\n// WithInitializers specifies how the Reconciler should initialize a\n// managed resource before calling any of the ExternalClient functions.\nfunc WithInitializers(i ...Initializer) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.managed.Initializer = InitializerChain(i)\n\t}\n}\n\n// WithFinalizer specifies how the Reconciler should add and remove\n// finalizers to and from the managed resource.\nfunc WithFinalizer(f resource.Finalizer) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.managed.Finalizer = f\n\t}\n}\n\n// WithReferenceResolver specifies how the Reconciler should resolve any\n// inter-resource references it encounters while reconciling managed resources.\nfunc WithReferenceResolver(rr ReferenceResolver) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.managed.ReferenceResolver = rr\n\t}\n}\n\n// WithLogger specifies how the Reconciler should log messages.\nfunc WithLogger(l logging.Logger) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.log = l\n\t}\n}\n\n// WithRecorder specifies how the Reconciler should record events.\nfunc WithRecorder(er event.Recorder) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.record = er\n\t}\n}\n\n// WithManagementPolicies enables support for management policies.\nfunc WithManagementPolicies() ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.features.Enable(feature.EnableBetaManagementPolicies)\n\t}\n}\n\n// WithReconcilerSupportedManagementPolicies configures which management policies are\n// supported by the reconciler.\nfunc WithReconcilerSupportedManagementPolicies(supported []sets.Set[xpv2.ManagementAction]) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.supportedManagementPolicies = supported\n\t}\n}\n\n// WithChangeLogger enables support for capturing change logs during\n// reconciliation.\nfunc WithChangeLogger(c ChangeLogger) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.change = c\n\t}\n}\n\n// WithDeterministicExternalName specifies that the external name of the MR is\n// deterministic. If this value is not \"true\", the provider will not re-queue the\n// managed resource in scenarios where creation is deemed incomplete. This behaviour\n// is a safeguard to avoid a leaked resource due to a non-deterministic name generated\n// by the external system. Conversely, if this value is \"true\", signifying that the\n// managed resources is deterministically named by the external system, then this\n// safeguard is ignored as it is safe to re-queue a deterministically named resource.\nfunc WithDeterministicExternalName(b bool) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.deterministicExternalName = b\n\t}\n}\n\n// effectivePollInterval returns the poll interval for the given resource,\n// taking into account any per-resource override via annotation. Overrides\n// below the configured minimum are clamped to the minimum.\nfunc (r *Reconciler) effectivePollInterval(o metav1.Object) time.Duration {\n\tif d, ok := meta.GetPollInterval(o); ok {\n\t\tif d >= r.minPollInterval {\n\t\t\treturn d\n\t\t}\n\t\treturn r.minPollInterval\n\t}\n\treturn r.pollInterval\n}\n\n// NewReconciler returns a Reconciler that reconciles managed resources of the\n// supplied ManagedKind with resources in an external system such as a cloud\n// provider API. It panics if asked to reconcile a managed resource kind that is\n// not registered with the supplied manager's runtime.Scheme. The returned\n// Reconciler reconciles with a dummy, no-op 'external system' by default;\n// callers should supply an ExternalConnector that returns an ExternalClient\n// capable of managing resources in a real system.\nfunc NewReconciler(m manager.Manager, of resource.ManagedKind, o ...ReconcilerOption) *Reconciler {\n\tnm := func() resource.Managed {\n\t\t//nolint:forcetypeassert // If this isn't an MR it's a programming error and we want to panic.\n\t\treturn resource.MustCreateObject(schema.GroupVersionKind(of), m.GetScheme()).(resource.Managed)\n\t}\n\n\t// Panic early if we've been asked to reconcile a resource kind that has not\n\t// been registered with our controller manager's scheme.\n\t_ = nm()\n\n\tr := &Reconciler{\n\t\tclient:                      m.GetClient(),\n\t\tnewManaged:                  nm,\n\t\tpollInterval:                defaultPollInterval,\n\t\tpollIntervalHook:            defaultPollIntervalHook,\n\t\tcreationGracePeriod:         defaultGracePeriod,\n\t\ttimeout:                     reconcileTimeout,\n\t\tmanaged:                     defaultMRManaged(m),\n\t\texternal:                    defaultMRExternal(),\n\t\tsupportedManagementPolicies: defaultSupportedManagementPolicies(),\n\t\tlog:                         logging.NewNopLogger(),\n\t\trecord:                      event.NewNopRecorder(),\n\t\tmetricRecorder:              NewNopMetricRecorder(),\n\t\tchange:                      newNopChangeLogger(),\n\t\tconditions:                  new(conditions.ObservedGenerationPropagationManager),\n\t}\n\n\tfor _, ro := range o {\n\t\tro(r)\n\t}\n\n\treturn r\n}\n\n// Reconcile a managed resource with an external resource.\nfunc (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (result reconcile.Result, err error) { //nolint:gocognit // See note below.\n\t// NOTE(negz): This method is a well over our cyclomatic complexity goal.\n\t// Be wary of adding additional complexity.\n\tdefer func() { result, err = errors.SilentlyRequeueOnConflict(result, err) }()\n\n\tlog := r.log.WithValues(\"request\", req)\n\tlog.Debug(\"Reconciling\")\n\n\tctx, cancel := context.WithTimeout(ctx, r.timeout+reconcileGracePeriod)\n\tdefer cancel()\n\n\texternalCtx, externalCancel := context.WithTimeout(ctx, r.timeout)\n\tdefer externalCancel()\n\n\tmanaged := r.newManaged()\n\tif err := r.client.Get(ctx, req.NamespacedName, managed); err != nil {\n\t\t// There's no need to requeue if we no longer exist. Otherwise we'll be\n\t\t// requeued implicitly because we return an error.\n\t\tlog.Debug(\"Cannot get managed resource\", \"error\", err)\n\t\treturn reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGetManaged)\n\t}\n\n\tr.metricRecorder.recordFirstTimeReconciled(managed)\n\tstatus := r.conditions.For(managed)\n\n\trecord := r.record.WithAnnotations(\"external-name\", meta.GetExternalName(managed))\n\tlog = log.WithValues(\n\t\t\"uid\", managed.GetUID(),\n\t\t\"version\", managed.GetResourceVersion(),\n\t\t\"external-name\", meta.GetExternalName(managed),\n\t)\n\n\tmanagementPoliciesEnabled := r.features.Enabled(feature.EnableBetaManagementPolicies)\n\tif managementPoliciesEnabled {\n\t\tlog.WithValues(\"managementPolicies\", managed.GetManagementPolicies())\n\t}\n\n\t// Create the management policy resolver which will assist us in determining\n\t// what actions to take on the managed resource based on the management\n\t// and deletion policies.\n\tvar policy ManagementPoliciesChecker\n\n\tswitch mg := managed.(type) {\n\tcase resource.LegacyManaged:\n\t\tpolicy = NewLegacyManagementPoliciesResolver(managementPoliciesEnabled, mg.GetManagementPolicies(), mg.GetDeletionPolicy(), WithSupportedManagementPolicies(r.supportedManagementPolicies))\n\tdefault:\n\t\tpolicy = NewManagementPoliciesResolver(managementPoliciesEnabled, managed.GetManagementPolicies(), WithSupportedManagementPolicies(r.supportedManagementPolicies))\n\t}\n\n\t// Check if the resource has paused reconciliation based on the\n\t// annotation or the management policies.\n\t// Log, publish an event and update the SYNC status condition.\n\tif meta.IsPaused(managed) || policy.IsPaused() {\n\t\tlog.Debug(\"Reconciliation is paused either through the `spec.managementPolicies` or the pause annotation\", \"annotation\", meta.AnnotationKeyReconciliationPaused)\n\t\trecord.Event(managed, event.Normal(reasonReconciliationPaused, \"Reconciliation is paused either through the `spec.managementPolicies` or the pause annotation\",\n\t\t\t\"annotation\", meta.AnnotationKeyReconciliationPaused))\n\t\tstatus.MarkConditions(xpv2.ReconcilePaused())\n\t\t// if the pause annotation is removed or the management policies changed, we will have a chance to reconcile\n\t\t// again and resume and if status update fails, we will reconcile again to retry to update the status\n\t\treturn reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)\n\t}\n\n\t// Detect a new reconcile-request token early and emit the event once.\n\t// The actual status mutation is deferred to updateStatus so it survives\n\t// full-object Updates (late-init, create annotations) that reset\n\t// in-memory status.\n\tvar reconcileRequestToken string\n\tif token, ok := meta.GetReconcileRequest(managed); ok {\n\t\tif tracker, ok := managed.(reconcileRequestTracker); ok {\n\t\t\tif tracker.GetLastHandledReconcileAt() != token {\n\t\t\t\tlog.Debug(\"Processing reconcile request\", \"token\", token)\n\t\t\t\trecord.Event(managed, event.Normal(reasonReconcileRequestHandled, \"Handling reconcile request\", \"token\", token))\n\t\t\t\treconcileRequestToken = token\n\t\t\t}\n\t\t}\n\t}\n\n\t// updateStatus applies the reconcile-request token (if any) immediately\n\t// before persisting status. This ensures the token is not lost by\n\t// intervening full-object Updates.\n\tupdateStatus := func() error {\n\t\tif reconcileRequestToken != \"\" {\n\t\t\tif tracker, ok := managed.(reconcileRequestTracker); ok {\n\t\t\t\ttracker.SetLastHandledReconcileAt(reconcileRequestToken)\n\t\t\t}\n\t\t}\n\t\treturn r.client.Status().Update(ctx, managed)\n\t}\n\n\t// Check if the ManagementPolicies is set to a non-default value while the\n\t// feature is not enabled. This is a safety check to let users know that\n\t// they need to enable the feature flag before using the feature. For\n\t// example, we wouldn't want someone to set the policy to ObserveOnly but\n\t// not realize that the controller is still trying to reconcile\n\t// (and modify or delete) the resource since they forgot to enable the\n\t// feature flag. Also checks if the management policy is set to a value\n\t// that is not supported by the controller.\n\tif err := policy.Validate(); err != nil {\n\t\tlog.Debug(err.Error())\n\n\t\tif kerrors.IsConflict(err) {\n\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t}\n\n\t\trecord.Event(managed, event.Warning(reasonManagementPolicyInvalid, err))\n\t\tstatus.MarkConditions(xpv2.ReconcileError(err))\n\n\t\treturn reconcile.Result{}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t}\n\n\t// If managed resource has a deletion timestamp and a deletion policy of\n\t// Orphan, we do not need to observe the external resource before attempting\n\t// to unpublish connection details and remove finalizer.\n\tif meta.WasDeleted(managed) && !policy.ShouldDelete() {\n\t\tlog = log.WithValues(\"deletion-timestamp\", managed.GetDeletionTimestamp())\n\n\t\t// Empty ConnectionDetails are passed to UnpublishConnection because we\n\t\t// have not retrieved them from the external resource. In practice we\n\t\t// currently only write connection details to a Secret, and we rely on\n\t\t// garbage collection to delete the entire secret, regardless of the\n\t\t// supplied connection details.\n\t\tif err := r.managed.UnpublishConnection(ctx, managed, ConnectionDetails{}); err != nil {\n\t\t\t// If this is the first time we encounter this issue we'll be\n\t\t\t// requeued implicitly when we update our status with the new error\n\t\t\t// condition. If not, we requeue explicitly, which will trigger\n\t\t\t// backoff.\n\t\t\tlog.Debug(\"Cannot unpublish connection details\", \"error\", err)\n\n\t\t\tif kerrors.IsConflict(err) {\n\t\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t\t}\n\n\t\t\trecord.Event(managed, event.Warning(reasonCannotUnpublish, err))\n\t\t\tstatus.MarkConditions(xpv2.Deleting(), xpv2.ReconcileError(err))\n\n\t\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t\t}\n\n\t\tif err := r.managed.RemoveFinalizer(ctx, managed); err != nil {\n\t\t\t// If this is the first time we encounter this issue we'll be\n\t\t\t// requeued implicitly when we update our status with the new error\n\t\t\t// condition. If not, we requeue explicitly, which will trigger\n\t\t\t// backoff.\n\t\t\tlog.Debug(\"Cannot remove managed resource finalizer\", \"error\", err)\n\n\t\t\tif kerrors.IsConflict(err) {\n\t\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t\t}\n\n\t\t\tstatus.MarkConditions(xpv2.Deleting(), xpv2.ReconcileError(err))\n\n\t\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t\t}\n\n\t\t// We've successfully unpublished our managed resource's connection\n\t\t// details and removed our finalizer. If we assume we were the only\n\t\t// controller that added a finalizer to this resource then it should no\n\t\t// longer exist and thus there is no point trying to update its status.\n\t\tr.metricRecorder.recordDeleted(managed)\n\t\tlog.Debug(\"Successfully deleted managed resource\")\n\n\t\treturn reconcile.Result{Requeue: false}, nil\n\t}\n\n\tif err := r.managed.Initialize(ctx, managed); err != nil {\n\t\t// If this is the first time we encounter this issue we'll be requeued\n\t\t// implicitly when we update our status with the new error condition. If\n\t\t// not, we requeue explicitly, which will trigger backoff.\n\t\tlog.Debug(\"Cannot initialize managed resource\", \"error\", err)\n\n\t\tif kerrors.IsConflict(err) {\n\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t}\n\n\t\trecord.Event(managed, event.Warning(reasonCannotInitialize, err))\n\t\tstatus.MarkConditions(xpv2.ReconcileError(err))\n\n\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t}\n\n\t// If we started but never completed creation of an external resource we\n\t// may have lost critical information. For example if we didn't persist\n\t// an updated external name which is non-deterministic, we have leaked a\n\t// resource. The safest thing to do is to refuse to proceed. However, if\n\t// the resource has a deterministic external name, it is safe to proceed.\n\tif meta.ExternalCreateIncomplete(managed) {\n\t\tif !r.deterministicExternalName {\n\t\t\tlog.Debug(errCreateIncomplete)\n\t\t\trecord.Event(managed, event.Warning(reasonCannotInitialize, errors.New(errCreateIncomplete)))\n\t\t\tstatus.MarkConditions(xpv2.Creating(), xpv2.ReconcileError(errors.New(errCreateIncomplete)))\n\n\t\t\treturn reconcile.Result{Requeue: false}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t\t}\n\n\t\tlog.Debug(\"Cannot determine creation result, but proceeding due to deterministic external name\")\n\t}\n\n\t// We resolve any references before observing our external resource because\n\t// in some rare examples we need a spec field to make the observe call, and\n\t// that spec field could be set by a reference.\n\t//\n\t// We do not resolve references when being deleted because it is likely that\n\t// the resources we reference are also being deleted, and would thus block\n\t// resolution due to being unready or non-existent. It is unlikely (but not\n\t// impossible) that we need to resolve a reference in order to process a\n\t// delete, and that reference is stale at delete time.\n\tif !meta.WasDeleted(managed) {\n\t\tif err := r.managed.ResolveReferences(ctx, managed); err != nil {\n\t\t\t// If any of our referenced resources are not yet ready (or if we\n\t\t\t// encountered an error resolving them) we want to try again. If\n\t\t\t// this is the first time we encounter this situation we'll be\n\t\t\t// requeued implicitly due to the status update. If not, we want\n\t\t\t// requeue explicitly, which will trigger backoff.\n\t\t\tlog.Debug(\"Cannot resolve managed resource references\", \"error\", err)\n\n\t\t\tif kerrors.IsConflict(err) {\n\t\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t\t}\n\n\t\t\trecord.Event(managed, event.Warning(reasonCannotResolveRefs, err))\n\t\t\tstatus.MarkConditions(xpv2.ReconcileError(err))\n\n\t\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t\t}\n\t}\n\n\texternal, err := r.external.Connect(externalCtx, managed)\n\tif err != nil {\n\t\t// We'll usually hit this case if our Provider or its secret are missing\n\t\t// or invalid. If this is first time we encounter this issue we'll be\n\t\t// requeued implicitly when we update our status with the new error\n\t\t// condition. If not, we requeue explicitly, which will trigger\n\t\t// backoff.\n\t\tlog.Debug(\"Cannot connect to provider\", \"error\", err)\n\n\t\tif kerrors.IsConflict(err) {\n\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t}\n\n\t\trecord.Event(managed, event.Warning(reasonCannotConnect, err))\n\t\tstatus.MarkConditions(xpv2.ReconcileError(errors.Wrap(err, errReconcileConnect)))\n\n\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t}\n\n\tdefer func() {\n\t\tif err := r.external.Disconnect(ctx); err != nil {\n\t\t\tlog.Debug(\"Cannot disconnect from provider\", \"error\", err)\n\t\t\trecord.Event(managed, event.Warning(reasonCannotDisconnect, err))\n\t\t}\n\n\t\tif external != nil {\n\t\t\tif err := external.Disconnect(ctx); err != nil {\n\t\t\t\tlog.Debug(\"Cannot disconnect from provider\", \"error\", err)\n\t\t\t\trecord.Event(managed, event.Warning(reasonCannotDisconnect, err))\n\t\t\t}\n\t\t}\n\t}()\n\n\tobservation, err := external.Observe(externalCtx, managed)\n\tif err != nil {\n\t\t// We'll usually hit this case if our Provider credentials are invalid\n\t\t// or insufficient for observing the external resource type we're\n\t\t// concerned with. If this is the first time we encounter this issue\n\t\t// we'll be requeued implicitly when we update our status with the new\n\t\t// error condition. If not, we requeue explicitly, which will\n\t\t// trigger backoff.\n\t\tlog.Debug(\"Cannot observe external resource\", \"error\", err)\n\n\t\tif kerrors.IsConflict(err) {\n\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t}\n\n\t\trecord.Event(managed, event.Warning(reasonCannotObserve, err))\n\t\tstatus.MarkConditions(xpv2.ReconcileError(errors.Wrap(err, errReconcileObserve)))\n\n\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t}\n\n\t// In the observe-only mode, !observation.ResourceExists will be an error\n\t// case, and we will explicitly return this information to the user.\n\tif !observation.ResourceExists && policy.ShouldOnlyObserve() {\n\t\trecord.Event(managed, event.Warning(reasonCannotObserve, errors.New(errExternalResourceNotExist)))\n\t\tstatus.MarkConditions(xpv2.ReconcileError(errors.Wrap(errors.New(errExternalResourceNotExist), errReconcileObserve)))\n\n\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t}\n\n\t// If this resource has a non-zero creation grace period we want to wait\n\t// for that period to expire before we trust that the resource really\n\t// doesn't exist. This is because some external APIs are eventually\n\t// consistent and may report that a recently created resource does not\n\t// exist.\n\tif !observation.ResourceExists && meta.ExternalCreateSucceededDuring(managed, r.creationGracePeriod) {\n\t\tlog.Debug(\"Waiting for external resource existence to be confirmed\")\n\t\trecord.Event(managed, event.Normal(reasonPending, \"Waiting for external resource existence to be confirmed\"))\n\n\t\treturn reconcile.Result{Requeue: true}, nil\n\t}\n\n\t// deep copy the managed resource now that we've called Observe() and have\n\t// not performed any external operations - we can use this as the\n\t// pre-operation managed resource state in the change logs later\n\t//nolint:forcetypeassert // managed.DeepCopyObject() will always be a resource.Managed.\n\tmanagedPreOp := managed.DeepCopyObject().(resource.Managed)\n\n\tif meta.WasDeleted(managed) {\n\t\tlog = log.WithValues(\"deletion-timestamp\", managed.GetDeletionTimestamp())\n\n\t\tif len(managed.GetFinalizers()) > 1 {\n\t\t\t// There are other controllers monitoring this resource so preserve the external instance\n\t\t\t// until all other finalizers have been removed\n\t\t\tlog.Debug(\"Delay external deletion until all finalizers have been removed\")\n\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t}\n\n\t\tif observation.ResourceExists && policy.ShouldDelete() {\n\t\t\tdeletion, err := external.Delete(externalCtx, managed)\n\t\t\tif err != nil {\n\t\t\t\t// We'll hit this condition if we can't delete our external\n\t\t\t\t// resource, for example if our provider credentials don't have\n\t\t\t\t// access to delete it. If this is the first time we encounter\n\t\t\t\t// this issue we'll be requeued implicitly when we update our\n\t\t\t\t// status with the new error condition. If not, we want requeue\n\t\t\t\t// explicitly, which will trigger backoff.\n\t\t\t\tlog.Debug(\"Cannot delete external resource\", \"error\", err)\n\n\t\t\t\tif err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_DELETE, err, deletion.AdditionalDetails); err != nil {\n\t\t\t\t\tlog.Info(errRecordChangeLog, \"error\", err)\n\t\t\t\t}\n\n\t\t\t\trecord.Event(managed, event.Warning(reasonCannotDelete, err))\n\t\t\t\tstatus.MarkConditions(xpv2.Deleting(), xpv2.ReconcileError(errors.Wrap(err, errReconcileDelete)))\n\n\t\t\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t\t\t}\n\n\t\t\t// We've successfully requested deletion of our external resource.\n\t\t\t// We queue another reconcile after a short wait rather than\n\t\t\t// immediately finalizing our delete in order to verify that the\n\t\t\t// external resource was actually deleted. If it no longer exists\n\t\t\t// we'll skip this block on the next reconcile and proceed to\n\t\t\t// unpublish and finalize. If it still exists we'll re-enter this\n\t\t\t// block and try again.\n\t\t\tlog.Debug(\"Successfully requested deletion of external resource\")\n\n\t\t\tif err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_DELETE, nil, deletion.AdditionalDetails); err != nil {\n\t\t\t\tlog.Info(errRecordChangeLog, \"error\", err)\n\t\t\t}\n\n\t\t\trecord.Event(managed, event.Normal(reasonDeleted, \"Successfully requested deletion of external resource\"))\n\t\t\tstatus.MarkConditions(xpv2.Deleting(), xpv2.ReconcileSuccess())\n\n\t\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t\t}\n\n\t\tif err := r.managed.UnpublishConnection(ctx, managed, observation.ConnectionDetails); err != nil {\n\t\t\t// If this is the first time we encounter this issue we'll be\n\t\t\t// requeued implicitly when we update our status with the new error\n\t\t\t// condition. If not, we requeue explicitly, which will trigger\n\t\t\t// backoff.\n\t\t\tlog.Debug(\"Cannot unpublish connection details\", \"error\", err)\n\n\t\t\tif kerrors.IsConflict(err) {\n\t\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t\t}\n\n\t\t\trecord.Event(managed, event.Warning(reasonCannotUnpublish, err))\n\t\t\tstatus.MarkConditions(xpv2.Deleting(), xpv2.ReconcileError(err))\n\n\t\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t\t}\n\n\t\tif err := r.managed.RemoveFinalizer(ctx, managed); err != nil {\n\t\t\t// If this is the first time we encounter this issue we'll be\n\t\t\t// requeued implicitly when we update our status with the new error\n\t\t\t// condition. If not, we requeue explicitly, which will trigger\n\t\t\t// backoff.\n\t\t\tlog.Debug(\"Cannot remove managed resource finalizer\", \"error\", err)\n\n\t\t\tif kerrors.IsConflict(err) {\n\t\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t\t}\n\n\t\t\tstatus.MarkConditions(xpv2.Deleting(), xpv2.ReconcileError(err))\n\n\t\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t\t}\n\n\t\t// We've successfully deleted our external resource (if necessary) and\n\t\t// removed our finalizer. If we assume we were the only controller that\n\t\t// added a finalizer to this resource then it should no longer exist and\n\t\t// thus there is no point trying to update its status.\n\t\tr.metricRecorder.recordDeleted(managed)\n\t\tlog.Debug(\"Successfully deleted managed resource\")\n\n\t\treturn reconcile.Result{Requeue: false}, nil\n\t}\n\n\tif _, err := r.managed.PublishConnection(ctx, managed, observation.ConnectionDetails); err != nil {\n\t\t// If this is the first time we encounter this issue we'll be requeued\n\t\t// implicitly when we update our status with the new error condition. If\n\t\t// not, we requeue explicitly, which will trigger backoff.\n\t\tlog.Debug(\"Cannot publish connection details\", \"error\", err)\n\n\t\tif kerrors.IsConflict(err) {\n\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t}\n\n\t\trecord.Event(managed, event.Warning(reasonCannotPublish, err))\n\t\tstatus.MarkConditions(xpv2.ReconcileError(err))\n\n\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t}\n\n\tif err := r.managed.AddFinalizer(ctx, managed); err != nil {\n\t\t// If this is the first time we encounter this issue we'll be requeued\n\t\t// implicitly when we update our status with the new error condition. If\n\t\t// not, we requeue explicitly, which will trigger backoff.\n\t\tlog.Debug(\"Cannot add finalizer\", \"error\", err)\n\n\t\tif kerrors.IsConflict(err) {\n\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t}\n\n\t\tstatus.MarkConditions(xpv2.ReconcileError(err))\n\n\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t}\n\n\tif !observation.ResourceExists && policy.ShouldCreate() {\n\t\t// We write this annotation for two reasons. Firstly, it helps\n\t\t// us to detect the case in which we fail to persist critical\n\t\t// information (like the external name) that may be set by the\n\t\t// subsequent external.Create call. Secondly, it guarantees that\n\t\t// we're operating on the latest version of our resource. We\n\t\t// don't use the CriticalAnnotationUpdater because we _want_ the\n\t\t// update to fail if we get a 409 due to a stale version.\n\t\tmeta.SetExternalCreatePending(managed, time.Now())\n\n\t\tif err := r.client.Update(ctx, managed); err != nil {\n\t\t\tlog.Debug(errUpdateManaged, \"error\", err)\n\n\t\t\tif kerrors.IsConflict(err) {\n\t\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t\t}\n\n\t\t\trecord.Event(managed, event.Warning(reasonCannotUpdateManaged, errors.Wrap(err, errUpdateManaged)))\n\t\t\tstatus.MarkConditions(xpv2.Creating(), xpv2.ReconcileError(errors.Wrap(err, errUpdateManaged)))\n\n\t\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t\t}\n\n\t\tcreation, err := external.Create(externalCtx, managed)\n\t\tif err != nil {\n\t\t\t// We'll hit this condition if we can't create our external\n\t\t\t// resource, for example if our provider credentials don't have\n\t\t\t// access to create it. If this is the first time we encounter this\n\t\t\t// issue we'll be requeued implicitly when we update our status with\n\t\t\t// the new error condition. If not, we requeue explicitly, which will trigger backoff.\n\t\t\tlog.Debug(\"Cannot create external resource\", \"error\", err)\n\n\t\t\tif !kerrors.IsConflict(err) {\n\t\t\t\trecord.Event(managed, event.Warning(reasonCannotCreate, err))\n\t\t\t}\n\n\t\t\t// We handle annotations specially here because it's\n\t\t\t// critical that they are persisted to the API server.\n\t\t\t// If we don't add the external-create-failed annotation\n\t\t\t// the reconciler will refuse to proceed, because it\n\t\t\t// won't know whether or not it created an external\n\t\t\t// resource.\n\t\t\tmeta.SetExternalCreateFailed(managed, time.Now())\n\n\t\t\tif err := r.managed.UpdateCriticalAnnotations(ctx, managed); err != nil {\n\t\t\t\tlog.Debug(errUpdateManagedAnnotations, \"error\", err)\n\t\t\t\trecord.Event(managed, event.Warning(reasonCannotUpdateManaged, errors.Wrap(err, errUpdateManagedAnnotations)))\n\n\t\t\t\t// We only log and emit an event here rather\n\t\t\t\t// than setting a status condition and returning\n\t\t\t\t// early because presumably it's more useful to\n\t\t\t\t// set our status condition to the reason the\n\t\t\t\t// create failed.\n\t\t\t}\n\n\t\t\tif err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_CREATE, err, creation.AdditionalDetails); err != nil {\n\t\t\t\tlog.Info(errRecordChangeLog, \"error\", err)\n\t\t\t}\n\n\t\t\tstatus.MarkConditions(xpv2.Creating(), xpv2.ReconcileError(errors.Wrap(err, errReconcileCreate)))\n\n\t\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t\t}\n\n\t\t// In some cases our external-name may be set by Create above.\n\t\tlog = log.WithValues(\"external-name\", meta.GetExternalName(managed))\n\t\trecord = r.record.WithAnnotations(\"external-name\", meta.GetExternalName(managed))\n\n\t\tif err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_CREATE, nil, creation.AdditionalDetails); err != nil {\n\t\t\tlog.Info(errRecordChangeLog, \"error\", err)\n\t\t}\n\n\t\t// We handle annotations specially here because it's critical\n\t\t// that they are persisted to the API server. If we don't remove\n\t\t// add the external-create-succeeded annotation the reconciler\n\t\t// will refuse to proceed, because it won't know whether or not\n\t\t// it created an external resource. This is also important in\n\t\t// cases where we must record an external-name annotation set by\n\t\t// the Create call. Any other changes made during Create will be\n\t\t// reverted when annotations are updated; at the time of writing\n\t\t// Create implementations are advised not to alter status, but\n\t\t// we may revisit this in future.\n\t\tmeta.SetExternalCreateSucceeded(managed, time.Now())\n\n\t\tif err := r.managed.UpdateCriticalAnnotations(ctx, managed); err != nil {\n\t\t\tlog.Debug(errUpdateManagedAnnotations, \"error\", err)\n\n\t\t\tif kerrors.IsConflict(err) {\n\t\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t\t}\n\n\t\t\trecord.Event(managed, event.Warning(reasonCannotUpdateManaged, errors.Wrap(err, errUpdateManagedAnnotations)))\n\t\t\tstatus.MarkConditions(xpv2.Creating(), xpv2.ReconcileError(errors.Wrap(err, errUpdateManagedAnnotations)))\n\n\t\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t\t}\n\n\t\tif _, err := r.managed.PublishConnection(ctx, managed, creation.ConnectionDetails); err != nil {\n\t\t\t// If this is the first time we encounter this issue we'll be\n\t\t\t// requeued implicitly when we update our status with the new error\n\t\t\t// condition. If not, we requeue explicitly, which will trigger backoff.\n\t\t\tlog.Debug(\"Cannot publish connection details\", \"error\", err)\n\n\t\t\tif kerrors.IsConflict(err) {\n\t\t\t\treturn reconcile.Result{Requeue: true}, nil\n\t\t\t}\n\n\t\t\trecord.Event(managed, event.Warning(reasonCannotPublish, err))\n\t\t\tstatus.MarkConditions(xpv2.Creating(), xpv2.ReconcileError(err))\n\n\t\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t\t}\n\n\t\t// We've successfully created our external resource. In many cases the\n\t\t// creation process takes a little time to finish. We requeue explicitly\n\t\t// order to observe the external resource to determine whether it's\n\t\t// ready for use.\n\t\tlog.Debug(\"Successfully requested creation of external resource\")\n\t\trecord.Event(managed, event.Normal(reasonCreated, \"Successfully requested creation of external resource\"))\n\t\tstatus.MarkConditions(xpv2.Creating(), xpv2.ReconcileSuccess())\n\n\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t}\n\n\tif observation.ResourceLateInitialized && policy.ShouldLateInitialize() {\n\t\t// Note that this update may reset any pending updates to the status of\n\t\t// the managed resource from when it was observed above. This is because\n\t\t// the API server replies to the update with its unchanged view of the\n\t\t// resource's status, which is subsequently deserialized into managed.\n\t\t// This is usually tolerable because the update will implicitly requeue\n\t\t// an immediate reconcile which should re-observe the external resource\n\t\t// and persist its status.\n\t\tif err := r.client.Update(ctx, managed); err != nil {\n\t\t\tlog.Debug(errUpdateManaged, \"error\", err)\n\t\t\trecord.Event(managed, event.Warning(reasonCannotUpdateManaged, err))\n\t\t\tstatus.MarkConditions(xpv2.ReconcileError(errors.Wrap(err, errUpdateManaged)))\n\n\t\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t\t}\n\t}\n\n\tif observation.ResourceUpToDate {\n\t\t// We did not need to create, update, or delete our external resource.\n\t\t// Per the below issue nothing will notify us if and when the external\n\t\t// resource we manage changes, so we requeue a speculative reconcile\n\t\t// after the specified poll interval in order to observe it and react\n\t\t// accordingly.\n\t\t// https://github.com/crossplane/crossplane/issues/289\n\t\treconcileAfter := r.pollIntervalHook(managed, r.effectivePollInterval(managed))\n\t\tlog.Debug(\"External resource is up to date\", \"requeue-after\", time.Now().Add(reconcileAfter))\n\t\tstatus.MarkConditions(xpv2.ReconcileSuccess())\n\t\tr.metricRecorder.recordFirstTimeReady(managed)\n\n\t\t// record that we intentionally did not update the managed resource\n\t\t// because no drift was detected. We call this so late in the reconcile\n\t\t// because all the cases above could contribute (for different reasons)\n\t\t// that the external object would not have been updated.\n\t\tr.metricRecorder.recordUnchanged(managed.GetName())\n\n\t\treturn reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t}\n\n\tif observation.Diff != \"\" {\n\t\tlog.Debug(\"External resource differs from desired state\", \"diff\", observation.Diff)\n\t}\n\n\t// skip the update if the management policy is set to ignore updates\n\tif !policy.ShouldUpdate() {\n\t\treconcileAfter := r.pollIntervalHook(managed, r.effectivePollInterval(managed))\n\t\tlog.Debug(\"Skipping update due to managementPolicies. Reconciliation succeeded\", \"requeue-after\", time.Now().Add(reconcileAfter))\n\t\tstatus.MarkConditions(xpv2.ReconcileSuccess())\n\n\t\treturn reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t}\n\n\tupdate, err := external.Update(externalCtx, managed)\n\tif err != nil {\n\t\t// We'll hit this condition if we can't update our external resource,\n\t\t// for example if our provider credentials don't have access to update\n\t\t// it. If this is the first time we encounter this issue we'll be\n\t\t// requeued implicitly when we update our status with the new error\n\t\t// condition. If not, we requeue explicitly, which will trigger backoff.\n\t\tlog.Debug(\"Cannot update external resource\")\n\n\t\tif err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_UPDATE, err, update.AdditionalDetails); err != nil {\n\t\t\tlog.Info(errRecordChangeLog, \"error\", err)\n\t\t}\n\n\t\trecord.Event(managed, event.Warning(reasonCannotUpdate, err))\n\t\tstatus.MarkConditions(xpv2.ReconcileError(errors.Wrap(err, errReconcileUpdate)))\n\n\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t}\n\n\t// record the drift after the successful update.\n\tr.metricRecorder.recordDrift(managed)\n\n\tif err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_UPDATE, nil, update.AdditionalDetails); err != nil {\n\t\tlog.Info(errRecordChangeLog, \"error\", err)\n\t}\n\n\tif _, err := r.managed.PublishConnection(ctx, managed, update.ConnectionDetails); err != nil {\n\t\t// If this is the first time we encounter this issue we'll be requeued\n\t\t// implicitly when we update our status with the new error condition. If\n\t\t// not, we requeue explicitly, which will trigger backoff.\n\t\tlog.Debug(\"Cannot publish connection details\", \"error\", err)\n\t\trecord.Event(managed, event.Warning(reasonCannotPublish, err))\n\t\tstatus.MarkConditions(xpv2.ReconcileError(err))\n\n\t\treturn reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n\t}\n\n\t// We've successfully updated our external resource. Per the below issue\n\t// nothing will notify us if and when the external resource we manage\n\t// changes, so we requeue a speculative reconcile after the specified poll\n\t// interval in order to observe it and react accordingly.\n\t// https://github.com/crossplane/crossplane/issues/289\n\treconcileAfter := r.pollIntervalHook(managed, r.effectivePollInterval(managed))\n\tlog.Debug(\"Successfully requested update of external resource\", \"requeue-after\", time.Now().Add(reconcileAfter))\n\trecord.Event(managed, event.Normal(reasonUpdated, \"Successfully requested update of external resource\"))\n\tstatus.MarkConditions(xpv2.ReconcileSuccess())\n\n\treturn reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(updateStatus(), errUpdateManagedStatus)\n}\n"
  },
  {
    "path": "pkg/reconciler/managed/reconciler_deprecated.go",
    "content": "/*\nCopyright 2025 The Crossplane 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 managed\n\nimport (\n\t\"context\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n)\n\n// ExternalConnecter an alias to ExternalConnector.\n//\n// Deprecated: use ExternalConnector.\ntype ExternalConnecter = ExternalConnector\n\n// TypedExternalConnecter an alias to TypedExternalConnector.\n//\n// Deprecated: use TypedExternalConnector.\ntype TypedExternalConnecter[managed resource.Managed] interface {\n\tTypedExternalConnector[managed]\n}\n\n// An ExternalDisconnector disconnects from a provider.\n//\n// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting\n// from the provider.\n//\n//nolint:iface // We know it is a redundant interface.\ntype ExternalDisconnector interface {\n\t// Disconnect from the provider and close the ExternalClient.\n\tDisconnect(ctx context.Context) error\n}\n\n// ExternalDisconnecter an alias to ExternalDisconnector.\n//\n// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting\n// from the provider.\n//\n//nolint:iface // We know it is a redundant interface\ntype ExternalDisconnecter interface {\n\tExternalDisconnector\n}\n\n// NopDisconnecter aliases NopDisconnector.\n//\n// Deprecated: Use NopDisconnector.\ntype NopDisconnecter = NopDisconnector\n\n// TODO: these types of aliases are only allowed in Go 1.23 and above.\n// type TypedNopDisconnecter[managed resource.Managed] = TypedNopDisconnector[managed]\n// type TypedNopDisconnecter[managed resource.Managed] = TypedNopDisconnector[managed]\n// type TypedExternalConnectDisconnecterFns[managed resource.Managed] =  TypedExternalConnectDisconnectorFns[managed]\n\n// NewNopDisconnecter an alias to NewNopDisconnector.\n//\n// Deprecated: use NewNopDisconnector.\nfunc NewNopDisconnecter(c ExternalConnector) ExternalConnectDisconnector {\n\treturn NewNopDisconnector(c)\n}\n\n// ExternalDisconnecterFn aliases ExternalDisconnectorFn.\n//\n// Deprecated: use ExternalDisconnectorFn.\ntype ExternalDisconnecterFn = ExternalDisconnectorFn\n\n// ExternalConnectDisconnecterFns aliases ExternalConnectDisconnectorFns.\n//\n// Deprecated: use ExternalConnectDisconnectorFns.\ntype ExternalConnectDisconnecterFns = ExternalConnectDisconnectorFns\n\n// NopConnecter aliases NopConnector.\n//\n// Deprecated: use NopConnector.\ntype NopConnecter = NopConnector\n\n// WithExternalConnecter aliases WithExternalConnector.\n//\n// Deprecated: use WithExternalConnector.\nfunc WithExternalConnecter(c ExternalConnector) ReconcilerOption {\n\treturn WithExternalConnector(c)\n}\n\n// WithExternalConnectDisconnector specifies how the Reconciler should connect and disconnect to the API\n// used to sync and delete external resources.\n//\n// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting from the provider.\nfunc WithExternalConnectDisconnector(c ExternalConnectDisconnector) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.external.ExternalConnectDisconnector = c\n\t}\n}\n\n// WithExternalConnectDisconnecter aliases WithExternalConnectDisconnector.\n//\n// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting from the provider.\nfunc WithExternalConnectDisconnecter(c ExternalConnectDisconnector) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.external.ExternalConnectDisconnector = c\n\t}\n}\n\n// WithTypedExternalConnectDisconnector specifies how the Reconciler should connect and disconnect to the API\n// used to sync and delete external resources.\n//\n// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting from the provider.\nfunc WithTypedExternalConnectDisconnector[managed resource.Managed](c TypedExternalConnectDisconnector[managed]) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.external.ExternalConnectDisconnector = &typedExternalConnectDisconnectorWrapper[managed]{c}\n\t}\n}\n\n// WithTypedExternalConnectDisconnecter aliases WithTypedExternalConnectDisconnector.\n//\n// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting from the provider.\nfunc WithTypedExternalConnectDisconnecter[managed resource.Managed](c TypedExternalConnectDisconnector[managed]) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.external.ExternalConnectDisconnector = &typedExternalConnectDisconnectorWrapper[managed]{c}\n\t}\n}\n"
  },
  {
    "path": "pkg/reconciler/managed/reconciler_legacy_test.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 managed\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\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\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/manager\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/apis/changelogs/proto/v1alpha1\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nvar _ reconcile.Reconciler = &Reconciler{}\n\nfunc TestReconciler(t *testing.T) {\n\ttype args struct {\n\t\tm  manager.Manager\n\t\tmg resource.ManagedKind\n\t\to  []ReconcilerOption\n\t}\n\n\ttype want struct {\n\t\tresult        reconcile.Result\n\t\tresultCmpOpts []cmp.Option\n\t\terr           error\n\t}\n\n\terrBoom := errors.New(\"boom\")\n\tnow := metav1.Now()\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"GetManagedError\": {\n\t\t\treason: \"Any error (except not found) encountered while getting the resource under reconciliation should be returned.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{MockGet: test.NewMockGetFn(errBoom)},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t},\n\t\t\twant: want{err: errors.Wrap(errBoom, errGetManaged)},\n\t\t},\n\t\t\"ManagedNotFound\": {\n\t\t\treason: \"Not found errors encountered while getting the resource under reconciliation should be ignored.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{MockGet: test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, \"\"))},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{}},\n\t\t},\n\t\t\"UnpublishConnectionDetailsDeletionPolicyDeleteOrpahn\": {\n\t\t\treason: \"Errors unpublishing connection details should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\tmg.SetDeletionPolicy(xpv2.DeletionOrphan)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\twant.SetDeletionPolicy(xpv2.DeletionOrphan)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Deleting().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors unpublishing connection details should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\twithConnectionPublishers(ConnectionPublisherFns{\n\t\t\t\t\t\tUnpublishConnectionFn: func(_ context.Context, _ resource.ConnectionSecretOwner, _ ConnectionDetails) error { return errBoom },\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"RemoveFinalizerErrorDeletionPolicyOrphan\": {\n\t\t\treason: \"Errors removing the managed resource finalizer should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\tmg.SetDeletionPolicy(xpv2.DeletionOrphan)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\twant.SetDeletionPolicy(xpv2.DeletionOrphan)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Deleting().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors removing the managed resource finalizer should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return errBoom }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"DeleteSuccessfulDeletionPolicyOrphan\": {\n\t\t\treason: \"Successful managed resource deletion with deletion policy Orphan should not trigger a requeue or status update.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\tmg.SetDeletionPolicy(xpv2.DeletionOrphan)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: false}},\n\t\t},\n\t\t\"InitializeError\": {\n\t\t\treason: \"Errors initializing the managed resource should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors initializing the managed resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(InitializerFn(func(_ context.Context, _ resource.Managed) error {\n\t\t\t\t\t\treturn errBoom\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExtraFinalizersDelayDelete\": {\n\t\t\treason: \"The existence of multiple finalizers should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\tmg.SetDeletionPolicy(xpv2.DeletionDelete)\n\t\t\t\t\t\t\tmg.SetFinalizers([]string{FinalizerName, \"finalizer2\"})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExternalCreatePending\": {\n\t\t\treason: \"We should return early if the managed resource appears to be pending creation. We might have leaked a resource and don't want to create another.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tasLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(obj, now.Time)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, now.Time)\n\t\t\t\t\t\t\twant.SetConditions(\n\t\t\t\t\t\t\t\txpv2.Creating().WithObservedGeneration(42),\n\t\t\t\t\t\t\t\txpv2.ReconcileError(errors.New(errCreateIncomplete)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"We should update our status when we're asked to reconcile a managed resource that is pending creation.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(InitializerFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: false}},\n\t\t},\n\t\t\"ResolveReferencesError\": {\n\t\t\treason: \"Errors during reference resolution references should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors during reference resolution should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error {\n\t\t\t\t\t\treturn errBoom\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExternalConnectError\": {\n\t\t\treason: \"Errors connecting to the provider should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, got client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errReconcileConnect)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, got, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors connecting to the provider should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\treturn nil, errBoom\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExternalDisconnectError\": {\n\t\t\treason: \"Error disconnecting from the provider should not trigger requeue.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A successful no-op reconcile should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ExternalObserveError\": {\n\t\t\treason: \"Errors observing the external resource should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errReconcileObserve)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors observing the managed resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"CreationGracePeriod\": {\n\t\t\treason: \"If our resource appears not to exist during the creation grace period we should return early.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(obj, time.Now())\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithCreationGracePeriod(1 * time.Minute),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExternalDeleteError\": {\n\t\t\treason: \"Errors deleting the external resource should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errReconcileDelete)).WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Deleting().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"An error deleting an external resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDeleteFn: func(_ context.Context, _ resource.Managed) (ExternalDelete, error) {\n\t\t\t\t\t\t\t\treturn ExternalDelete{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExternalDeleteSuccessful\": {\n\t\t\treason: \"A deleted managed resource with the 'delete' reclaim policy should delete its external resource then requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Deleting().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A deleted external resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDeleteFn: func(_ context.Context, _ resource.Managed) (ExternalDelete, error) {\n\t\t\t\t\t\t\t\treturn ExternalDelete{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"UnpublishConnectionDetailsDeletionPolicyDeleteError\": {\n\t\t\treason: \"Errors unpublishing connection details should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Deleting().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors unpublishing connection details should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\twithConnectionPublishers(ConnectionPublisherFns{\n\t\t\t\t\t\tUnpublishConnectionFn: func(_ context.Context, _ resource.ConnectionSecretOwner, _ ConnectionDetails) error { return errBoom },\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"RemoveFinalizerErrorDeletionPolicyDelete\": {\n\t\t\treason: \"Errors removing the managed resource finalizer should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Deleting().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors removing the managed resource finalizer should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return errBoom }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"DeleteSuccessfulDeletionPolicyDelete\": {\n\t\t\treason: \"Successful managed resource deletion with deletion policy Delete should not trigger a requeue or status update.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: false}},\n\t\t},\n\t\t\"PublishObservationConnectionDetailsError\": {\n\t\t\treason: \"Errors publishing connection details after observation should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors publishing connection details after observation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(&NopConnector{}),\n\t\t\t\t\twithConnectionPublishers(ConnectionPublisherFns{\n\t\t\t\t\t\tPublishConnectionFn: func(_ context.Context, _ resource.ConnectionSecretOwner, _ ConnectionDetails) (bool, error) {\n\t\t\t\t\t\t\treturn false, errBoom\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\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"AddFinalizerError\": {\n\t\t\treason: \"Errors adding a finalizer should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors adding a finalizer should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(&NopConnector{}),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return errBoom }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"UpdateCreatePendingError\": {\n\t\t\treason: \"Errors while updating our external-create-pending annotation should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:    legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(errBoom),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(\n\t\t\t\t\t\t\t\txpv2.Creating().WithObservedGeneration(42),\n\t\t\t\t\t\t\t\txpv2.ReconcileError(errors.Wrap(errBoom, errUpdateManaged)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors while creating an external resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCreateFn: func(_ context.Context, _ resource.Managed) (ExternalCreation, error) {\n\t\t\t\t\t\t\t\treturn ExternalCreation{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"CreateExternalError\": {\n\t\t\treason: \"Errors while creating an external resource should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:    legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateFailed(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errReconcileCreate)).WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors while creating an external resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCreateFn: func(_ context.Context, _ resource.Managed) (ExternalCreation, error) {\n\t\t\t\t\t\t\t\treturn ExternalCreation{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\t// We simulate our critical annotation update failing too here.\n\t\t\t\t\t// This is mostly just to exercise the code, which just creates a log and an event.\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return errBoom })),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"UpdateCriticalAnnotationsError\": {\n\t\t\treason: \"Errors updating critical annotations after creation should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:    legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errUpdateManagedAnnotations)).WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors updating critical annotations after creation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCreateFn: func(_ context.Context, _ resource.Managed) (ExternalCreation, error) {\n\t\t\t\t\t\t\t\treturn ExternalCreation{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return errBoom })),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"PublishCreationConnectionDetailsError\": {\n\t\t\treason: \"Errors publishing connection details after creation should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:    legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors publishing connection details after creation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCreateFn: func(_ context.Context, _ resource.Managed) (ExternalCreation, error) {\n\t\t\t\t\t\t\t\tcd := ConnectionDetails{\"create\": []byte{}}\n\t\t\t\t\t\t\t\treturn ExternalCreation{ConnectionDetails: cd}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return nil })),\n\t\t\t\t\twithConnectionPublishers(ConnectionPublisherFns{\n\t\t\t\t\t\tPublishConnectionFn: func(_ context.Context, _ resource.ConnectionSecretOwner, cd ConnectionDetails) (bool, error) {\n\t\t\t\t\t\t\t// We're called after observe, create, and update\n\t\t\t\t\t\t\t// but we only want to fail when publishing details\n\t\t\t\t\t\t\t// after a creation.\n\t\t\t\t\t\t\tif _, ok := cd[\"create\"]; ok {\n\t\t\t\t\t\t\t\treturn false, errBoom\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn true, nil\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"CreateSuccessful\": {\n\t\t\treason: \"Successful managed resource creation should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:    legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Successful managed resource creation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(&NopConnector{}),\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return nil })),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"CreateSuccessfulAfterExternalCreatePendingAndDeterministicName\": {\n\t\t\treason: \"Successful managed resource creation which was previously pending and has a deterministic external name should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tasLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(obj, now.Time)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Successful managed resource creation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(&NopConnector{}),\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return nil })),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t\tWithDeterministicExternalName(true),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"LateInitializeUpdateError\": {\n\t\t\treason: \"Errors updating a managed resource to persist late initialized fields should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:    legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(errBoom),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errUpdateManaged)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors updating a managed resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true, ResourceLateInitialized: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExternalResourceUpToDate\": {\n\t\t\treason: \"When the external resource exists and is up to date a requeue should be triggered after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A successful no-op reconcile should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ExternalResourceUpToDateWithJitter\": {\n\t\t\treason: \"When the external resource exists and is up to date a requeue should be triggered after a long wait with jitter added.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t\tWithPollJitterHook(time.Second),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: defaultPollInterval},\n\t\t\t\tresultCmpOpts: []cmp.Option{cmp.Comparer(func(l, r time.Duration) bool {\n\t\t\t\t\tdiff := l - r\n\t\t\t\t\tif diff < 0 {\n\t\t\t\t\t\tdiff = -diff\n\t\t\t\t\t}\n\n\t\t\t\t\treturn diff < time.Second\n\t\t\t\t})},\n\t\t\t},\n\t\t},\n\t\t\"ExternalResourceUpToDateWithPollIntervalHook\": {\n\t\t\treason: \"When the external resource exists and is up to date a requeue should be triggered after a long wait processed by the interval hook.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t\tWithPollIntervalHook(func(_ resource.Managed, pollInterval time.Duration) time.Duration {\n\t\t\t\t\t\treturn 2 * pollInterval\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: 2 * defaultPollInterval},\n\t\t\t},\n\t\t},\n\t\t\"ExternalResourceUpToDateWithMultiplePollIntervalHooks\": {\n\t\t\treason: \"When the external resource exists and is up to date a requeue should be triggered after a long wait processed by the latest interval hook.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t\tWithPollJitterHook(time.Second),\n\t\t\t\t\tWithPollIntervalHook(func(_ resource.Managed, pollInterval time.Duration) time.Duration {\n\t\t\t\t\t\treturn 2 * pollInterval\n\t\t\t\t\t}),\n\t\t\t\t\tWithPollIntervalHook(func(_ resource.Managed, pollInterval time.Duration) time.Duration {\n\t\t\t\t\t\treturn 3 * pollInterval\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: 3 * defaultPollInterval},\n\t\t\t},\n\t\t},\n\t\t\"UpdateExternalError\": {\n\t\t\treason: \"Errors while updating an external resource should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errReconcileUpdate)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors while updating an external resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"PublishUpdateConnectionDetailsError\": {\n\t\t\treason: \"Errors publishing connection details after an update should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors publishing connection details after an update should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\tcd := ConnectionDetails{\"update\": []byte{}}\n\t\t\t\t\t\t\t\treturn ExternalUpdate{ConnectionDetails: cd}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\twithConnectionPublishers(ConnectionPublisherFns{\n\t\t\t\t\t\tPublishConnectionFn: func(_ context.Context, _ resource.ConnectionSecretOwner, cd ConnectionDetails) (bool, error) {\n\t\t\t\t\t\t\t// We're called after observe, create, and update\n\t\t\t\t\t\t\t// but we only want to fail when publishing details\n\t\t\t\t\t\t\t// after an update.\n\t\t\t\t\t\t\tif _, ok := cd[\"update\"]; ok {\n\t\t\t\t\t\t\t\treturn false, errBoom\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn false, nil\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"UpdateSuccessful\": {\n\t\t\treason: \"A successful managed resource update should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A successful managed resource update should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"TypedReconcilerUpdateSuccessful\": {\n\t\t\treason: \"A successful managed resource update should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A successful managed resource update should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithTypedExternalConnector(TypedExternalConnectorFn[*fake.LegacyManaged](func(_ context.Context, _ *fake.LegacyManaged) (TypedExternalClient[*fake.LegacyManaged], error) {\n\t\t\t\t\t\tc := &TypedExternalClientFns[*fake.LegacyManaged]{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ *fake.LegacyManaged) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ *fake.LegacyManaged) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: defaultPollInterval},\n\t\t\t},\n\t\t},\n\t\t\"ReconciliationPausedSuccessful\": {\n\t\t\treason: `If a managed resource has the pause annotation with value \"true\", there should be no further requeue requests.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetAnnotations(map[string]string{meta.AnnotationKeyReconciliationPaused: \"true\"})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetAnnotations(map[string]string{meta.AnnotationKeyReconciliationPaused: \"true\"})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcilePaused().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `If managed resource has the pause annotation with value \"true\", it should acquire \"Synced\" status condition with the status \"False\" and the reason \"ReconcilePaused\".`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{}},\n\t\t},\n\t\t\"ManagementPolicyReconciliationPausedSuccessful\": {\n\t\t\treason: `If a managed resource has the pause annotation with value \"true\", there should be no further requeue requests.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcilePaused().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `If managed resource has the pause annotation with value \"true\", it should acquire \"Synced\" status condition with the status \"False\" and the reason \"ReconcilePaused\".`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{}},\n\t\t},\n\t\t\"ReconciliationResumes\": {\n\t\t\treason: `If a managed resource has the pause annotation with some value other than \"true\" and the Synced=False/ReconcilePaused status condition, reconciliation should resume with requeueing.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetAnnotations(map[string]string{meta.AnnotationKeyReconciliationPaused: \"false\"})\n\t\t\t\t\t\t\tmg.SetConditions(xpv2.ReconcilePaused())\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetAnnotations(map[string]string{meta.AnnotationKeyReconciliationPaused: \"false\"})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `Managed resource should acquire Synced=False/ReconcileSuccess status condition after a resume.`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ReconciliationPausedError\": {\n\t\t\treason: `If a managed resource has the pause annotation with value \"true\" and the status update due to reconciliation being paused fails, error should be reported causing an exponentially backed-off requeue.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetAnnotations(map[string]string{meta.AnnotationKeyReconciliationPaused: \"true\"})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\treturn errBoom\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t},\n\t\t\twant: want{err: errors.Wrap(errBoom, errUpdateManagedStatus)},\n\t\t},\n\t\t\"ManagementPoliciesUsedButNotEnabled\": {\n\t\t\treason: `If management policies tried to be used without enabling the feature, we should throw an error.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionCreate})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionCreate})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(fmt.Errorf(errFmtManagementPolicyNonDefault, xpv2.ManagementPolicies{xpv2.ManagementActionCreate})).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `If managed resource has a non default management policy but feature not enabled, it should return a proper error.`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{}},\n\t\t},\n\t\t\"ManagementPolicyNotSupported\": {\n\t\t\treason: `If an unsupported management policy is used, we should throw an error.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionCreate})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionCreate})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(fmt.Errorf(errFmtManagementPolicyNotSupported, xpv2.ManagementPolicies{xpv2.ManagementActionCreate})).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `If managed resource has non supported management policy, it should return a proper error.`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{}},\n\t\t},\n\t\t\"CustomManagementPolicyNotSupported\": {\n\t\t\treason: `If a custom unsupported management policy is used, we should throw an error.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(fmt.Errorf(errFmtManagementPolicyNotSupported, xpv2.ManagementPolicies{xpv2.ManagementActionAll})).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `If managed resource has non supported management policy, it should return a proper error.`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReconcilerSupportedManagementPolicies([]sets.Set[xpv2.ManagementAction]{sets.New(xpv2.ManagementActionObserve)}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{}},\n\t\t},\n\t\t\"ObserveOnlyResourceDoesNotExist\": {\n\t\t\treason: \"With only Observe management action, observing a resource that does not exist should be reported as a conditioned status error.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errors.New(errExternalResourceNotExist), errReconcileObserve)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Resource does not exist should be reported as a conditioned status when ObserveOnly.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ObserveOnlyPublishConnectionDetailsError\": {\n\t\t\treason: \"With Observe, errors publishing connection details after observation should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors publishing connection details after observation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\twithConnectionPublishers(ConnectionPublisherFns{\n\t\t\t\t\t\tPublishConnectionFn: func(_ context.Context, _ resource.ConnectionSecretOwner, _ ConnectionDetails) (bool, error) {\n\t\t\t\t\t\t\treturn false, errBoom\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\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ObserveOnlySuccessfulObserve\": {\n\t\t\treason: \"With Observe, a successful managed resource observe should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"With ObserveOnly, a successful managed resource observation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\twithConnectionPublishers(ConnectionPublisherFns{\n\t\t\t\t\t\tPublishConnectionFn: func(_ context.Context, _ resource.ConnectionSecretOwner, _ ConnectionDetails) (bool, error) {\n\t\t\t\t\t\t\treturn false, nil\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ManagementPolicyAllCreateSuccessful\": {\n\t\t\treason: \"Successful managed resource creation using management policy all should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Successful managed resource creation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(&NopConnector{}),\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return nil })),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ManagementPolicyCreateCreateSuccessful\": {\n\t\t\treason: \"Successful managed resource creation using management policy Create should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Successful managed resource creation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(&NopConnector{}),\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return nil })),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ManagementPolicyImmutable\": {\n\t\t\treason: \"Successful reconciliation skipping update should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionLateInitialize, xpv2.ManagementActionCreate, xpv2.ManagementActionDelete})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(errBoom),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionLateInitialize, xpv2.ManagementActionCreate, xpv2.ManagementActionDelete})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `Managed resource should acquire Synced=False/ReconcileSuccess status condition.`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ManagementPolicyAllUpdateSuccessful\": {\n\t\t\treason: \"A successful managed resource update using management policies should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A successful managed resource update should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ManagementPolicyUpdateUpdateSuccessful\": {\n\t\t\treason: \"A successful managed resource update using management policies should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A successful managed resource update should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ManagementPolicySkipLateInitialize\": {\n\t\t\treason: \"Should skip updating a managed resource to persist late initialized fields and should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionUpdate, xpv2.ManagementActionCreate, xpv2.ManagementActionDelete})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(errBoom),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newLegacyManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionUpdate, xpv2.ManagementActionCreate, xpv2.ManagementActionDelete})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors updating a managed resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true, ResourceLateInitialized: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ObserveAndLateInitializePolicy\": {\n\t\t\treason: \"If management policy is set to Observe and LateInitialize, reconciliation should proceed\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionLateInitialize})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReconcilerSupportedManagementPolicies(defaultSupportedManagementPolicies()),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ObserveUpdateAndLateInitializePolicy\": {\n\t\t\treason: \"If management policy is set to Observe, Update and LateInitialize, reconciliation should proceed\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{\n\t\t\t\t\t\t\t\txpv2.ManagementActionObserve,\n\t\t\t\t\t\t\t\txpv2.ManagementActionUpdate,\n\t\t\t\t\t\t\t\txpv2.ManagementActionLateInitialize,\n\t\t\t\t\t\t\t})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReconcilerSupportedManagementPolicies(defaultSupportedManagementPolicies()),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewReconciler(tc.args.m, tc.args.mg, tc.args.o...)\n\n\t\t\tgot, err := r.Reconcile(context.Background(), reconcile.Request{})\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nr.Reconcile(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.result, got, tc.want.resultCmpOpts...); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nr.Reconcile(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestTestLegacyManagementPoliciesResolverIsPaused(t *testing.T) {\n\ttype args struct {\n\t\tenabled bool\n\t\tpolicy  xpv2.ManagementPolicies\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   bool\n\t}{\n\t\t\"Disabled\": {\n\t\t\treason: \"Should return false if management policies are disabled\",\n\t\t\targs: args{\n\t\t\t\tenabled: false,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t\t\"EnabledEmptyPolicies\": {\n\t\t\treason: \"Should return true if the management policies are enabled and empty\",\n\t\t\targs: args{\n\t\t\t\tenabled: true,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"EnabledNonEmptyPolicies\": {\n\t\t\treason: \"Should return true if the management policies are enabled and non empty\",\n\t\t\targs: args{\n\t\t\t\tenabled: true,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewLegacyManagementPoliciesResolver(tc.args.enabled, tc.args.policy, xpv2.DeletionDelete)\n\t\t\tif diff := cmp.Diff(tc.want, r.IsPaused()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nIsPaused(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLegacyManagementPoliciesResolverValidate(t *testing.T) {\n\ttype args struct {\n\t\tenabled bool\n\t\tpolicy  xpv2.ManagementPolicies\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   error\n\t}{\n\t\t\"Enabled\": {\n\t\t\treason: \"Should return nil if the management policy is enabled.\",\n\t\t\targs: args{\n\t\t\t\tenabled: true,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"DisabledNonDefault\": {\n\t\t\treason: \"Should return error if the management policy is non-default and disabled.\",\n\t\t\targs: args{\n\t\t\t\tenabled: false,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{xpv2.ManagementActionCreate},\n\t\t\t},\n\t\t\twant: fmt.Errorf(errFmtManagementPolicyNonDefault, []xpv2.ManagementAction{xpv2.ManagementActionCreate}),\n\t\t},\n\t\t\"DisabledDefault\": {\n\t\t\treason: \"Should return nil if the management policy is default and disabled.\",\n\t\t\targs: args{\n\t\t\t\tenabled: false,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"EnabledSupported\": {\n\t\t\treason: \"Should return nil if the management policy is supported.\",\n\t\t\targs: args{\n\t\t\t\tenabled: true,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"EnabledNotSupported\": {\n\t\t\treason: \"Should return err if the management policy is not supported.\",\n\t\t\targs: args{\n\t\t\t\tenabled: true,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{xpv2.ManagementActionDelete},\n\t\t\t},\n\t\t\twant: fmt.Errorf(errFmtManagementPolicyNotSupported, []xpv2.ManagementAction{xpv2.ManagementActionDelete}),\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewLegacyManagementPoliciesResolver(tc.args.enabled, tc.args.policy, xpv2.DeletionDelete)\n\t\t\tif diff := cmp.Diff(tc.want, r.Validate(), test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nIsNonDefault(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLegacyManagementPoliciesResolverShouldCreate(t *testing.T) {\n\ttype args struct {\n\t\tmanagementPoliciesEnabled bool\n\t\tpolicy                    xpv2.ManagementPolicies\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   bool\n\t}{\n\t\t\"ManagementPoliciesDisabled\": {\n\t\t\treason: \"Should return true if management policies are disabled\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: false,\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasCreate\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action Create\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionCreate},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasCreateAll\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action All\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledActionNotAllowed\": {\n\t\t\treason: \"Should return false if management policies are enabled and managementPolicies does not have Create\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionObserve},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewLegacyManagementPoliciesResolver(tc.args.managementPoliciesEnabled, tc.args.policy, xpv2.DeletionOrphan)\n\t\t\tif diff := cmp.Diff(tc.want, r.ShouldCreate()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nShouldCreate(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLegacyManagementPoliciesResolverShouldUpdate(t *testing.T) {\n\ttype args struct {\n\t\tmanagementPoliciesEnabled bool\n\t\tpolicy                    xpv2.ManagementPolicies\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   bool\n\t}{\n\t\t\"ManagementPoliciesDisabled\": {\n\t\t\treason: \"Should return true if management policies are disabled\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: false,\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasUpdate\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action Update\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionUpdate},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasUpdateAll\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action All\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledActionNotAllowed\": {\n\t\t\treason: \"Should return false if management policies are enabled and managementPolicies does not have Update\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionObserve},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewLegacyManagementPoliciesResolver(tc.args.managementPoliciesEnabled, tc.args.policy, xpv2.DeletionOrphan)\n\t\t\tif diff := cmp.Diff(tc.want, r.ShouldUpdate()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nShouldUpdate(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLegacyManagementPoliciesResolverShouldLateInitialize(t *testing.T) {\n\ttype args struct {\n\t\tmanagementPoliciesEnabled bool\n\t\tpolicy                    xpv2.ManagementPolicies\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   bool\n\t}{\n\t\t\"ManagementPoliciesDisabled\": {\n\t\t\treason: \"Should return true if management policies are disabled\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: false,\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasLateInitialize\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action LateInitialize\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionLateInitialize},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasLateInitializeAll\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action All\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledActionNotAllowed\": {\n\t\t\treason: \"Should return false if management policies are enabled and managementPolicies does not have LateInitialize\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionObserve},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewLegacyManagementPoliciesResolver(tc.args.managementPoliciesEnabled, tc.args.policy, xpv2.DeletionOrphan)\n\t\t\tif diff := cmp.Diff(tc.want, r.ShouldLateInitialize()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nShouldLateInitialize(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLegacyManagementPoliciesResolverOnlyObserve(t *testing.T) {\n\ttype args struct {\n\t\tmanagementPoliciesEnabled bool\n\t\tpolicy                    xpv2.ManagementPolicies\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   bool\n\t}{\n\t\t\"ManagementPoliciesDisabled\": {\n\t\t\treason: \"Should return false if management policies are disabled\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: false,\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasOnlyObserve\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action LateInitialize\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionObserve},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasMultipleActions\": {\n\t\t\treason: \"Should return false if management policies are enabled and managementPolicies has multiple actions\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionLateInitialize, xpv2.ManagementActionObserve},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewLegacyManagementPoliciesResolver(tc.args.managementPoliciesEnabled, tc.args.policy, xpv2.DeletionOrphan)\n\t\t\tif diff := cmp.Diff(tc.want, r.ShouldOnlyObserve()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nShouldOnlyObserve(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLegacyShouldDelete(t *testing.T) {\n\ttype args struct {\n\t\tmanagementPoliciesEnabled bool\n\t\tmanaged                   resource.LegacyManaged\n\t}\n\n\ttype want struct {\n\t\tdelete bool\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"DeletionOrphan\": {\n\t\t\treason: \"Should orphan if management policies are disabled and deletion policy is set to Orphan.\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: false,\n\t\t\t\tmanaged: &fake.LegacyManaged{\n\t\t\t\t\tOrphanable: fake.Orphanable{\n\t\t\t\t\t\tPolicy: xpv2.DeletionOrphan,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{delete: false},\n\t\t},\n\t\t\"DeletionDelete\": {\n\t\t\treason: \"Should delete if management policies are disabled and deletion policy is set to Delete.\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: false,\n\t\t\t\tmanaged: &fake.LegacyManaged{\n\t\t\t\t\tOrphanable: fake.Orphanable{\n\t\t\t\t\t\tPolicy: xpv2.DeletionDelete,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{delete: true},\n\t\t},\n\t\t\"DeletionDeleteManagementActionAll\": {\n\t\t\treason: \"Should delete if management policies are enabled and deletion policy is set to Delete and management policy is set to All.\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tmanaged: &fake.LegacyManaged{\n\t\t\t\t\tOrphanable: fake.Orphanable{\n\t\t\t\t\t\tPolicy: xpv2.DeletionDelete,\n\t\t\t\t\t},\n\t\t\t\t\tManageable: fake.Manageable{\n\t\t\t\t\t\tPolicy: xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{delete: true},\n\t\t},\n\t\t\"DeletionOrphanManagementActionAll\": {\n\t\t\treason: \"Should orphan if management policies are enabled and deletion policy is set to Orphan and management policy is set to All.\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tmanaged: &fake.LegacyManaged{\n\t\t\t\t\tOrphanable: fake.Orphanable{\n\t\t\t\t\t\tPolicy: xpv2.DeletionOrphan,\n\t\t\t\t\t},\n\t\t\t\t\tManageable: fake.Manageable{\n\t\t\t\t\t\tPolicy: xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{delete: false},\n\t\t},\n\t\t\"DeletionDeleteManagementActionDelete\": {\n\t\t\treason: \"Should delete if management policies are enabled and deletion policy is set to Delete and management policy has action Delete.\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tmanaged: &fake.LegacyManaged{\n\t\t\t\t\tOrphanable: fake.Orphanable{\n\t\t\t\t\t\tPolicy: xpv2.DeletionDelete,\n\t\t\t\t\t},\n\t\t\t\t\tManageable: fake.Manageable{\n\t\t\t\t\t\tPolicy: xpv2.ManagementPolicies{xpv2.ManagementActionDelete},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{delete: true},\n\t\t},\n\t\t\"DeletionOrphanManagementActionDelete\": {\n\t\t\treason: \"Should delete if management policies are enabled and deletion policy is set to Orphan and management policy has action Delete.\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tmanaged: &fake.LegacyManaged{\n\t\t\t\t\tOrphanable: fake.Orphanable{\n\t\t\t\t\t\tPolicy: xpv2.DeletionOrphan,\n\t\t\t\t\t},\n\t\t\t\t\tManageable: fake.Manageable{\n\t\t\t\t\t\tPolicy: xpv2.ManagementPolicies{xpv2.ManagementActionDelete},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{delete: true},\n\t\t},\n\t\t\"DeletionDeleteManagementActionNoDelete\": {\n\t\t\treason: \"Should orphan if management policies are enabled and deletion policy is set to Delete and management policy does not have action Delete.\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tmanaged: &fake.LegacyManaged{\n\t\t\t\t\tOrphanable: fake.Orphanable{\n\t\t\t\t\t\tPolicy: xpv2.DeletionDelete,\n\t\t\t\t\t},\n\t\t\t\t\tManageable: fake.Manageable{\n\t\t\t\t\t\tPolicy: xpv2.ManagementPolicies{xpv2.ManagementActionObserve},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{delete: false},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewLegacyManagementPoliciesResolver(tc.args.managementPoliciesEnabled, tc.args.managed.GetManagementPolicies(), tc.args.managed.GetDeletionPolicy())\n\t\t\tif diff := cmp.Diff(tc.want.delete, r.ShouldDelete()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nShouldDelete(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLegacyReconcilerChangeLogs(t *testing.T) {\n\ttype args struct {\n\t\tm  manager.Manager\n\t\tmg resource.ManagedKind\n\t\to  []ReconcilerOption\n\t\tc  *changeLogServiceClient\n\t}\n\n\ttype want struct {\n\t\tcallCount  int\n\t\topType     v1alpha1.OperationType\n\t\terrMessage string\n\t}\n\n\tnow := metav1.Now()\n\terrBoom := errors.New(\"boom\")\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"CreateSuccessfulWithChangeLogs\": {\n\t\t\treason: \"Successful managed resource creation should send a create change log entry when change logs are enabled.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:          legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error { return nil }),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\t// resource doesn't exist, which should trigger a create operation\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCreateFn: func(_ context.Context, _ resource.Managed) (ExternalCreation, error) {\n\t\t\t\t\t\t\t\treturn ExternalCreation{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t\tc: &changeLogServiceClient{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcallCount:  1,\n\t\t\t\topType:     v1alpha1.OperationType_OPERATION_TYPE_CREATE,\n\t\t\t\terrMessage: \"\",\n\t\t\t},\n\t\t},\n\t\t\"CreateFailureWithChangeLogs\": {\n\t\t\treason: \"Failed managed resource creation should send a create change log entry with the error when change logs are enabled.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:          legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error { return nil }),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\t// resource doesn't exist, which should trigger a create operation\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCreateFn: func(_ context.Context, _ resource.Managed) (ExternalCreation, error) {\n\t\t\t\t\t\t\t\t// return an error from Create to simulate a failed creation\n\t\t\t\t\t\t\t\treturn ExternalCreation{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t\tc: &changeLogServiceClient{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcallCount:  1,\n\t\t\t\topType:     v1alpha1.OperationType_OPERATION_TYPE_CREATE,\n\t\t\t\terrMessage: errBoom.Error(),\n\t\t\t},\n\t\t},\n\t\t\"UpdateSuccessfulWithChangeLogs\": {\n\t\t\treason: \"Successful managed resource update should send an update change log entry when change logs are enabled.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:          legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error { return nil }),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\t// resource exists but isn't up to date, which should trigger an update operation\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t\tc: &changeLogServiceClient{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcallCount:  1,\n\t\t\t\topType:     v1alpha1.OperationType_OPERATION_TYPE_UPDATE,\n\t\t\t\terrMessage: \"\",\n\t\t\t},\n\t\t},\n\t\t\"UpdateFailureWithChangeLogs\": {\n\t\t\treason: \"Failed managed resource update should send an update change log entry with the error when change logs are enabled.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:          legacyManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error { return nil }),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\t// resource exists but isn't up to date, which should trigger an update operation\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\t// return an error from Update to simulate a failed update\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t\tc: &changeLogServiceClient{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcallCount:  1,\n\t\t\t\topType:     v1alpha1.OperationType_OPERATION_TYPE_UPDATE,\n\t\t\t\terrMessage: errBoom.Error(),\n\t\t\t},\n\t\t},\n\t\t\"DeleteSuccessfulWithChangeLogs\": {\n\t\t\treason: \"Successful managed resource delete should send a delete change log entry when change logs are enabled.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\t// set a deletion timestamp, which should trigger a delete operation\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error { return nil }),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\t// resource exists but we set a deletion timestamp above, which should trigger a delete operation\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDeleteFn: func(_ context.Context, _ resource.Managed) (ExternalDelete, error) {\n\t\t\t\t\t\t\t\treturn ExternalDelete{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t\tc: &changeLogServiceClient{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcallCount:  1,\n\t\t\t\topType:     v1alpha1.OperationType_OPERATION_TYPE_DELETE,\n\t\t\t\terrMessage: \"\",\n\t\t\t},\n\t\t},\n\t\t\"DeleteFailureWithChangeLogs\": {\n\t\t\treason: \"Failed managed resource delete should send a delete change log entry with the error when change logs are enabled.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\t// set a deletion timestamp, which should trigger a delete operation\n\t\t\t\t\t\t\tmg := asLegacyManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error { return nil }),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.LegacyManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.LegacyManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\t// resource exists but we set a deletion timestamp above, which should trigger a delete operation\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDeleteFn: func(_ context.Context, _ resource.Managed) (ExternalDelete, error) {\n\t\t\t\t\t\t\t\t// return an error from Delete to simulate a failed delete\n\t\t\t\t\t\t\t\treturn ExternalDelete{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t\tc: &changeLogServiceClient{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcallCount:  1,\n\t\t\t\topType:     v1alpha1.OperationType_OPERATION_TYPE_DELETE,\n\t\t\t\terrMessage: errBoom.Error(),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.args.o = append(tc.args.o, WithChangeLogger(NewGRPCChangeLogger(tc.args.c, WithProviderVersion(\"provider-cool:v9.99.999\"))))\n\t\t\tr := NewReconciler(tc.args.m, tc.args.mg, tc.args.o...)\n\t\t\tr.Reconcile(context.Background(), reconcile.Request{})\n\n\t\t\tif diff := cmp.Diff(tc.want.callCount, len(tc.args.c.requests)); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nr.Reconcile(...): -want callCount, +got callCount:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.opType, tc.args.c.requests[0].GetEntry().GetOperation()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nr.Reconcile(...): -want opType, +got opType:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.errMessage, tc.args.c.requests[0].GetEntry().GetErrorMessage()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nr.Reconcile(...): -want errMessage, +got errMessage:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc asLegacyManaged(obj client.Object, generation int64) *fake.LegacyManaged {\n\tmg := obj.(*fake.LegacyManaged)\n\tmg.Generation = generation\n\n\treturn mg\n}\n\nfunc newLegacyManaged(generation int64) *fake.LegacyManaged {\n\tmg := &fake.LegacyManaged{}\n\tmg.Generation = generation\n\n\treturn mg\n}\n\nfunc legacyManagedMockGetFn(err error, generation int64) test.MockGetFn {\n\treturn test.NewMockGetFn(err, func(obj client.Object) error {\n\t\tasLegacyManaged(obj, generation)\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "pkg/reconciler/managed/reconciler_modern_test.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 managed\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\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\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/manager\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/apis/changelogs/proto/v1alpha1\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nvar _ reconcile.Reconciler = &Reconciler{}\n\nfunc TestModernReconciler(t *testing.T) {\n\ttype args struct {\n\t\tm  manager.Manager\n\t\tmg resource.ManagedKind\n\t\to  []ReconcilerOption\n\t}\n\n\ttype want struct {\n\t\tresult        reconcile.Result\n\t\tresultCmpOpts []cmp.Option\n\t\terr           error\n\t}\n\n\terrBoom := errors.New(\"boom\")\n\tnow := metav1.Now()\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"GetManagedError\": {\n\t\t\treason: \"Any error (except not found) encountered while getting the resource under reconciliation should be returned.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{MockGet: test.NewMockGetFn(errBoom)},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t},\n\t\t\twant: want{err: errors.Wrap(errBoom, errGetManaged)},\n\t\t},\n\t\t\"ManagedNotFound\": {\n\t\t\treason: \"Not found errors encountered while getting the resource under reconciliation should be ignored.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{MockGet: test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, \"\"))},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{}},\n\t\t},\n\t\t\"UnpublishConnectionDetailsDeletionPolicyOrphan\": {\n\t\t\treason: \"Errors unpublishing connection details should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionCreate, xpv2.ManagementActionUpdate, xpv2.ManagementActionLateInitialize})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionCreate, xpv2.ManagementActionUpdate, xpv2.ManagementActionLateInitialize})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Deleting().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors unpublishing connection details should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\twithLocalConnectionPublishers(LocalConnectionPublisherFns{\n\t\t\t\t\t\tUnpublishConnectionFn: func(_ context.Context, _ resource.LocalConnectionSecretOwner, _ ConnectionDetails) error {\n\t\t\t\t\t\t\treturn errBoom\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\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"RemoveFinalizerErrorDeletionPolicyOrphan\": {\n\t\t\treason: \"Errors removing the managed resource finalizer should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionCreate, xpv2.ManagementActionUpdate, xpv2.ManagementActionLateInitialize})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionCreate, xpv2.ManagementActionUpdate, xpv2.ManagementActionLateInitialize})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Deleting().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors removing the managed resource finalizer should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return errBoom }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"DeleteSuccessfulDeletionPolicyOrphan\": {\n\t\t\treason: \"Successful managed resource deletion with no-delete management policy should not trigger a requeue or status update.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: false}},\n\t\t},\n\t\t\"InitializeError\": {\n\t\t\treason: \"Errors initializing the managed resource should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors initializing the managed resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(InitializerFn(func(_ context.Context, _ resource.Managed) error {\n\t\t\t\t\t\treturn errBoom\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExtraFinalizersDelayDelete\": {\n\t\t\treason: \"The existence of multiple finalizers should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\tmg.SetFinalizers([]string{FinalizerName, \"finalizer2\"})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExternalCreatePending\": {\n\t\t\treason: \"We should return early if the managed resource appears to be pending creation. We might have leaked a resource and don't want to create another.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tasModernManaged(obj, 42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(obj, now.Time)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, now.Time)\n\t\t\t\t\t\t\twant.SetConditions(\n\t\t\t\t\t\t\t\txpv2.Creating().WithObservedGeneration(42),\n\t\t\t\t\t\t\t\txpv2.ReconcileError(errors.New(errCreateIncomplete)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"We should update our status when we're asked to reconcile a managed resource that is pending creation.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(InitializerFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: false}},\n\t\t},\n\t\t\"ResolveReferencesError\": {\n\t\t\treason: \"Errors during reference resolution references should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors during reference resolution should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error {\n\t\t\t\t\t\treturn errBoom\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExternalConnectError\": {\n\t\t\treason: \"Errors connecting to the provider should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, got client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errReconcileConnect)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, got, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors connecting to the provider should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\treturn nil, errBoom\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExternalDisconnectError\": {\n\t\t\treason: \"Error disconnecting from the provider should not trigger requeue.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A successful no-op reconcile should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ExternalObserveError\": {\n\t\t\treason: \"Errors observing the external resource should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errReconcileObserve)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors observing the managed resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"CreationGracePeriod\": {\n\t\t\treason: \"If our resource appears not to exist during the creation grace period we should return early.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(obj, time.Now())\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithCreationGracePeriod(1 * time.Minute),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExternalDeleteError\": {\n\t\t\treason: \"Errors deleting the external resource should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errReconcileDelete)).WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Deleting().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"An error deleting an external resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDeleteFn: func(_ context.Context, _ resource.Managed) (ExternalDelete, error) {\n\t\t\t\t\t\t\t\treturn ExternalDelete{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExternalDeleteSuccessful\": {\n\t\t\treason: \"A deleted managed resource with the 'delete' reclaim policy should delete its external resource then requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Deleting().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A deleted external resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDeleteFn: func(_ context.Context, _ resource.Managed) (ExternalDelete, error) {\n\t\t\t\t\t\t\t\treturn ExternalDelete{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"UnpublishConnectionDetailsDeletionPolicyDeleteError\": {\n\t\t\treason: \"Errors unpublishing connection details should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Deleting().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors unpublishing connection details should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\twithLocalConnectionPublishers(LocalConnectionPublisherFns{\n\t\t\t\t\t\tUnpublishConnectionFn: func(_ context.Context, _ resource.LocalConnectionSecretOwner, _ ConnectionDetails) error {\n\t\t\t\t\t\t\treturn errBoom\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\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"RemoveFinalizerErrorDeletionPolicyDelete\": {\n\t\t\treason: \"Errors removing the managed resource finalizer should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Deleting().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors removing the managed resource finalizer should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return errBoom }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"DeleteSuccessfulDeletionPolicyDelete\": {\n\t\t\treason: \"Successful managed resource deletion with deletion policy Delete should not trigger a requeue or status update.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: false}},\n\t\t},\n\t\t\"PublishObservationConnectionDetailsError\": {\n\t\t\treason: \"Errors publishing connection details after observation should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors publishing connection details after observation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(&NopConnector{}),\n\t\t\t\t\twithLocalConnectionPublishers(LocalConnectionPublisherFns{\n\t\t\t\t\t\tPublishConnectionFn: func(_ context.Context, _ resource.LocalConnectionSecretOwner, _ ConnectionDetails) (bool, error) {\n\t\t\t\t\t\t\treturn false, errBoom\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\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"AddFinalizerError\": {\n\t\t\treason: \"Errors adding a finalizer should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors adding a finalizer should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(&NopConnector{}),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return errBoom }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"UpdateCreatePendingError\": {\n\t\t\treason: \"Errors while updating our external-create-pending annotation should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:    modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(errBoom),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(\n\t\t\t\t\t\t\t\txpv2.Creating().WithObservedGeneration(42),\n\t\t\t\t\t\t\t\txpv2.ReconcileError(errors.Wrap(errBoom, errUpdateManaged)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors while creating an external resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCreateFn: func(_ context.Context, _ resource.Managed) (ExternalCreation, error) {\n\t\t\t\t\t\t\t\treturn ExternalCreation{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"CreateExternalError\": {\n\t\t\treason: \"Errors while creating an external resource should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:    modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateFailed(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errReconcileCreate)).WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors while creating an external resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCreateFn: func(_ context.Context, _ resource.Managed) (ExternalCreation, error) {\n\t\t\t\t\t\t\t\treturn ExternalCreation{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\t// We simulate our critical annotation update failing too here.\n\t\t\t\t\t// This is mostly just to exercise the code, which just creates a log and an event.\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return errBoom })),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"UpdateCriticalAnnotationsError\": {\n\t\t\treason: \"Errors updating critical annotations after creation should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:    modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errUpdateManagedAnnotations)).WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors updating critical annotations after creation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCreateFn: func(_ context.Context, _ resource.Managed) (ExternalCreation, error) {\n\t\t\t\t\t\t\t\treturn ExternalCreation{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return errBoom })),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"PublishCreationConnectionDetailsError\": {\n\t\t\treason: \"Errors publishing connection details after creation should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:    modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors publishing connection details after creation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCreateFn: func(_ context.Context, _ resource.Managed) (ExternalCreation, error) {\n\t\t\t\t\t\t\t\tcd := ConnectionDetails{\"create\": []byte{}}\n\t\t\t\t\t\t\t\treturn ExternalCreation{ConnectionDetails: cd}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return nil })),\n\t\t\t\t\twithLocalConnectionPublishers(LocalConnectionPublisherFns{\n\t\t\t\t\t\tPublishConnectionFn: func(_ context.Context, _ resource.LocalConnectionSecretOwner, cd ConnectionDetails) (bool, error) {\n\t\t\t\t\t\t\t// We're called after observe, create, and update\n\t\t\t\t\t\t\t// but we only want to fail when publishing details\n\t\t\t\t\t\t\t// after a creation.\n\t\t\t\t\t\t\tif _, ok := cd[\"create\"]; ok {\n\t\t\t\t\t\t\t\treturn false, errBoom\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn true, nil\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"CreateSuccessful\": {\n\t\t\treason: \"Successful managed resource creation should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:    modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Successful managed resource creation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(&NopConnector{}),\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return nil })),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"CreateSuccessfulAfterExternalCreatePendingAndDeterministicName\": {\n\t\t\treason: \"Successful managed resource creation which was previously pending and has a deterministic external name should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tasModernManaged(obj, 42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(obj, now.Time)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Successful managed resource creation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(&NopConnector{}),\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return nil })),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t\tWithDeterministicExternalName(true),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"LateInitializeUpdateError\": {\n\t\t\treason: \"Errors updating a managed resource to persist late initialized fields should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:    modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(errBoom),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errUpdateManaged)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors updating a managed resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true, ResourceLateInitialized: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ExternalResourceUpToDate\": {\n\t\t\treason: \"When the external resource exists and is up to date a requeue should be triggered after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A successful no-op reconcile should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ExternalResourceUpToDateWithJitter\": {\n\t\t\treason: \"When the external resource exists and is up to date a requeue should be triggered after a long wait with jitter added.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t\tWithPollJitterHook(time.Second),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: defaultPollInterval},\n\t\t\t\tresultCmpOpts: []cmp.Option{cmp.Comparer(func(l, r time.Duration) bool {\n\t\t\t\t\tdiff := l - r\n\t\t\t\t\tif diff < 0 {\n\t\t\t\t\t\tdiff = -diff\n\t\t\t\t\t}\n\n\t\t\t\t\treturn diff < time.Second\n\t\t\t\t})},\n\t\t\t},\n\t\t},\n\t\t\"ExternalResourceUpToDateWithPollIntervalHook\": {\n\t\t\treason: \"When the external resource exists and is up to date a requeue should be triggered after a long wait processed by the interval hook.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t\tWithPollIntervalHook(func(_ resource.Managed, pollInterval time.Duration) time.Duration {\n\t\t\t\t\t\treturn 2 * pollInterval\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: 2 * defaultPollInterval},\n\t\t\t},\n\t\t},\n\t\t\"ExternalResourceUpToDateWithMultiplePollIntervalHooks\": {\n\t\t\treason: \"When the external resource exists and is up to date a requeue should be triggered after a long wait processed by the latest interval hook.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t\tWithPollJitterHook(time.Second),\n\t\t\t\t\tWithPollIntervalHook(func(_ resource.Managed, pollInterval time.Duration) time.Duration {\n\t\t\t\t\t\treturn 2 * pollInterval\n\t\t\t\t\t}),\n\t\t\t\t\tWithPollIntervalHook(func(_ resource.Managed, pollInterval time.Duration) time.Duration {\n\t\t\t\t\t\treturn 3 * pollInterval\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: 3 * defaultPollInterval},\n\t\t\t},\n\t\t},\n\t\t\"UpdateExternalError\": {\n\t\t\treason: \"Errors while updating an external resource should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errReconcileUpdate)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors while updating an external resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"PublishUpdateConnectionDetailsError\": {\n\t\t\treason: \"Errors publishing connection details after an update should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors publishing connection details after an update should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\tcd := ConnectionDetails{\"update\": []byte{}}\n\t\t\t\t\t\t\t\treturn ExternalUpdate{ConnectionDetails: cd}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\twithLocalConnectionPublishers(LocalConnectionPublisherFns{\n\t\t\t\t\t\tPublishConnectionFn: func(_ context.Context, _ resource.LocalConnectionSecretOwner, cd ConnectionDetails) (bool, error) {\n\t\t\t\t\t\t\t// We're called after observe, create, and update\n\t\t\t\t\t\t\t// but we only want to fail when publishing details\n\t\t\t\t\t\t\t// after an update.\n\t\t\t\t\t\t\tif _, ok := cd[\"update\"]; ok {\n\t\t\t\t\t\t\t\treturn false, errBoom\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn false, nil\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"UpdateSuccessful\": {\n\t\t\treason: \"A successful managed resource update should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A successful managed resource update should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"TypedReconcilerUpdateSuccessful\": {\n\t\t\treason: \"A successful managed resource update should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A successful managed resource update should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithTypedExternalConnector(TypedExternalConnectorFn[*fake.ModernManaged](func(_ context.Context, _ *fake.ModernManaged) (TypedExternalClient[*fake.ModernManaged], error) {\n\t\t\t\t\t\tc := &TypedExternalClientFns[*fake.ModernManaged]{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ *fake.ModernManaged) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ *fake.ModernManaged) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: defaultPollInterval},\n\t\t\t},\n\t\t},\n\t\t\"ReconciliationPausedSuccessful\": {\n\t\t\treason: `If a managed resource has the pause annotation with value \"true\", there should be no further requeue requests.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetAnnotations(map[string]string{meta.AnnotationKeyReconciliationPaused: \"true\"})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetAnnotations(map[string]string{meta.AnnotationKeyReconciliationPaused: \"true\"})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcilePaused().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `If managed resource has the pause annotation with value \"true\", it should acquire \"Synced\" status condition with the status \"False\" and the reason \"ReconcilePaused\".`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{}},\n\t\t},\n\t\t\"ManagementPolicyReconciliationPausedSuccessful\": {\n\t\t\treason: `If a managed resource has the pause annotation with value \"true\", there should be no further requeue requests.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcilePaused().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `If managed resource has the pause annotation with value \"true\", it should acquire \"Synced\" status condition with the status \"False\" and the reason \"ReconcilePaused\".`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{}},\n\t\t},\n\t\t\"ReconciliationResumes\": {\n\t\t\treason: `If a managed resource has the pause annotation with some value other than \"true\" and the Synced=False/ReconcilePaused status condition, reconciliation should resume with requeueing.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetAnnotations(map[string]string{meta.AnnotationKeyReconciliationPaused: \"false\"})\n\t\t\t\t\t\t\tmg.SetConditions(xpv2.ReconcilePaused())\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetAnnotations(map[string]string{meta.AnnotationKeyReconciliationPaused: \"false\"})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `Managed resource should acquire Synced=False/ReconcileSuccess status condition after a resume.`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ReconciliationPausedError\": {\n\t\t\treason: `If a managed resource has the pause annotation with value \"true\" and the status update due to reconciliation being paused fails, error should be reported causing an exponentially backed-off requeue.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetAnnotations(map[string]string{meta.AnnotationKeyReconciliationPaused: \"true\"})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\treturn errBoom\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t},\n\t\t\twant: want{err: errors.Wrap(errBoom, errUpdateManagedStatus)},\n\t\t},\n\t\t\"ManagementPoliciesUsedButNotEnabled\": {\n\t\t\treason: `If management policies tried to be used without enabling the feature, we should throw an error.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionCreate})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionCreate})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(fmt.Errorf(errFmtManagementPolicyNonDefault, xpv2.ManagementPolicies{xpv2.ManagementActionCreate})).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `If managed resource has a non default management policy but feature not enabled, it should return a proper error.`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{}},\n\t\t},\n\t\t\"ManagementPolicyNotSupported\": {\n\t\t\treason: `If an unsupported management policy is used, we should throw an error.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionCreate})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionCreate})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(fmt.Errorf(errFmtManagementPolicyNotSupported, xpv2.ManagementPolicies{xpv2.ManagementActionCreate})).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `If managed resource has non supported management policy, it should return a proper error.`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{}},\n\t\t},\n\t\t\"CustomManagementPolicyNotSupported\": {\n\t\t\treason: `If a custom unsupported management policy is used, we should throw an error.`,\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(fmt.Errorf(errFmtManagementPolicyNotSupported, xpv2.ManagementPolicies{xpv2.ManagementActionAll})).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `If managed resource has non supported management policy, it should return a proper error.`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReconcilerSupportedManagementPolicies([]sets.Set[xpv2.ManagementAction]{sets.New(xpv2.ManagementActionObserve)}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{}},\n\t\t},\n\t\t\"ObserveOnlyResourceDoesNotExist\": {\n\t\t\treason: \"With only Observe management action, observing a resource that does not exist should be reported as a conditioned status error.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errors.Wrap(errors.New(errExternalResourceNotExist), errReconcileObserve)).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Resource does not exist should be reported as a conditioned status when ObserveOnly.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ObserveOnlyPublishConnectionDetailsError\": {\n\t\t\treason: \"With Observe, errors publishing connection details after observation should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileError(errBoom).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors publishing connection details after observation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\twithLocalConnectionPublishers(LocalConnectionPublisherFns{\n\t\t\t\t\t\tPublishConnectionFn: func(_ context.Context, _ resource.LocalConnectionSecretOwner, _ ConnectionDetails) (bool, error) {\n\t\t\t\t\t\t\treturn false, errBoom\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\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ObserveOnlySuccessfulObserve\": {\n\t\t\treason: \"With Observe, a successful managed resource observe should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"With ObserveOnly, a successful managed resource observation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\twithLocalConnectionPublishers(LocalConnectionPublisherFns{\n\t\t\t\t\t\tPublishConnectionFn: func(_ context.Context, _ resource.LocalConnectionSecretOwner, _ ConnectionDetails) (bool, error) {\n\t\t\t\t\t\t\treturn false, nil\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ManagementPolicyAllCreateSuccessful\": {\n\t\t\treason: \"Successful managed resource creation using management policy all should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Successful managed resource creation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(&NopConnector{}),\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return nil })),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ManagementPolicyCreateCreateSuccessful\": {\n\t\t\treason: \"Successful managed resource creation using management policy Create should trigger a requeue after a short wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\t\t\t\t\t\t\tmeta.SetExternalCreatePending(want, time.Now())\n\t\t\t\t\t\t\tmeta.SetExternalCreateSucceeded(want, time.Now())\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\t\t\t\t\t\t\twant.SetConditions(xpv2.Creating().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Successful managed resource creation should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(&NopConnector{}),\n\t\t\t\t\tWithCriticalAnnotationUpdater(CriticalAnnotationUpdateFn(func(_ context.Context, _ client.Object) error { return nil })),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{Requeue: true}},\n\t\t},\n\t\t\"ManagementPolicyImmutable\": {\n\t\t\treason: \"Successful reconciliation skipping update should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionLateInitialize, xpv2.ManagementActionCreate, xpv2.ManagementActionDelete})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(errBoom),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionLateInitialize, xpv2.ManagementActionCreate, xpv2.ManagementActionDelete})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := `Managed resource should acquire Synced=False/ReconcileSuccess status condition.`\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ManagementPolicyAllUpdateSuccessful\": {\n\t\t\treason: \"A successful managed resource update using management policies should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42).WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A successful managed resource update should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ManagementPolicyUpdateUpdateSuccessful\": {\n\t\t\treason: \"A successful managed resource update using management policies should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"A successful managed resource update should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ManagementPolicySkipLateInitialize\": {\n\t\t\treason: \"Should skip updating a managed resource to persist late initialized fields and should trigger a requeue after a long wait.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionUpdate, xpv2.ManagementActionCreate, xpv2.ManagementActionDelete})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(errBoom),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\twant := newModernManaged(42)\n\t\t\t\t\t\t\twant.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionUpdate, xpv2.ManagementActionCreate, xpv2.ManagementActionDelete})\n\t\t\t\t\t\t\twant.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))\n\n\t\t\t\t\t\t\tif diff := cmp.Diff(want, obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\t\t\t\t\treason := \"Errors updating a managed resource should be reported as a conditioned status.\"\n\t\t\t\t\t\t\t\tt.Errorf(\"\\nReason: %s\\n-want, +got:\\n%s\", reason, diff)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithInitializers(),\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true, ResourceLateInitialized: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ObserveAndLateInitializePolicy\": {\n\t\t\treason: \"If management policy is set to Observe and LateInitialize, reconciliation should proceed\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionLateInitialize})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReconcilerSupportedManagementPolicies(defaultSupportedManagementPolicies()),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t\t\"ObserveUpdateAndLateInitializePolicy\": {\n\t\t\treason: \"If management policy is set to Observe, Update and LateInitialize, reconciliation should proceed\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetManagementPolicies(xpv2.ManagementPolicies{\n\t\t\t\t\t\t\t\txpv2.ManagementActionObserve,\n\t\t\t\t\t\t\t\txpv2.ManagementActionUpdate,\n\t\t\t\t\t\t\t\txpv2.ManagementActionLateInitialize,\n\t\t\t\t\t\t\t})\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithManagementPolicies(),\n\t\t\t\t\tWithReconcilerSupportedManagementPolicies(defaultSupportedManagementPolicies()),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{result: reconcile.Result{RequeueAfter: defaultPollInterval}},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewReconciler(tc.args.m, tc.args.mg, tc.args.o...)\n\n\t\t\tgot, err := r.Reconcile(context.Background(), reconcile.Request{})\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nr.Reconcile(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.result, got, tc.want.resultCmpOpts...); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nr.Reconcile(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestManagementPoliciesResolverIsPaused(t *testing.T) {\n\ttype args struct {\n\t\tenabled bool\n\t\tpolicy  xpv2.ManagementPolicies\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   bool\n\t}{\n\t\t\"Disabled\": {\n\t\t\treason: \"Should return false if management policies are disabled\",\n\t\t\targs: args{\n\t\t\t\tenabled: false,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t\t\"EnabledEmptyPolicies\": {\n\t\t\treason: \"Should return true if the management policies are enabled and empty\",\n\t\t\targs: args{\n\t\t\t\tenabled: true,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"EnabledNonEmptyPolicies\": {\n\t\t\treason: \"Should return true if the management policies are enabled and non empty\",\n\t\t\targs: args{\n\t\t\t\tenabled: true,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewManagementPoliciesResolver(tc.args.enabled, tc.args.policy)\n\t\t\tif diff := cmp.Diff(tc.want, r.IsPaused()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nIsPaused(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestManagementPoliciesResolverValidate(t *testing.T) {\n\ttype args struct {\n\t\tenabled bool\n\t\tpolicy  xpv2.ManagementPolicies\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   error\n\t}{\n\t\t\"Enabled\": {\n\t\t\treason: \"Should return nil if the management policy is enabled.\",\n\t\t\targs: args{\n\t\t\t\tenabled: true,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"DisabledNonDefault\": {\n\t\t\treason: \"Should return error if the management policy is non-default and disabled.\",\n\t\t\targs: args{\n\t\t\t\tenabled: false,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{xpv2.ManagementActionCreate},\n\t\t\t},\n\t\t\twant: fmt.Errorf(errFmtManagementPolicyNonDefault, []xpv2.ManagementAction{xpv2.ManagementActionCreate}),\n\t\t},\n\t\t\"DisabledDefault\": {\n\t\t\treason: \"Should return nil if the management policy is default and disabled.\",\n\t\t\targs: args{\n\t\t\t\tenabled: false,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"EnabledSupported\": {\n\t\t\treason: \"Should return nil if the management policy is supported.\",\n\t\t\targs: args{\n\t\t\t\tenabled: true,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"EnabledNotSupported\": {\n\t\t\treason: \"Should return err if the management policy is not supported.\",\n\t\t\targs: args{\n\t\t\t\tenabled: true,\n\t\t\t\tpolicy:  xpv2.ManagementPolicies{xpv2.ManagementActionDelete},\n\t\t\t},\n\t\t\twant: fmt.Errorf(errFmtManagementPolicyNotSupported, []xpv2.ManagementAction{xpv2.ManagementActionDelete}),\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewManagementPoliciesResolver(tc.args.enabled, tc.args.policy)\n\t\t\tif diff := cmp.Diff(tc.want, r.Validate(), test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nIsNonDefault(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestManagementPoliciesResolverShouldCreate(t *testing.T) {\n\ttype args struct {\n\t\tmanagementPoliciesEnabled bool\n\t\tpolicy                    xpv2.ManagementPolicies\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   bool\n\t}{\n\t\t\"ManagementPoliciesDisabled\": {\n\t\t\treason: \"Should return true if management policies are disabled\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: false,\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasCreate\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action Create\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionCreate},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasCreateAll\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action All\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledActionNotAllowed\": {\n\t\t\treason: \"Should return false if management policies are enabled and managementPolicies does not have Create\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionObserve},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewManagementPoliciesResolver(tc.args.managementPoliciesEnabled, tc.args.policy)\n\t\t\tif diff := cmp.Diff(tc.want, r.ShouldCreate()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nShouldCreate(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestManagementPoliciesResolverShouldUpdate(t *testing.T) {\n\ttype args struct {\n\t\tmanagementPoliciesEnabled bool\n\t\tpolicy                    xpv2.ManagementPolicies\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   bool\n\t}{\n\t\t\"ManagementPoliciesDisabled\": {\n\t\t\treason: \"Should return true if management policies are disabled\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: false,\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasUpdate\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action Update\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionUpdate},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasUpdateAll\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action All\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledActionNotAllowed\": {\n\t\t\treason: \"Should return false if management policies are enabled and managementPolicies does not have Update\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionObserve},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewManagementPoliciesResolver(tc.args.managementPoliciesEnabled, tc.args.policy)\n\t\t\tif diff := cmp.Diff(tc.want, r.ShouldUpdate()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nShouldUpdate(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestManagementPoliciesResolverShouldLateInitialize(t *testing.T) {\n\ttype args struct {\n\t\tmanagementPoliciesEnabled bool\n\t\tpolicy                    xpv2.ManagementPolicies\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   bool\n\t}{\n\t\t\"ManagementPoliciesDisabled\": {\n\t\t\treason: \"Should return true if management policies are disabled\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: false,\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasLateInitialize\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action LateInitialize\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionLateInitialize},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasLateInitializeAll\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action All\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledActionNotAllowed\": {\n\t\t\treason: \"Should return false if management policies are enabled and managementPolicies does not have LateInitialize\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionObserve},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewManagementPoliciesResolver(tc.args.managementPoliciesEnabled, tc.args.policy)\n\t\t\tif diff := cmp.Diff(tc.want, r.ShouldLateInitialize()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nShouldLateInitialize(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestManagementPoliciesResolverOnlyObserve(t *testing.T) {\n\ttype args struct {\n\t\tmanagementPoliciesEnabled bool\n\t\tpolicy                    xpv2.ManagementPolicies\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   bool\n\t}{\n\t\t\"ManagementPoliciesDisabled\": {\n\t\t\treason: \"Should return false if management policies are disabled\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: false,\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasOnlyObserve\": {\n\t\t\treason: \"Should return true if management policies are enabled and managementPolicies has action LateInitialize\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionObserve},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ManagementPoliciesEnabledHasMultipleActions\": {\n\t\t\treason: \"Should return false if management policies are enabled and managementPolicies has multiple actions\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tpolicy:                    xpv2.ManagementPolicies{xpv2.ManagementActionLateInitialize, xpv2.ManagementActionObserve},\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewManagementPoliciesResolver(tc.args.managementPoliciesEnabled, tc.args.policy)\n\t\t\tif diff := cmp.Diff(tc.want, r.ShouldOnlyObserve()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nShouldOnlyObserve(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestShouldDelete(t *testing.T) {\n\ttype args struct {\n\t\tmanagementPoliciesEnabled bool\n\t\tmanaged                   resource.Managed\n\t}\n\n\ttype want struct {\n\t\tdelete bool\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"ManagementPoliciesDisabled\": {\n\t\t\treason: \"Should delete if management policies are disabled\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: false,\n\t\t\t\tmanaged:                   &fake.ModernManaged{},\n\t\t\t},\n\t\t\twant: want{delete: true},\n\t\t},\n\t\t\"DeleteManagementActionAll\": {\n\t\t\treason: \"Should delete if management policies are enabled and management policy is set to All.\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tmanaged: &fake.ModernManaged{\n\t\t\t\t\tManageable: fake.Manageable{\n\t\t\t\t\t\tPolicy: xpv2.ManagementPolicies{xpv2.ManagementActionAll},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{delete: true},\n\t\t},\n\t\t\"ManagementActionDelete\": {\n\t\t\treason: \"Should delete if management policies are enabled and management policy has action Delete.\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tmanaged: &fake.ModernManaged{\n\t\t\t\t\tManageable: fake.Manageable{\n\t\t\t\t\t\tPolicy: xpv2.ManagementPolicies{xpv2.ManagementActionDelete},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{delete: true},\n\t\t},\n\t\t\"ManagementActionNoDelete\": {\n\t\t\treason: \"Should orphan if management policies are enabled and management policy does not have action Delete.\",\n\t\t\targs: args{\n\t\t\t\tmanagementPoliciesEnabled: true,\n\t\t\t\tmanaged: &fake.ModernManaged{\n\t\t\t\t\tManageable: fake.Manageable{\n\t\t\t\t\t\tPolicy: xpv2.ManagementPolicies{xpv2.ManagementActionObserve},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{delete: false},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewManagementPoliciesResolver(tc.args.managementPoliciesEnabled, tc.args.managed.GetManagementPolicies())\n\t\t\tif diff := cmp.Diff(tc.want.delete, r.ShouldDelete()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nShouldDelete(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReconcilerChangeLogs(t *testing.T) {\n\ttype args struct {\n\t\tm  manager.Manager\n\t\tmg resource.ManagedKind\n\t\to  []ReconcilerOption\n\t\tc  *changeLogServiceClient\n\t}\n\n\ttype want struct {\n\t\tcallCount  int\n\t\topType     v1alpha1.OperationType\n\t\terrMessage string\n\t}\n\n\tnow := metav1.Now()\n\terrBoom := errors.New(\"boom\")\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"CreateSuccessfulWithChangeLogs\": {\n\t\t\treason: \"Successful managed resource creation should send a create change log entry when change logs are enabled.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:          modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error { return nil }),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\t// resource doesn't exist, which should trigger a create operation\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCreateFn: func(_ context.Context, _ resource.Managed) (ExternalCreation, error) {\n\t\t\t\t\t\t\t\treturn ExternalCreation{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t\tc: &changeLogServiceClient{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcallCount:  1,\n\t\t\t\topType:     v1alpha1.OperationType_OPERATION_TYPE_CREATE,\n\t\t\t\terrMessage: \"\",\n\t\t\t},\n\t\t},\n\t\t\"CreateFailureWithChangeLogs\": {\n\t\t\treason: \"Failed managed resource creation should send a create change log entry with the error when change logs are enabled.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:          modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error { return nil }),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\t// resource doesn't exist, which should trigger a create operation\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: false, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tCreateFn: func(_ context.Context, _ resource.Managed) (ExternalCreation, error) {\n\t\t\t\t\t\t\t\t// return an error from Create to simulate a failed creation\n\t\t\t\t\t\t\t\treturn ExternalCreation{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t\tc: &changeLogServiceClient{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcallCount:  1,\n\t\t\t\topType:     v1alpha1.OperationType_OPERATION_TYPE_CREATE,\n\t\t\t\terrMessage: errBoom.Error(),\n\t\t\t},\n\t\t},\n\t\t\"UpdateSuccessfulWithChangeLogs\": {\n\t\t\treason: \"Successful managed resource update should send an update change log entry when change logs are enabled.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:          modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error { return nil }),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\t// resource exists but isn't up to date, which should trigger an update operation\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t\tc: &changeLogServiceClient{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcallCount:  1,\n\t\t\t\topType:     v1alpha1.OperationType_OPERATION_TYPE_UPDATE,\n\t\t\t\terrMessage: \"\",\n\t\t\t},\n\t\t},\n\t\t\"UpdateFailureWithChangeLogs\": {\n\t\t\treason: \"Failed managed resource update should send an update change log entry with the error when change logs are enabled.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:          modernManagedMockGetFn(nil, 42),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error { return nil }),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\t// resource exists but isn't up to date, which should trigger an update operation\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tUpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {\n\t\t\t\t\t\t\t\t// return an error from Update to simulate a failed update\n\t\t\t\t\t\t\t\treturn ExternalUpdate{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t\tc: &changeLogServiceClient{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcallCount:  1,\n\t\t\t\topType:     v1alpha1.OperationType_OPERATION_TYPE_UPDATE,\n\t\t\t\terrMessage: errBoom.Error(),\n\t\t\t},\n\t\t},\n\t\t\"DeleteSuccessfulWithChangeLogs\": {\n\t\t\treason: \"Successful managed resource delete should send a delete change log entry when change logs are enabled.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\t// set a deletion timestamp, which should trigger a delete operation\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error { return nil }),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\t// resource exists but we set a deletion timestamp above, which should trigger a delete operation\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDeleteFn: func(_ context.Context, _ resource.Managed) (ExternalDelete, error) {\n\t\t\t\t\t\t\t\treturn ExternalDelete{}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t\tc: &changeLogServiceClient{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcallCount:  1,\n\t\t\t\topType:     v1alpha1.OperationType_OPERATION_TYPE_DELETE,\n\t\t\t\terrMessage: \"\",\n\t\t\t},\n\t\t},\n\t\t\"DeleteFailureWithChangeLogs\": {\n\t\t\treason: \"Failed managed resource delete should send a delete change log entry with the error when change logs are enabled.\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\t// set a deletion timestamp, which should trigger a delete operation\n\t\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\t\tmg.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error { return nil }),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t\t},\n\t\t\t\tmg: resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\to: []ReconcilerOption{\n\t\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\t\tc := &ExternalClientFns{\n\t\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\t\t// resource exists but we set a deletion timestamp above, which should trigger a delete operation\n\t\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDeleteFn: func(_ context.Context, _ resource.Managed) (ExternalDelete, error) {\n\t\t\t\t\t\t\t\t// return an error from Delete to simulate a failed delete\n\t\t\t\t\t\t\t\treturn ExternalDelete{}, errBoom\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn c, nil\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t\tc: &changeLogServiceClient{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcallCount:  1,\n\t\t\t\topType:     v1alpha1.OperationType_OPERATION_TYPE_DELETE,\n\t\t\t\terrMessage: errBoom.Error(),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.args.o = append(tc.args.o, WithChangeLogger(NewGRPCChangeLogger(tc.args.c, WithProviderVersion(\"provider-cool:v9.99.999\"))))\n\t\t\tr := NewReconciler(tc.args.m, tc.args.mg, tc.args.o...)\n\t\t\tr.Reconcile(context.Background(), reconcile.Request{})\n\n\t\t\tif diff := cmp.Diff(tc.want.callCount, len(tc.args.c.requests)); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nr.Reconcile(...): -want callCount, +got callCount:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.opType, tc.args.c.requests[0].GetEntry().GetOperation()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nr.Reconcile(...): -want opType, +got opType:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.errMessage, tc.args.c.requests[0].GetEntry().GetErrorMessage()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nReason: %s\\nr.Reconcile(...): -want errMessage, +got errMessage:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestEffectivePollInterval(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason          string\n\t\tpollInterval    time.Duration\n\t\tminPollInterval time.Duration\n\t\tannotation      string\n\t\twant            time.Duration\n\t}{\n\t\t\"NoAnnotationReturnsDefault\": {\n\t\t\treason:          \"When no annotation is set, the default poll interval is returned.\",\n\t\t\tpollInterval:    10 * time.Minute,\n\t\t\tminPollInterval: 1 * time.Second,\n\t\t\twant:            10 * time.Minute,\n\t\t},\n\t\t\"ValidAnnotationAboveMinimumReturnsAnnotation\": {\n\t\t\treason:          \"When the annotation is at or above the minimum, it overrides the default.\",\n\t\t\tpollInterval:    10 * time.Minute,\n\t\t\tminPollInterval: 1 * time.Second,\n\t\t\tannotation:      \"24h\",\n\t\t\twant:            24 * time.Hour,\n\t\t},\n\t\t\"ValidAnnotationAtMinimumReturnsAnnotation\": {\n\t\t\treason:          \"When the annotation equals the minimum exactly, it is returned as-is.\",\n\t\t\tpollInterval:    10 * time.Minute,\n\t\t\tminPollInterval: 30 * time.Second,\n\t\t\tannotation:      \"30s\",\n\t\t\twant:            30 * time.Second,\n\t\t},\n\t\t\"ValidAnnotationBelowMinimumReturnsMinimum\": {\n\t\t\treason:          \"When the annotation is below the minimum, the minimum is returned.\",\n\t\t\tpollInterval:    10 * time.Minute,\n\t\t\tminPollInterval: 30 * time.Second,\n\t\t\tannotation:      \"1s\",\n\t\t\twant:            30 * time.Second,\n\t\t},\n\t\t\"InvalidAnnotationReturnsDefault\": {\n\t\t\treason:          \"When the annotation cannot be parsed, the default poll interval is returned.\",\n\t\t\tpollInterval:    5 * time.Minute,\n\t\t\tminPollInterval: 1 * time.Second,\n\t\t\tannotation:      \"not-a-duration\",\n\t\t\twant:            5 * time.Minute,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := &Reconciler{\n\t\t\t\tpollInterval:    tc.pollInterval,\n\t\t\t\tminPollInterval: tc.minPollInterval,\n\t\t\t}\n\n\t\t\tmg := &fake.ModernManaged{}\n\t\t\tif tc.annotation != \"\" {\n\t\t\t\tmg.SetAnnotations(map[string]string{\n\t\t\t\t\tmeta.AnnotationKeyPollInterval: tc.annotation,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tgot := r.effectivePollInterval(mg)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\neffectivePollInterval(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReconcilePollIntervalAnnotation(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason          string\n\t\tpollInterval    time.Duration\n\t\tminPollInterval time.Duration\n\t\tannotation      string\n\t\twantApprox      time.Duration\n\t\twantTolerance   time.Duration\n\t}{\n\t\t\"AnnotationOverridesPollInterval\": {\n\t\t\treason:          \"When a valid poll interval annotation is set, it should override the controller-level poll interval.\",\n\t\t\tpollInterval:    1 * time.Minute,\n\t\t\tminPollInterval: 1 * time.Second,\n\t\t\tannotation:      \"24h\",\n\t\t\twantApprox:      24 * time.Hour,\n\t\t\twantTolerance:   1 * time.Second,\n\t\t},\n\t\t\"InvalidAnnotationFallsBack\": {\n\t\t\treason:          \"When an invalid poll interval annotation is set, the controller-level poll interval should be used.\",\n\t\t\tpollInterval:    5 * time.Minute,\n\t\t\tminPollInterval: 1 * time.Second,\n\t\t\tannotation:      \"not-a-duration\",\n\t\t\twantApprox:      5 * time.Minute,\n\t\t\twantTolerance:   1 * time.Second,\n\t\t},\n\t\t\"AnnotationBelowMinimumClampsToMin\": {\n\t\t\treason:          \"When a poll interval annotation is below the configured minimum, the minimum should be used.\",\n\t\t\tpollInterval:    10 * time.Minute,\n\t\t\tminPollInterval: 30 * time.Second,\n\t\t\tannotation:      \"1s\",\n\t\t\twantApprox:      30 * time.Second,\n\t\t\twantTolerance:   1 * time.Second,\n\t\t},\n\t\t\"NoAnnotationUsesDefault\": {\n\t\t\treason:          \"When no poll interval annotation is set, the controller-level poll interval should be used.\",\n\t\t\tpollInterval:    10 * time.Minute,\n\t\t\tminPollInterval: 1 * time.Second,\n\t\t\twantApprox:      10 * time.Minute,\n\t\t\twantTolerance:   1 * time.Second,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewReconciler(&fake.Manager{\n\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\tif tc.annotation != \"\" {\n\t\t\t\t\t\t\tmg.SetAnnotations(map[string]string{\n\t\t\t\t\t\t\t\tmeta.AnnotationKeyPollInterval: tc.annotation,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}),\n\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t}, resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\tWithPollInterval(tc.pollInterval),\n\t\t\t\tWithMinPollInterval(tc.minPollInterval),\n\t\t\t\tWithInitializers(),\n\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\treturn &ExternalClientFns{\n\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t},\n\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error { return nil },\n\t\t\t\t\t}, nil\n\t\t\t\t})),\n\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t)\n\n\t\t\tgot, err := r.Reconcile(context.Background(), reconcile.Request{})\n\t\t\tif diff := cmp.Diff(nil, err, cmpopts.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"\\n%s\\nr.Reconcile(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tdiff := got.RequeueAfter - tc.wantApprox\n\t\t\tif diff < 0 {\n\t\t\t\tdiff = -diff\n\t\t\t}\n\t\t\tif diff > tc.wantTolerance {\n\t\t\t\tt.Errorf(\"\\n%s\\nr.Reconcile(...): want RequeueAfter ~%v (±%v), got %v\",\n\t\t\t\t\ttc.reason, tc.wantApprox, tc.wantTolerance, got.RequeueAfter)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReconcileRequestAnnotation(t *testing.T) {\n\ttype want struct {\n\t\tresult reconcile.Result\n\t\terr    error\n\t\ttoken  string\n\t}\n\n\tcases := map[string]struct {\n\t\treason     string\n\t\tannotation string\n\t\thandled    string\n\t\twant       want\n\t}{\n\t\t\"NewReconcileRequestRecordsToken\": {\n\t\t\treason:     \"A new reconcile-requested-at token should be recorded in status.lastHandledReconcileAt.\",\n\t\t\tannotation: \"1705312200\",\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: defaultPollInterval},\n\t\t\t\ttoken:  \"1705312200\",\n\t\t\t},\n\t\t},\n\t\t\"AlreadyHandledReconcileRequestIsNoOp\": {\n\t\t\treason:     \"When the token matches lastHandledReconcileAt, status should remain unchanged.\",\n\t\t\tannotation: \"already-handled\",\n\t\t\thandled:    \"already-handled\",\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: defaultPollInterval},\n\t\t\t\ttoken:  \"already-handled\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewReconciler(&fake.Manager{\n\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\tmg := asModernManaged(obj, 42)\n\t\t\t\t\t\tmg.SetAnnotations(map[string]string{\n\t\t\t\t\t\t\tmeta.AnnotationKeyReconcileRequestedAt: tc.annotation,\n\t\t\t\t\t\t})\n\t\t\t\t\t\tif tc.handled != \"\" {\n\t\t\t\t\t\t\tmg.SetLastHandledReconcileAt(tc.handled)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}),\n\t\t\t\t\tMockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\t\t\t\t\tmg := obj.(*fake.ModernManaged)\n\t\t\t\t\t\tgot := mg.GetLastHandledReconcileAt()\n\t\t\t\t\t\tif diff := cmp.Diff(tc.want.token, got); diff != \"\" {\n\t\t\t\t\t\t\tt.Errorf(\"\\n%s\\nstatus.lastHandledReconcileAt: -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\tScheme: fake.SchemeWith(&fake.ModernManaged{}),\n\t\t\t}, resource.ManagedKind(fake.GVK(&fake.ModernManaged{})),\n\t\t\t\tWithInitializers(),\n\t\t\t\tWithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.Managed) error { return nil })),\n\t\t\t\tWithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {\n\t\t\t\t\treturn &ExternalClientFns{\n\t\t\t\t\t\tObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {\n\t\t\t\t\t\t\treturn ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil\n\t\t\t\t\t\t},\n\t\t\t\t\t\tDisconnectFn: func(_ context.Context) error { return nil },\n\t\t\t\t\t}, nil\n\t\t\t\t})),\n\t\t\t\tWithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),\n\t\t\t)\n\n\t\t\tgot, err := r.Reconcile(context.Background(), reconcile.Request{})\n\t\t\tif diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nr.Reconcile(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t\tif diff := cmp.Diff(tc.want.result, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nr.Reconcile(...): -want result, +got result:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc asModernManaged(obj client.Object, generation int64) *fake.ModernManaged {\n\tmg := obj.(*fake.ModernManaged)\n\tmg.Generation = generation\n\n\treturn mg\n}\n\nfunc newModernManaged(generation int64) *fake.ModernManaged {\n\tmg := &fake.ModernManaged{}\n\tmg.Generation = generation\n\n\treturn mg\n}\n\nfunc modernManagedMockGetFn(err error, generation int64) test.MockGetFn {\n\treturn test.NewMockGetFn(err, func(obj client.Object) error {\n\t\tasModernManaged(obj, generation)\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "pkg/reconciler/managed/reconciler_typed.go",
    "content": "package managed\n\nimport (\n\t\"context\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n)\n\nconst errFmtUnexpectedObjectType = \"unexpected object type %T\"\n\n// typedExternalConnectDisconnectorWrapper wraps a TypedExternalConnector to a\n// common ExternalConnector.\ntype typedExternalConnectDisconnectorWrapper[managed resource.Managed] struct {\n\tc TypedExternalConnectDisconnector[managed]\n}\n\nfunc (c *typedExternalConnectDisconnectorWrapper[managed]) Connect(ctx context.Context, mg resource.Managed) (ExternalClient, error) {\n\tcr, ok := mg.(managed)\n\tif !ok {\n\t\treturn nil, errors.Errorf(errFmtUnexpectedObjectType, mg)\n\t}\n\n\texternal, err := c.c.Connect(ctx, cr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &typedExternalClientWrapper[managed]{c: external}, nil\n}\n\nfunc (c *typedExternalConnectDisconnectorWrapper[managed]) Disconnect(ctx context.Context) error {\n\treturn c.c.Disconnect(ctx)\n}\n\n// typedExternalClientWrapper wraps a TypedExternalClient to a common\n// ExternalClient.\ntype typedExternalClientWrapper[managed resource.Managed] struct {\n\tc TypedExternalClient[managed]\n}\n\nfunc (c *typedExternalClientWrapper[managed]) Observe(ctx context.Context, mg resource.Managed) (ExternalObservation, error) {\n\tcr, ok := mg.(managed)\n\tif !ok {\n\t\treturn ExternalObservation{}, errors.Errorf(errFmtUnexpectedObjectType, mg)\n\t}\n\n\treturn c.c.Observe(ctx, cr)\n}\n\nfunc (c *typedExternalClientWrapper[managed]) Create(ctx context.Context, mg resource.Managed) (ExternalCreation, error) {\n\tcr, ok := mg.(managed)\n\tif !ok {\n\t\treturn ExternalCreation{}, errors.Errorf(errFmtUnexpectedObjectType, mg)\n\t}\n\n\treturn c.c.Create(ctx, cr)\n}\n\nfunc (c *typedExternalClientWrapper[managed]) Update(ctx context.Context, mg resource.Managed) (ExternalUpdate, error) {\n\tcr, ok := mg.(managed)\n\tif !ok {\n\t\treturn ExternalUpdate{}, errors.Errorf(errFmtUnexpectedObjectType, mg)\n\t}\n\n\treturn c.c.Update(ctx, cr)\n}\n\nfunc (c *typedExternalClientWrapper[managed]) Delete(ctx context.Context, mg resource.Managed) (ExternalDelete, error) {\n\tcr, ok := mg.(managed)\n\tif !ok {\n\t\treturn ExternalDelete{}, errors.Errorf(errFmtUnexpectedObjectType, mg)\n\t}\n\n\treturn c.c.Delete(ctx, cr)\n}\n\nfunc (c *typedExternalClientWrapper[managed]) Disconnect(ctx context.Context) error {\n\treturn c.c.Disconnect(ctx)\n}\n"
  },
  {
    "path": "pkg/reconciler/providerconfig/reconciler.go",
    "content": "/*\nCopyright 2020 The Crossplane 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// Package providerconfig provides a reconciler that manages the lifecycle of a\n// ProviderConfig.\npackage providerconfig\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"time\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/manager\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/event\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/logging\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n)\n\nconst (\n\tfinalizer = \"in-use.crossplane.io\"\n\tshortWait = 30 * time.Second\n\ttimeout   = 2 * time.Minute\n\n\terrGetPC        = \"cannot get ProviderConfig\"\n\terrListPCUs     = \"cannot list ProviderConfigUsages\"\n\terrDeletePCU    = \"cannot delete ProviderConfigUsage\"\n\terrUpdate       = \"cannot update ProviderConfig\"\n\terrUpdateStatus = \"cannot update ProviderConfig status\"\n)\n\n// Event reasons.\nconst (\n\treasonAccount event.Reason = \"UsageAccounting\"\n)\n\n// Condition types and reasons.\nconst (\n\tTypeTerminating xpv2.ConditionType   = \"Terminating\"\n\tReasonInUse     xpv2.ConditionReason = \"InUse\"\n)\n\n// Terminating indicates a ProviderConfig has been deleted, but that the\n// deletion is being blocked because it is still in use.\nfunc Terminating() xpv2.Condition {\n\treturn xpv2.Condition{\n\t\tType:               TypeTerminating,\n\t\tStatus:             corev1.ConditionTrue,\n\t\tLastTransitionTime: metav1.Now(),\n\t\tReason:             ReasonInUse,\n\t}\n}\n\n// ControllerName returns the recommended name for controllers that use this\n// package to reconcile a particular kind of managed resource.\nfunc ControllerName(kind string) string {\n\treturn \"providerconfig/\" + strings.ToLower(kind)\n}\n\n// A Reconciler reconciles managed resources by creating and managing the\n// lifecycle of an external resource, i.e. a resource in an external system such\n// as a cloud provider API. Each controller must watch the managed resource kind\n// for which it is responsible.\ntype Reconciler struct {\n\tclient client.Client\n\n\tnewConfig    func() resource.ProviderConfig\n\tnewUsageList func() resource.ProviderConfigUsageList\n\n\tlegacyPCU bool\n\n\tlog    logging.Logger\n\trecord event.Recorder\n}\n\n// A ReconcilerOption configures a Reconciler.\ntype ReconcilerOption func(*Reconciler)\n\n// WithLogger specifies how the Reconciler should log messages.\nfunc WithLogger(l logging.Logger) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.log = l\n\t}\n}\n\n// WithRecorder specifies how the Reconciler should record events.\nfunc WithRecorder(er event.Recorder) ReconcilerOption {\n\treturn func(r *Reconciler) {\n\t\tr.record = er\n\t}\n}\n\n// NewReconciler returns a Reconciler of ProviderConfigs.\nfunc NewReconciler(m manager.Manager, of resource.ProviderConfigKinds, o ...ReconcilerOption) *Reconciler {\n\tnc := func() resource.ProviderConfig {\n\t\t//nolint:forcetypeassert // If this isn't a ProviderConfig it's a programming error and we want to panic.\n\t\treturn resource.MustCreateObject(of.Config, m.GetScheme()).(resource.ProviderConfig)\n\t}\n\tnul := func() resource.ProviderConfigUsageList {\n\t\t//nolint:forcetypeassert // If this isn't a ProviderConfigUsage it's a programming error and we want to panic.\n\t\treturn resource.MustCreateObject(of.UsageList, m.GetScheme()).(resource.ProviderConfigUsageList)\n\t}\n\t_, isLegacyPCU := resource.MustCreateObject(of.Usage, m.GetScheme()).(resource.LegacyProviderConfigUsage)\n\n\t// Panic early if we've been asked to reconcile a resource kind that has not\n\t// been registered with our controller manager's scheme.\n\t_, _ = nc(), nul()\n\n\tr := &Reconciler{\n\t\tclient: m.GetClient(),\n\n\t\tnewConfig:    nc,\n\t\tnewUsageList: nul,\n\t\tlegacyPCU:    isLegacyPCU,\n\n\t\tlog:    logging.NewNopLogger(),\n\t\trecord: event.NewNopRecorder(),\n\t}\n\n\tfor _, ro := range o {\n\t\tro(r)\n\t}\n\n\treturn r\n}\n\n// Reconcile a ProviderConfig by accounting for the managed resources that are\n// using it, and ensuring it cannot be deleted until it is no longer in use.\nfunc (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {\n\tlog := r.log.WithValues(\"request\", req)\n\tlog.Debug(\"Reconciling\")\n\n\tctx, cancel := context.WithTimeout(ctx, timeout)\n\tdefer cancel()\n\n\tpc := r.newConfig()\n\tif err := r.client.Get(ctx, req.NamespacedName, pc); err != nil {\n\t\t// In case object is not found, most likely the object was deleted and\n\t\t// then disappeared while the event was in the processing queue. We\n\t\t// don't need to take any action in that case.\n\t\tlog.Debug(errGetPC, \"error\", err)\n\t\treturn reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGetPC)\n\t}\n\n\tlog = log.WithValues(\n\t\t\"uid\", pc.GetUID(),\n\t\t\"version\", pc.GetResourceVersion(),\n\t\t\"name\", pc.GetName(),\n\t\t\"namespace\", pc.GetNamespace(),\n\t)\n\n\tl := r.newUsageList()\n\n\tmatchingLabels := client.MatchingLabels{\n\t\txpv2.LabelKeyProviderName: pc.GetName(),\n\t}\n\n\tif !r.legacyPCU {\n\t\tmatchingLabels[xpv2.LabelKeyProviderKind] = pc.GetObjectKind().GroupVersionKind().Kind\n\t}\n\n\tlistOpts := []client.ListOption{matchingLabels}\n\tif pc.GetNamespace() != \"\" {\n\t\tlistOpts = append(listOpts, client.InNamespace(pc.GetNamespace()))\n\t}\n\n\tif err := r.client.List(ctx, l, listOpts...); err != nil {\n\t\tlog.Debug(errListPCUs, \"error\", err)\n\t\tr.record.Event(pc, event.Warning(reasonAccount, errors.Wrap(err, errListPCUs)))\n\n\t\treturn reconcile.Result{RequeueAfter: shortWait}, nil\n\t}\n\n\tusers := int64(len(l.GetItems()))\n\tfor _, pcu := range l.GetItems() {\n\t\tif metav1.GetControllerOf(pcu) == nil {\n\t\t\t// Usages should always have a controller reference. If this one has\n\t\t\t// none it's probably been stripped off (e.g. by a Velero restore).\n\t\t\t// We can safely delete it - it's either stale, or will be recreated\n\t\t\t// next time the relevant managed resource connects.\n\t\t\tif err := r.client.Delete(ctx, pcu); resource.IgnoreNotFound(err) != nil {\n\t\t\t\tlog.Debug(errDeletePCU, \"error\", err)\n\t\t\t\tr.record.Event(pc, event.Warning(reasonAccount, errors.Wrap(err, errDeletePCU)))\n\n\t\t\t\treturn reconcile.Result{RequeueAfter: shortWait}, nil\n\t\t\t}\n\n\t\t\tusers--\n\t\t}\n\t}\n\n\tlog = log.WithValues(\"usages\", users)\n\n\tif meta.WasDeleted(pc) {\n\t\tif users > 0 {\n\t\t\tmsg := \"Blocking deletion while usages still exist\"\n\n\t\t\tlog.Debug(msg)\n\t\t\tr.record.Event(pc, event.Warning(reasonAccount, errors.New(msg)))\n\n\t\t\t// We're watching our usages, so we'll be requeued when they go.\n\t\t\tpc.SetUsers(users)\n\t\t\tpc.SetConditions(Terminating().WithMessage(msg))\n\n\t\t\treturn reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, pc), errUpdateStatus)\n\t\t}\n\n\t\tmeta.RemoveFinalizer(pc, finalizer)\n\n\t\tif err := r.client.Update(ctx, pc); err != nil {\n\t\t\tr.log.Debug(errUpdate, \"error\", err)\n\t\t\treturn reconcile.Result{RequeueAfter: shortWait}, nil\n\t\t}\n\n\t\t// We've been deleted - there's no more work to do.\n\t\treturn reconcile.Result{Requeue: false}, nil\n\t}\n\n\tmeta.AddFinalizer(pc, finalizer)\n\n\tif err := r.client.Update(ctx, pc); err != nil {\n\t\tr.log.Debug(errUpdate, \"error\", err)\n\t\treturn reconcile.Result{RequeueAfter: shortWait}, nil\n\t}\n\n\t// There's no need to requeue explicitly - we're watching all PCs.\n\tpc.SetUsers(users)\n\n\treturn reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, pc), errUpdateStatus)\n}\n"
  },
  {
    "path": "pkg/reconciler/providerconfig/reconciler_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 providerconfig\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\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\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/manager\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\n// This can't live in fake, because it would cause an import cycle due to\n// GetItems returning managed.ProviderConfigUsage.\ntype ProviderConfigUsageList struct {\n\tclient.ObjectList\n\tItems []resource.ProviderConfigUsage\n}\n\nfunc (p *ProviderConfigUsageList) GetObjectKind() schema.ObjectKind {\n\treturn schema.EmptyObjectKind\n}\n\nfunc (p *ProviderConfigUsageList) DeepCopyObject() runtime.Object {\n\tout := &ProviderConfigUsageList{}\n\n\tj, err := json.Marshal(p) //nolint:musttag // We're just using this to round-trip convert.\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_ = json.Unmarshal(j, out) //nolint:musttag // We're just using this to round-trip convert.\n\n\treturn out\n}\n\nfunc (p *ProviderConfigUsageList) GetItems() []resource.ProviderConfigUsage {\n\treturn p.Items\n}\n\nfunc TestReconciler(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\tnow := metav1.Now()\n\tuid := types.UID(\"so-unique\")\n\tctrl := true\n\n\ttype args struct {\n\t\tm  manager.Manager\n\t\tof resource.ProviderConfigKinds\n\t}\n\n\ttype want struct {\n\t\tresult  reconcile.Result\n\t\terr     error\n\t\trequest reconcile.Request\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"GetProviderConfigError\": {\n\t\t\treason: \"Errors getting a provider config should be returned\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(errBoom),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ProviderConfig{}, &fake.ProviderConfigUsage{}, &ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t\tof: resource.ProviderConfigKinds{\n\t\t\t\t\tConfig:    fake.GVK(&fake.ProviderConfig{}),\n\t\t\t\t\tUsage:     fake.GVK(&fake.ProviderConfigUsage{}),\n\t\t\t\t\tUsageList: fake.GVK(&ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{},\n\t\t\t\terr:    errors.Wrap(errBoom, errGetPC),\n\t\t\t},\n\t\t},\n\t\t\"ProviderConfigNotFound\": {\n\t\t\treason: \"We should return without requeueing if the provider config no longer exists\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, \"\")),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ProviderConfig{}, &fake.ProviderConfigUsage{}, &ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t\tof: resource.ProviderConfigKinds{\n\t\t\t\t\tConfig:    fake.GVK(&fake.ProviderConfig{}),\n\t\t\t\t\tUsage:     fake.GVK(&fake.ProviderConfigUsage{}),\n\t\t\t\t\tUsageList: fake.GVK(&ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{},\n\t\t\t\terr:    nil,\n\t\t\t},\n\t\t},\n\t\t\"ListProviderConfigUsageError\": {\n\t\t\treason: \"We should requeue after a short wait if we encounter an error listing provider config usages\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:  test.NewMockGetFn(nil),\n\t\t\t\t\t\tMockList: test.NewMockListFn(errBoom),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ProviderConfig{}, &fake.ProviderConfigUsage{}, &ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t\tof: resource.ProviderConfigKinds{\n\t\t\t\t\tConfig:    fake.GVK(&fake.ProviderConfig{}),\n\t\t\t\t\tUsage:     fake.GVK(&fake.ProviderConfigUsage{}),\n\t\t\t\t\tUsageList: fake.GVK(&ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: shortWait},\n\t\t\t},\n\t\t},\n\t\t\"DeleteProviderConfigUsageError\": {\n\t\t\treason: \"We should requeue after a short wait if we encounter an error deleting a provider config usage\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil),\n\t\t\t\t\t\tMockList: test.NewMockListFn(nil, func(obj client.ObjectList) error {\n\t\t\t\t\t\t\tl := obj.(*ProviderConfigUsageList)\n\t\t\t\t\t\t\tl.Items = []resource.ProviderConfigUsage{\n\t\t\t\t\t\t\t\t&fake.ProviderConfigUsage{},\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockDelete: test.NewMockDeleteFn(errBoom),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ProviderConfig{}, &fake.ProviderConfigUsage{}, &ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t\tof: resource.ProviderConfigKinds{\n\t\t\t\t\tConfig:    fake.GVK(&fake.ProviderConfig{}),\n\t\t\t\t\tUsage:     fake.GVK(&fake.ProviderConfigUsage{}),\n\t\t\t\t\tUsageList: fake.GVK(&ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: shortWait},\n\t\t\t},\n\t\t},\n\t\t\"BlockDeleteWhileInUse\": {\n\t\t\treason: \"We should return without requeueing if the provider config is still in use\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tpc := obj.(*fake.ProviderConfig)\n\t\t\t\t\t\t\tpc.SetDeletionTimestamp(&now)\n\t\t\t\t\t\t\tpc.SetUID(uid)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockList: test.NewMockListFn(nil, func(obj client.ObjectList) error {\n\t\t\t\t\t\t\tl := obj.(*ProviderConfigUsageList)\n\t\t\t\t\t\t\tl.Items = []resource.ProviderConfigUsage{\n\t\t\t\t\t\t\t\t&fake.ProviderConfigUsage{\n\t\t\t\t\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{{\n\t\t\t\t\t\t\t\t\t\t\tUID:        uid,\n\t\t\t\t\t\t\t\t\t\t\tController: &ctrl,\n\t\t\t\t\t\t\t\t\t\t}},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockStatusUpdate: test.NewMockSubResourceUpdateFn(nil),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ProviderConfig{}, &fake.ProviderConfigUsage{}, &ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t\tof: resource.ProviderConfigKinds{\n\t\t\t\t\tConfig:    fake.GVK(&fake.ProviderConfig{}),\n\t\t\t\t\tUsage:     fake.GVK(&fake.ProviderConfigUsage{}),\n\t\t\t\t\tUsageList: fake.GVK(&ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{Requeue: false},\n\t\t\t},\n\t\t},\n\t\t\"RemoveFinalizerError\": {\n\t\t\treason: \"We should requeue after a short wait if we encounter an error while removing our finalizer\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tpc := obj.(*fake.ProviderConfig)\n\t\t\t\t\t\t\tpc.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockList:   test.NewMockListFn(nil),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(errBoom),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ProviderConfig{}, &fake.ProviderConfigUsage{}, &ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t\tof: resource.ProviderConfigKinds{\n\t\t\t\t\tConfig:    fake.GVK(&fake.ProviderConfig{}),\n\t\t\t\t\tUsage:     fake.GVK(&fake.ProviderConfigUsage{}),\n\t\t\t\t\tUsageList: fake.GVK(&ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: shortWait},\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulDelete\": {\n\t\t\treason: \"We should return without requeueing when we successfully remove our finalizer\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tpc := obj.(*fake.ProviderConfig)\n\t\t\t\t\t\t\tpc.SetDeletionTimestamp(&now)\n\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockList:   test.NewMockListFn(nil),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ProviderConfig{}, &fake.ProviderConfigUsage{}, &ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t\tof: resource.ProviderConfigKinds{\n\t\t\t\t\tConfig:    fake.GVK(&fake.ProviderConfig{}),\n\t\t\t\t\tUsage:     fake.GVK(&fake.ProviderConfigUsage{}),\n\t\t\t\t\tUsageList: fake.GVK(&ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{Requeue: false},\n\t\t\t},\n\t\t},\n\t\t\"AddFinalizerError\": {\n\t\t\treason: \"We should requeue after a short wait if we encounter an error while adding our finalizer\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:    test.NewMockGetFn(nil),\n\t\t\t\t\t\tMockList:   test.NewMockListFn(nil),\n\t\t\t\t\t\tMockUpdate: test.NewMockUpdateFn(errBoom),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ProviderConfig{}, &fake.ProviderConfigUsage{}, &ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t\tof: resource.ProviderConfigKinds{\n\t\t\t\t\tConfig:    fake.GVK(&fake.ProviderConfig{}),\n\t\t\t\t\tUsage:     fake.GVK(&fake.ProviderConfigUsage{}),\n\t\t\t\t\tUsageList: fake.GVK(&ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{RequeueAfter: shortWait},\n\t\t\t},\n\t\t},\n\t\t\"UpdateStatusError\": {\n\t\t\treason: \"We return errors encountered while updating our status\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:          test.NewMockGetFn(nil),\n\t\t\t\t\t\tMockList:         test.NewMockListFn(nil),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.NewMockSubResourceUpdateFn(errBoom),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ProviderConfig{}, &fake.ProviderConfigUsage{}, &ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t\tof: resource.ProviderConfigKinds{\n\t\t\t\t\tConfig:    fake.GVK(&fake.ProviderConfig{}),\n\t\t\t\t\tUsage:     fake.GVK(&fake.ProviderConfigUsage{}),\n\t\t\t\t\tUsageList: fake.GVK(&ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{Requeue: false},\n\t\t\t\terr:    errors.Wrap(errBoom, errUpdateStatus),\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulSetUsers\": {\n\t\t\treason: \"We should return without requeuing if we successfully update our user count\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet:          test.NewMockGetFn(nil),\n\t\t\t\t\t\tMockList:         test.NewMockListFn(nil),\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.NewMockSubResourceUpdateFn(nil),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ProviderConfig{}, &fake.ProviderConfigUsage{}, &ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t\tof: resource.ProviderConfigKinds{\n\t\t\t\t\tConfig:    fake.GVK(&fake.ProviderConfig{}),\n\t\t\t\t\tUsage:     fake.GVK(&fake.ProviderConfigUsage{}),\n\t\t\t\t\tUsageList: fake.GVK(&ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult: reconcile.Result{Requeue: false},\n\t\t\t},\n\t\t},\n\t\t\"ListUsagesScopedToNamespaceWhenNamespaced\": {\n\t\t\treason: \"When ProviderConfig is namespaced, List should be called with InNamespace option\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tpc := obj.(*fake.ProviderConfig)\n\t\t\t\t\t\t\tpc.SetNamespace(\"test-ns\")\n\t\t\t\t\t\t\tpc.SetName(\"my-pc\")\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockList: func(_ context.Context, _ client.ObjectList, opts ...client.ListOption) error {\n\t\t\t\t\t\t\t// Capture and verify list options: should include InNamespace(\"test-ns\")\n\t\t\t\t\t\t\tlistOpts := &client.ListOptions{}\n\t\t\t\t\t\t\tfor _, opt := range opts {\n\t\t\t\t\t\t\t\topt.ApplyToList(listOpts)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif listOpts.Namespace != \"test-ns\" {\n\t\t\t\t\t\t\t\tt.Errorf(\"List called with namespace %q, want InNamespace(\\\"test-ns\\\")\", listOpts.Namespace)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t},\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.NewMockSubResourceUpdateFn(nil),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ProviderConfig{}, &fake.ProviderConfigUsage{}, &ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t\tof: resource.ProviderConfigKinds{\n\t\t\t\t\tConfig:    fake.GVK(&fake.ProviderConfig{}),\n\t\t\t\t\tUsage:     fake.GVK(&fake.ProviderConfigUsage{}),\n\t\t\t\t\tUsageList: fake.GVK(&ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  reconcile.Result{Requeue: false},\n\t\t\t\trequest: reconcile.Request{NamespacedName: types.NamespacedName{Name: \"my-pc\", Namespace: \"test-ns\"}},\n\t\t\t},\n\t\t},\n\t\t\"ListUsagesNotScopedToNamespaceWhenClusterScoped\": {\n\t\t\treason: \"When ProviderConfig is cluster-scoped (empty namespace), List should not include InNamespace\",\n\t\t\targs: args{\n\t\t\t\tm: &fake.Manager{\n\t\t\t\t\tClient: &test.MockClient{\n\t\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\t\t\tpc := obj.(*fake.ProviderConfig)\n\t\t\t\t\t\t\tpc.SetNamespace(\"\") // cluster-scoped\n\t\t\t\t\t\t\tpc.SetName(\"my-pc\")\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMockList: func(_ context.Context, _ client.ObjectList, opts ...client.ListOption) error {\n\t\t\t\t\t\t\tlistOpts := &client.ListOptions{}\n\t\t\t\t\t\t\tfor _, opt := range opts {\n\t\t\t\t\t\t\t\topt.ApplyToList(listOpts)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif listOpts.Namespace != \"\" {\n\t\t\t\t\t\t\t\tt.Errorf(\"List called with namespace %q for cluster-scoped ProviderConfig, want empty\", listOpts.Namespace)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t},\n\t\t\t\t\t\tMockUpdate:       test.NewMockUpdateFn(nil),\n\t\t\t\t\t\tMockStatusUpdate: test.NewMockSubResourceUpdateFn(nil),\n\t\t\t\t\t},\n\t\t\t\t\tScheme: fake.SchemeWith(&fake.ProviderConfig{}, &fake.ProviderConfigUsage{}, &ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t\tof: resource.ProviderConfigKinds{\n\t\t\t\t\tConfig:    fake.GVK(&fake.ProviderConfig{}),\n\t\t\t\t\tUsage:     fake.GVK(&fake.ProviderConfigUsage{}),\n\t\t\t\t\tUsageList: fake.GVK(&ProviderConfigUsageList{}),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  reconcile.Result{Requeue: false},\n\t\t\t\trequest: reconcile.Request{NamespacedName: types.NamespacedName{Name: \"my-pc\"}},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewReconciler(tc.args.m, tc.args.of)\n\n\t\t\treq := tc.want.request\n\t\t\tgot, err := r.Reconcile(context.Background(), req)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nr.Reconcile(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.result, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nr.Reconcile(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/reference/namespaced_reference.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 reference\n\nimport (\n\t\"context\"\n\t\"maps\"\n\t\"slices\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n)\n\n// A NamespacedResolutionRequest requests that a reference to a particular kind of\n// managed resource be resolved.\ntype NamespacedResolutionRequest struct {\n\tCurrentValue string\n\tReference    *xpv2.NamespacedReference\n\tSelector     *xpv2.NamespacedSelector\n\tTo           To\n\tExtract      ExtractValueFn\n\tNamespace    string\n}\n\n// IsNoOp returns true if the supplied NamespacedResolutionRequest cannot or should not be\n// processed.\nfunc (rr *NamespacedResolutionRequest) IsNoOp() bool {\n\tisAlways := false\n\n\tif rr.Selector != nil {\n\t\tif rr.Selector.Policy.IsResolvePolicyAlways() {\n\t\t\trr.Reference = nil\n\t\t\tisAlways = true\n\t\t}\n\t} else if rr.Reference != nil {\n\t\tif rr.Reference.Policy.IsResolvePolicyAlways() {\n\t\t\tisAlways = true\n\t\t}\n\t}\n\n\t// We don't resolve values that are already set (if reference resolution policy\n\t// is not set to Always); we effectively cache resolved values. The CR author\n\t// can invalidate the cache and trigger a new resolution by explicitly clearing\n\t// the resolved value.\n\tif rr.CurrentValue != \"\" && !isAlways {\n\t\treturn true\n\t}\n\n\t// We can't resolve anything if neither a reference nor a selector were\n\t// provided.\n\treturn rr.Reference == nil && rr.Selector == nil\n}\n\n// A NamespacedResolutionResponse returns the result of a reference resolution. The\n// returned values are always safe to set if resolution was successful.\ntype NamespacedResolutionResponse struct {\n\tResolvedValue     string\n\tResolvedReference *xpv2.NamespacedReference\n}\n\n// Validate this NamespacedResolutionResponse.\nfunc (rr NamespacedResolutionResponse) Validate() error {\n\tif rr.ResolvedValue == \"\" {\n\t\treturn errors.New(errNoValue)\n\t}\n\n\treturn nil\n}\n\n// A MultiNamespacedResolutionRequest requests that several references to a particular\n// kind of managed resource be resolved.\ntype MultiNamespacedResolutionRequest struct {\n\tCurrentValues []string\n\tReferences    []xpv2.NamespacedReference\n\tSelector      *xpv2.NamespacedSelector\n\tTo            To\n\tExtract       ExtractValueFn\n\tNamespace     string\n}\n\n// IsNoOp returns true if the supplied MultiNamespacedResolutionRequest cannot or should\n// not be processed.\nfunc (rr *MultiNamespacedResolutionRequest) IsNoOp() bool {\n\tisAlways := false\n\n\tif rr.Selector != nil {\n\t\tif rr.Selector.Policy.IsResolvePolicyAlways() {\n\t\t\trr.References = nil\n\t\t\tisAlways = true\n\t\t}\n\t} else {\n\t\tfor _, r := range rr.References {\n\t\t\tif r.Policy.IsResolvePolicyAlways() {\n\t\t\t\tisAlways = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\t// We don't resolve values that are already set (if reference resolution policy\n\t// is not set to Always); we effectively cache resolved values. The CR author\n\t// can invalidate the cache and trigger a new resolution by explicitly clearing\n\t// the resolved values. This is a little unintuitive for the APIMultiResolver\n\t// but mimics the UX of the MultiNamespacedResolutionRequest and simplifies the overall mental model.\n\tif len(rr.CurrentValues) > 0 && !isAlways {\n\t\treturn true\n\t}\n\n\t// We can't resolve anything if neither a reference nor a selector were\n\t// provided.\n\treturn len(rr.References) == 0 && rr.Selector == nil\n}\n\n// A MultiNamespacedResolutionResponse returns the result of several reference\n// resolutions. The returned values are always safe to set if resolution was\n// successful.\ntype MultiNamespacedResolutionResponse struct {\n\tResolvedValues     []string\n\tResolvedReferences []xpv2.NamespacedReference\n}\n\n// Validate this MultiNamespacedResolutionResponse.\nfunc (rr MultiNamespacedResolutionResponse) Validate() error {\n\tif len(rr.ResolvedValues) == 0 {\n\t\treturn errors.New(errNoMatches)\n\t}\n\n\tfor i, v := range rr.ResolvedValues {\n\t\tif v == \"\" {\n\t\t\treturn getResolutionError(rr.ResolvedReferences[i].Policy, errors.New(errNoValue))\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// An APINamespacedResolver selects and resolves references to managed resources in the\n// Kubernetes API server.\ntype APINamespacedResolver struct {\n\tclient client.Reader\n\tfrom   resource.Managed\n}\n\n// NewAPINamespacedResolver returns a Resolver that selects and resolves references from\n// the supplied managed resource to other managed resources in the Kubernetes\n// API server.\nfunc NewAPINamespacedResolver(c client.Reader, from resource.Managed) *APINamespacedResolver {\n\treturn &APINamespacedResolver{client: c, from: from}\n}\n\n// Resolve the supplied NamespacedResolutionRequest. The returned NamespacedResolutionResponse\n// always contains valid values unless an error was returned.\nfunc (r *APINamespacedResolver) Resolve(ctx context.Context, req NamespacedResolutionRequest) (NamespacedResolutionResponse, error) {\n\t// Return early if from is being deleted, or the request is a no-op.\n\tif meta.WasDeleted(r.from) || req.IsNoOp() {\n\t\treturn NamespacedResolutionResponse{ResolvedValue: req.CurrentValue, ResolvedReference: req.Reference}, nil\n\t}\n\n\t// The reference is already set - resolve it.\n\tif req.Reference != nil {\n\t\t// default to same namespace\n\t\tns := req.Reference.Namespace\n\t\tif ns == \"\" {\n\t\t\tns = r.from.GetNamespace()\n\t\t}\n\n\t\tif err := r.client.Get(ctx, types.NamespacedName{Name: req.Reference.Name, Namespace: ns}, req.To.Managed); err != nil {\n\t\t\tif kerrors.IsNotFound(err) {\n\t\t\t\treturn NamespacedResolutionResponse{}, getResolutionError(req.Reference.Policy, errors.Wrap(err, errGetManaged))\n\t\t\t}\n\n\t\t\treturn NamespacedResolutionResponse{}, errors.Wrap(err, errGetManaged)\n\t\t}\n\n\t\trsp := NamespacedResolutionResponse{ResolvedValue: req.Extract(req.To.Managed), ResolvedReference: req.Reference}\n\n\t\treturn rsp, getResolutionError(req.Reference.Policy, rsp.Validate())\n\t}\n\n\t// The reference was not set, but a selector was. Select a reference. If the\n\t// request has no namespace, then InNamespace is a no-op.\n\tns := req.Selector.Namespace\n\tif ns == \"\" {\n\t\tns = r.from.GetNamespace()\n\t}\n\n\tif err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels), client.InNamespace(ns)); err != nil {\n\t\treturn NamespacedResolutionResponse{}, errors.Wrap(err, errListManaged)\n\t}\n\n\tfor _, to := range req.To.List.GetItems() {\n\t\tif ControllersMustMatchNamespaced(req.Selector) && !meta.HaveSameController(r.from, to) {\n\t\t\tcontinue\n\t\t}\n\n\t\trsp := NamespacedResolutionResponse{ResolvedValue: req.Extract(to), ResolvedReference: &xpv2.NamespacedReference{Name: to.GetName(), Namespace: ns}}\n\n\t\treturn rsp, getResolutionError(req.Selector.Policy, rsp.Validate())\n\t}\n\n\t// We couldn't resolve anything.\n\treturn NamespacedResolutionResponse{}, getResolutionError(req.Selector.Policy, errors.New(errNoMatches))\n}\n\n// ResolveMultiple resolves the supplied MultiNamespacedResolutionRequest. The returned\n// MultiNamespacedResolutionResponse always contains valid values unless an error was\n// returned.\nfunc (r *APINamespacedResolver) ResolveMultiple(ctx context.Context, req MultiNamespacedResolutionRequest) (MultiNamespacedResolutionResponse, error) { //nolint: gocyclo // Only at 11.\n\t// Return early if from is being deleted, or the request is a no-op.\n\tif meta.WasDeleted(r.from) || req.IsNoOp() {\n\t\treturn MultiNamespacedResolutionResponse{ResolvedValues: req.CurrentValues, ResolvedReferences: req.References}, nil\n\t}\n\n\t// The references are already set - resolve them.\n\tif len(req.References) > 0 {\n\t\tresolvedVals := make([]string, len(req.References))\n\t\tfor i := range req.References {\n\t\t\tns := req.References[i].Namespace\n\t\t\tif ns == \"\" {\n\t\t\t\tns = r.from.GetNamespace()\n\t\t\t}\n\n\t\t\tif err := r.client.Get(ctx, types.NamespacedName{Name: req.References[i].Name, Namespace: ns}, req.To.Managed); err != nil {\n\t\t\t\tif kerrors.IsNotFound(err) {\n\t\t\t\t\treturn MultiNamespacedResolutionResponse{}, getResolutionError(req.References[i].Policy, errors.Wrap(err, errGetManaged))\n\t\t\t\t}\n\n\t\t\t\treturn MultiNamespacedResolutionResponse{}, errors.Wrap(err, errGetManaged)\n\t\t\t}\n\n\t\t\tresolvedVals[i] = req.Extract(req.To.Managed)\n\t\t}\n\n\t\trsp := MultiNamespacedResolutionResponse{ResolvedValues: resolvedVals, ResolvedReferences: req.References}\n\n\t\treturn rsp, rsp.Validate()\n\t}\n\n\t// No references were set, but a selector was. Select and resolve\n\t// references. If the request has no namespace, then InNamespace is a no-op.\n\tns := req.Selector.Namespace\n\tif ns == \"\" {\n\t\tns = r.from.GetNamespace()\n\t}\n\n\tif err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels), client.InNamespace(ns)); err != nil {\n\t\treturn MultiNamespacedResolutionResponse{}, errors.Wrap(err, errListManaged)\n\t}\n\n\tvalueMap := make(map[string]xpv2.NamespacedReference)\n\tfor _, to := range req.To.List.GetItems() {\n\t\tif ControllersMustMatchNamespaced(req.Selector) && !meta.HaveSameController(r.from, to) {\n\t\t\tcontinue\n\t\t}\n\n\t\tvalueMap[req.Extract(to)] = xpv2.NamespacedReference{Name: to.GetName(), Namespace: ns}\n\t}\n\n\tsortedKeys, sortedRefs := sortGenericMapByKeys(valueMap)\n\n\trsp := MultiNamespacedResolutionResponse{ResolvedValues: sortedKeys, ResolvedReferences: sortedRefs}\n\n\treturn rsp, getResolutionError(req.Selector.Policy, rsp.Validate())\n}\n\nfunc sortGenericMapByKeys[T any](m map[string]T) ([]string, []T) {\n\tkeys := slices.Sorted(maps.Keys(m))\n\n\tvalues := make([]T, 0, len(keys))\n\tfor _, k := range keys {\n\t\tvalues = append(values, m[k])\n\t}\n\n\treturn keys, values\n}\n\n// ControllersMustMatchNamespaced returns true if the supplied Selector requires that a\n// reference be to a managed resource whose controller reference matches the\n// referencing resource.\nfunc ControllersMustMatchNamespaced(s *xpv2.NamespacedSelector) bool {\n\tif s == nil {\n\t\treturn false\n\t}\n\n\treturn s.MatchControllerRef != nil && *s.MatchControllerRef\n}\n"
  },
  {
    "path": "pkg/reference/namespaced_reference_test.go",
    "content": "package reference\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nfunc prepareTestExamplesNamespaced(numExamples int) ([]string, []xpv2.NamespacedReference, []*fake.Managed) {\n\tvalues := make([]string, numExamples)\n\trefs := make([]xpv2.NamespacedReference, numExamples)\n\tcontrolledObj := make([]*fake.Managed, numExamples)\n\tfor i := range numExamples {\n\t\tvalues[i] = fmt.Sprintf(\"%s%d\", testValuePrefix, i)\n\t\trefs[i] = xpv2.NamespacedReference{\n\t\t\tName: fmt.Sprintf(\"%s%d\", testResourceNamePrefix, i),\n\t\t}\n\t\tcontrolled := &fake.Managed{}\n\t\tcontrolled.SetName(refs[i].Name)\n\t\tmeta.SetExternalName(controlled, values[i])\n\t\t_ = meta.AddControllerReference(controlled, meta.AsController(&xpv2.TypedReference{UID: testControllerUID}))\n\t\tcontrolledObj[i] = controlled\n\t}\n\treturn values, refs, controlledObj\n}\n\nvar nsTestValues, nsTestRefs, nsTestControlled = prepareTestExamplesNamespaced(10)\n\nfunc TestNamespacedResolve(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\tnow := metav1.Now()\n\tvalue := \"coolv\"\n\tref := &xpv2.NamespacedReference{Name: \"cool\", Namespace: \"cool-ns\"}\n\tnsOmittedRef := &xpv2.NamespacedReference{Name: \"cool\"}\n\toptionalPolicy := xpv2.ResolutionPolicyOptional\n\talwaysPolicy := xpv2.ResolvePolicyAlways\n\toptionalRef := &xpv2.NamespacedReference{Name: \"cool\", Namespace: \"cool-ns\", Policy: &xpv2.Policy{Resolution: &optionalPolicy}}\n\talwaysRef := &xpv2.NamespacedReference{Name: \"cool\", Namespace: \"cool-ns\", Policy: &xpv2.Policy{Resolve: &alwaysPolicy}}\n\n\tcontrolled := &fake.Managed{}\n\tcontrolled.SetName(value)\n\tmeta.SetExternalName(controlled, value)\n\tmeta.AddControllerReference(controlled, meta.AsController(&xpv2.TypedReference{UID: types.UID(\"very-unique\")}))\n\n\ttype args struct {\n\t\tctx context.Context\n\t\treq NamespacedResolutionRequest\n\t}\n\n\ttype want struct {\n\t\trsp NamespacedResolutionResponse\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tc      client.Reader\n\t\tfrom   resource.Managed\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"FromDeleted\": {\n\t\t\treason: \"Should return early if the referencing managed resource was deleted\",\n\t\t\tfrom:   &fake.Managed{ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: &now}},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlreadyResolved\": {\n\t\t\treason: \"Should return early if the current value is non-zero\",\n\t\t\tfrom:   &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{CurrentValue: value},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{ResolvedValue: value},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlwaysResolveReference\": {\n\t\t\treason: \"Should not return early if the current value is non-zero, when the resolve policy is set to\" +\n\t\t\t\t\"Always\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tReference:    alwaysRef,\n\t\t\t\t\tTo:           To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:      ExternalName(),\n\t\t\t\t\tCurrentValue: \"oldValue\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: alwaysRef,\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"Unresolvable\": {\n\t\t\treason: \"Should return early if neither a reference or selector were provided\",\n\t\t\tfrom:   &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"GetError\": {\n\t\t\treason: \"Should return errors encountered while getting the referenced resource\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(errBoom),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tReference: ref,\n\t\t\t\t\tTo:        To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:   ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errGetManaged),\n\t\t\t},\n\t\t},\n\t\t\"ResolvedNoValue\": {\n\t\t\treason: \"Should return an error if the extract function returns the empty string\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tReference: ref,\n\t\t\t\t\tTo:        To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:   func(resource.Managed) string { return \"\" },\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{\n\t\t\t\t\tResolvedReference: ref,\n\t\t\t\t},\n\t\t\t\terr: errors.New(errNoValue),\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulResolve\": {\n\t\t\treason: \"No error should be returned when the value is successfully extracted\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tReference: ref,\n\t\t\t\t\tTo:        To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:   ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: ref,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulResolveNamespaced\": {\n\t\t\treason: \"Resolve should be successful when a namespace is given\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tReference: ref,\n\t\t\t\t\tTo:        To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:   ExternalName(),\n\t\t\t\t\tNamespace: \"cool-ns\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: ref,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulResolveInferredNamespace\": {\n\t\t\treason: \"Resolve should be successful with namespace inferred from MR, when reference omits namespace\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error {\n\t\t\t\t\tif key.Namespace == \"from-ns\" {\n\t\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\n\t\t\t\t\tt.Errorf(\"Resolve did not infer to the MR namespace: %v\", key)\n\n\t\t\t\t\treturn errBoom\n\t\t\t\t},\n\t\t\t},\n\t\t\tfrom: &fake.Managed{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"some-mr\",\n\t\t\t\t\tNamespace: \"from-ns\",\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tReference: nsOmittedRef,\n\t\t\t\t\tTo:        To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:   ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: nsOmittedRef,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulResolveCrossNamespace\": {\n\t\t\treason: \"Resolve should be successful when a namespace is given\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error {\n\t\t\t\t\tif key.Namespace == \"cool-ns\" {\n\t\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\n\t\t\t\t\tt.Errorf(\"Resolve did not infer to the other namespace: %v\", key)\n\n\t\t\t\t\treturn errBoom\n\t\t\t\t},\n\t\t\t},\n\t\t\tfrom: &fake.Managed{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"some-mr\",\n\t\t\t\t\tNamespace: \"from-ns\",\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tReference: ref,\n\t\t\t\t\tTo:        To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:   ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: ref,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"OptionalReference\": {\n\t\t\treason: \"No error should be returned when the resolution policy is Optional\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tReference: optionalRef,\n\t\t\t\t\tTo:        To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:   func(resource.Managed) string { return \"\" },\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{\n\t\t\t\t\tResolvedReference: optionalRef,\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"ListError\": {\n\t\t\treason: \"Should return errors encountered while listing potential referenced resources\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(errBoom),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{},\n\t\t\t\terr: errors.Wrap(errBoom, errListManaged),\n\t\t\t},\n\t\t},\n\t\t\"NoMatches\": {\n\t\t\treason: \"Should return an error when no managed resources match the selector\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{},\n\t\t\t\t\tTo:       To{List: &FakeManagedList{}},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{},\n\t\t\t\terr: errors.New(errNoMatches),\n\t\t\t},\n\t\t},\n\t\t\"OptionalSelector\": {\n\t\t\treason: \"No error should be returned when the resolution policy is Optional\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{\n\t\t\t\t\t\tPolicy: &xpv2.Policy{Resolution: &optionalPolicy},\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{}},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulSelect\": {\n\t\t\treason: \"A managed resource with a matching controller reference should be selected and returned\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{Items: []resource.Managed{\n\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t}}},\n\t\t\t\t\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: &xpv2.NamespacedReference{Name: value},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulSelectNamespaced\": {\n\t\t\treason: \"Resolve should be successful when a namespace is given\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{Items: []resource.Managed{\n\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t}}},\n\t\t\t\t\tExtract:   ExternalName(),\n\t\t\t\t\tNamespace: \"cool-ns\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: &xpv2.NamespacedReference{Name: value},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlwaysResolveSelector\": {\n\t\t\treason: \"Should not return early if the current value is non-zero, when the resolve policy is set to\" +\n\t\t\t\t\"Always\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t\tPolicy:             &xpv2.Policy{Resolve: &alwaysPolicy},\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{Items: []resource.Managed{\n\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t}}},\n\t\t\t\t\tExtract:      ExternalName(),\n\t\t\t\t\tCurrentValue: \"oldValue\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: &xpv2.NamespacedReference{Name: value},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"BothReferenceSelector\": {\n\t\t\treason: \"When both Reference and Selector fields set and Policy is not set, the Reference must be resolved\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: NamespacedResolutionRequest{\n\t\t\t\t\tReference: ref,\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo:      To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: NamespacedResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: ref,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewAPINamespacedResolver(tc.c, tc.from)\n\n\t\t\tgot, err := r.Resolve(tc.args.ctx, tc.args.req)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nControllersMustMatch(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.rsp, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nControllersMustMatch(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNamespacedResolveMultiple(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\tnow := metav1.Now()\n\tvalue := \"coolv\"\n\tvalue2 := \"cooler\"\n\tref := xpv2.NamespacedReference{Name: \"cool\", Namespace: \"cool-ns\"}\n\tnsOmittedRef := xpv2.NamespacedReference{Name: \"cool\"}\n\toptionalPolicy := xpv2.ResolutionPolicyOptional\n\talwaysPolicy := xpv2.ResolvePolicyAlways\n\toptionalRef := xpv2.NamespacedReference{Name: \"cool\", Policy: &xpv2.Policy{Resolution: &optionalPolicy}}\n\talwaysRef := xpv2.NamespacedReference{Name: \"cool\", Policy: &xpv2.Policy{Resolve: &alwaysPolicy}}\n\n\tcontrolled := &fake.Managed{}\n\tcontrolled.SetName(value)\n\tmeta.SetExternalName(controlled, value)\n\tmeta.AddControllerReference(controlled, meta.AsController(&xpv2.TypedReference{UID: types.UID(\"very-unique\")}))\n\n\tcontrolled2 := &fake.Managed{}\n\tcontrolled2.SetName(value2)\n\tmeta.SetExternalName(controlled2, value2)\n\tmeta.AddControllerReference(controlled2, meta.AsController(&xpv2.TypedReference{UID: types.UID(\"very-unique\")}))\n\n\ttype args struct {\n\t\tctx context.Context\n\t\treq MultiNamespacedResolutionRequest\n\t}\n\n\ttype want struct {\n\t\trsp MultiNamespacedResolutionResponse\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tc      client.Reader\n\t\tfrom   resource.Managed\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"FromDeleted\": {\n\t\t\treason: \"Should return early if the referencing managed resource was deleted\",\n\t\t\tfrom:   &fake.Managed{ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: &now}},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlreadyResolved\": {\n\t\t\treason: \"Should return early if the current value is non-zero\",\n\t\t\tfrom:   &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{CurrentValues: []string{value}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{ResolvedValues: []string{value}},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlwaysResolveReference\": {\n\t\t\treason: \"Should not return early if the current value is non-zero, when the resolve policy is set to\" +\n\t\t\t\t\"Always\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tReferences:    []xpv2.NamespacedReference{alwaysRef},\n\t\t\t\t\tTo:            To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:       ExternalName(),\n\t\t\t\t\tCurrentValues: []string{\"oldValue\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{alwaysRef},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"Unresolvable\": {\n\t\t\treason: \"Should return early if neither a reference or selector were provided\",\n\t\t\tfrom:   &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"GetError\": {\n\t\t\treason: \"Should return errors encountered while getting the referenced resource\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(errBoom),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.NamespacedReference{ref},\n\t\t\t\t\tTo:         To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:    ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errGetManaged),\n\t\t\t},\n\t\t},\n\t\t\"ResolvedNoValue\": {\n\t\t\treason: \"Should return an error if the extract function returns the empty string\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.NamespacedReference{ref},\n\t\t\t\t\tTo:         To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:    func(resource.Managed) string { return \"\" },\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{\"\"},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{ref},\n\t\t\t\t},\n\t\t\t\terr: errors.New(errNoValue),\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulResolve\": {\n\t\t\treason: \"No error should be returned when the value is successfully extracted\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.NamespacedReference{ref},\n\t\t\t\t\tTo:         To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:    ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{ref},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulResolveNamespaced\": {\n\t\t\treason: \"Resolve should be successful when a namespace is given\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error {\n\t\t\t\t\tif key.Namespace == ref.Namespace {\n\t\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\n\t\t\t\t\tt.Errorf(\"Resolve did not infer to the MR namespace: %v\", key)\n\n\t\t\t\t\treturn errBoom\n\t\t\t\t},\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.NamespacedReference{ref},\n\t\t\t\t\tTo:         To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:    ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{ref},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulResolveInferredNamespace\": {\n\t\t\treason: \"Resolve should be successful when a namespace is given\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error {\n\t\t\t\t\tif key.Namespace == \"from-ns\" {\n\t\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\n\t\t\t\t\tt.Errorf(\"Resolve did not infer to the MR namespace: %v\", key)\n\n\t\t\t\t\treturn errBoom\n\t\t\t\t},\n\t\t\t},\n\t\t\tfrom: &fake.Managed{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"some-mr\",\n\t\t\t\t\tNamespace: \"from-ns\",\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.NamespacedReference{nsOmittedRef},\n\t\t\t\t\tTo:         To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:    ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{nsOmittedRef},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulResolveCrossNamespace\": {\n\t\t\treason: \"Resolve should be successful when a namespace is given\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error {\n\t\t\t\t\tif key.Namespace == ref.Namespace {\n\t\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\n\t\t\t\t\tt.Errorf(\"Resolve did not infer to the MR namespace: %v\", key)\n\n\t\t\t\t\treturn errBoom\n\t\t\t\t},\n\t\t\t},\n\t\t\tfrom: &fake.Managed{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"some-mr\",\n\t\t\t\t\tNamespace: \"from-ns\",\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.NamespacedReference{ref},\n\t\t\t\t\tTo:         To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:    ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{ref},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"OptionalReference\": {\n\t\t\treason: \"No error should be returned when the resolution policy is Optional\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.NamespacedReference{optionalRef},\n\t\t\t\t\tTo:         To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:    func(resource.Managed) string { return \"\" },\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{\"\"},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{optionalRef},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"ListError\": {\n\t\t\treason: \"Should return errors encountered while listing potential referenced resources\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(errBoom),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{},\n\t\t\t\terr: errors.Wrap(errBoom, errListManaged),\n\t\t\t},\n\t\t},\n\t\t\"NoMatches\": {\n\t\t\treason: \"Should return an error when no managed resources match the selector\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{},\n\t\t\t\t\tTo:       To{List: &FakeManagedList{}},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{},\n\t\t\t\terr: errors.New(errNoMatches),\n\t\t\t},\n\t\t},\n\t\t\"OptionalSelector\": {\n\t\t\treason: \"No error should be returned when the resolution policy is Optional\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{\n\t\t\t\t\t\tPolicy: &xpv2.Policy{Resolution: &optionalPolicy},\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{}},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulSelect\": {\n\t\t\treason: \"A managed resource with a matching controller reference should be selected and returned\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{Items: []resource.Managed{\n\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t}}},\n\t\t\t\t\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{{Name: value}},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulSelectNamespaced\": {\n\t\t\treason: \"Resolve should be successful when a namespace is given\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{Items: []resource.Managed{\n\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t}}},\n\t\t\t\t\tExtract:   ExternalName(),\n\t\t\t\t\tNamespace: \"cool-ns\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{{Name: value}},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlwaysResolveSelector\": {\n\t\t\treason: \"Should not return early if the current value is non-zero, when the resolve policy is set to\" +\n\t\t\t\t\"Always\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t\tPolicy:             &xpv2.Policy{Resolve: &alwaysPolicy},\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{Items: []resource.Managed{\n\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t}}},\n\t\t\t\t\tExtract:       ExternalName(),\n\t\t\t\t\tCurrentValues: []string{\"oldValue\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{{Name: value}},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"BothReferenceSelector\": {\n\t\t\treason: \"When both Reference and Selector fields set and Policy is not set, the Reference must be resolved\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(errors.New(\"unexpected call to List when resolving Refs only\")),\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.NamespacedReference{ref},\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo:      To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{ref},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SelectorOrderOutput\": {\n\t\t\treason: \"Resolved values should be ordered when resolving a selector\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{\n\t\t\t\t\t\tItems: []resource.Managed{\n\t\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\t\tcontrolled2,     // A resource with a matching controller reference.\n\t\t\t\t\t\t},\n\t\t\t\t\t}},\n\t\t\t\t\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value2, value},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{{Name: value2}, {Name: value}},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"NoSelectorOnlyRefs\": {\n\t\t\treason: \"Refs should not be re-ordered when selector is omitted\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(errors.New(\"unexpected call to List when resolving Refs only\")),\n\t\t\t\tMockGet: func(_ context.Context, objKey client.ObjectKey, obj client.Object) error {\n\t\t\t\t\tif !strings.HasPrefix(objKey.Name, testResourceNamePrefix) {\n\t\t\t\t\t\treturn errors.New(\"test resource not found\")\n\t\t\t\t\t}\n\t\t\t\t\tval := strings.Replace(objKey.Name, testResourceNamePrefix, testValuePrefix, 1)\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), val)\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.NamespacedReference{nsTestRefs[2], nsTestRefs[3], nsTestRefs[0], nsTestRefs[1]},\n\t\t\t\t\tTo: To{\n\t\t\t\t\t\tManaged: &fake.Managed{},\n\t\t\t\t\t},\n\t\t\t\t\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{nsTestValues[2], nsTestValues[3], nsTestValues[0], nsTestValues[1]},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{nsTestRefs[2], nsTestRefs[3], nsTestRefs[0], nsTestRefs[1]},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlwaysResolveSelector_NewValuesOrdered\": {\n\t\t\treason: \"Must resolve new matches and reorder resolved values & refs, when Selector policy is Always and have existing refs and values\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: MultiNamespacedResolutionRequest{\n\t\t\t\t\tCurrentValues: []string{nsTestValues[1], nsTestValues[4]},\n\t\t\t\t\tReferences:    []xpv2.NamespacedReference{nsTestRefs[1], nsTestRefs[4]},\n\t\t\t\t\tSelector: &xpv2.NamespacedSelector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t\tPolicy:             &xpv2.Policy{Resolve: &alwaysPolicy},\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{\n\t\t\t\t\t\tManaged: &fake.Managed{},\n\t\t\t\t\t\tList: &FakeManagedList{\n\t\t\t\t\t\t\t// List result is not ordered\n\t\t\t\t\t\t\tItems: []resource.Managed{\n\t\t\t\t\t\t\t\t&fake.Managed{},     // A resource that does not match.\n\t\t\t\t\t\t\t\tnsTestControlled[2], // A resource with a matching controller reference.\n\t\t\t\t\t\t\t\t&fake.Managed{},     // A resource that does not match.\n\t\t\t\t\t\t\t\tnsTestControlled[4], // A resource with a matching controller reference.\n\t\t\t\t\t\t\t\tnsTestControlled[1], // A resource with a matching controller reference and newly introduced\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\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiNamespacedResolutionResponse{\n\t\t\t\t\t// expect ordered resolved values\n\t\t\t\t\tResolvedValues:     []string{nsTestValues[1], nsTestValues[2], nsTestValues[4]},\n\t\t\t\t\tResolvedReferences: []xpv2.NamespacedReference{nsTestRefs[1], nsTestRefs[2], nsTestRefs[4]},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewAPINamespacedResolver(tc.c, tc.from)\n\n\t\t\tgot, err := r.ResolveMultiple(tc.args.ctx, tc.args.req)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nControllersMustMatch(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.rsp, got, cmpopts.EquateEmpty()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nControllersMustMatch(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNamespacedControllersMustMatch(t *testing.T) {\n\tcases := map[string]struct {\n\t\ts    *xpv2.NamespacedSelector\n\t\twant bool\n\t}{\n\t\t\"NilSelector\": {\n\t\t\ts:    nil,\n\t\t\twant: false,\n\t\t},\n\t\t\"NilMatchControllerRef\": {\n\t\t\ts:    &xpv2.NamespacedSelector{},\n\t\t\twant: false,\n\t\t},\n\t\t\"False\": {\n\t\t\ts:    &xpv2.NamespacedSelector{MatchControllerRef: func() *bool { f := false; return &f }()},\n\t\t\twant: false,\n\t\t},\n\t\t\"True\": {\n\t\t\ts:    &xpv2.NamespacedSelector{MatchControllerRef: func() *bool { t := true; return &t }()},\n\t\t\twant: true,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := ControllersMustMatchNamespaced(tc.s)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"ControllersMustMatch(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/reference/reference.go",
    "content": "/*\nCopyright 2019 The Crossplane 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// Package reference contains utilities for working with cross-resource\n// references.\npackage reference\n\nimport (\n\t\"context\"\n\t\"maps\"\n\t\"slices\"\n\t\"strconv\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n)\n\n// Error strings.\nconst (\n\terrGetManaged  = \"cannot get referenced resource\"\n\terrListManaged = \"cannot list resources that match selector\"\n\terrNoMatches   = \"no resources matched selector\"\n\terrNoValue     = \"referenced field was empty (referenced resource may not yet be ready)\"\n)\n\n// NOTE(negz): There are many equivalents of FromPtrValue and ToPtrValue\n// throughout the Crossplane codebase. We duplicate them here to reduce the\n// number of packages our API types have to import to support references.\n\n// FromPtrValue adapts a string pointer field for use as a CurrentValue.\nfunc FromPtrValue(v *string) string {\n\tif v == nil {\n\t\treturn \"\"\n\t}\n\n\treturn *v\n}\n\n// FromFloatPtrValue adapts a float pointer field for use as a CurrentValue.\nfunc FromFloatPtrValue(v *float64) string {\n\tif v == nil {\n\t\treturn \"\"\n\t}\n\n\treturn strconv.FormatFloat(*v, 'f', 0, 64)\n}\n\n// FromIntPtrValue adapts an int pointer field for use as a CurrentValue.\nfunc FromIntPtrValue(v *int64) string {\n\tif v == nil {\n\t\treturn \"\"\n\t}\n\n\treturn strconv.FormatInt(*v, 10)\n}\n\n// ToPtrValue adapts a ResolvedValue for use as a string pointer field.\nfunc ToPtrValue(v string) *string {\n\tif v == \"\" {\n\t\treturn nil\n\t}\n\n\treturn &v\n}\n\n// ToFloatPtrValue adapts a ResolvedValue for use as a float64 pointer field.\nfunc ToFloatPtrValue(v string) *float64 {\n\tif v == \"\" {\n\t\treturn nil\n\t}\n\n\tvParsed, err := strconv.ParseFloat(v, 64)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\treturn &vParsed\n}\n\n// ToIntPtrValue adapts a ResolvedValue for use as an int pointer field.\nfunc ToIntPtrValue(v string) *int64 {\n\tif v == \"\" {\n\t\treturn nil\n\t}\n\n\tvParsed, err := strconv.ParseInt(v, 10, 64)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\treturn &vParsed\n}\n\n// FromPtrValues adapts a slice of string pointer fields for use as CurrentValues.\n// NOTE: Do not use this utility function unless you have to.\n// Using pointer slices does not adhere to our current API practices.\n// The current use case is where generated code creates reference-able fields in a provider which are\n// string pointers and need to be resolved as part of `ResolveMultiple`.\nfunc FromPtrValues(v []*string) []string {\n\tres := make([]string, len(v))\n\tfor i := range v {\n\t\tres[i] = FromPtrValue(v[i])\n\t}\n\n\treturn res\n}\n\n// FromFloatPtrValues adapts a slice of float64 pointer fields for use as CurrentValues.\nfunc FromFloatPtrValues(v []*float64) []string {\n\tres := make([]string, len(v))\n\tfor i := range v {\n\t\tres[i] = FromFloatPtrValue(v[i])\n\t}\n\n\treturn res\n}\n\n// FromIntPtrValues adapts a slice of int64 pointer fields for use as CurrentValues.\nfunc FromIntPtrValues(v []*int64) []string {\n\tres := make([]string, len(v))\n\tfor i := range v {\n\t\tres[i] = FromIntPtrValue(v[i])\n\t}\n\n\treturn res\n}\n\n// ToPtrValues adapts ResolvedValues for use as a slice of string pointer fields.\n// NOTE: Do not use this utility function unless you have to.\n// Using pointer slices does not adhere to our current API practices.\n// The current use case is where generated code creates reference-able fields in a provider which are\n// string pointers and need to be resolved as part of `ResolveMultiple`.\nfunc ToPtrValues(v []string) []*string {\n\tres := make([]*string, len(v))\n\tfor i := range v {\n\t\tres[i] = ToPtrValue(v[i])\n\t}\n\n\treturn res\n}\n\n// ToFloatPtrValues adapts ResolvedValues for use as a slice of float64 pointer fields.\nfunc ToFloatPtrValues(v []string) []*float64 {\n\tres := make([]*float64, len(v))\n\tfor i := range v {\n\t\tres[i] = ToFloatPtrValue(v[i])\n\t}\n\n\treturn res\n}\n\n// ToIntPtrValues adapts ResolvedValues for use as a slice of int64 pointer fields.\nfunc ToIntPtrValues(v []string) []*int64 {\n\tres := make([]*int64, len(v))\n\tfor i := range v {\n\t\tres[i] = ToIntPtrValue(v[i])\n\t}\n\n\treturn res\n}\n\n// To indicates the kind of managed resource a reference is to.\ntype To struct {\n\tManaged resource.Managed\n\tList    resource.ManagedList\n}\n\n// An ExtractValueFn specifies how to extract a value from the resolved managed\n// resource.\ntype ExtractValueFn func(resource.Managed) string\n\n// ExternalName extracts the resolved managed resource's external name from its\n// external name annotation.\nfunc ExternalName() ExtractValueFn {\n\treturn func(mg resource.Managed) string {\n\t\treturn meta.GetExternalName(mg)\n\t}\n}\n\n// A ResolutionRequest requests that a reference to a particular kind of\n// managed resource be resolved.\ntype ResolutionRequest struct {\n\tCurrentValue string\n\tReference    *xpv2.Reference\n\tSelector     *xpv2.Selector\n\tTo           To\n\tExtract      ExtractValueFn\n\tNamespace    string\n}\n\n// IsNoOp returns true if the supplied ResolutionRequest cannot or should not be\n// processed.\nfunc (rr *ResolutionRequest) IsNoOp() bool {\n\tisAlways := false\n\n\tif rr.Selector != nil {\n\t\tif rr.Selector.Policy.IsResolvePolicyAlways() {\n\t\t\trr.Reference = nil\n\t\t\tisAlways = true\n\t\t}\n\t} else if rr.Reference != nil {\n\t\tif rr.Reference.Policy.IsResolvePolicyAlways() {\n\t\t\tisAlways = true\n\t\t}\n\t}\n\n\t// We don't resolve values that are already set (if reference resolution policy\n\t// is not set to Always); we effectively cache resolved values. The CR author\n\t// can invalidate the cache and trigger a new resolution by explicitly clearing\n\t// the resolved value.\n\tif rr.CurrentValue != \"\" && !isAlways {\n\t\treturn true\n\t}\n\n\t// We can't resolve anything if neither a reference nor a selector were\n\t// provided.\n\treturn rr.Reference == nil && rr.Selector == nil\n}\n\n// A ResolutionResponse returns the result of a reference resolution. The\n// returned values are always safe to set if resolution was successful.\ntype ResolutionResponse struct {\n\tResolvedValue     string\n\tResolvedReference *xpv2.Reference\n}\n\n// Validate this ResolutionResponse.\nfunc (rr ResolutionResponse) Validate() error {\n\tif rr.ResolvedValue == \"\" {\n\t\treturn errors.New(errNoValue)\n\t}\n\n\treturn nil\n}\n\n// A MultiResolutionRequest requests that several references to a particular\n// kind of managed resource be resolved.\ntype MultiResolutionRequest struct {\n\tCurrentValues []string\n\tReferences    []xpv2.Reference\n\tSelector      *xpv2.Selector\n\tTo            To\n\tExtract       ExtractValueFn\n\tNamespace     string\n}\n\n// IsNoOp returns true if the supplied MultiResolutionRequest cannot or should\n// not be processed.\nfunc (rr *MultiResolutionRequest) IsNoOp() bool {\n\tisAlways := false\n\n\tif rr.Selector != nil {\n\t\tif rr.Selector.Policy.IsResolvePolicyAlways() {\n\t\t\trr.References = nil\n\t\t\tisAlways = true\n\t\t}\n\t} else {\n\t\tfor _, r := range rr.References {\n\t\t\tif r.Policy.IsResolvePolicyAlways() {\n\t\t\t\tisAlways = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\t// We don't resolve values that are already set (if reference resolution policy\n\t// is not set to Always); we effectively cache resolved values. The CR author\n\t// can invalidate the cache and trigger a new resolution by explicitly clearing\n\t// the resolved values. This is a little unintuitive for the APIMultiResolver\n\t// but mimics the UX of the APIResolver and simplifies the overall mental model.\n\tif len(rr.CurrentValues) > 0 && !isAlways {\n\t\treturn true\n\t}\n\n\t// We can't resolve anything if neither a reference nor a selector were\n\t// provided.\n\treturn len(rr.References) == 0 && rr.Selector == nil\n}\n\n// A MultiResolutionResponse returns the result of several reference\n// resolutions. The returned values are always safe to set if resolution was\n// successful.\ntype MultiResolutionResponse struct {\n\tResolvedValues     []string\n\tResolvedReferences []xpv2.Reference\n}\n\n// Validate this MultiResolutionResponse.\nfunc (rr MultiResolutionResponse) Validate() error {\n\tif len(rr.ResolvedValues) == 0 {\n\t\treturn errors.New(errNoMatches)\n\t}\n\n\tfor i, v := range rr.ResolvedValues {\n\t\tif v == \"\" {\n\t\t\treturn getResolutionError(rr.ResolvedReferences[i].Policy, errors.New(errNoValue))\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// An APIResolver selects and resolves references to managed resources in the\n// Kubernetes API server.\ntype APIResolver struct {\n\tclient client.Reader\n\tfrom   resource.Managed\n}\n\n// NewAPIResolver returns a Resolver that selects and resolves references from\n// the supplied managed resource to other managed resources in the Kubernetes\n// API server.\nfunc NewAPIResolver(c client.Reader, from resource.Managed) *APIResolver {\n\treturn &APIResolver{client: c, from: from}\n}\n\n// Resolve the supplied ResolutionRequest. The returned ResolutionResponse\n// always contains valid values unless an error was returned.\nfunc (r *APIResolver) Resolve(ctx context.Context, req ResolutionRequest) (ResolutionResponse, error) {\n\t// Return early if from is being deleted, or the request is a no-op.\n\tif meta.WasDeleted(r.from) || req.IsNoOp() {\n\t\treturn ResolutionResponse{ResolvedValue: req.CurrentValue, ResolvedReference: req.Reference}, nil\n\t}\n\n\t// The reference is already set - resolve it.\n\tif req.Reference != nil {\n\t\tif err := r.client.Get(ctx, types.NamespacedName{Name: req.Reference.Name, Namespace: req.Namespace}, req.To.Managed); err != nil {\n\t\t\tif kerrors.IsNotFound(err) {\n\t\t\t\treturn ResolutionResponse{}, getResolutionError(req.Reference.Policy, errors.Wrap(err, errGetManaged))\n\t\t\t}\n\n\t\t\treturn ResolutionResponse{}, errors.Wrap(err, errGetManaged)\n\t\t}\n\n\t\trsp := ResolutionResponse{ResolvedValue: req.Extract(req.To.Managed), ResolvedReference: req.Reference}\n\n\t\treturn rsp, getResolutionError(req.Reference.Policy, rsp.Validate())\n\t}\n\n\t// The reference was not set, but a selector was. Select a reference. If the\n\t// request has no namespace, then InNamespace is a no-op.\n\tif err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels), client.InNamespace(req.Namespace)); err != nil {\n\t\treturn ResolutionResponse{}, errors.Wrap(err, errListManaged)\n\t}\n\n\tfor _, to := range req.To.List.GetItems() {\n\t\tif ControllersMustMatch(req.Selector) && !meta.HaveSameController(r.from, to) {\n\t\t\tcontinue\n\t\t}\n\n\t\trsp := ResolutionResponse{ResolvedValue: req.Extract(to), ResolvedReference: &xpv2.Reference{Name: to.GetName()}}\n\n\t\treturn rsp, getResolutionError(req.Selector.Policy, rsp.Validate())\n\t}\n\n\t// We couldn't resolve anything.\n\treturn ResolutionResponse{}, getResolutionError(req.Selector.Policy, errors.New(errNoMatches))\n}\n\n// ResolveMultiple resolves the supplied MultiResolutionRequest. The returned\n// MultiResolutionResponse always contains valid values unless an error was\n// returned.\nfunc (r *APIResolver) ResolveMultiple(ctx context.Context, req MultiResolutionRequest) (MultiResolutionResponse, error) { //nolint: gocyclo // Only at 11.\n\t// Return early if from is being deleted, or the request is a no-op.\n\tif meta.WasDeleted(r.from) || req.IsNoOp() {\n\t\treturn MultiResolutionResponse{ResolvedValues: req.CurrentValues, ResolvedReferences: req.References}, nil\n\t}\n\n\t// The references are already set - resolve them.\n\tif len(req.References) > 0 {\n\t\tresolvedVals := make([]string, len(req.References))\n\t\tfor i := range req.References {\n\t\t\tif err := r.client.Get(ctx, types.NamespacedName{Name: req.References[i].Name, Namespace: req.Namespace}, req.To.Managed); err != nil {\n\t\t\t\tif kerrors.IsNotFound(err) {\n\t\t\t\t\treturn MultiResolutionResponse{}, getResolutionError(req.References[i].Policy, errors.Wrap(err, errGetManaged))\n\t\t\t\t}\n\n\t\t\t\treturn MultiResolutionResponse{}, errors.Wrap(err, errGetManaged)\n\t\t\t}\n\n\t\t\tresolvedVals[i] = req.Extract(req.To.Managed)\n\t\t}\n\n\t\trsp := MultiResolutionResponse{ResolvedValues: resolvedVals, ResolvedReferences: req.References}\n\n\t\treturn rsp, rsp.Validate()\n\t}\n\n\t// No references were set, but a selector was. Select and resolve\n\t// references. If the request has no namespace, then InNamespace is a no-op.\n\tif err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels), client.InNamespace(req.Namespace)); err != nil {\n\t\treturn MultiResolutionResponse{}, errors.Wrap(err, errListManaged)\n\t}\n\n\tvalueMap := make(map[string]xpv2.Reference)\n\tfor _, to := range req.To.List.GetItems() {\n\t\tif ControllersMustMatch(req.Selector) && !meta.HaveSameController(r.from, to) {\n\t\t\tcontinue\n\t\t}\n\n\t\tvalueMap[req.Extract(to)] = xpv2.Reference{Name: to.GetName()}\n\t}\n\n\tsortedKeys, sortedRefs := sortMapByKeys(valueMap)\n\n\trsp := MultiResolutionResponse{ResolvedValues: sortedKeys, ResolvedReferences: sortedRefs}\n\n\treturn rsp, getResolutionError(req.Selector.Policy, rsp.Validate())\n}\n\nfunc getResolutionError(p *xpv2.Policy, err error) error {\n\tif !p.IsResolutionPolicyOptional() {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc sortMapByKeys(m map[string]xpv2.Reference) ([]string, []xpv2.Reference) {\n\tkeys := slices.Sorted(maps.Keys(m))\n\n\tvalues := make([]xpv2.Reference, 0, len(keys))\n\tfor _, k := range keys {\n\t\tvalues = append(values, m[k])\n\t}\n\n\treturn keys, values\n}\n\n// ControllersMustMatch returns true if the supplied Selector requires that a\n// reference be to a managed resource whose controller reference matches the\n// referencing resource.\nfunc ControllersMustMatch(s *xpv2.Selector) bool {\n\tif s == nil {\n\t\treturn false\n\t}\n\n\treturn s.MatchControllerRef != nil && *s.MatchControllerRef\n}\n"
  },
  {
    "path": "pkg/reference/reference_test.go",
    "content": "/*\nCopyright 2019 The Crossplane 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    htcp://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 reference\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nconst (\n\ttestResourceNamePrefix = \"cool-resource-\"\n\ttestValuePrefix        = \"cool-value-\"\n\ttestControllerUID      = types.UID(\"very-unique\")\n)\n\nfunc prepareTestExamples(numExamples int) ([]string, []xpv2.Reference, []*fake.Managed) {\n\tvalues := make([]string, numExamples)\n\trefs := make([]xpv2.Reference, numExamples)\n\tcontrolledObj := make([]*fake.Managed, numExamples)\n\tfor i := range numExamples {\n\t\tvalues[i] = fmt.Sprintf(\"%s%d\", testValuePrefix, i)\n\t\trefs[i] = xpv2.Reference{\n\t\t\tName: fmt.Sprintf(\"%s%d\", testResourceNamePrefix, i),\n\t\t}\n\t\tcontrolled := &fake.Managed{}\n\t\tcontrolled.SetName(refs[i].Name)\n\t\tmeta.SetExternalName(controlled, values[i])\n\t\t_ = meta.AddControllerReference(controlled, meta.AsController(&xpv2.TypedReference{UID: testControllerUID}))\n\t\tcontrolledObj[i] = controlled\n\t}\n\treturn values, refs, controlledObj\n}\n\nvar testValues, testRefs, testControlled = prepareTestExamples(10)\n\n// TODO(negz): Find a better home for this. It can't currently live alongside\n// its contemporaries in pkg/resource/fake because it would cause an import\n// cycle.\ntype FakeManagedList struct {\n\tclient.ObjectList\n\n\tItems []resource.Managed\n}\n\nfunc (fml *FakeManagedList) GetItems() []resource.Managed {\n\treturn fml.Items\n}\n\nfunc TestToAndFromPtr(t *testing.T) {\n\tcases := map[string]struct {\n\t\twant string\n\t}{\n\t\t\"Zero\":    {want: \"\"},\n\t\t\"NonZero\": {want: \"pointy\"},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := FromPtrValue(ToPtrValue(tc.want))\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"FromPtrValue(ToPtrValue(%s): -want, +got: %s\", tc.want, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestToAndFromFloatPtr(t *testing.T) {\n\tcases := map[string]struct {\n\t\twant string\n\t}{\n\t\t\"Zero\":    {want: \"\"},\n\t\t\"NonZero\": {want: \"1123581321\"},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := FromFloatPtrValue(ToFloatPtrValue(tc.want))\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"FromPtrValue(ToPtrValue(%s): -want, +got: %s\", tc.want, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestToAndFromPtrValues(t *testing.T) {\n\tcases := map[string]struct {\n\t\twant []string\n\t}{\n\t\t\"Nil\":      {want: []string{}},\n\t\t\"Zero\":     {want: []string{\"\"}},\n\t\t\"NonZero\":  {want: []string{\"pointy\"}},\n\t\t\"Multiple\": {want: []string{\"pointy\", \"pointers\"}},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := FromPtrValues(ToPtrValues(tc.want))\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"FromPtrValues(ToPtrValues(%s): -want, +got: %s\", tc.want, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestToAndFromFloatPtrValues(t *testing.T) {\n\tcases := map[string]struct {\n\t\twant []string\n\t}{\n\t\t\"Nil\":      {want: []string{}},\n\t\t\"Zero\":     {want: []string{\"\"}},\n\t\t\"NonZero\":  {want: []string{\"1123581321\"}},\n\t\t\"Multiple\": {want: []string{\"1123581321\", \"1234567890\"}},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := FromFloatPtrValues(ToFloatPtrValues(tc.want))\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"FromPtrValues(ToPtrValues(%s): -want, +got: %s\", tc.want, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestToAndFromIntPtrValues(t *testing.T) {\n\tcases := map[string]struct {\n\t\twant []string\n\t}{\n\t\t\"Nil\":      {want: []string{}},\n\t\t\"Zero\":     {want: []string{\"\"}},\n\t\t\"NonZero\":  {want: []string{\"1123581321\"}},\n\t\t\"Multiple\": {want: []string{\"1123581321\", \"1234567890\"}},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := FromIntPtrValues(ToIntPtrValues(tc.want))\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"FromIntPtrValues(ToIntPtrValues(%s): -want, +got: %s\", tc.want, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestResolve(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\tnow := metav1.Now()\n\tvalue := \"coolv\"\n\tref := &xpv2.Reference{Name: \"cool\"}\n\toptionalPolicy := xpv2.ResolutionPolicyOptional\n\talwaysPolicy := xpv2.ResolvePolicyAlways\n\toptionalRef := &xpv2.Reference{Name: \"cool\", Policy: &xpv2.Policy{Resolution: &optionalPolicy}}\n\talwaysRef := &xpv2.Reference{Name: \"cool\", Policy: &xpv2.Policy{Resolve: &alwaysPolicy}}\n\n\tcontrolled := &fake.Managed{}\n\tcontrolled.SetName(value)\n\tmeta.SetExternalName(controlled, value)\n\tmeta.AddControllerReference(controlled, meta.AsController(&xpv2.TypedReference{UID: types.UID(\"very-unique\")}))\n\n\ttype args struct {\n\t\tctx context.Context\n\t\treq ResolutionRequest\n\t}\n\n\ttype want struct {\n\t\trsp ResolutionResponse\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tc      client.Reader\n\t\tfrom   resource.Managed\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"FromDeleted\": {\n\t\t\treason: \"Should return early if the referencing managed resource was deleted\",\n\t\t\tfrom:   &fake.Managed{ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: &now}},\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlreadyResolved\": {\n\t\t\treason: \"Should return early if the current value is non-zero\",\n\t\t\tfrom:   &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{CurrentValue: value},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{ResolvedValue: value},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlwaysResolveReference\": {\n\t\t\treason: \"Should not return early if the current value is non-zero, when the resolve policy is set to\" +\n\t\t\t\t\"Always\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{\n\t\t\t\t\tReference:    alwaysRef,\n\t\t\t\t\tTo:           To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:      ExternalName(),\n\t\t\t\t\tCurrentValue: \"oldValue\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: alwaysRef,\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"Unresolvable\": {\n\t\t\treason: \"Should return early if neither a reference or selector were provided\",\n\t\t\tfrom:   &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"GetError\": {\n\t\t\treason: \"Should return errors encountered while getting the referenced resource\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(errBoom),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{\n\t\t\t\t\tReference: ref,\n\t\t\t\t\tTo:        To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:   ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errGetManaged),\n\t\t\t},\n\t\t},\n\t\t\"ResolvedNoValue\": {\n\t\t\treason: \"Should return an error if the extract function returns the empty string\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{\n\t\t\t\t\tReference: ref,\n\t\t\t\t\tTo:        To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:   func(resource.Managed) string { return \"\" },\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{\n\t\t\t\t\tResolvedReference: ref,\n\t\t\t\t},\n\t\t\t\terr: errors.New(errNoValue),\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulResolve\": {\n\t\t\treason: \"No error should be returned when the value is successfully extracted\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{\n\t\t\t\t\tReference: ref,\n\t\t\t\t\tTo:        To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:   ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: ref,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulResolveNamespaced\": {\n\t\t\treason: \"Resolve should be successful when a namespace is given\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{\n\t\t\t\t\tReference: ref,\n\t\t\t\t\tTo:        To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:   ExternalName(),\n\t\t\t\t\tNamespace: \"cool-ns\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: ref,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"OptionalReference\": {\n\t\t\treason: \"No error should be returned when the resolution policy is Optional\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{\n\t\t\t\t\tReference: optionalRef,\n\t\t\t\t\tTo:        To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:   func(resource.Managed) string { return \"\" },\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{\n\t\t\t\t\tResolvedReference: optionalRef,\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"ListError\": {\n\t\t\treason: \"Should return errors encountered while listing potential referenced resources\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(errBoom),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.Selector{},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{},\n\t\t\t\terr: errors.Wrap(errBoom, errListManaged),\n\t\t\t},\n\t\t},\n\t\t\"NoMatches\": {\n\t\t\treason: \"Should return an error when no managed resources match the selector\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.Selector{},\n\t\t\t\t\tTo:       To{List: &FakeManagedList{}},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{},\n\t\t\t\terr: errors.New(errNoMatches),\n\t\t\t},\n\t\t},\n\t\t\"OptionalSelector\": {\n\t\t\treason: \"No error should be returned when the resolution policy is Optional\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.Selector{\n\t\t\t\t\t\tPolicy: &xpv2.Policy{Resolution: &optionalPolicy},\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{}},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulSelect\": {\n\t\t\treason: \"A managed resource with a matching controller reference should be selected and returned\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.Selector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{Items: []resource.Managed{\n\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t}}},\n\t\t\t\t\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: &xpv2.Reference{Name: value},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulSelectNamespaced\": {\n\t\t\treason: \"Resolve should be successful when a namespace is given\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.Selector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{Items: []resource.Managed{\n\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t}}},\n\t\t\t\t\tExtract:   ExternalName(),\n\t\t\t\t\tNamespace: \"cool-ns\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: &xpv2.Reference{Name: value},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlwaysResolveSelector\": {\n\t\t\treason: \"Should not return early if the current value is non-zero, when the resolve policy is set to\" +\n\t\t\t\t\"Always\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.Selector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t\tPolicy:             &xpv2.Policy{Resolve: &alwaysPolicy},\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{Items: []resource.Managed{\n\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t}}},\n\t\t\t\t\tExtract:      ExternalName(),\n\t\t\t\t\tCurrentValue: \"oldValue\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: &xpv2.Reference{Name: value},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"BothReferenceSelector\": {\n\t\t\treason: \"When both Reference and Selector fields set and Policy is not set, the Reference must be resolved\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: ResolutionRequest{\n\t\t\t\t\tReference: ref,\n\t\t\t\t\tSelector: &xpv2.Selector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo:      To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: ResolutionResponse{\n\t\t\t\t\tResolvedValue:     value,\n\t\t\t\t\tResolvedReference: ref,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewAPIResolver(tc.c, tc.from)\n\n\t\t\tgot, err := r.Resolve(tc.args.ctx, tc.args.req)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nControllersMustMatch(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.rsp, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nControllersMustMatch(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestResolveMultiple(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\tnow := metav1.Now()\n\tvalue := \"coolv\"\n\tvalue2 := \"cooler\"\n\tref := xpv2.Reference{Name: \"cool\"}\n\toptionalPolicy := xpv2.ResolutionPolicyOptional\n\talwaysPolicy := xpv2.ResolvePolicyAlways\n\toptionalRef := xpv2.Reference{Name: \"cool\", Policy: &xpv2.Policy{Resolution: &optionalPolicy}}\n\talwaysRef := xpv2.Reference{Name: \"cool\", Policy: &xpv2.Policy{Resolve: &alwaysPolicy}}\n\n\tcontrolled := &fake.Managed{}\n\tcontrolled.SetName(value)\n\tmeta.SetExternalName(controlled, value)\n\tmeta.AddControllerReference(controlled, meta.AsController(&xpv2.TypedReference{UID: types.UID(\"very-unique\")}))\n\n\tcontrolled2 := &fake.Managed{}\n\tcontrolled2.SetName(value2)\n\tmeta.SetExternalName(controlled2, value2)\n\tmeta.AddControllerReference(controlled2, meta.AsController(&xpv2.TypedReference{UID: types.UID(\"very-unique\")}))\n\n\ttype args struct {\n\t\tctx context.Context\n\t\treq MultiResolutionRequest\n\t}\n\n\ttype want struct {\n\t\trsp MultiResolutionResponse\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tc      client.Reader\n\t\tfrom   resource.Managed\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"FromDeleted\": {\n\t\t\treason: \"Should return early if the referencing managed resource was deleted\",\n\t\t\tfrom:   &fake.Managed{ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: &now}},\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlreadyResolved\": {\n\t\t\treason: \"Should return early if the current value is non-zero\",\n\t\t\tfrom:   &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{CurrentValues: []string{value}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{ResolvedValues: []string{value}},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlwaysResolveReference\": {\n\t\t\treason: \"Should not return early if the current value is non-zero, when the resolve policy is set to\" +\n\t\t\t\t\"Always\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tReferences:    []xpv2.Reference{alwaysRef},\n\t\t\t\t\tTo:            To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:       ExternalName(),\n\t\t\t\t\tCurrentValues: []string{\"oldValue\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.Reference{alwaysRef},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"Unresolvable\": {\n\t\t\treason: \"Should return early if neither a reference or selector were provided\",\n\t\t\tfrom:   &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"GetError\": {\n\t\t\treason: \"Should return errors encountered while getting the referenced resource\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(errBoom),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.Reference{ref},\n\t\t\t\t\tTo:         To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:    ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errGetManaged),\n\t\t\t},\n\t\t},\n\t\t\"ResolvedNoValue\": {\n\t\t\treason: \"Should return an error if the extract function returns the empty string\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.Reference{ref},\n\t\t\t\t\tTo:         To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:    func(resource.Managed) string { return \"\" },\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{\"\"},\n\t\t\t\t\tResolvedReferences: []xpv2.Reference{ref},\n\t\t\t\t},\n\t\t\t\terr: errors.New(errNoValue),\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulResolve\": {\n\t\t\treason: \"No error should be returned when the value is successfully extracted\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.Reference{ref},\n\t\t\t\t\tTo:         To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:    ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.Reference{ref},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulResolveNamespaced\": {\n\t\t\treason: \"Resolve should be successful when a namespace is given\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.Reference{ref},\n\t\t\t\t\tTo:         To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:    ExternalName(),\n\t\t\t\t\tNamespace:  \"cool-ns\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.Reference{ref},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"OptionalReference\": {\n\t\t\treason: \"No error should be returned when the resolution policy is Optional\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.Reference{optionalRef},\n\t\t\t\t\tTo:         To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract:    func(resource.Managed) string { return \"\" },\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{\"\"},\n\t\t\t\t\tResolvedReferences: []xpv2.Reference{optionalRef},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"ListError\": {\n\t\t\treason: \"Should return errors encountered while listing potential referenced resources\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(errBoom),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.Selector{},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{},\n\t\t\t\terr: errors.Wrap(errBoom, errListManaged),\n\t\t\t},\n\t\t},\n\t\t\"NoMatches\": {\n\t\t\treason: \"Should return an error when no managed resources match the selector\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.Selector{},\n\t\t\t\t\tTo:       To{List: &FakeManagedList{}},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{},\n\t\t\t\terr: errors.New(errNoMatches),\n\t\t\t},\n\t\t},\n\t\t\"OptionalSelector\": {\n\t\t\treason: \"No error should be returned when the resolution policy is Optional\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.Selector{\n\t\t\t\t\t\tPolicy: &xpv2.Policy{Resolution: &optionalPolicy},\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{}},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulSelect\": {\n\t\t\treason: \"A managed resource with a matching controller reference should be selected and returned\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.Selector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{Items: []resource.Managed{\n\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t}}},\n\t\t\t\t\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.Reference{{Name: value}},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulSelectNamespaced\": {\n\t\t\treason: \"Resolve should be successful when a namespace is given\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.Selector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{Items: []resource.Managed{\n\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t}}},\n\t\t\t\t\tExtract:   ExternalName(),\n\t\t\t\t\tNamespace: \"cool-ns\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.Reference{{Name: value}},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlwaysResolveSelector\": {\n\t\t\treason: \"Should not return early if the current value is non-zero, when the resolve policy is set to\" +\n\t\t\t\t\"Always\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.Selector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t\tPolicy:             &xpv2.Policy{Resolve: &alwaysPolicy},\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{Items: []resource.Managed{\n\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t}}},\n\t\t\t\t\tExtract:       ExternalName(),\n\t\t\t\t\tCurrentValues: []string{\"oldValue\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.Reference{{Name: value}},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"BothReferenceSelector\": {\n\t\t\treason: \"When both Reference and Selector fields set and Policy is not set, the Reference must be resolved\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(errors.New(\"unexpected call to List when resolving Refs only\")),\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: &fake.Managed{},\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.Reference{ref},\n\t\t\t\t\tSelector: &xpv2.Selector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo:      To{Managed: &fake.Managed{}},\n\t\t\t\t\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value},\n\t\t\t\t\tResolvedReferences: []xpv2.Reference{ref},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SelectorOrderOutput\": {\n\t\t\treason: \"Resolved values should be ordered when resolving a selector\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tSelector: &xpv2.Selector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{List: &FakeManagedList{\n\t\t\t\t\t\tItems: []resource.Managed{\n\t\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\t\tcontrolled,      // A resource with a matching controller reference.\n\t\t\t\t\t\t\t&fake.Managed{}, // A resource that does not match.\n\t\t\t\t\t\t\tcontrolled2,     // A resource with a matching controller reference.\n\t\t\t\t\t\t},\n\t\t\t\t\t}},\n\t\t\t\t\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{value2, value},\n\t\t\t\t\tResolvedReferences: []xpv2.Reference{{Name: value2}, {Name: value}},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"NoSelectorOnlyRefs\": {\n\t\t\treason: \"Refs should not be re-ordered when selector is omitted\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(errors.New(\"unexpected call to List when resolving Refs only\")),\n\t\t\t\tMockGet: func(_ context.Context, objKey client.ObjectKey, obj client.Object) error {\n\t\t\t\t\tif !strings.HasPrefix(objKey.Name, testResourceNamePrefix) {\n\t\t\t\t\t\treturn errors.New(\"test resource not found\")\n\t\t\t\t\t}\n\t\t\t\t\tval := strings.Replace(objKey.Name, testResourceNamePrefix, testValuePrefix, 1)\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), val)\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tReferences: []xpv2.Reference{testRefs[2], testRefs[3], testRefs[0], testRefs[1]},\n\t\t\t\t\tTo: To{\n\t\t\t\t\t\tManaged: &fake.Managed{},\n\t\t\t\t\t},\n\t\t\t\t\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{\n\t\t\t\t\tResolvedValues:     []string{testValues[2], testValues[3], testValues[0], testValues[1]},\n\t\t\t\t\tResolvedReferences: []xpv2.Reference{testRefs[2], testRefs[3], testRefs[0], testRefs[1]},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t\"AlwaysResolveSelector_NewValuesOrdered\": {\n\t\t\treason: \"Must resolve new matches and reorder resolved values & refs, when Selector policy is Always and have existing refs and values\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockList: test.NewMockListFn(nil),\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\t\tmeta.SetExternalName(obj.(metav1.Object), value)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\tfrom: controlled,\n\t\t\targs: args{\n\t\t\t\treq: MultiResolutionRequest{\n\t\t\t\t\tCurrentValues: []string{testValues[1], testValues[4]},\n\t\t\t\t\tReferences:    []xpv2.Reference{testRefs[1], testRefs[4]},\n\t\t\t\t\tSelector: &xpv2.Selector{\n\t\t\t\t\t\tMatchControllerRef: func() *bool { t := true; return &t }(),\n\t\t\t\t\t\tPolicy:             &xpv2.Policy{Resolve: &alwaysPolicy},\n\t\t\t\t\t},\n\t\t\t\t\tTo: To{\n\t\t\t\t\t\tManaged: &fake.Managed{},\n\t\t\t\t\t\tList: &FakeManagedList{\n\t\t\t\t\t\t\t// List result is not ordered\n\t\t\t\t\t\t\tItems: []resource.Managed{\n\t\t\t\t\t\t\t\t&fake.Managed{},   // A resource that does not match.\n\t\t\t\t\t\t\t\ttestControlled[2], // A resource with a matching controller reference.\n\t\t\t\t\t\t\t\t&fake.Managed{},   // A resource that does not match.\n\t\t\t\t\t\t\t\ttestControlled[4], // A resource with a matching controller reference.\n\t\t\t\t\t\t\t\ttestControlled[1], // A resource with a matching controller reference and newly introduced\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\tExtract: ExternalName(),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\trsp: MultiResolutionResponse{\n\t\t\t\t\t// expect ordered resolved values\n\t\t\t\t\tResolvedValues:     []string{testValues[1], testValues[2], testValues[4]},\n\t\t\t\t\tResolvedReferences: []xpv2.Reference{testRefs[1], testRefs[2], testRefs[4]},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr := NewAPIResolver(tc.c, tc.from)\n\n\t\t\tgot, err := r.ResolveMultiple(tc.args.ctx, tc.args.req)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nControllersMustMatch(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.rsp, got, cmpopts.EquateEmpty()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nControllersMustMatch(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestControllersMustMatch(t *testing.T) {\n\tcases := map[string]struct {\n\t\ts    *xpv2.Selector\n\t\twant bool\n\t}{\n\t\t\"NilSelector\": {\n\t\t\ts:    nil,\n\t\t\twant: false,\n\t\t},\n\t\t\"NilMatchControllerRef\": {\n\t\t\ts:    &xpv2.Selector{},\n\t\t\twant: false,\n\t\t},\n\t\t\"False\": {\n\t\t\ts:    &xpv2.Selector{MatchControllerRef: func() *bool { f := false; return &f }()},\n\t\t\twant: false,\n\t\t},\n\t\t\"True\": {\n\t\t\ts:    &xpv2.Selector{MatchControllerRef: func() *bool { t := true; return &t }()},\n\t\t\twant: true,\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := ControllersMustMatch(tc.s)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"ControllersMustMatch(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/resource/api.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 resource\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\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\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n)\n\n// Error strings.\nconst (\n\terrUpdateObject = \"cannot update object\"\n)\n\n// An APIPatchingApplicator applies changes to an object by either creating or\n// patching it in a Kubernetes API server.\ntype APIPatchingApplicator struct {\n\tclient client.Client\n}\n\n// NewAPIPatchingApplicator returns an Applicator that applies changes to an\n// object by either creating or patching it in a Kubernetes API server.\nfunc NewAPIPatchingApplicator(c client.Client) *APIPatchingApplicator {\n\treturn &APIPatchingApplicator{client: c}\n}\n\n// Apply changes to the supplied object. The object will be created if it does\n// not exist, or patched if it does. If the object does exist, it will only be\n// patched if the passed object has the same or an empty resource version.\nfunc (a *APIPatchingApplicator) Apply(ctx context.Context, o client.Object, ao ...ApplyOption) error {\n\tm, ok := o.(metav1.Object)\n\tif !ok {\n\t\treturn errors.New(\"cannot access object metadata\")\n\t}\n\n\tif m.GetName() == \"\" && m.GetGenerateName() != \"\" {\n\t\treturn errors.Wrap(a.client.Create(ctx, o), \"cannot create object\")\n\t}\n\n\tdesired := o.DeepCopyObject()\n\n\terr := a.client.Get(ctx, types.NamespacedName{Name: m.GetName(), Namespace: m.GetNamespace()}, o)\n\tif kerrors.IsNotFound(err) {\n\t\t// TODO(negz): Apply ApplyOptions here too?\n\t\treturn errors.Wrap(a.client.Create(ctx, o), \"cannot create object\")\n\t}\n\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"cannot get object\")\n\t}\n\n\tfor _, fn := range ao {\n\t\tif err := fn(ctx, o, desired); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// TODO(negz): Allow callers to override the kind of patch used.\n\treturn errors.Wrap(a.client.Patch(ctx, o, &patch{desired}), \"cannot patch object\")\n}\n\ntype patch struct{ from runtime.Object }\n\nfunc (p *patch) Type() types.PatchType                { return types.MergePatchType }\nfunc (p *patch) Data(_ client.Object) ([]byte, error) { return json.Marshal(p.from) }\n\n// An APIUpdatingApplicator applies changes to an object by either creating or\n// updating it in a Kubernetes API server.\ntype APIUpdatingApplicator struct {\n\tclient client.Client\n}\n\n// NewAPIUpdatingApplicator returns an Applicator that applies changes to an\n// object by either creating or updating it in a Kubernetes API server.\nfunc NewAPIUpdatingApplicator(c client.Client) *APIUpdatingApplicator {\n\treturn &APIUpdatingApplicator{client: c}\n}\n\n// Apply changes to the supplied object. The object will be created if it does\n// not exist, or updated if it does.\nfunc (a *APIUpdatingApplicator) Apply(ctx context.Context, o client.Object, ao ...ApplyOption) error {\n\tm, ok := o.(Object)\n\tif !ok {\n\t\treturn errors.New(\"cannot access object metadata\")\n\t}\n\n\tif m.GetName() == \"\" && m.GetGenerateName() != \"\" {\n\t\treturn errors.Wrap(a.client.Create(ctx, o), \"cannot create object\")\n\t}\n\n\t//nolint:forcetypeassert // Will always be a client.Object.\n\tcurrent := o.DeepCopyObject().(client.Object)\n\n\terr := a.client.Get(ctx, types.NamespacedName{Name: m.GetName(), Namespace: m.GetNamespace()}, current)\n\tif kerrors.IsNotFound(err) {\n\t\t// TODO(negz): Apply ApplyOptions here too?\n\t\treturn errors.Wrap(a.client.Create(ctx, m), \"cannot create object\")\n\t}\n\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"cannot get object\")\n\t}\n\n\tfor _, fn := range ao {\n\t\tif err := fn(ctx, current, m); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// NOTE(hasheddan): we must set the resource version of the desired object\n\t// to that of the current or the update will always fail.\n\tm.SetResourceVersion(current.GetResourceVersion())\n\n\treturn errors.Wrap(a.client.Update(ctx, m), \"cannot update object\")\n}\n\n// An APIFinalizer adds and removes finalizers to and from a resource.\ntype APIFinalizer struct {\n\tclient    client.Client\n\tfinalizer string\n}\n\n// NewNopFinalizer returns a Finalizer that does nothing.\nfunc NewNopFinalizer() Finalizer { return nopFinalizer{} }\n\ntype nopFinalizer struct{}\n\nfunc (f nopFinalizer) AddFinalizer(_ context.Context, _ Object) error {\n\treturn nil\n}\n\nfunc (f nopFinalizer) RemoveFinalizer(_ context.Context, _ Object) error {\n\treturn nil\n}\n\n// NewAPIFinalizer returns a new APIFinalizer.\nfunc NewAPIFinalizer(c client.Client, finalizer string) *APIFinalizer {\n\treturn &APIFinalizer{client: c, finalizer: finalizer}\n}\n\n// AddFinalizer to the supplied Managed resource.\nfunc (a *APIFinalizer) AddFinalizer(ctx context.Context, obj Object) error {\n\tif meta.FinalizerExists(obj, a.finalizer) {\n\t\treturn nil\n\t}\n\n\tmeta.AddFinalizer(obj, a.finalizer)\n\n\treturn errors.Wrap(a.client.Update(ctx, obj), errUpdateObject)\n}\n\n// RemoveFinalizer from the supplied Managed resource.\nfunc (a *APIFinalizer) RemoveFinalizer(ctx context.Context, obj Object) error {\n\tif !meta.FinalizerExists(obj, a.finalizer) {\n\t\treturn nil\n\t}\n\n\tmeta.RemoveFinalizer(obj, a.finalizer)\n\n\treturn errors.Wrap(IgnoreNotFound(a.client.Update(ctx, obj)), errUpdateObject)\n}\n\n// A FinalizerFns satisfy the Finalizer interface.\ntype FinalizerFns struct {\n\tAddFinalizerFn    func(ctx context.Context, obj Object) error\n\tRemoveFinalizerFn func(ctx context.Context, obj Object) error\n}\n\n// AddFinalizer to the supplied resource.\nfunc (f FinalizerFns) AddFinalizer(ctx context.Context, obj Object) error {\n\treturn f.AddFinalizerFn(ctx, obj)\n}\n\n// RemoveFinalizer from the supplied resource.\nfunc (f FinalizerFns) RemoveFinalizer(ctx context.Context, obj Object) error {\n\treturn f.RemoveFinalizerFn(ctx, obj)\n}\n"
  },
  {
    "path": "pkg/resource/api_test.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 resource\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\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\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nfunc TestAPIPatchingApplicator(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\tdesired := &object{}\n\tdesired.SetName(\"desired\")\n\n\ttype args struct {\n\t\tctx context.Context\n\t\to   client.Object\n\t\tao  []ApplyOption\n\t}\n\n\ttype want struct {\n\t\to   client.Object\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tc      client.Client\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"GetError\": {\n\t\t\treason: \"An error should be returned if we can't get the object\",\n\t\t\tc:      &test.MockClient{MockGet: test.NewMockGetFn(errBoom)},\n\t\t\targs: args{\n\t\t\t\to: &object{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\to:   &object{},\n\t\t\t\terr: errors.Wrap(errBoom, \"cannot get object\"),\n\t\t\t},\n\t\t},\n\t\t\"CreateError\": {\n\t\t\treason: \"No error should be returned if we successfully create a new object\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet:    test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, \"\")),\n\t\t\t\tMockCreate: test.NewMockCreateFn(errBoom),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\to: &object{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\to:   &object{},\n\t\t\t\terr: errors.Wrap(errBoom, \"cannot create object\"),\n\t\t\t},\n\t\t},\n\t\t\"ApplyOptionError\": {\n\t\t\treason: \"Any errors from an apply option should be returned\",\n\t\t\tc:      &test.MockClient{MockGet: test.NewMockGetFn(nil)},\n\t\t\targs: args{\n\t\t\t\to:  &object{},\n\t\t\t\tao: []ApplyOption{func(_ context.Context, _, _ runtime.Object) error { return errBoom }},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\to:   &object{},\n\t\t\t\terr: errBoom,\n\t\t\t},\n\t\t},\n\t\t\"PatchError\": {\n\t\t\treason: \"An error should be returned if we can't patch the object\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet:   test.NewMockGetFn(nil),\n\t\t\t\tMockPatch: test.NewMockPatchFn(errBoom),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\to: &object{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\to:   &object{},\n\t\t\t\terr: errors.Wrap(errBoom, \"cannot patch object\"),\n\t\t\t},\n\t\t},\n\t\t\"Created\": {\n\t\t\treason: \"No error should be returned if we successfully create a new object\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, \"\")),\n\t\t\t\tMockCreate: test.NewMockCreateFn(nil, func(o client.Object) error {\n\t\t\t\t\t*o.(*object) = *desired\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\to: desired,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\to: desired,\n\t\t\t},\n\t\t},\n\t\t\"Patched\": {\n\t\t\treason: \"No error should be returned if we successfully patch an existing object\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil),\n\t\t\t\tMockPatch: test.NewMockPatchFn(nil, func(o client.Object) error {\n\t\t\t\t\t*o.(*object) = *desired\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\to: desired,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\to: desired,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ta := NewAPIPatchingApplicator(tc.c)\n\n\t\t\terr := a.Apply(tc.args.ctx, tc.args.o, tc.args.ao...)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nApply(...): -want error, +got error\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.o, tc.args.o); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nApply(...): -want, +got\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAPIUpdatingApplicator(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\tdesired := &object{}\n\tdesired.SetName(\"desired\")\n\n\tcurrent := &object{}\n\tcurrent.SetName(\"current\")\n\n\ttype args struct {\n\t\tctx context.Context\n\t\to   client.Object\n\t\tao  []ApplyOption\n\t}\n\n\ttype want struct {\n\t\to   client.Object\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tc      client.Client\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"GetError\": {\n\t\t\treason: \"An error should be returned if we can't get the object\",\n\t\t\tc:      &test.MockClient{MockGet: test.NewMockGetFn(errBoom)},\n\t\t\targs: args{\n\t\t\t\to: &object{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\to:   &object{},\n\t\t\t\terr: errors.Wrap(errBoom, \"cannot get object\"),\n\t\t\t},\n\t\t},\n\t\t\"CreateError\": {\n\t\t\treason: \"No error should be returned if we successfully create a new object\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet:    test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, \"\")),\n\t\t\t\tMockCreate: test.NewMockCreateFn(errBoom),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\to: &object{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\to:   &object{},\n\t\t\t\terr: errors.Wrap(errBoom, \"cannot create object\"),\n\t\t\t},\n\t\t},\n\t\t\"ApplyOptionError\": {\n\t\t\treason: \"Any errors from an apply option should be returned\",\n\t\t\tc:      &test.MockClient{MockGet: test.NewMockGetFn(nil)},\n\t\t\targs: args{\n\t\t\t\to:  &object{},\n\t\t\t\tao: []ApplyOption{func(_ context.Context, _, _ runtime.Object) error { return errBoom }},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\to:   &object{},\n\t\t\t\terr: errBoom,\n\t\t\t},\n\t\t},\n\t\t\"UpdateError\": {\n\t\t\treason: \"An error should be returned if we can't update the object\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet:    test.NewMockGetFn(nil),\n\t\t\t\tMockUpdate: test.NewMockUpdateFn(errBoom),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\to: &object{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\to:   &object{},\n\t\t\t\terr: errors.Wrap(errBoom, \"cannot update object\"),\n\t\t\t},\n\t\t},\n\t\t\"Created\": {\n\t\t\treason: \"No error should be returned if we successfully create a new object\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, \"\")),\n\t\t\t\tMockCreate: test.NewMockCreateFn(nil, func(o client.Object) error {\n\t\t\t\t\t*o.(*object) = *desired\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\to: desired,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\to: desired,\n\t\t\t},\n\t\t},\n\t\t\"Updated\": {\n\t\t\treason: \"No error should be returned if we successfully update an existing object. If no ApplyOption is passed the existing should not be modified\",\n\t\t\tc: &test.MockClient{\n\t\t\t\tMockGet: test.NewMockGetFn(nil, func(o client.Object) error {\n\t\t\t\t\t*o.(*object) = *current\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t\tMockUpdate: test.NewMockUpdateFn(nil, func(o client.Object) error {\n\t\t\t\t\tif diff := cmp.Diff(*desired, *o.(*object)); diff != \"\" {\n\t\t\t\t\t\tt.Errorf(\"r: -want, +got:\\n%s\", diff)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\to: desired,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\to: desired,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ta := NewAPIUpdatingApplicator(tc.c)\n\n\t\t\terr := a.Apply(tc.args.ctx, tc.args.o, tc.args.ao...)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nApply(...): -want error, +got error\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.o, tc.args.o); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nApply(...): -want, +got\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestManagedRemoveFinalizer(t *testing.T) {\n\tfinalizer := \"veryfinal\"\n\n\ttype args struct {\n\t\tctx context.Context\n\t\tobj Object\n\t}\n\n\ttype want struct {\n\t\terr error\n\t\tobj Object\n\t}\n\n\terrBoom := errors.New(\"boom\")\n\n\tcases := map[string]struct {\n\t\tclient client.Client\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"UpdateError\": {\n\t\t\tclient: &test.MockClient{MockUpdate: test.NewMockUpdateFn(errBoom)},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tobj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errUpdateObject),\n\t\t\t\tobj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},\n\t\t\t},\n\t\t},\n\t\t\"Successful\": {\n\t\t\tclient: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil)},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tobj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: nil,\n\t\t\t\tobj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tapi := NewAPIFinalizer(tc.client, finalizer)\n\n\t\t\terr := api.RemoveFinalizer(tc.args.ctx, tc.args.obj)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"api.RemoveFinalizer(...): -want error, +got error:\\n%s\", diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.obj, tc.args.obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\tt.Errorf(\"api.RemoveFinalizer(...) Managed: -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAPIFinalizerAdder(t *testing.T) {\n\tfinalizer := \"veryfinal\"\n\n\ttype args struct {\n\t\tctx context.Context\n\t\tobj Object\n\t}\n\n\ttype want struct {\n\t\terr error\n\t\tobj Object\n\t}\n\n\terrBoom := errors.New(\"boom\")\n\n\tcases := map[string]struct {\n\t\tclient client.Client\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"UpdateError\": {\n\t\t\tclient: &test.MockClient{MockUpdate: test.NewMockUpdateFn(errBoom)},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tobj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errUpdateObject),\n\t\t\t\tobj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},\n\t\t\t},\n\t\t},\n\t\t\"Successful\": {\n\t\t\tclient: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil)},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tobj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: nil,\n\t\t\t\tobj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tapi := NewAPIFinalizer(tc.client, finalizer)\n\n\t\t\terr := api.AddFinalizer(tc.args.ctx, tc.args.obj)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"api.Initialize(...): -want error, +got error:\\n%s\", diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.obj, tc.args.obj, test.EquateConditions()); diff != \"\" {\n\t\t\t\tt.Errorf(\"api.Initialize(...) Managed: -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/resource/doc.go",
    "content": "/*\nCopyright 2019 The Crossplane 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// Package resource provides types and functions that can be used to build\n// Kubernetes controllers that reconcile Crossplane resources.\npackage resource\n"
  },
  {
    "path": "pkg/resource/enqueue_handlers.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 resource\n\nimport (\n\t\"context\"\n\t\"strings\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"k8s.io/client-go/util/workqueue\"\n\t\"sigs.k8s.io/controller-runtime/pkg/event\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n)\n\ntype adder interface {\n\tAdd(item reconcile.Request)\n}\n\n// RateLimitingInterface for an EnqueueRequestForProviderConfig.\ntype RateLimitingInterface = workqueue.TypedRateLimitingInterface[reconcile.Request]\n\n// EnqueueRequestForProviderConfig enqueues a reconcile.Request for a referenced\n// ProviderConfig.\ntype EnqueueRequestForProviderConfig struct {\n\t// Kind is the expected ProviderConfig kind this handler should process.\n\t// If empty, all kinds are processed (backward compatibility).\n\tKind string\n}\n\n// Create adds a NamespacedName for the supplied CreateEvent if its Object is a\n// ProviderConfigReferencer.\nfunc (e *EnqueueRequestForProviderConfig) Create(_ context.Context, evt event.CreateEvent, q RateLimitingInterface) {\n\te.addProviderConfig(evt.Object, q)\n}\n\n// Update adds a NamespacedName for the supplied UpdateEvent if its Objects are\n// a ProviderConfigReferencer.\nfunc (e *EnqueueRequestForProviderConfig) Update(_ context.Context, evt event.UpdateEvent, q RateLimitingInterface) {\n\te.addProviderConfig(evt.ObjectOld, q)\n\te.addProviderConfig(evt.ObjectNew, q)\n}\n\n// Delete adds a NamespacedName for the supplied DeleteEvent if its Object is a\n// ProviderConfigReferencer.\nfunc (e *EnqueueRequestForProviderConfig) Delete(_ context.Context, evt event.DeleteEvent, q RateLimitingInterface) {\n\te.addProviderConfig(evt.Object, q)\n}\n\n// Generic adds a NamespacedName for the supplied GenericEvent if its Object is\n// a ProviderConfigReferencer.\nfunc (e *EnqueueRequestForProviderConfig) Generic(_ context.Context, evt event.GenericEvent, q RateLimitingInterface) {\n\te.addProviderConfig(evt.Object, q)\n}\n\nfunc (e *EnqueueRequestForProviderConfig) addProviderConfig(obj runtime.Object, queue adder) {\n\tswitch pcr := obj.(type) {\n\tcase TypedProviderConfigUsage:\n\t\tref := pcr.GetProviderConfigReference()\n\t\trefKind := ref.Kind\n\t\tif refKind == \"\" {\n\t\t\trefKind = \"ProviderConfig\"\n\t\t}\n\t\tif e.Kind != \"\" && refKind != e.Kind {\n\t\t\treturn\n\t\t}\n\n\t\tif strings.HasPrefix(refKind, \"Cluster\") {\n\t\t\tqueue.Add(reconcile.Request{NamespacedName: types.NamespacedName{Name: ref.Name}})\n\t\t} else {\n\t\t\tqueue.Add(reconcile.Request{NamespacedName: types.NamespacedName{Name: ref.Name, Namespace: pcr.GetNamespace()}})\n\t\t}\n\tcase LegacyProviderConfigUsage:\n\t\tqueue.Add(reconcile.Request{NamespacedName: types.NamespacedName{Name: pcr.GetProviderConfigReference().Name}})\n\t}\n}\n"
  },
  {
    "path": "pkg/resource/enqueue_handlers_test.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 resource\n\nimport (\n\t\"testing\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\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\"sigs.k8s.io/controller-runtime/pkg/handler\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n)\n\nvar _ handler.EventHandler = &EnqueueRequestForProviderConfig{}\n\ntype addFn func(item any)\n\nfunc (fn addFn) Add(item reconcile.Request) {\n\tfn(item)\n}\n\nfunc TestAddProviderConfig(t *testing.T) {\n\tname := \"coolname\"\n\n\tcases := map[string]struct {\n\t\thandler *EnqueueRequestForProviderConfig\n\t\tobj     runtime.Object\n\t\tqueue   adder\n\t}{\n\t\t\"NotProviderConfigReferencer\": {\n\t\t\thandler: &EnqueueRequestForProviderConfig{},\n\t\t\tqueue:   addFn(func(_ any) { t.Errorf(\"queue.Add() called unexpectedly\") }),\n\t\t},\n\t\t\"IsLegacyProviderConfigReferencer\": {\n\t\t\thandler: &EnqueueRequestForProviderConfig{},\n\t\t\tobj: &fake.LegacyProviderConfigUsage{\n\t\t\t\tRequiredProviderConfigReferencer: fake.RequiredProviderConfigReferencer{\n\t\t\t\t\tRef: xpv2.Reference{Name: name},\n\t\t\t\t},\n\t\t\t},\n\t\t\tqueue: addFn(func(got any) {\n\t\t\t\twant := reconcile.Request{NamespacedName: types.NamespacedName{Name: name}}\n\t\t\t\tif diff := cmp.Diff(want, got); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"-want, +got:\\n%s\", diff)\n\t\t\t\t}\n\t\t\t}),\n\t\t},\n\t\t\"IsTypedProviderConfigReferencer\": {\n\t\t\thandler: &EnqueueRequestForProviderConfig{},\n\t\t\tobj: &fake.ProviderConfigUsage{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"some-pcu\",\n\t\t\t\t\tNamespace: \"foo\",\n\t\t\t\t},\n\t\t\t\tRequiredTypedProviderConfigReferencer: fake.RequiredTypedProviderConfigReferencer{\n\t\t\t\t\tRef: xpv2.ProviderConfigReference{Name: name, Kind: \"ProviderConfig\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tqueue: addFn(func(got any) {\n\t\t\t\twant := reconcile.Request{NamespacedName: types.NamespacedName{Name: name, Namespace: \"foo\"}}\n\t\t\t\tif diff := cmp.Diff(want, got); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"-want, +got:\\n%s\", diff)\n\t\t\t\t}\n\t\t\t}),\n\t\t},\n\t\t\"ClusterScopedProviderConfigOmitsNamespace\": {\n\t\t\thandler: &EnqueueRequestForProviderConfig{Kind: \"ClusterProviderConfig\"},\n\t\t\tobj: &fake.ProviderConfigUsage{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"some-pcu\",\n\t\t\t\t\tNamespace: \"foo\",\n\t\t\t\t},\n\t\t\t\tRequiredTypedProviderConfigReferencer: fake.RequiredTypedProviderConfigReferencer{\n\t\t\t\t\tRef: xpv2.ProviderConfigReference{Name: name, Kind: \"ClusterProviderConfig\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tqueue: addFn(func(got any) {\n\t\t\t\twant := reconcile.Request{NamespacedName: types.NamespacedName{Name: name}}\n\t\t\t\tif diff := cmp.Diff(want, got); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"-want, +got:\\n%s\", diff)\n\t\t\t\t}\n\t\t\t}),\n\t\t},\n\t\t\"KindFilterMatchesKind\": {\n\t\t\thandler: &EnqueueRequestForProviderConfig{Kind: \"ProviderConfig\"},\n\t\t\tobj: &fake.ProviderConfigUsage{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"some-pcu\",\n\t\t\t\t\tNamespace: \"bar\",\n\t\t\t\t},\n\t\t\t\tRequiredTypedProviderConfigReferencer: fake.RequiredTypedProviderConfigReferencer{\n\t\t\t\t\tRef: xpv2.ProviderConfigReference{Name: name, Kind: \"ProviderConfig\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tqueue: addFn(func(got any) {\n\t\t\t\twant := reconcile.Request{NamespacedName: types.NamespacedName{Name: name, Namespace: \"bar\"}}\n\t\t\t\tif diff := cmp.Diff(want, got); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"-want, +got:\\n%s\", diff)\n\t\t\t\t}\n\t\t\t}),\n\t\t},\n\t\t\"KindFilterSkipsNonMatchingKind\": {\n\t\t\thandler: &EnqueueRequestForProviderConfig{Kind: \"ProviderConfig\"},\n\t\t\tobj: &fake.ProviderConfigUsage{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"some-pcu\",\n\t\t\t\t\tNamespace: \"bar\",\n\t\t\t\t},\n\t\t\t\tRequiredTypedProviderConfigReferencer: fake.RequiredTypedProviderConfigReferencer{\n\t\t\t\t\tRef: xpv2.ProviderConfigReference{Name: name, Kind: \"OtherProviderConfig\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tqueue: addFn(func(_ any) { t.Errorf(\"queue.Add() called unexpectedly for non-matching kind\") }),\n\t\t},\n\t\t\"EmptyRefKindDefaultsToProviderConfig\": {\n\t\t\thandler: &EnqueueRequestForProviderConfig{Kind: \"ProviderConfig\"},\n\t\t\tobj: &fake.ProviderConfigUsage{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      \"some-pcu\",\n\t\t\t\t\tNamespace: \"baz\",\n\t\t\t\t},\n\t\t\t\tRequiredTypedProviderConfigReferencer: fake.RequiredTypedProviderConfigReferencer{\n\t\t\t\t\tRef: xpv2.ProviderConfigReference{Name: name, Kind: \"\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tqueue: addFn(func(got any) {\n\t\t\t\twant := reconcile.Request{NamespacedName: types.NamespacedName{Name: name, Namespace: \"baz\"}}\n\t\t\t\tif diff := cmp.Diff(want, got); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"-want, +got:\\n%s\", diff)\n\t\t\t\t}\n\t\t\t}),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(_ *testing.T) {\n\t\t\ttc.handler.addProviderConfig(tc.obj, tc.queue)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/resource/fake/mocks.go",
    "content": "/*\nCopyright 2019 The Crossplane 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// Package fake provides fake Crossplane resources for use in tests.\n//\n//nolint:musttag // We only use JSON to round-trip convert these mocks.\npackage fake\n\nimport (\n\t\"encoding/json\"\n\t\"reflect\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/go-logr/logr\"\n\tcorev1 \"k8s.io/api/core/v1\"\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/rest\"\n\t\"sigs.k8s.io/controller-runtime/pkg/cache\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/manager\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/unstructured/reference\"\n)\n\n// Conditioned is a mock that implements Conditioned interface.\ntype Conditioned struct{ Conditions []xpv2.Condition }\n\n// SetConditions sets the Conditions.\nfunc (m *Conditioned) SetConditions(c ...xpv2.Condition) { m.Conditions = c }\n\n// GetCondition get the Condition with the given ConditionType.\nfunc (m *Conditioned) GetCondition(ct xpv2.ConditionType) xpv2.Condition {\n\treturn xpv2.Condition{Type: ct, Status: corev1.ConditionUnknown}\n}\n\n// ClaimReferencer is a mock that implements ClaimReferencer interface.\ntype ClaimReferencer struct{ Ref *reference.Claim }\n\n// SetClaimReference sets the ClaimReference.\nfunc (m *ClaimReferencer) SetClaimReference(r *reference.Claim) { m.Ref = r }\n\n// GetClaimReference gets the ClaimReference.\nfunc (m *ClaimReferencer) GetClaimReference() *reference.Claim { return m.Ref }\n\n// ManagedResourceReferencer is a mock that implements ManagedResourceReferencer interface.\ntype ManagedResourceReferencer struct{ Ref *corev1.ObjectReference }\n\n// SetResourceReference sets the ResourceReference.\nfunc (m *ManagedResourceReferencer) SetResourceReference(r *corev1.ObjectReference) { m.Ref = r }\n\n// GetResourceReference gets the ResourceReference.\nfunc (m *ManagedResourceReferencer) GetResourceReference() *corev1.ObjectReference { return m.Ref }\n\n// TypedProviderConfigReferencer is a mock that implements resource.TypedProviderConfigReferencer interface.\ntype TypedProviderConfigReferencer struct{ Ref *xpv2.ProviderConfigReference }\n\n// SetProviderConfigReference sets the ProviderConfigReference.\nfunc (m *TypedProviderConfigReferencer) SetProviderConfigReference(p *xpv2.ProviderConfigReference) {\n\tm.Ref = p\n}\n\n// GetProviderConfigReference gets the ProviderConfigReference.\nfunc (m *TypedProviderConfigReferencer) GetProviderConfigReference() *xpv2.ProviderConfigReference {\n\treturn m.Ref\n}\n\n// LegacyProviderConfigReferencer is a mock that implements resource.ProviderConfigReferencer interface.\ntype LegacyProviderConfigReferencer struct{ Ref *xpv2.Reference }\n\n// SetProviderConfigReference sets the ProviderConfigReference.\nfunc (m *LegacyProviderConfigReferencer) SetProviderConfigReference(p *xpv2.Reference) { m.Ref = p }\n\n// GetProviderConfigReference gets the ProviderConfigReference.\nfunc (m *LegacyProviderConfigReferencer) GetProviderConfigReference() *xpv2.Reference {\n\treturn m.Ref\n}\n\n// RequiredProviderConfigReferencer is a mock that implements the\n// RequiredProviderConfigReferencer interface.\ntype RequiredProviderConfigReferencer struct{ Ref xpv2.Reference }\n\n// SetProviderConfigReference sets the ProviderConfigReference.\nfunc (m *RequiredProviderConfigReferencer) SetProviderConfigReference(p xpv2.Reference) {\n\tm.Ref = p\n}\n\n// GetProviderConfigReference gets the ProviderConfigReference.\nfunc (m *RequiredProviderConfigReferencer) GetProviderConfigReference() xpv2.Reference {\n\treturn m.Ref\n}\n\n// RequiredTypedProviderConfigReferencer is a mock that implements the\n// RequiredTypedProviderConfigReferencer interface.\ntype RequiredTypedProviderConfigReferencer struct{ Ref xpv2.ProviderConfigReference }\n\n// SetProviderConfigReference sets the ProviderConfigReference.\nfunc (m *RequiredTypedProviderConfigReferencer) SetProviderConfigReference(p xpv2.ProviderConfigReference) {\n\tm.Ref = p\n}\n\n// GetProviderConfigReference gets the ProviderConfigReference.\nfunc (m *RequiredTypedProviderConfigReferencer) GetProviderConfigReference() xpv2.ProviderConfigReference {\n\treturn m.Ref\n}\n\n// RequiredTypedResourceReferencer is a mock that implements the\n// RequiredTypedResourceReferencer interface.\ntype RequiredTypedResourceReferencer struct{ Ref xpv2.TypedReference }\n\n// SetResourceReference sets the ResourceReference.\nfunc (m *RequiredTypedResourceReferencer) SetResourceReference(p xpv2.TypedReference) {\n\tm.Ref = p\n}\n\n// GetResourceReference gets the ResourceReference.\nfunc (m *RequiredTypedResourceReferencer) GetResourceReference() xpv2.TypedReference {\n\treturn m.Ref\n}\n\n// LocalConnectionSecretWriterTo is a mock that implements LocalConnectionSecretWriterTo interface.\ntype LocalConnectionSecretWriterTo struct {\n\tRef *xpv2.LocalSecretReference\n}\n\n// SetWriteConnectionSecretToReference sets the WriteConnectionSecretToReference.\nfunc (m *LocalConnectionSecretWriterTo) SetWriteConnectionSecretToReference(r *xpv2.LocalSecretReference) {\n\tm.Ref = r\n}\n\n// GetWriteConnectionSecretToReference gets the WriteConnectionSecretToReference.\nfunc (m *LocalConnectionSecretWriterTo) GetWriteConnectionSecretToReference() *xpv2.LocalSecretReference {\n\treturn m.Ref\n}\n\n// ConnectionSecretWriterTo is a mock that implements ConnectionSecretWriterTo interface.\ntype ConnectionSecretWriterTo struct{ Ref *xpv2.SecretReference }\n\n// SetWriteConnectionSecretToReference sets the WriteConnectionSecretToReference.\nfunc (m *ConnectionSecretWriterTo) SetWriteConnectionSecretToReference(r *xpv2.SecretReference) {\n\tm.Ref = r\n}\n\n// GetWriteConnectionSecretToReference gets the WriteConnectionSecretToReference.\nfunc (m *ConnectionSecretWriterTo) GetWriteConnectionSecretToReference() *xpv2.SecretReference {\n\treturn m.Ref\n}\n\n// Manageable implements the Manageable interface.\ntype Manageable struct{ Policy xpv2.ManagementPolicies }\n\n// SetManagementPolicies sets the ManagementPolicies.\nfunc (m *Manageable) SetManagementPolicies(p xpv2.ManagementPolicies) { m.Policy = p }\n\n// GetManagementPolicies gets the ManagementPolicies.\nfunc (m *Manageable) GetManagementPolicies() xpv2.ManagementPolicies { return m.Policy }\n\n// Orphanable implements the Orphanable interface.\ntype Orphanable struct{ Policy xpv2.DeletionPolicy }\n\n// SetDeletionPolicy sets the DeletionPolicy.\nfunc (m *Orphanable) SetDeletionPolicy(p xpv2.DeletionPolicy) { m.Policy = p }\n\n// GetDeletionPolicy gets the DeletionPolicy.\nfunc (m *Orphanable) GetDeletionPolicy() xpv2.DeletionPolicy { return m.Policy }\n\n// CompositionReferencer is a mock that implements CompositionReferencer interface.\ntype CompositionReferencer struct{ Ref *corev1.ObjectReference }\n\n// SetCompositionReference sets the CompositionReference.\nfunc (m *CompositionReferencer) SetCompositionReference(r *corev1.ObjectReference) { m.Ref = r }\n\n// GetCompositionReference gets the CompositionReference.\nfunc (m *CompositionReferencer) GetCompositionReference() *corev1.ObjectReference { return m.Ref }\n\n// CompositionSelector is a mock that implements CompositionSelector interface.\ntype CompositionSelector struct{ Sel *metav1.LabelSelector }\n\n// SetCompositionSelector sets the CompositionSelector.\nfunc (m *CompositionSelector) SetCompositionSelector(s *metav1.LabelSelector) { m.Sel = s }\n\n// GetCompositionSelector gets the CompositionSelector.\nfunc (m *CompositionSelector) GetCompositionSelector() *metav1.LabelSelector { return m.Sel }\n\n// CompositionRevisionReferencer is a mock that implements CompositionRevisionReferencer interface.\ntype CompositionRevisionReferencer struct{ Ref *corev1.LocalObjectReference }\n\n// SetCompositionRevisionReference sets the CompositionRevisionReference.\nfunc (m *CompositionRevisionReferencer) SetCompositionRevisionReference(r *corev1.LocalObjectReference) {\n\tm.Ref = r\n}\n\n// GetCompositionRevisionReference gets the CompositionRevisionReference.\nfunc (m *CompositionRevisionReferencer) GetCompositionRevisionReference() *corev1.LocalObjectReference {\n\treturn m.Ref\n}\n\n// CompositionRevisionSelector is a mock that implements CompositionRevisionSelector interface.\ntype CompositionRevisionSelector struct{ Sel *metav1.LabelSelector }\n\n// SetCompositionRevisionSelector sets the CompositionRevisionSelector.\nfunc (m *CompositionRevisionSelector) SetCompositionRevisionSelector(ls *metav1.LabelSelector) {\n\tm.Sel = ls\n}\n\n// GetCompositionRevisionSelector gets the CompositionRevisionSelector.\nfunc (m *CompositionRevisionSelector) GetCompositionRevisionSelector() *metav1.LabelSelector {\n\treturn m.Sel\n}\n\n// CompositionUpdater is a mock that implements CompositionUpdater interface.\ntype CompositionUpdater struct{ Policy *xpv2.UpdatePolicy }\n\n// SetCompositionUpdatePolicy sets the CompositionUpdatePolicy.\nfunc (m *CompositionUpdater) SetCompositionUpdatePolicy(p *xpv2.UpdatePolicy) {\n\tm.Policy = p\n}\n\n// GetCompositionUpdatePolicy gets the CompositionUpdatePolicy.\nfunc (m *CompositionUpdater) GetCompositionUpdatePolicy() *xpv2.UpdatePolicy {\n\treturn m.Policy\n}\n\n// CompositeResourceDeleter is a mock that implements CompositeResourceDeleter interface.\ntype CompositeResourceDeleter struct{ Policy *xpv2.CompositeDeletePolicy }\n\n// SetCompositeDeletePolicy sets the CompositeDeletePolicy.\nfunc (m *CompositeResourceDeleter) SetCompositeDeletePolicy(p *xpv2.CompositeDeletePolicy) {\n\tm.Policy = p\n}\n\n// GetCompositeDeletePolicy gets the CompositeDeletePolicy.\nfunc (m *CompositeResourceDeleter) GetCompositeDeletePolicy() *xpv2.CompositeDeletePolicy {\n\treturn m.Policy\n}\n\n// CompositeResourceReferencer is a mock that implements CompositeResourceReferencer interface.\ntype CompositeResourceReferencer struct{ Ref *reference.Composite }\n\n// SetResourceReference sets the composite resource reference.\nfunc (m *CompositeResourceReferencer) SetResourceReference(p *reference.Composite) { m.Ref = p }\n\n// GetResourceReference gets the composite resource reference.\nfunc (m *CompositeResourceReferencer) GetResourceReference() *reference.Composite { return m.Ref }\n\n// ComposedResourcesReferencer is a mock that implements ComposedResourcesReferencer interface.\ntype ComposedResourcesReferencer struct{ Refs []corev1.ObjectReference }\n\n// SetResourceReferences sets the composed references.\nfunc (m *ComposedResourcesReferencer) SetResourceReferences(r []corev1.ObjectReference) { m.Refs = r }\n\n// GetResourceReferences gets the composed references.\nfunc (m *ComposedResourcesReferencer) GetResourceReferences() []corev1.ObjectReference { return m.Refs }\n\n// An EnvironmentConfigReferencer is a mock that implements the\n// EnvironmentConfigReferencer interface.\ntype EnvironmentConfigReferencer struct{ Refs []corev1.ObjectReference }\n\n// SetEnvironmentConfigReferences sets the EnvironmentConfig references.\nfunc (m *EnvironmentConfigReferencer) SetEnvironmentConfigReferences(refs []corev1.ObjectReference) {\n\tm.Refs = refs\n}\n\n// GetEnvironmentConfigReferences gets the EnvironmentConfig references.\nfunc (m *EnvironmentConfigReferencer) GetEnvironmentConfigReferences() []corev1.ObjectReference {\n\treturn m.Refs\n}\n\n// ConnectionDetailsLastPublishedTimer is a mock that implements the\n// ConnectionDetailsLastPublishedTimer interface.\ntype ConnectionDetailsLastPublishedTimer struct {\n\t// NOTE: runtime.DefaultUnstructuredConverter.ToUnstructured\n\t// cannot currently handle if `Time` is nil here.\n\t// The `omitempty` json tag is a workaround that\n\t// prevents a panic.\n\tTime *metav1.Time `json:\"lastPublishedTime,omitempty\"`\n}\n\n// SetConnectionDetailsLastPublishedTime sets the published time.\nfunc (c *ConnectionDetailsLastPublishedTimer) SetConnectionDetailsLastPublishedTime(t *metav1.Time) {\n\tc.Time = t\n}\n\n// GetConnectionDetailsLastPublishedTime gets the published time.\nfunc (c *ConnectionDetailsLastPublishedTimer) GetConnectionDetailsLastPublishedTime() *metav1.Time {\n\treturn c.Time\n}\n\n// UserCounter is a mock that satisfies UserCounter\n// interface.\ntype UserCounter struct{ Users int64 }\n\n// SetUsers sets the count of users.\nfunc (m *UserCounter) SetUsers(i int64) {\n\tm.Users = i\n}\n\n// GetUsers gets the count of users.\nfunc (m *UserCounter) GetUsers() int64 {\n\treturn m.Users\n}\n\n// Object is a mock that implements Object interface.\ntype Object struct {\n\tmetav1.ObjectMeta\n\truntime.Object\n}\n\n// GetObjectKind returns schema.ObjectKind.\nfunc (o *Object) GetObjectKind() schema.ObjectKind {\n\treturn schema.EmptyObjectKind\n}\n\n// DeepCopyObject returns a copy of the object as runtime.Object.\nfunc (o *Object) DeepCopyObject() runtime.Object {\n\tout := &Object{}\n\n\tj, err := json.Marshal(o)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_ = json.Unmarshal(j, out)\n\n\treturn out\n}\n\n// Managed is a mock that implements Managed interface.\ntype Managed struct {\n\tmetav1.ObjectMeta\n\tManageable\n\txpv2.ConditionedStatus\n}\n\n// GetObjectKind returns schema.ObjectKind.\nfunc (m *Managed) GetObjectKind() schema.ObjectKind {\n\treturn schema.EmptyObjectKind\n}\n\n// DeepCopyObject returns a copy of the object as runtime.Object.\nfunc (m *Managed) DeepCopyObject() runtime.Object {\n\tout := &Managed{}\n\n\tj, err := json.Marshal(m)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_ = json.Unmarshal(j, out)\n\n\treturn out\n}\n\n// ModernManaged is a mock that implements ModernManaged interface.\ntype ModernManaged struct {\n\tmetav1.ObjectMeta\n\tTypedProviderConfigReferencer\n\tLocalConnectionSecretWriterTo\n\tManageable\n\txpv2.ConditionedStatus\n\txpv2.ObservedStatus\n}\n\n// GetObjectKind returns schema.ObjectKind.\nfunc (m *ModernManaged) GetObjectKind() schema.ObjectKind {\n\treturn schema.EmptyObjectKind\n}\n\n// DeepCopyObject returns a copy of the object as runtime.Object.\nfunc (m *ModernManaged) DeepCopyObject() runtime.Object {\n\tout := &ModernManaged{}\n\n\tj, err := json.Marshal(m)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_ = json.Unmarshal(j, out)\n\n\treturn out\n}\n\n// LegacyManaged is a mock that implements LegacyManaged interface.\ntype LegacyManaged struct {\n\tmetav1.ObjectMeta\n\tLegacyProviderConfigReferencer\n\tConnectionSecretWriterTo\n\tManageable\n\tOrphanable\n\txpv2.ConditionedStatus\n}\n\n// GetObjectKind returns schema.ObjectKind.\nfunc (m *LegacyManaged) GetObjectKind() schema.ObjectKind {\n\treturn schema.EmptyObjectKind\n}\n\n// DeepCopyObject returns a copy of the object as runtime.Object.\nfunc (m *LegacyManaged) DeepCopyObject() runtime.Object {\n\tout := &LegacyManaged{}\n\n\tj, err := json.Marshal(m)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_ = json.Unmarshal(j, out)\n\n\treturn out\n}\n\n// Composite is a mock that implements Composite interface.\ntype Composite struct {\n\tmetav1.ObjectMeta\n\tCompositionSelector\n\tCompositionReferencer\n\tCompositionRevisionReferencer\n\tCompositionRevisionSelector\n\tCompositionUpdater\n\tComposedResourcesReferencer\n\tEnvironmentConfigReferencer\n\tClaimReferencer\n\tConnectionSecretWriterTo\n\n\txpv2.ConditionedStatus\n\txpv2.ObservedStatus\n\tConnectionDetailsLastPublishedTimer\n}\n\n// GetObjectKind returns schema.ObjectKind.\nfunc (m *Composite) GetObjectKind() schema.ObjectKind {\n\treturn schema.EmptyObjectKind\n}\n\n// DeepCopyObject returns a copy of the object as runtime.Object.\nfunc (m *Composite) DeepCopyObject() runtime.Object {\n\tout := &Composite{}\n\n\tj, err := json.Marshal(m)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_ = json.Unmarshal(j, out)\n\n\treturn out\n}\n\n// Composed is a mock that implements Composed interface.\ntype Composed struct {\n\tmetav1.ObjectMeta\n\tConnectionSecretWriterTo\n\txpv2.ConditionedStatus\n\txpv2.ObservedStatus\n}\n\n// GetObjectKind returns schema.ObjectKind.\nfunc (m *Composed) GetObjectKind() schema.ObjectKind {\n\treturn schema.EmptyObjectKind\n}\n\n// DeepCopyObject returns a copy of the object as runtime.Object.\nfunc (m *Composed) DeepCopyObject() runtime.Object {\n\tout := &Composed{}\n\n\tj, err := json.Marshal(m)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_ = json.Unmarshal(j, out)\n\n\treturn out\n}\n\n// CompositeClaim is a mock that implements the CompositeClaim interface.\ntype CompositeClaim struct {\n\tmetav1.ObjectMeta\n\tCompositionSelector\n\tCompositionReferencer\n\tCompositionRevisionReferencer\n\tCompositionRevisionSelector\n\tCompositeResourceDeleter\n\tCompositionUpdater\n\tCompositeResourceReferencer\n\tLocalConnectionSecretWriterTo\n\n\txpv2.ConditionedStatus\n\txpv2.ObservedStatus\n\tConnectionDetailsLastPublishedTimer\n}\n\n// GetObjectKind returns schema.ObjectKind.\nfunc (m *CompositeClaim) GetObjectKind() schema.ObjectKind {\n\treturn schema.EmptyObjectKind\n}\n\n// DeepCopyObject returns a copy of the object as runtime.Object.\nfunc (m *CompositeClaim) DeepCopyObject() runtime.Object {\n\tout := &CompositeClaim{}\n\n\tj, err := json.Marshal(m)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_ = json.Unmarshal(j, out)\n\n\treturn out\n}\n\n// Manager is a mock object that satisfies manager.Manager interface.\ntype Manager struct {\n\tmanager.Manager\n\n\tCache      cache.Cache\n\tClient     client.Client\n\tScheme     *runtime.Scheme\n\tConfig     *rest.Config\n\tRESTMapper meta.RESTMapper\n\tLogger     logr.Logger\n}\n\n// Elected returns a closed channel.\nfunc (m *Manager) Elected() <-chan struct{} {\n\te := make(chan struct{})\n\tclose(e)\n\n\treturn e\n}\n\n// GetCache returns the cache.\nfunc (m *Manager) GetCache() cache.Cache { return m.Cache }\n\n// GetClient returns the client.\nfunc (m *Manager) GetClient() client.Client { return m.Client }\n\n// GetScheme returns the scheme.\nfunc (m *Manager) GetScheme() *runtime.Scheme { return m.Scheme }\n\n// GetConfig returns the config.\nfunc (m *Manager) GetConfig() *rest.Config { return m.Config }\n\n// GetRESTMapper returns the REST mapper.\nfunc (m *Manager) GetRESTMapper() meta.RESTMapper { return m.RESTMapper }\n\n// GetLogger returns the logger.\nfunc (m *Manager) GetLogger() logr.Logger { return m.Logger }\n\n// GV returns a mock schema.GroupVersion.\nvar GV = schema.GroupVersion{Group: \"g\", Version: \"v\"} //nolint:gochecknoglobals // We treat this as a constant.\n\n// GVK returns the mock GVK of the given object.\nfunc GVK(o runtime.Object) schema.GroupVersionKind {\n\treturn GV.WithKind(reflect.TypeOf(o).Elem().Name())\n}\n\n// SchemeWith returns a scheme with list of `runtime.Object`s registered.\nfunc SchemeWith(o ...runtime.Object) *runtime.Scheme {\n\ts := runtime.NewScheme()\n\ts.AddKnownTypes(GV, o...)\n\n\treturn s\n}\n\n// MockConnectionSecretOwner is a mock object that satisfies ConnectionSecretOwner\n// interface.\ntype MockConnectionSecretOwner struct {\n\truntime.Object\n\tmetav1.ObjectMeta\n\n\tWriterTo *xpv2.SecretReference\n}\n\n// GetWriteConnectionSecretToReference returns the connection secret reference.\nfunc (m *MockConnectionSecretOwner) GetWriteConnectionSecretToReference() *xpv2.SecretReference {\n\treturn m.WriterTo\n}\n\n// SetWriteConnectionSecretToReference sets the connection secret reference.\nfunc (m *MockConnectionSecretOwner) SetWriteConnectionSecretToReference(r *xpv2.SecretReference) {\n\tm.WriterTo = r\n}\n\n// GetObjectKind returns schema.ObjectKind.\nfunc (m *MockConnectionSecretOwner) GetObjectKind() schema.ObjectKind {\n\treturn schema.EmptyObjectKind\n}\n\n// DeepCopyObject returns a copy of the object as runtime.Object.\nfunc (m *MockConnectionSecretOwner) DeepCopyObject() runtime.Object {\n\tout := &MockConnectionSecretOwner{}\n\n\tj, err := json.Marshal(m)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_ = json.Unmarshal(j, out)\n\n\treturn out\n}\n\n// MockLocalConnectionSecretOwner is a mock object that satisfies LocalConnectionSecretOwner\n// interface.\ntype MockLocalConnectionSecretOwner struct {\n\truntime.Object\n\tmetav1.ObjectMeta\n\n\tRef *xpv2.LocalSecretReference\n}\n\n// GetWriteConnectionSecretToReference returns the connection secret reference.\nfunc (m *MockLocalConnectionSecretOwner) GetWriteConnectionSecretToReference() *xpv2.LocalSecretReference {\n\treturn m.Ref\n}\n\n// SetWriteConnectionSecretToReference sets the connection secret reference.\nfunc (m *MockLocalConnectionSecretOwner) SetWriteConnectionSecretToReference(r *xpv2.LocalSecretReference) {\n\tm.Ref = r\n}\n\n// GetObjectKind returns schema.ObjectKind.\nfunc (m *MockLocalConnectionSecretOwner) GetObjectKind() schema.ObjectKind {\n\treturn schema.EmptyObjectKind\n}\n\n// DeepCopyObject returns a copy of the object as runtime.Object.\nfunc (m *MockLocalConnectionSecretOwner) DeepCopyObject() runtime.Object {\n\tout := &MockLocalConnectionSecretOwner{}\n\n\tj, err := json.Marshal(m)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_ = json.Unmarshal(j, out)\n\n\treturn out\n}\n\n// ProviderConfig is a mock implementation of the ProviderConfig interface.\ntype ProviderConfig struct {\n\tmetav1.ObjectMeta\n\n\tUserCounter\n\txpv2.ConditionedStatus\n}\n\n// GetObjectKind returns schema.ObjectKind.\nfunc (p *ProviderConfig) GetObjectKind() schema.ObjectKind {\n\treturn schema.EmptyObjectKind\n}\n\n// DeepCopyObject returns a copy of the object as runtime.Object.\nfunc (p *ProviderConfig) DeepCopyObject() runtime.Object {\n\tout := &ProviderConfig{}\n\n\tj, err := json.Marshal(p)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_ = json.Unmarshal(j, out)\n\n\treturn out\n}\n\n// ProviderConfigUsage is a mock implementation of the ProviderConfigUsage\n// interface.\ntype ProviderConfigUsage struct {\n\tmetav1.ObjectMeta\n\n\tRequiredTypedProviderConfigReferencer\n\tRequiredTypedResourceReferencer\n}\n\n// GetObjectKind returns schema.ObjectKind.\nfunc (p *ProviderConfigUsage) GetObjectKind() schema.ObjectKind {\n\treturn schema.EmptyObjectKind\n}\n\n// DeepCopyObject returns a copy of the object as runtime.Object.\nfunc (p *ProviderConfigUsage) DeepCopyObject() runtime.Object {\n\tout := &ProviderConfigUsage{}\n\n\tj, err := json.Marshal(p)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_ = json.Unmarshal(j, out)\n\n\treturn out\n}\n\n// LegacyProviderConfigUsage is a mock implementation of the LegacyProviderConfigUsage\n// interface.\ntype LegacyProviderConfigUsage struct {\n\tmetav1.ObjectMeta\n\n\tRequiredProviderConfigReferencer\n\tRequiredTypedResourceReferencer\n}\n\n// GetObjectKind returns schema.ObjectKind.\nfunc (p *LegacyProviderConfigUsage) GetObjectKind() schema.ObjectKind {\n\treturn schema.EmptyObjectKind\n}\n\n// DeepCopyObject returns a copy of the object as runtime.Object.\nfunc (p *LegacyProviderConfigUsage) DeepCopyObject() runtime.Object {\n\tout := &LegacyProviderConfigUsage{}\n\n\tj, err := json.Marshal(p)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_ = json.Unmarshal(j, out)\n\n\treturn out\n}\n"
  },
  {
    "path": "pkg/resource/interfaces.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 resource\n\nimport (\n\t\"context\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/unstructured/reference\"\n)\n\n// A Conditioned may have conditions set or retrieved. Conditions are typically\n// indicate the status of both a resource and its reconciliation process.\ntype Conditioned interface {\n\tSetConditions(c ...xpv2.Condition)\n\tGetCondition(ct xpv2.ConditionType) xpv2.Condition\n}\n\n// A ClaimReferencer may reference a resource claim.\ntype ClaimReferencer interface {\n\tSetClaimReference(r *reference.Claim)\n\tGetClaimReference() *reference.Claim\n}\n\n// A ManagedResourceReferencer may reference a concrete managed resource.\ntype ManagedResourceReferencer interface {\n\tSetResourceReference(r *corev1.ObjectReference)\n\tGetResourceReference() *corev1.ObjectReference\n}\n\n// A LocalConnectionSecretWriterTo may write a connection secret to its own\n// namespace.\ntype LocalConnectionSecretWriterTo interface {\n\tSetWriteConnectionSecretToReference(r *xpv2.LocalSecretReference)\n\tGetWriteConnectionSecretToReference() *xpv2.LocalSecretReference\n}\n\n// A ConnectionSecretWriterTo may write a connection secret to an arbitrary\n// namespace.\ntype ConnectionSecretWriterTo interface {\n\tSetWriteConnectionSecretToReference(r *xpv2.SecretReference)\n\tGetWriteConnectionSecretToReference() *xpv2.SecretReference\n}\n\n// A Manageable resource may specify a ManagementPolicies.\ntype Manageable interface {\n\tSetManagementPolicies(p xpv2.ManagementPolicies)\n\tGetManagementPolicies() xpv2.ManagementPolicies\n}\n\n// An Orphanable resource may specify a DeletionPolicy.\ntype Orphanable interface {\n\tSetDeletionPolicy(p xpv2.DeletionPolicy)\n\tGetDeletionPolicy() xpv2.DeletionPolicy\n}\n\n// A ProviderConfigReferencer may reference a provider config resource.\ntype ProviderConfigReferencer interface {\n\tGetProviderConfigReference() *xpv2.Reference\n\tSetProviderConfigReference(p *xpv2.Reference)\n}\n\n// A TypedProviderConfigReferencer may reference a provider config resource\n// with its kind.\ntype TypedProviderConfigReferencer interface {\n\tGetProviderConfigReference() *xpv2.ProviderConfigReference\n\tSetProviderConfigReference(p *xpv2.ProviderConfigReference)\n}\n\n// A RequiredProviderConfigReferencer may reference a provider config resource.\n// Unlike ProviderConfigReferencer, the reference is required (i.e. not nil).\ntype RequiredProviderConfigReferencer interface {\n\tGetProviderConfigReference() xpv2.Reference\n\tSetProviderConfigReference(p xpv2.Reference)\n}\n\n// A RequiredTypedProviderConfigReferencer may reference a provider config resource.\n// Unlike TypedProviderConfigReferencer, the reference is required (i.e. not nil).\ntype RequiredTypedProviderConfigReferencer interface {\n\tGetProviderConfigReference() xpv2.ProviderConfigReference\n\tSetProviderConfigReference(p xpv2.ProviderConfigReference)\n}\n\n// A RequiredTypedResourceReferencer can reference a resource.\ntype RequiredTypedResourceReferencer interface {\n\tSetResourceReference(r xpv2.TypedReference)\n\tGetResourceReference() xpv2.TypedReference\n}\n\n// A Finalizer manages the finalizers on the resource.\ntype Finalizer interface {\n\tAddFinalizer(ctx context.Context, obj Object) error\n\tRemoveFinalizer(ctx context.Context, obj Object) error\n}\n\n// A CompositionSelector may select a composition of resources.\ntype CompositionSelector interface {\n\tSetCompositionSelector(s *metav1.LabelSelector)\n\tGetCompositionSelector() *metav1.LabelSelector\n}\n\n// A CompositionReferencer may reference a composition of resources.\ntype CompositionReferencer interface {\n\tSetCompositionReference(ref *corev1.ObjectReference)\n\tGetCompositionReference() *corev1.ObjectReference\n}\n\n// A CompositionRevisionReferencer may reference a specific revision of a\n// composition of resources.\ntype CompositionRevisionReferencer interface {\n\tSetCompositionRevisionReference(ref *corev1.LocalObjectReference)\n\tGetCompositionRevisionReference() *corev1.LocalObjectReference\n}\n\n// A CompositionRevisionSelector may reference a set of\n// composition revisions.\ntype CompositionRevisionSelector interface {\n\tSetCompositionRevisionSelector(selector *metav1.LabelSelector)\n\tGetCompositionRevisionSelector() *metav1.LabelSelector\n}\n\n// A CompositionUpdater uses a composition, and may update which revision of\n// that composition it uses.\ntype CompositionUpdater interface {\n\tSetCompositionUpdatePolicy(p *xpv2.UpdatePolicy)\n\tGetCompositionUpdatePolicy() *xpv2.UpdatePolicy\n}\n\n// A CompositeResourceDeleter creates a composite, and controls the policy\n// used to delete the composite.\ntype CompositeResourceDeleter interface {\n\tSetCompositeDeletePolicy(policy *xpv2.CompositeDeletePolicy)\n\tGetCompositeDeletePolicy() *xpv2.CompositeDeletePolicy\n}\n\n// A ComposedResourcesReferencer may reference the resources it composes.\ntype ComposedResourcesReferencer interface {\n\tSetResourceReferences(refs []corev1.ObjectReference)\n\tGetResourceReferences() []corev1.ObjectReference\n}\n\n// A CompositeResourceReferencer can reference a composite resource.\ntype CompositeResourceReferencer interface {\n\tSetResourceReference(r *reference.Composite)\n\tGetResourceReference() *reference.Composite\n}\n\n// An EnvironmentConfigReferencer references a list of EnvironmentConfigs.\ntype EnvironmentConfigReferencer interface {\n\tSetEnvironmentConfigReferences(refs []corev1.ObjectReference)\n\tGetEnvironmentConfigReferences() []corev1.ObjectReference\n}\n\n// A UserCounter can count how many users it has.\ntype UserCounter interface {\n\tSetUsers(i int64)\n\tGetUsers() int64\n}\n\n// A ConnectionDetailsPublishedTimer can record the last time its connection\n// details were published.\ntype ConnectionDetailsPublishedTimer interface {\n\tSetConnectionDetailsLastPublishedTime(t *metav1.Time)\n\tGetConnectionDetailsLastPublishedTime() *metav1.Time\n}\n\n// ReconciliationObserver can track data observed by resource reconciler.\ntype ReconciliationObserver interface {\n\tSetObservedGeneration(generation int64)\n\tGetObservedGeneration() int64\n}\n\n// An Object is a Kubernetes object.\ntype Object interface {\n\tmetav1.Object\n\truntime.Object\n}\n\n// A Managed is a Kubernetes object representing a concrete managed\n// resource (e.g. a CloudSQL instance).\ntype Managed interface {\n\tObject\n\tManageable\n\tConditioned\n}\n\n// A ModernManaged is a Kubernetes object representing a concrete managed\n// resource with local connection secret references and typed provider\n// config reference.\ntype ModernManaged interface {\n\tManaged\n\tLocalConnectionSecretWriterTo\n\tTypedProviderConfigReferencer\n}\n\n// A LegacyManaged is a cluster-scoped Kubernetes object representing a\n// concrete managed resource, with namespaced connection secret referencers\n// and untyped provider config reference.\n//\n// Deprecated: new namespace-scoped MRs should implement ModernManaged.\ntype LegacyManaged interface {\n\tManaged\n\tConnectionSecretWriterTo\n\tProviderConfigReferencer\n\tOrphanable\n}\n\n// A ManagedList is a list of managed resources.\ntype ManagedList interface {\n\tclient.ObjectList\n\n\t// GetItems returns the list of managed resources.\n\tGetItems() []Managed\n}\n\n// A LegacyManagedList is a list of managed resources.\n//\n// Deprecated: new types should implement ManagedList.\ntype LegacyManagedList interface {\n\tclient.ObjectList\n\n\t// GetItems returns the list of managed resources.\n\tGetItems() []LegacyManaged\n}\n\n// A ProviderConfig configures a Crossplane provider.\ntype ProviderConfig interface {\n\tObject\n\n\tUserCounter\n\tConditioned\n}\n\n// A ProviderConfigUsage indicates a usage of a Crossplane provider config.\ntype ProviderConfigUsage interface {\n\tObject\n\tRequiredTypedResourceReferencer\n}\n\n// A TypedProviderConfigUsage is a ProviderConfigUsage that\n// has a typed reference to the ProviderConfig.\ntype TypedProviderConfigUsage interface {\n\tProviderConfigUsage\n\tRequiredTypedProviderConfigReferencer\n}\n\n// A LegacyProviderConfigUsage is a ProviderConfigUsage that\n// has an untyped reference to a provider config.\n//\n// Deprecated: new PCUs should implement TypedProviderConfigUsage.\ntype LegacyProviderConfigUsage interface {\n\tProviderConfigUsage\n\tRequiredProviderConfigReferencer\n}\n\n// A ProviderConfigUsageList is a list of provider config usages.\ntype ProviderConfigUsageList interface {\n\tclient.ObjectList\n\n\t// GetItems returns the list of provider config usages.\n\tGetItems() []ProviderConfigUsage\n}\n\n// A Composite resource (or XR) is composed of other resources.\ntype Composite interface { //nolint:interfacebloat // This interface has to be big.\n\tObject\n\n\tCompositionSelector\n\tCompositionReferencer\n\tCompositionUpdater\n\tCompositionRevisionReferencer\n\tCompositionRevisionSelector\n\tComposedResourcesReferencer\n\n\tConditioned\n\tReconciliationObserver\n}\n\n// A LegacyComposite is a Crossplane v1 style legacy XR.\ntype LegacyComposite interface {\n\tComposite\n\tClaimReferencer\n\tConnectionSecretWriterTo\n\tConnectionDetailsPublishedTimer\n}\n\n// Composed resources can be a composed into a Composite resource.\ntype Composed interface {\n\tObject\n\n\tConditioned\n\tConnectionSecretWriterTo\n\tReconciliationObserver\n}\n\n// A CompositeClaim of a composite resource (XR).\ntype CompositeClaim interface { //nolint:interfacebloat // This interface has to be big.\n\tObject\n\n\tCompositionSelector\n\tCompositionReferencer\n\tCompositionUpdater\n\tCompositionRevisionReferencer\n\tCompositionRevisionSelector\n\tCompositeResourceDeleter\n\tCompositeResourceReferencer\n\tLocalConnectionSecretWriterTo\n\n\tConditioned\n\tConnectionDetailsPublishedTimer\n\tReconciliationObserver\n}\n\n// A Claim of a composite resource (XR).\ntype Claim = CompositeClaim\n"
  },
  {
    "path": "pkg/resource/interfaces_test.go",
    "content": "/*\nCopyright 2021 The Crossplane 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 resource\n\nimport (\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/unstructured/claim\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/unstructured/composed\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/unstructured/composite\"\n)\n\n// We test that our fakes satisfy our interfaces here rather than in the fake\n// package to avoid a cyclic dependency.\n\nvar (\n\t_ Managed             = &fake.Managed{}\n\t_ ProviderConfig      = &fake.ProviderConfig{}\n\t_ ProviderConfigUsage = &fake.ProviderConfigUsage{}\n\t_ ProviderConfigUsage = &fake.LegacyProviderConfigUsage{}\n\n\t_ ModernManaged             = &fake.ModernManaged{}\n\t_ TypedProviderConfigUsage  = &fake.ProviderConfigUsage{}\n\t_ LegacyManaged             = &fake.LegacyManaged{}\n\t_ LegacyProviderConfigUsage = &fake.LegacyProviderConfigUsage{}\n\n\t_ CompositeClaim = &fake.CompositeClaim{}\n\t_ Composite      = &fake.Composite{}\n\t_ Composed       = &fake.Composed{}\n\n\t_ CompositeClaim = &claim.Unstructured{}\n\t_ Composite      = &composite.Unstructured{}\n\t_ Composed       = &composed.Unstructured{}\n)\n"
  },
  {
    "path": "pkg/resource/late_initializer.go",
    "content": "/*\nCopyright 2021 The Crossplane 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 resource\n\nimport (\n\t\"time\"\n\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\n// NewLateInitializer returns a new instance of *LateInitializer.\nfunc NewLateInitializer() *LateInitializer {\n\treturn &LateInitializer{}\n}\n\n// LateInitializer contains functions to late initialize two fields with varying\n// types. The main purpose of LateInitializer is to be able to report whether\n// anything different from the original value has been returned after all late\n// initialization calls.\ntype LateInitializer struct {\n\tchanged bool\n}\n\n// IsChanged reports whether the second argument is ever used in late initialization\n// function calls.\nfunc (li *LateInitializer) IsChanged() bool {\n\treturn li.changed\n}\n\n// SetChanged marks the LateInitializer such that users can tell whether any\n// of the late initialization calls returned the non-original argument.\nfunc (li *LateInitializer) SetChanged() {\n\tli.changed = true\n}\n\n// LateInitializeStringPtr implements late initialization for *string.\nfunc (li *LateInitializer) LateInitializeStringPtr(org *string, from *string) *string {\n\tif org != nil || from == nil {\n\t\treturn org\n\t}\n\n\tli.SetChanged()\n\n\treturn from\n}\n\n// LateInitializeInt64Ptr implements late initialization for *int64.\nfunc (li *LateInitializer) LateInitializeInt64Ptr(org *int64, from *int64) *int64 {\n\tif org != nil || from == nil {\n\t\treturn org\n\t}\n\n\tli.SetChanged()\n\n\treturn from\n}\n\n// LateInitializeBoolPtr implements late initialization for *bool.\nfunc (li *LateInitializer) LateInitializeBoolPtr(org *bool, from *bool) *bool {\n\tif org != nil || from == nil {\n\t\treturn org\n\t}\n\n\tli.SetChanged()\n\n\treturn from\n}\n\n// LateInitializeTimePtr implements late initialization for *metav1.Time from\n// *time.Time.\nfunc (li *LateInitializer) LateInitializeTimePtr(org *metav1.Time, from *time.Time) *metav1.Time {\n\tif org != nil || from == nil {\n\t\treturn org\n\t}\n\n\tli.SetChanged()\n\n\tt := metav1.NewTime(*from)\n\n\treturn &t\n}\n"
  },
  {
    "path": "pkg/resource/late_initializer_test.go",
    "content": "/*\nCopyright 2021 The Crossplane 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 resource\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nfunc TestLateInitializeStringPtr(t *testing.T) {\n\ts1 := \"desired\"\n\ts2 := \"observed\"\n\n\ttype args struct {\n\t\torg  *string\n\t\tfrom *string\n\t}\n\n\ttype want struct {\n\t\tresult  *string\n\t\tchanged bool\n\t}\n\n\tcases := map[string]struct {\n\t\targs\n\t\twant\n\t}{\n\t\t\"Original\": {\n\t\t\targs: args{\n\t\t\t\torg:  &s1,\n\t\t\t\tfrom: &s2,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  &s1,\n\t\t\t\tchanged: false,\n\t\t\t},\n\t\t},\n\t\t\"LateInitialized\": {\n\t\t\targs: args{\n\t\t\t\torg:  nil,\n\t\t\t\tfrom: &s2,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  &s2,\n\t\t\t\tchanged: true,\n\t\t\t},\n\t\t},\n\t\t\"Neither\": {\n\t\t\targs: args{\n\t\t\t\torg:  nil,\n\t\t\t\tfrom: nil,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  nil,\n\t\t\t\tchanged: false,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tli := NewLateInitializer()\n\n\t\t\tgot := li.LateInitializeStringPtr(tc.org, tc.from)\n\t\t\tif diff := cmp.Diff(tc.result, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"LateInitializeStringPtr(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.changed, li.IsChanged()); diff != \"\" {\n\t\t\t\tt.Errorf(\"IsChanged(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLateInitializeInt64Ptr(t *testing.T) {\n\ti1 := int64(10)\n\ti2 := int64(20)\n\n\ttype args struct {\n\t\torg  *int64\n\t\tfrom *int64\n\t}\n\n\ttype want struct {\n\t\tresult  *int64\n\t\tchanged bool\n\t}\n\n\tcases := map[string]struct {\n\t\targs\n\t\twant\n\t}{\n\t\t\"Original\": {\n\t\t\targs: args{\n\t\t\t\torg:  &i1,\n\t\t\t\tfrom: &i2,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  &i1,\n\t\t\t\tchanged: false,\n\t\t\t},\n\t\t},\n\t\t\"LateInitialized\": {\n\t\t\targs: args{\n\t\t\t\torg:  nil,\n\t\t\t\tfrom: &i2,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  &i2,\n\t\t\t\tchanged: true,\n\t\t\t},\n\t\t},\n\t\t\"Neither\": {\n\t\t\targs: args{\n\t\t\t\torg:  nil,\n\t\t\t\tfrom: nil,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  nil,\n\t\t\t\tchanged: false,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tli := NewLateInitializer()\n\n\t\t\tgot := li.LateInitializeInt64Ptr(tc.org, tc.from)\n\t\t\tif diff := cmp.Diff(tc.result, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"LateInitializeBoolPtr(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.changed, li.IsChanged()); diff != \"\" {\n\t\t\t\tt.Errorf(\"IsChanged(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLateInitializeBoolPtr(t *testing.T) {\n\ttrueVal := true\n\tfalseVal := false\n\n\ttype args struct {\n\t\torg  *bool\n\t\tfrom *bool\n\t}\n\n\ttype want struct {\n\t\tresult  *bool\n\t\tchanged bool\n\t}\n\n\tcases := map[string]struct {\n\t\targs\n\t\twant\n\t}{\n\t\t\"Original\": {\n\t\t\targs: args{\n\t\t\t\torg:  &trueVal,\n\t\t\t\tfrom: &falseVal,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  &trueVal,\n\t\t\t\tchanged: false,\n\t\t\t},\n\t\t},\n\t\t\"LateInitialized\": {\n\t\t\targs: args{\n\t\t\t\torg:  nil,\n\t\t\t\tfrom: &trueVal,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  &trueVal,\n\t\t\t\tchanged: true,\n\t\t\t},\n\t\t},\n\t\t\"Neither\": {\n\t\t\targs: args{\n\t\t\t\torg:  nil,\n\t\t\t\tfrom: nil,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  nil,\n\t\t\t\tchanged: false,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tli := NewLateInitializer()\n\n\t\t\tgot := li.LateInitializeBoolPtr(tc.org, tc.from)\n\t\t\tif diff := cmp.Diff(tc.result, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"LateInitializeBoolPtr(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.changed, li.IsChanged()); diff != \"\" {\n\t\t\t\tt.Errorf(\"IsChanged(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLateInitializeTimePtr(t *testing.T) {\n\tt1 := metav1.Now()\n\tt2 := time.Now().Add(time.Minute)\n\tt2m := metav1.NewTime(t2)\n\n\ttype args struct {\n\t\torg  *metav1.Time\n\t\tfrom *time.Time\n\t}\n\n\ttype want struct {\n\t\tresult  *metav1.Time\n\t\tchanged bool\n\t}\n\n\tcases := map[string]struct {\n\t\targs\n\t\twant\n\t}{\n\t\t\"Original\": {\n\t\t\targs: args{\n\t\t\t\torg:  &t1,\n\t\t\t\tfrom: &t2,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  &t1,\n\t\t\t\tchanged: false,\n\t\t\t},\n\t\t},\n\t\t\"LateInitialized\": {\n\t\t\targs: args{\n\t\t\t\torg:  nil,\n\t\t\t\tfrom: &t2,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  &t2m,\n\t\t\t\tchanged: true,\n\t\t\t},\n\t\t},\n\t\t\"Neither\": {\n\t\t\targs: args{\n\t\t\t\torg:  nil,\n\t\t\t\tfrom: nil,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tresult:  nil,\n\t\t\t\tchanged: false,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tli := NewLateInitializer()\n\n\t\t\tgot := li.LateInitializeTimePtr(tc.org, tc.from)\n\t\t\tif diff := cmp.Diff(tc.result, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"LateInitializeTimePtr(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.changed, li.IsChanged()); diff != \"\" {\n\t\t\t\tt.Errorf(\"IsChanged(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/resource/predicates.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 resource\n\nimport (\n\t\"maps\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/event\"\n\t\"sigs.k8s.io/controller-runtime/pkg/predicate\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n)\n\n// A PredicateFn returns true if the supplied object should be reconciled.\n//\n// Deprecated: This type will be removed soon. Please use\n// controller-runtime's predicate.NewPredicateFuncs instead.\ntype PredicateFn func(obj runtime.Object) bool\n\n// NewPredicates returns a set of Funcs that are all satisfied by the supplied\n// PredicateFn. The PredicateFn is run against the new object during updates.\n//\n// Deprecated: This function will be removed soon. Please use\n// controller-runtime's predicate.NewPredicateFuncs instead.\nfunc NewPredicates(fn PredicateFn) predicate.Funcs {\n\treturn predicate.Funcs{\n\t\tCreateFunc:  func(e event.CreateEvent) bool { return fn(e.Object) },\n\t\tDeleteFunc:  func(e event.DeleteEvent) bool { return fn(e.Object) },\n\t\tUpdateFunc:  func(e event.UpdateEvent) bool { return fn(e.ObjectNew) },\n\t\tGenericFunc: func(e event.GenericEvent) bool { return fn(e.Object) },\n\t}\n}\n\n// DesiredStateChanged accepts objects that have changed their desired state, i.e.\n// the state that is not managed by the controller.\n// To be more specific, it accepts update events that have changes in one of the followings:\n// - `metadata.annotations` (except for certain annotations)\n// - `metadata.labels`\n// - `spec`.\nfunc DesiredStateChanged() predicate.Predicate {\n\treturn predicate.Or(\n\t\tAnnotationChangedPredicate{\n\t\t\tignored: []string{\n\t\t\t\t// These annotations are managed by the controller and should\n\t\t\t\t// not be considered as a change in desired state. The managed\n\t\t\t\t// reconciler explicitly requests a new reconcile already after\n\t\t\t\t// updating these annotations.\n\t\t\t\tmeta.AnnotationKeyExternalCreateFailed,\n\t\t\t\tmeta.AnnotationKeyExternalCreatePending,\n\t\t\t},\n\t\t},\n\t\tpredicate.LabelChangedPredicate{},\n\t\tpredicate.GenerationChangedPredicate{},\n\t)\n}\n\n// AnnotationChangedPredicate implements a default update predicate function on\n// annotation change by ignoring the given annotation keys, if any.\n//\n// This predicate extends controller-runtime's AnnotationChangedPredicate by\n// being able to ignore certain annotations.\ntype AnnotationChangedPredicate struct {\n\tpredicate.Funcs\n\n\tignored []string\n}\n\nfunc copyAnnotations(an map[string]string) map[string]string {\n\tr := make(map[string]string, len(an))\n\tmaps.Copy(r, an)\n\n\treturn r\n}\n\n// Update implements default UpdateEvent filter for validating annotation change.\nfunc (a AnnotationChangedPredicate) Update(e event.UpdateEvent) bool {\n\tif e.ObjectOld == nil {\n\t\t// Update event has no old object to update\n\t\treturn false\n\t}\n\n\tif e.ObjectNew == nil {\n\t\t// Update event has no new object for update\n\t\treturn false\n\t}\n\n\tna := copyAnnotations(e.ObjectNew.GetAnnotations())\n\toa := copyAnnotations(e.ObjectOld.GetAnnotations())\n\n\tfor _, k := range a.ignored {\n\t\tdelete(na, k)\n\t\tdelete(oa, k)\n\t}\n\n\t// Below is the same as controller-runtime's AnnotationChangedPredicate\n\t// implementation but optimized to avoid using reflect.DeepEqual.\n\tif len(na) != len(oa) {\n\t\t// annotation length changed\n\t\treturn true\n\t}\n\n\tfor k, v := range na {\n\t\tif oa[k] != v {\n\t\t\t// annotation value changed\n\t\t\treturn true\n\t\t}\n\t}\n\n\t// annotations unchanged.\n\treturn false\n}\n"
  },
  {
    "path": "pkg/resource/predicates_test.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 resource\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/event\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n)\n\nfunc TestDesiredStateChanged(t *testing.T) {\n\ttype args struct {\n\t\told client.Object\n\t\tnew client.Object\n\t}\n\n\ttype want struct {\n\t\tdesiredStateChanged bool\n\t}\n\n\tcases := map[string]struct {\n\t\targs\n\t\twant\n\t}{\n\t\t\"NothingChanged\": {\n\t\t\targs: args{\n\t\t\t\told: func() client.Object {\n\t\t\t\t\tmg := &fake.Managed{}\n\t\t\t\t\treturn mg\n\t\t\t\t}(),\n\t\t\t\tnew: func() client.Object {\n\t\t\t\t\tmg := &fake.Managed{}\n\t\t\t\t\treturn mg\n\t\t\t\t}(),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tdesiredStateChanged: false,\n\t\t\t},\n\t\t},\n\t\t\"StatusChanged\": {\n\t\t\targs: args{\n\t\t\t\told: func() client.Object {\n\t\t\t\t\tmg := &fake.Managed{}\n\t\t\t\t\treturn mg\n\t\t\t\t}(),\n\t\t\t\tnew: func() client.Object {\n\t\t\t\t\tmg := &fake.Managed{}\n\t\t\t\t\tmg.SetConditions(xpv2.ReconcileSuccess())\n\n\t\t\t\t\treturn mg\n\t\t\t\t}(),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tdesiredStateChanged: false,\n\t\t\t},\n\t\t},\n\t\t\"IgnoredAnnotationsChanged\": {\n\t\t\targs: args{\n\t\t\t\told: func() client.Object {\n\t\t\t\t\tmg := &fake.Managed{}\n\t\t\t\t\treturn mg\n\t\t\t\t}(),\n\t\t\t\tnew: func() client.Object {\n\t\t\t\t\tmg := &fake.Managed{}\n\t\t\t\t\tmg.SetAnnotations(map[string]string{meta.AnnotationKeyExternalCreatePending: time.Now().String()})\n\n\t\t\t\t\treturn mg\n\t\t\t\t}(),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tdesiredStateChanged: false,\n\t\t\t},\n\t\t},\n\t\t\"AnnotationsChanged\": {\n\t\t\targs: args{\n\t\t\t\told: func() client.Object {\n\t\t\t\t\tmg := &fake.Managed{}\n\t\t\t\t\treturn mg\n\t\t\t\t}(),\n\t\t\t\tnew: func() client.Object {\n\t\t\t\t\tmg := &fake.Managed{}\n\t\t\t\t\tmg.SetAnnotations(map[string]string{\"foo\": \"bar\"})\n\n\t\t\t\t\treturn mg\n\t\t\t\t}(),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tdesiredStateChanged: true,\n\t\t\t},\n\t\t},\n\t\t\"LabelsChanged\": {\n\t\t\targs: args{\n\t\t\t\told: func() client.Object {\n\t\t\t\t\tmg := &fake.Managed{}\n\t\t\t\t\treturn mg\n\t\t\t\t}(),\n\t\t\t\tnew: func() client.Object {\n\t\t\t\t\tmg := &fake.Managed{}\n\t\t\t\t\tmg.SetLabels(map[string]string{\"foo\": \"bar\"})\n\n\t\t\t\t\treturn mg\n\t\t\t\t}(),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tdesiredStateChanged: true,\n\t\t\t},\n\t\t},\n\t\t// This happens when spec is changed.\n\t\t\"GenerationChanged\": {\n\t\t\targs: args{\n\t\t\t\told: func() client.Object {\n\t\t\t\t\tmg := &fake.Managed{}\n\t\t\t\t\tmg.SetGeneration(1)\n\n\t\t\t\t\treturn mg\n\t\t\t\t}(),\n\t\t\t\tnew: func() client.Object {\n\t\t\t\t\tmg := &fake.Managed{}\n\t\t\t\t\tmg.SetGeneration(2)\n\n\t\t\t\t\treturn mg\n\t\t\t\t}(),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tdesiredStateChanged: true,\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := DesiredStateChanged().Update(event.UpdateEvent{\n\t\t\t\tObjectOld: tc.old,\n\t\t\t\tObjectNew: tc.new,\n\t\t\t})\n\n\t\t\tif diff := cmp.Diff(tc.desiredStateChanged, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"DesiredStateChanged(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/resource/providerconfig.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 resource\n\nimport (\n\t\"context\"\n\t\"os\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/spf13/afero\"\n\tcorev1 \"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/types\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n)\n\nconst (\n\terrExtractEnv            = \"cannot extract from environment variable when none specified\"\n\terrExtractFs             = \"cannot extract from filesystem when no path specified\"\n\terrExtractSecretKey      = \"cannot extract from secret key when none specified\"\n\terrGetCredentialsSecret  = \"cannot get credentials secret\"\n\terrNoHandlerForSourceFmt = \"no extraction handler registered for source: %s\"\n\terrMissingPCRef          = \"managed resource does not reference a ProviderConfig\"\n\terrMissingPCRefKind      = \"managed resource ProviderConfig reference has no Kind\"\n\terrApplyPCU              = \"cannot apply ProviderConfigUsage\"\n)\n\ntype missingRefError struct{ error }\n\nfunc (m missingRefError) MissingReference() bool { return true }\n\n// IsMissingReference returns true if an error indicates that a managed\n// resource is missing a required reference..\nfunc IsMissingReference(err error) bool {\n\t_, ok := err.(interface {\n\t\tMissingReference() bool\n\t})\n\n\treturn ok\n}\n\n// EnvLookupFn looks up an environment variable.\ntype EnvLookupFn func(string) string\n\n// ExtractEnv extracts credentials from an environment variable.\nfunc ExtractEnv(_ context.Context, e EnvLookupFn, s xpv2.CommonCredentialSelectors) ([]byte, error) {\n\tif s.Env == nil {\n\t\treturn nil, errors.New(errExtractEnv)\n\t}\n\n\treturn []byte(e(s.Env.Name)), nil\n}\n\n// ExtractFs extracts credentials from the filesystem.\nfunc ExtractFs(_ context.Context, fs afero.Fs, s xpv2.CommonCredentialSelectors) ([]byte, error) {\n\tif s.Fs == nil {\n\t\treturn nil, errors.New(errExtractFs)\n\t}\n\n\treturn afero.ReadFile(fs, s.Fs.Path)\n}\n\n// ExtractSecret extracts credentials from a Kubernetes secret.\nfunc ExtractSecret(ctx context.Context, client client.Client, s xpv2.CommonCredentialSelectors) ([]byte, error) {\n\tif s.SecretRef == nil {\n\t\treturn nil, errors.New(errExtractSecretKey)\n\t}\n\n\tsecret := &corev1.Secret{}\n\tif err := client.Get(ctx, types.NamespacedName{Namespace: s.SecretRef.Namespace, Name: s.SecretRef.Name}, secret); err != nil {\n\t\treturn nil, errors.Wrap(err, errGetCredentialsSecret)\n\t}\n\n\treturn secret.Data[s.SecretRef.Key], nil\n}\n\n// CommonCredentialExtractor extracts credentials from common sources.\nfunc CommonCredentialExtractor(ctx context.Context, source xpv2.CredentialsSource, client client.Client, selector xpv2.CommonCredentialSelectors) ([]byte, error) {\n\tswitch source {\n\tcase xpv2.CredentialsSourceEnvironment:\n\t\treturn ExtractEnv(ctx, os.Getenv, selector)\n\tcase xpv2.CredentialsSourceFilesystem:\n\t\treturn ExtractFs(ctx, afero.NewOsFs(), selector)\n\tcase xpv2.CredentialsSourceSecret:\n\t\treturn ExtractSecret(ctx, client, selector)\n\tcase xpv2.CredentialsSourceNone:\n\t\treturn nil, nil\n\tcase xpv2.CredentialsSourceInjectedIdentity:\n\t\t// There is no common injected identity extractor. Each provider must\n\t\t// implement their own.\n\t\tfallthrough\n\tdefault:\n\t\treturn nil, errors.Errorf(errNoHandlerForSourceFmt, source)\n\t}\n}\n\n// A Tracker tracks managed resources.\ntype Tracker interface {\n\t// Track the supplied managed resource.\n\tTrack(ctx context.Context, mg Managed) error\n}\n\n// A TrackerFn is a function that tracks managed resources.\ntype TrackerFn func(ctx context.Context, mg Managed) error\n\n// Track the supplied managed resource.\nfunc (fn TrackerFn) Track(ctx context.Context, mg Managed) error {\n\treturn fn(ctx, mg)\n}\n\n// A LegacyTracker tracks legacy managed resources.\ntype LegacyTracker interface {\n\t// Track the supplied legacy managed resource.\n\tTrack(ctx context.Context, mg LegacyManaged) error\n}\n\n// A LegacyTrackerFn is a function that tracks legacy managed resources.\ntype LegacyTrackerFn func(ctx context.Context, mg LegacyManaged) error\n\n// Track the supplied legacy managed resource.\nfunc (fn LegacyTrackerFn) Track(ctx context.Context, mg LegacyManaged) error {\n\treturn fn(ctx, mg)\n}\n\n// A ModernTracker tracks modern managed resources.\ntype ModernTracker interface {\n\t// Track the supplied modern managed resource.\n\tTrack(ctx context.Context, mg ModernManaged) error\n}\n\n// A ModernTrackerFn is a function that tracks modern managed resources.\ntype ModernTrackerFn func(ctx context.Context, mg ModernManaged) error\n\n// Track the supplied modern managed resource.\nfunc (fn ModernTrackerFn) Track(ctx context.Context, mg ModernManaged) error {\n\treturn fn(ctx, mg)\n}\n\n// A ProviderConfigUsageTracker tracks usages of a ProviderConfig by creating or\n// updating the appropriate ProviderConfigUsage.\ntype ProviderConfigUsageTracker struct {\n\tc  Applicator\n\tof ProviderConfigUsage\n}\n\n// NewProviderConfigUsageTracker creates a ProviderConfigUsageTracker.\nfunc NewProviderConfigUsageTracker(c client.Client, of TypedProviderConfigUsage) *ProviderConfigUsageTracker {\n\treturn &ProviderConfigUsageTracker{c: NewAPIUpdatingApplicator(c), of: of}\n}\n\n// Track that the supplied Managed resource is using the ProviderConfig it\n// references by creating or updating a ProviderConfigUsage. Track should be\n// called _before_ attempting to use the ProviderConfig. This ensures the\n// managed resource's usage is updated if the managed resource is updated to\n// reference a misconfigured ProviderConfig.\nfunc (u *ProviderConfigUsageTracker) Track(ctx context.Context, mg ModernManaged) error {\n\t//nolint:forcetypeassert // Will always be a PCU.\n\tpcu := u.of.DeepCopyObject().(TypedProviderConfigUsage)\n\tgvk := mg.GetObjectKind().GroupVersionKind()\n\n\tref := mg.GetProviderConfigReference()\n\tif ref == nil {\n\t\treturn missingRefError{errors.New(errMissingPCRef)}\n\t}\n\n\tif ref.Kind == \"\" {\n\t\treturn missingRefError{errors.New(errMissingPCRefKind)}\n\t}\n\n\tpcu.SetName(string(mg.GetUID()))\n\tpcu.SetNamespace(mg.GetNamespace())\n\tpcu.SetLabels(map[string]string{xpv2.LabelKeyProviderName: ref.Name, xpv2.LabelKeyProviderKind: ref.Kind})\n\tpcu.SetOwnerReferences([]metav1.OwnerReference{meta.AsController(meta.TypedReferenceTo(mg, gvk))})\n\tpcu.SetProviderConfigReference(xpv2.ProviderConfigReference{Name: ref.Name, Kind: ref.Kind})\n\tpcu.SetResourceReference(xpv2.TypedReference{\n\t\tAPIVersion: gvk.GroupVersion().String(),\n\t\tKind:       gvk.Kind,\n\t\tName:       mg.GetName(),\n\t})\n\n\terr := u.c.Apply(ctx, pcu,\n\t\tMustBeControllableBy(mg.GetUID()),\n\t\tAllowUpdateIf(func(current, _ runtime.Object) bool {\n\t\t\t//nolint:forcetypeassert // Will always be a PCU.\n\t\t\treturn current.(TypedProviderConfigUsage).GetProviderConfigReference() != pcu.GetProviderConfigReference()\n\t\t}),\n\t)\n\n\treturn errors.Wrap(Ignore(IsNotAllowed, err), errApplyPCU)\n}\n\n// A LegacyProviderConfigUsageTracker tracks usages of a by creating or\n// updating the appropriate LegacyProviderConfigUsage.\ntype LegacyProviderConfigUsageTracker struct {\n\tc  Applicator\n\tof LegacyProviderConfigUsage\n}\n\n// NewLegacyProviderConfigUsageTracker tracks usages of a by creating or\n// updating the appropriate LegacyProviderConfigUsage.\nfunc NewLegacyProviderConfigUsageTracker(c client.Client, of LegacyProviderConfigUsage) *LegacyProviderConfigUsageTracker {\n\treturn &LegacyProviderConfigUsageTracker{c: NewAPIUpdatingApplicator(c), of: of}\n}\n\n// Track that the supplied LegacyManaged resource is using the ProviderConfig it\n// references by creating or updating a ProviderConfigUsage. Track should be\n// called _before_ attempting to use the ProviderConfig. This ensures the\n// managed resource's usage is updated if the managed resource is updated to\n// reference a misconfigured ProviderConfig.\nfunc (u *LegacyProviderConfigUsageTracker) Track(ctx context.Context, mg LegacyManaged) error {\n\t//nolint:forcetypeassert // Will always be a legacy PCU.\n\tpcu := u.of.DeepCopyObject().(LegacyProviderConfigUsage)\n\n\tgvk := mg.GetObjectKind().GroupVersionKind()\n\n\tref := mg.GetProviderConfigReference()\n\tif ref == nil {\n\t\treturn missingRefError{errors.New(errMissingPCRef)}\n\t}\n\n\tpcu.SetName(string(mg.GetUID()))\n\tpcu.SetLabels(map[string]string{xpv2.LabelKeyProviderName: ref.Name})\n\tpcu.SetOwnerReferences([]metav1.OwnerReference{meta.AsController(meta.TypedReferenceTo(mg, gvk))})\n\tpcu.SetProviderConfigReference(xpv2.Reference{Name: ref.Name})\n\tpcu.SetResourceReference(xpv2.TypedReference{\n\t\tAPIVersion: gvk.GroupVersion().String(),\n\t\tKind:       gvk.Kind,\n\t\tName:       mg.GetName(),\n\t})\n\n\terr := u.c.Apply(ctx, pcu,\n\t\tMustBeControllableBy(mg.GetUID()),\n\t\tAllowUpdateIf(func(current, _ runtime.Object) bool {\n\t\t\t//nolint:forcetypeassert // Will always be a PCU.\n\t\t\treturn current.(LegacyProviderConfigUsage).GetProviderConfigReference() != pcu.GetProviderConfigReference()\n\t\t}),\n\t)\n\n\treturn errors.Wrap(Ignore(IsNotAllowed, err), errApplyPCU)\n}\n"
  },
  {
    "path": "pkg/resource/providerconfig_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 resource\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/spf13/afero\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nfunc TestExtractEnv(t *testing.T) {\n\tcredentials := []byte(\"supersecretcreds\")\n\n\ttype args struct {\n\t\te     EnvLookupFn\n\t\tcreds xpv2.CommonCredentialSelectors\n\t}\n\n\ttype want struct {\n\t\tb   []byte\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"EnvVarSuccess\": {\n\t\t\treason: \"Successful extraction of credentials from environment variable\",\n\t\t\targs: args{\n\t\t\t\te: func(string) string { return string(credentials) },\n\t\t\t\tcreds: xpv2.CommonCredentialSelectors{\n\t\t\t\t\tEnv: &xpv2.EnvSelector{\n\t\t\t\t\t\tName: \"SECRET_CREDS\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tb: credentials,\n\t\t\t},\n\t\t},\n\t\t\"EnvVarFail\": {\n\t\t\treason: \"Failed extraction of credentials from environment variable\",\n\t\t\targs: args{\n\t\t\t\te: func(string) string { return string(credentials) },\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.New(errExtractEnv),\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot, err := ExtractEnv(context.TODO(), tc.args.e, tc.args.creds)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\npc.ExtractEnv(...): -want error, +got error:\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.b, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\npc.ExtractEnv(...): -want, +got:\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExtractFs(t *testing.T) {\n\tcredentials := []byte(\"supersecretcreds\")\n\tmockFs := afero.NewMemMapFs()\n\tf, _ := mockFs.Create(\"credentials.txt\")\n\tf.Write(credentials)\n\tf.Close()\n\n\ttype args struct {\n\t\tfs    afero.Fs\n\t\tcreds xpv2.CommonCredentialSelectors\n\t}\n\n\ttype want struct {\n\t\tb   []byte\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"FsSuccess\": {\n\t\t\treason: \"Successful extraction of credentials from filesystem\",\n\t\t\targs: args{\n\t\t\t\tfs: mockFs,\n\t\t\t\tcreds: xpv2.CommonCredentialSelectors{\n\t\t\t\t\tFs: &xpv2.FsSelector{\n\t\t\t\t\t\tPath: \"credentials.txt\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tb: credentials,\n\t\t\t},\n\t\t},\n\t\t\"FsFailure\": {\n\t\t\treason: \"Failed extraction of credentials from filesystem\",\n\t\t\targs: args{\n\t\t\t\tfs: mockFs,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.New(errExtractFs),\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot, err := ExtractFs(context.TODO(), tc.args.fs, tc.args.creds)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\npc.ExtractFs(...): -want error, +got error:\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.b, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\npc.ExtractFs(...): -want, +got:\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExtractSecret(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\tcredentials := []byte(\"supersecretcreds\")\n\n\ttype args struct {\n\t\tclient client.Client\n\t\tcreds  xpv2.CommonCredentialSelectors\n\t}\n\n\ttype want struct {\n\t\tb   []byte\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"SecretSuccess\": {\n\t\t\treason: \"Successful extraction of credentials from Secret\",\n\t\t\targs: args{\n\t\t\t\tclient: &test.MockClient{\n\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(o client.Object) error {\n\t\t\t\t\t\ts, _ := o.(*corev1.Secret)\n\t\t\t\t\t\ts.Data = map[string][]byte{\n\t\t\t\t\t\t\t\"creds\": credentials,\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\tcreds: xpv2.CommonCredentialSelectors{\n\t\t\t\t\tSecretRef: &xpv2.SecretKeySelector{\n\t\t\t\t\t\tSecretReference: xpv2.SecretReference{\n\t\t\t\t\t\t\tName:      \"super\",\n\t\t\t\t\t\t\tNamespace: \"secret\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tKey: \"creds\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tb: credentials,\n\t\t\t},\n\t\t},\n\t\t\"SecretFailureNotDefined\": {\n\t\t\treason: \"Failed extraction of credentials from Secret when key not defined\",\n\t\t\targs:   args{},\n\t\t\twant: want{\n\t\t\t\terr: errors.New(errExtractSecretKey),\n\t\t\t},\n\t\t},\n\t\t\"SecretFailureGet\": {\n\t\t\treason: \"Failed extraction of credentials from Secret when client fails\",\n\t\t\targs: args{\n\t\t\t\tclient: &test.MockClient{\n\t\t\t\t\tMockGet: test.NewMockGetFn(nil, func(client.Object) error {\n\t\t\t\t\t\treturn errBoom\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\tcreds: xpv2.CommonCredentialSelectors{\n\t\t\t\t\tSecretRef: &xpv2.SecretKeySelector{\n\t\t\t\t\t\tSecretReference: xpv2.SecretReference{\n\t\t\t\t\t\t\tName:      \"super\",\n\t\t\t\t\t\t\tNamespace: \"secret\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tKey: \"creds\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errGetCredentialsSecret),\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot, err := ExtractSecret(context.TODO(), tc.args.client, tc.args.creds)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\npc.ExtractSecret(...): -want error, +got error:\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.b, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\npc.ExtractSecret(...): -want, +got:\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestTrackLegacy(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\tname := \"provisional\"\n\n\ttype fields struct {\n\t\tc  Applicator\n\t\tof LegacyProviderConfigUsage\n\t}\n\n\ttype args struct {\n\t\tctx context.Context\n\t\tmg  LegacyManaged\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tfields fields\n\t\targs   args\n\t\twant   error\n\t}{\n\t\t\"MissingRef\": {\n\t\t\treason: \"An error that satisfies IsMissingReference should be returned if the managed resource has no provider config reference\",\n\t\t\tfields: fields{\n\t\t\t\tof: &fake.LegacyProviderConfigUsage{},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tmg: &fake.LegacyManaged{},\n\t\t\t},\n\t\t\twant: missingRefError{errors.New(errMissingPCRef)},\n\t\t},\n\t\t\"NopUpdate\": {\n\t\t\treason: \"No error should be returned if the apply fails because it would be a no-op\",\n\t\t\tfields: fields{\n\t\t\t\tc: ApplyFn(func(ctx context.Context, _ client.Object, ao ...ApplyOption) error {\n\t\t\t\t\tfor _, fn := range ao {\n\t\t\t\t\t\t// Exercise the MustBeControllableBy and AllowUpdateIf\n\t\t\t\t\t\t// ApplyOptions. The former should pass because the\n\t\t\t\t\t\t// current object has no controller ref. The latter\n\t\t\t\t\t\t// should return an error that satisfies IsNotAllowed\n\t\t\t\t\t\t// because the current object has the same PC ref as the\n\t\t\t\t\t\t// new one we would apply.\n\t\t\t\t\t\tcurrent := &fake.LegacyProviderConfigUsage{\n\t\t\t\t\t\t\tRequiredProviderConfigReferencer: fake.RequiredProviderConfigReferencer{\n\t\t\t\t\t\t\t\tRef: xpv2.Reference{Name: name},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif err := fn(ctx, current, nil); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn errBoom\n\t\t\t\t}),\n\t\t\t\tof: &fake.LegacyProviderConfigUsage{},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tmg: &fake.LegacyManaged{\n\t\t\t\t\tLegacyProviderConfigReferencer: fake.LegacyProviderConfigReferencer{\n\t\t\t\t\t\tRef: &xpv2.Reference{Name: name},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"ApplyError\": {\n\t\t\treason: \"Errors applying the ProviderConfigUsage should be returned\",\n\t\t\tfields: fields{\n\t\t\t\tc: ApplyFn(func(_ context.Context, _ client.Object, _ ...ApplyOption) error {\n\t\t\t\t\treturn errBoom\n\t\t\t\t}),\n\t\t\t\tof: &fake.LegacyProviderConfigUsage{},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tmg: &fake.LegacyManaged{\n\t\t\t\t\tLegacyProviderConfigReferencer: fake.LegacyProviderConfigReferencer{\n\t\t\t\t\t\tRef: &xpv2.Reference{Name: name},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: errors.Wrap(errBoom, errApplyPCU),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tut := &LegacyProviderConfigUsageTracker{c: tc.fields.c, of: tc.fields.of}\n\n\t\t\tgot := ut.Track(tc.args.ctx, tc.args.mg)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nut.Track(...): -want error, +got error:\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestTrackModern(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\tname := \"provisional\"\n\n\ttype fields struct {\n\t\tc  Applicator\n\t\tof TypedProviderConfigUsage\n\t}\n\n\ttype args struct {\n\t\tctx context.Context\n\t\tmg  ModernManaged\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tfields fields\n\t\targs   args\n\t\twant   error\n\t}{\n\t\t\"MissingRef\": {\n\t\t\treason: \"An error that satisfies IsMissingReference should be returned if the managed resource has no provider config reference\",\n\t\t\tfields: fields{\n\t\t\t\tof: &fake.ProviderConfigUsage{},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tmg: &fake.ModernManaged{},\n\t\t\t},\n\t\t\twant: missingRefError{errors.New(errMissingPCRef)},\n\t\t},\n\t\t\"NopUpdate\": {\n\t\t\treason: \"No error should be returned if the apply fails because it would be a no-op\",\n\t\t\tfields: fields{\n\t\t\t\tc: ApplyFn(func(ctx context.Context, _ client.Object, ao ...ApplyOption) error {\n\t\t\t\t\tfor _, fn := range ao {\n\t\t\t\t\t\t// Exercise the MustBeControllableBy and AllowUpdateIf\n\t\t\t\t\t\t// ApplyOptions. The former should pass because the\n\t\t\t\t\t\t// current object has no controller ref. The latter\n\t\t\t\t\t\t// should return an error that satisfies IsNotAllowed\n\t\t\t\t\t\t// because the current object has the same PC ref as the\n\t\t\t\t\t\t// new one we would apply.\n\t\t\t\t\t\tcurrent := &fake.ProviderConfigUsage{\n\t\t\t\t\t\t\tRequiredTypedProviderConfigReferencer: fake.RequiredTypedProviderConfigReferencer{\n\t\t\t\t\t\t\t\tRef: xpv2.ProviderConfigReference{Name: name, Kind: \"ProviderConfig\"},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif err := fn(ctx, current, nil); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn errBoom\n\t\t\t\t}),\n\t\t\t\tof: &fake.ProviderConfigUsage{},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tmg: &fake.ModernManaged{\n\t\t\t\t\tTypedProviderConfigReferencer: fake.TypedProviderConfigReferencer{\n\t\t\t\t\t\tRef: &xpv2.ProviderConfigReference{Name: name, Kind: \"ProviderConfig\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"ApplyError\": {\n\t\t\treason: \"Errors applying the ProviderConfigUsage should be returned\",\n\t\t\tfields: fields{\n\t\t\t\tc: ApplyFn(func(_ context.Context, _ client.Object, _ ...ApplyOption) error {\n\t\t\t\t\treturn errBoom\n\t\t\t\t}),\n\t\t\t\tof: &fake.ProviderConfigUsage{},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tmg: &fake.ModernManaged{\n\t\t\t\t\tTypedProviderConfigReferencer: fake.TypedProviderConfigReferencer{\n\t\t\t\t\t\tRef: &xpv2.ProviderConfigReference{Name: name, Kind: \"ProviderConfig\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: errors.Wrap(errBoom, errApplyPCU),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tut := &ProviderConfigUsageTracker{c: tc.fields.c, of: tc.fields.of}\n\n\t\t\tgot := ut.Track(tc.args.ctx, tc.args.mg)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nut.Track(...): -want error, +got error:\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/resource/reference.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 resource\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n)\n\n// ReferenceStatusType is an enum type for the possible values for a Reference Status.\ntype ReferenceStatusType int\n\n// Reference statuses.\nconst (\n\tReferenceStatusUnknown ReferenceStatusType = iota\n\tReferenceNotFound\n\tReferenceNotReady\n\tReferenceReady\n)\n\nfunc (t ReferenceStatusType) String() string {\n\treturn []string{\"Unknown\", \"NotFound\", \"NotReady\", \"Ready\"}[t]\n}\n\n// ReferenceStatus has the name and status of a reference.\ntype ReferenceStatus struct {\n\tName   string\n\tStatus ReferenceStatusType\n}\n\nfunc (r ReferenceStatus) String() string {\n\treturn fmt.Sprintf(\"{reference:%s status:%s}\", r.Name, r.Status)\n}\n\n// A CanReference is a resource that can reference another resource in its\n// spec in order to automatically resolve corresponding spec field values\n// by inspecting the referenced resource.\ntype CanReference runtime.Object\n\n// An AttributeReferencer resolves cross-resource attribute references. See\n// https://github.com/crossplane/crossplane/blob/main/design/one-pager-cross-resource-referencing.md\n// for more information.\ntype AttributeReferencer interface {\n\t// GetStatus retries the referenced resource, as well as other non-managed\n\t// resources (like a `Provider`) and reports their readiness for use as a\n\t// referenced resource.\n\tGetStatus(ctx context.Context, res CanReference, r client.Reader) ([]ReferenceStatus, error)\n\n\t// Build retrieves the referenced resource, as well as other non-managed\n\t// resources (like a `Provider`), and builds the referenced attribute,\n\t// returning it as a string value.\n\tBuild(ctx context.Context, res CanReference, r client.Reader) (value string, err error)\n\n\t// Assign accepts a managed resource object, and assigns the given value to\n\t// its corresponding property.\n\tAssign(res CanReference, value string) error\n}\n"
  },
  {
    "path": "pkg/resource/reference_test.go",
    "content": "/*\nCopyright 2021 The Crossplane 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 resource\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestReferenceStatusType_String(t *testing.T) {\n\ttests := map[string]struct {\n\t\tt    ReferenceStatusType\n\t\twant string\n\t}{\n\t\t\"ReferenceStatusUnknown\": {\n\t\t\tt:    ReferenceStatusUnknown,\n\t\t\twant: \"Unknown\",\n\t\t},\n\t\t\"ReferenceNotFound\": {\n\t\t\tt:    ReferenceNotFound,\n\t\t\twant: \"NotFound\",\n\t\t},\n\t\t\"ReferenceNotReady\": {\n\t\t\tt:    ReferenceNotReady,\n\t\t\twant: \"NotReady\",\n\t\t},\n\t\t\"ReferenceReady\": {\n\t\t\tt:    ReferenceReady,\n\t\t\twant: \"Ready\",\n\t\t},\n\t}\n\n\tfor name, tt := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tif got := tt.t.String(); got != tt.want {\n\t\t\t\tt.Errorf(\"String() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReferenceStatus_String(t *testing.T) {\n\ttests := map[string]struct {\n\t\trs   ReferenceStatus\n\t\twant string\n\t}{\n\t\t\"test-name-ready\": {\n\t\t\trs: ReferenceStatus{\n\t\t\t\tName:   \"test-name\",\n\t\t\t\tStatus: ReferenceReady,\n\t\t\t},\n\t\t\twant: fmt.Sprintf(\"{reference:test-name status:%s}\", ReferenceReady.String()),\n\t\t},\n\t}\n\n\tfor name, tt := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tif got := tt.rs.String(); got != tt.want {\n\t\t\t\tt.Errorf(\"String() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/resource/resource.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 resource\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"google.golang.org/protobuf/types/known/structpb\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\tkunstructured \"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/wait\"\n\t\"k8s.io/client-go/util/retry\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/unstructured\"\n)\n\n// SecretTypeConnection is the type of Crossplane connection secrets.\nconst SecretTypeConnection corev1.SecretType = \"connection.crossplane.io/v1alpha1\"\n\n// External resources are tagged/labelled with the following keys in the cloud\n// provider API if the type supports.\nconst (\n\tExternalResourceTagKeyKind               = \"crossplane-kind\"\n\tExternalResourceTagKeyName               = \"crossplane-name\"\n\tExternalResourceTagKeyNamespace          = \"crossplane-namespace\"\n\tExternalResourceTagKeyProvider           = \"crossplane-providerconfig\"\n\tExternalResourceTagKeyProviderConfigKind = \"crossplane-providerconfig-kind\"\n\n\terrMarshalJSON            = \"cannot marshal to JSON\"\n\terrUnmarshalJSON          = \"cannot unmarshal JSON data\"\n\terrStructFromUnstructured = \"cannot create Struct\"\n)\n\n// A ManagedKind contains the type metadata for a kind of managed resource.\ntype ManagedKind schema.GroupVersionKind\n\n// A CompositeKind contains the type metadata for a kind of composite resource.\ntype CompositeKind schema.GroupVersionKind\n\n// A CompositeClaimKind contains the type metadata for a kind of composite\n// resource claim.\ntype CompositeClaimKind schema.GroupVersionKind\n\n// ProviderConfigKinds contains the type metadata for a kind of provider config.\ntype ProviderConfigKinds struct {\n\tConfig    schema.GroupVersionKind\n\tUsage     schema.GroupVersionKind\n\tUsageList schema.GroupVersionKind\n}\n\n// A ConnectionSecretOwner is a Kubernetes object that owns a connection secret.\ntype ConnectionSecretOwner interface {\n\tObject\n\n\tConnectionSecretWriterTo\n}\n\n// A LocalConnectionSecretOwner may create and manage a connection secret in its\n// own namespace.\ntype LocalConnectionSecretOwner interface {\n\truntime.Object\n\tmetav1.Object\n\n\tLocalConnectionSecretWriterTo\n}\n\n// LocalConnectionSecretFor creates a connection secret in the namespace of the\n// supplied LocalConnectionSecretOwner, assumed to be of the supplied kind.\nfunc LocalConnectionSecretFor(o LocalConnectionSecretOwner, kind schema.GroupVersionKind) *corev1.Secret {\n\treturn &corev1.Secret{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tNamespace:       o.GetNamespace(),\n\t\t\tName:            o.GetWriteConnectionSecretToReference().Name,\n\t\t\tOwnerReferences: []metav1.OwnerReference{meta.AsController(meta.TypedReferenceTo(o, kind))},\n\t\t},\n\t\tType: SecretTypeConnection,\n\t\tData: make(map[string][]byte),\n\t}\n}\n\n// ConnectionSecretFor creates a connection for the supplied\n// ConnectionSecretOwner, assumed to be of the supplied kind. The secret is\n// written to 'default' namespace if the ConnectionSecretOwner does not specify\n// a namespace.\nfunc ConnectionSecretFor(o ConnectionSecretOwner, kind schema.GroupVersionKind) *corev1.Secret {\n\treturn &corev1.Secret{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tNamespace:       o.GetWriteConnectionSecretToReference().Namespace,\n\t\t\tName:            o.GetWriteConnectionSecretToReference().Name,\n\t\t\tOwnerReferences: []metav1.OwnerReference{meta.AsController(meta.TypedReferenceTo(o, kind))},\n\t\t},\n\t\tType: SecretTypeConnection,\n\t\tData: make(map[string][]byte),\n\t}\n}\n\n// MustCreateObject returns a new Object of the supplied kind. It panics if the\n// kind is unknown to the supplied ObjectCreator.\nfunc MustCreateObject(kind schema.GroupVersionKind, oc runtime.ObjectCreater) runtime.Object {\n\tobj, err := oc.New(kind)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn obj\n}\n\n// GetKind returns the GroupVersionKind of the supplied object. It return an\n// error if the object is unknown to the supplied ObjectTyper, the object is\n// unversioned, or the object does not have exactly one registered kind.\nfunc GetKind(obj runtime.Object, ot runtime.ObjectTyper) (schema.GroupVersionKind, error) {\n\tkinds, unversioned, err := ot.ObjectKinds(obj)\n\tif err != nil {\n\t\treturn schema.GroupVersionKind{}, errors.Wrap(err, \"cannot get kind of supplied object\")\n\t}\n\n\tif unversioned {\n\t\treturn schema.GroupVersionKind{}, errors.New(\"supplied object is unversioned\")\n\t}\n\n\tif len(kinds) != 1 {\n\t\treturn schema.GroupVersionKind{}, errors.New(\"supplied object does not have exactly one kind\")\n\t}\n\n\treturn kinds[0], nil\n}\n\n// MustGetKind returns the GroupVersionKind of the supplied object. It panics if\n// the object is unknown to the supplied ObjectTyper, the object is unversioned,\n// or the object does not have exactly one registered kind.\nfunc MustGetKind(obj runtime.Object, ot runtime.ObjectTyper) schema.GroupVersionKind {\n\tgvk, err := GetKind(obj, ot)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn gvk\n}\n\n// An ErrorIs function returns true if an error satisfies a particular condition.\ntype ErrorIs func(err error) bool\n\n// Ignore any errors that satisfy the supplied ErrorIs function by returning\n// nil. Errors that do not satisfy the supplied function are returned unmodified.\nfunc Ignore(is ErrorIs, err error) error {\n\tif is(err) {\n\t\treturn nil\n\t}\n\n\treturn err\n}\n\n// IgnoreAny ignores errors that satisfy any of the supplied ErrorIs functions\n// by returning nil. Errors that do not satisfy any of the supplied functions\n// are returned unmodified.\nfunc IgnoreAny(err error, is ...ErrorIs) error {\n\tfor _, f := range is {\n\t\tif f(err) {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn err\n}\n\n// IgnoreNotFound returns the supplied error, or nil if the error indicates a\n// Kubernetes resource was not found.\nfunc IgnoreNotFound(err error) error {\n\treturn Ignore(kerrors.IsNotFound, err)\n}\n\n// IsAPIError returns true if the given error's type is of Kubernetes API error.\nfunc IsAPIError(err error) bool {\n\t_, ok := err.(kerrors.APIStatus)\n\treturn ok\n}\n\n// IsAPIErrorWrapped returns true if err is a K8s API error, or recursively wraps a K8s API error.\nfunc IsAPIErrorWrapped(err error) bool {\n\treturn IsAPIError(errors.Cause(err))\n}\n\n// IsConditionTrue returns if condition status is true.\nfunc IsConditionTrue(c xpv2.Condition) bool {\n\treturn c.Status == corev1.ConditionTrue\n}\n\n// An Applicator applies changes to an object.\ntype Applicator interface {\n\tApply(ctx context.Context, obj client.Object, o ...ApplyOption) error\n}\n\ntype shouldRetryFunc func(error) bool\n\n// An ApplicatorWithRetry applies changes to an object, retrying on transient failures.\ntype ApplicatorWithRetry struct {\n\tApplicator\n\n\tshouldRetry shouldRetryFunc\n\tbackoff     wait.Backoff\n}\n\n// Apply invokes nested Applicator's Apply retrying on designated errors.\nfunc (awr *ApplicatorWithRetry) Apply(ctx context.Context, c client.Object, opts ...ApplyOption) error {\n\treturn retry.OnError(awr.backoff, awr.shouldRetry, func() error {\n\t\treturn awr.Applicator.Apply(ctx, c, opts...)\n\t})\n}\n\n// NewApplicatorWithRetry returns an ApplicatorWithRetry for the specified\n// applicator and with the specified retry function.\n//\n//\tIf backoff is nil, then retry.DefaultRetry is used as the default.\nfunc NewApplicatorWithRetry(applicator Applicator, shouldRetry shouldRetryFunc, backoff *wait.Backoff) *ApplicatorWithRetry {\n\tresult := &ApplicatorWithRetry{\n\t\tApplicator:  applicator,\n\t\tshouldRetry: shouldRetry,\n\t\tbackoff:     retry.DefaultRetry,\n\t}\n\n\tif backoff != nil {\n\t\tresult.backoff = *backoff\n\t}\n\n\treturn result\n}\n\n// A ClientApplicator may be used to build a single 'client' that satisfies both\n// client.Client and Applicator.\ntype ClientApplicator struct {\n\tclient.Client\n\tApplicator\n}\n\n// An ApplyFn is a function that satisfies the Applicator interface.\ntype ApplyFn func(context.Context, client.Object, ...ApplyOption) error\n\n// Apply changes to the supplied object.\nfunc (fn ApplyFn) Apply(ctx context.Context, o client.Object, ao ...ApplyOption) error {\n\treturn fn(ctx, o, ao...)\n}\n\n// An ApplyOption is called before patching the current object to match the\n// desired object. ApplyOptions are not called if no current object exists.\ntype ApplyOption func(ctx context.Context, current, desired runtime.Object) error\n\n// UpdateFn returns an ApplyOption that is used to modify the current object to\n// match fields of the desired.\nfunc UpdateFn(fn func(current, desired runtime.Object)) ApplyOption {\n\treturn func(_ context.Context, c, d runtime.Object) error {\n\t\tfn(c, d)\n\t\treturn nil\n\t}\n}\n\ntype notControllableError struct{ error }\n\nfunc (e notControllableError) NotControllable() bool {\n\treturn true\n}\n\n// IsNotControllable returns true if the supplied error indicates that a\n// resource is not controllable - i.e. that it another resource is not and may\n// not become its controller reference.\nfunc IsNotControllable(err error) bool {\n\t_, ok := err.(interface {\n\t\tNotControllable() bool\n\t})\n\n\treturn ok\n}\n\n// MustBeControllableBy requires that the current object is controllable by an\n// object with the supplied UID. An object is controllable if its controller\n// reference matches the supplied UID, or it has no controller reference. An\n// error that satisfies IsNotControllable will be returned if the current object\n// cannot be controlled by the supplied UID.\nfunc MustBeControllableBy(u types.UID) ApplyOption {\n\treturn func(_ context.Context, current, _ runtime.Object) error {\n\t\tmo, ok := current.(metav1.Object)\n\t\tif !ok {\n\t\t\treturn notControllableError{errors.Errorf(\"existing object is missing object metadata\")}\n\t\t}\n\n\t\tc := metav1.GetControllerOf(mo)\n\t\tif c == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\tif c.UID != u {\n\t\t\treturn notControllableError{errors.Errorf(\"existing object is not controlled by UID %q\", u)}\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\n// ConnectionSecretMustBeControllableBy requires that the current object is a\n// connection secret that is controllable by an object with the supplied UID.\n// Contemporary connection secrets are of SecretTypeConnection, while legacy\n// connection secrets are of corev1.SecretTypeOpaque. Contemporary connection\n// secrets are considered controllable if they are already controlled by the\n// supplied UID, or have no controller reference. Legacy connection secrets are\n// only considered controllable if they are already controlled by the supplied\n// UID. It is not safe to assume legacy connection secrets without a controller\n// reference are controllable because they are indistinguishable from Kubernetes\n// secrets that have nothing to do with Crossplane. An error that satisfies\n// IsNotControllable will be returned if the current secret is not a connection\n// secret or cannot be controlled by the supplied UID.\nfunc ConnectionSecretMustBeControllableBy(u types.UID) ApplyOption {\n\treturn func(_ context.Context, current, _ runtime.Object) error {\n\t\ts, ok := current.(*corev1.Secret)\n\t\tif !ok {\n\t\t\treturn errors.New(\"current resource is not a Secret\")\n\t\t}\n\n\t\tc := metav1.GetControllerOf(s)\n\n\t\tswitch {\n\t\tcase c == nil && s.Type != SecretTypeConnection:\n\t\t\treturn notControllableError{errors.Errorf(\"refusing to modify uncontrolled secret of type %q\", s.Type)}\n\t\tcase c == nil:\n\t\t\treturn nil\n\t\tcase c.UID != u:\n\t\t\treturn notControllableError{errors.Errorf(\"existing secret is not controlled by UID %q\", u)}\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\ntype notAllowedError struct{ error }\n\nfunc (e notAllowedError) NotAllowed() bool {\n\treturn true\n}\n\n// NewNotAllowed returns a new NotAllowed error.\nfunc NewNotAllowed(message string) error {\n\treturn notAllowedError{error: errors.New(message)}\n}\n\n// IsNotAllowed returns true if the supplied error indicates that an operation\n// was not allowed.\nfunc IsNotAllowed(err error) bool {\n\t_, ok := err.(interface {\n\t\tNotAllowed() bool\n\t})\n\n\treturn ok\n}\n\n// AllowUpdateIf will only update the current object if the supplied fn returns\n// true. An error that satisfies IsNotAllowed will be returned if the supplied\n// function returns false. Creation of a desired object that does not currently\n// exist is always allowed.\nfunc AllowUpdateIf(fn func(current, desired runtime.Object) bool) ApplyOption {\n\treturn func(_ context.Context, current, desired runtime.Object) error {\n\t\tif fn(current, desired) {\n\t\t\treturn nil\n\t\t}\n\n\t\treturn notAllowedError{errors.New(\"update not allowed\")}\n\t}\n}\n\n// StoreCurrentRV stores the resource version of the current object in the\n// supplied string pointer. This is useful to detect whether the Apply call\n// was a no-op.\nfunc StoreCurrentRV(origRV *string) ApplyOption {\n\treturn func(_ context.Context, current, _ runtime.Object) error {\n\t\tmo, ok := current.(metav1.Object)\n\t\tif !ok {\n\t\t\treturn errors.New(\"current resource is missing object metadata\")\n\t\t}\n\n\t\t*origRV = mo.GetResourceVersion()\n\n\t\treturn nil\n\t}\n}\n\n// GetExternalTags returns the identifying tags to be used to tag the external\n// resource in provider API.\nfunc GetExternalTags(mg Managed) map[string]string {\n\ttags := map[string]string{\n\t\tExternalResourceTagKeyKind: strings.ToLower(mg.GetObjectKind().GroupVersionKind().GroupKind().String()),\n\t\tExternalResourceTagKeyName: mg.GetName(),\n\t}\n\n\tif namespace := mg.GetNamespace(); namespace != \"\" {\n\t\ttags[ExternalResourceTagKeyNamespace] = namespace\n\t}\n\n\tswitch mg := mg.(type) {\n\tcase TypedProviderConfigReferencer:\n\t\tif pcRef := mg.GetProviderConfigReference(); pcRef != nil {\n\t\t\tif pcRef.Name != \"\" {\n\t\t\t\ttags[ExternalResourceTagKeyProvider] = pcRef.Name\n\t\t\t}\n\n\t\t\tif pcRef.Kind != \"\" {\n\t\t\t\ttags[ExternalResourceTagKeyProviderConfigKind] = pcRef.Kind\n\t\t\t}\n\t\t}\n\tcase ProviderConfigReferencer:\n\t\tif pcRef := mg.GetProviderConfigReference(); pcRef != nil && pcRef.Name != \"\" {\n\t\t\ttags[ExternalResourceTagKeyProvider] = pcRef.Name\n\t\t}\n\t}\n\n\treturn tags\n}\n\n// DefaultFirstN is the default number of names to return in FirstNAndSomeMore.\nconst DefaultFirstN = 3\n\n// FirstNAndSomeMore returns a string that contains the first n names in the\n// supplied slice, followed by \", and <count> more\" if there are more than n.\n// The slice is not sorted, i.e. the caller must make sure the order is stable\n// e.g. when using this in conditions.\nfunc FirstNAndSomeMore(n int, names []string) string {\n\tif n <= 0 {\n\t\treturn fmt.Sprintf(\"%d\", len(names))\n\t}\n\n\tif len(names) > n {\n\t\treturn fmt.Sprintf(\"%s, and %d more\", strings.Join(names[:n], \", \"), len(names)-n)\n\t}\n\n\tif len(names) == n {\n\t\treturn fmt.Sprintf(\"%s, and %s\", strings.Join(names[:n-1], \", \"), names[n-1])\n\t}\n\n\treturn strings.Join(names, \", \")\n}\n\n// StableNAndSomeMore is like FirstNAndSomeMore, but sorts the names before.\n// The input slice is not modified.\nfunc StableNAndSomeMore(n int, names []string) string {\n\tcpy := make([]string, len(names))\n\tcopy(cpy, names)\n\tsort.Strings(cpy)\n\n\treturn FirstNAndSomeMore(n, cpy)\n}\n\n// AsProtobufStruct converts the given object to a structpb.Struct for usage with gRPC\n// connections.\n// Copied from:\n// https://github.com/crossplane/crossplane/blob/release-1.16/internal/controller/apiextensions/composite/composition_functions.go#L761\nfunc AsProtobufStruct(o runtime.Object) (*structpb.Struct, error) {\n\t// If the supplied object is *Unstructured we don't need to round-trip.\n\tif u, ok := o.(*kunstructured.Unstructured); ok {\n\t\ts, err := structpb.NewStruct(u.Object)\n\t\treturn s, errors.Wrap(err, errStructFromUnstructured)\n\t}\n\n\t// If the supplied object wraps *Unstructured we don't need to round-trip.\n\tif w, ok := o.(unstructured.Wrapper); ok {\n\t\ts, err := structpb.NewStruct(w.GetUnstructured().Object)\n\t\treturn s, errors.Wrap(err, errStructFromUnstructured)\n\t}\n\n\t// Fall back to a JSON round-trip.\n\tb, err := json.Marshal(o)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, errMarshalJSON)\n\t}\n\n\ts := &structpb.Struct{}\n\n\treturn s, errors.Wrap(s.UnmarshalJSON(b), errUnmarshalJSON)\n}\n"
  },
  {
    "path": "pkg/resource/resource_test.go",
    "content": "/*\nCopyright 2019 The Crossplane 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    htcp://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 resource\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"testing\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tkerrors \"k8s.io/apimachinery/pkg/api/errors\"\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/util/wait\"\n\t\"k8s.io/client-go/util/retry\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/fake\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nconst (\n\tnamespace = \"coolns\"\n\tname      = \"cool\"\n\tuid       = types.UID(\"definitely-a-uuid\")\n\ttestSteps = 3\n)\n\nvar (\n\tMockOwnerGVK = schema.GroupVersionKind{\n\t\tGroup:   \"cool\",\n\t\tVersion: \"large\",\n\t\tKind:    \"MockOwner\",\n\t}\n\n\ttestBackoff = wait.Backoff{}\n\terrTest     = errors.New(\"test-error\")\n)\n\nfunc TestLocalConnectionSecretFor(t *testing.T) {\n\tsecretName := \"coolsecret\"\n\n\ttype args struct {\n\t\to    LocalConnectionSecretOwner\n\t\tkind schema.GroupVersionKind\n\t}\n\n\tcontroller := true\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant *corev1.Secret\n\t}{\n\t\t\"Success\": {\n\t\t\targs: args{\n\t\t\t\to: &fake.MockLocalConnectionSecretOwner{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tNamespace: namespace,\n\t\t\t\t\t\tName:      name,\n\t\t\t\t\t\tUID:       uid,\n\t\t\t\t\t},\n\t\t\t\t\tRef: &xpv2.LocalSecretReference{Name: secretName},\n\t\t\t\t},\n\t\t\t\tkind: MockOwnerGVK,\n\t\t\t},\n\t\t\twant: &corev1.Secret{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tNamespace: namespace,\n\t\t\t\t\tName:      secretName,\n\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{{\n\t\t\t\t\t\tAPIVersion:         MockOwnerGVK.GroupVersion().String(),\n\t\t\t\t\t\tKind:               MockOwnerGVK.Kind,\n\t\t\t\t\t\tName:               name,\n\t\t\t\t\t\tUID:                uid,\n\t\t\t\t\t\tController:         &controller,\n\t\t\t\t\t\tBlockOwnerDeletion: &controller,\n\t\t\t\t\t}},\n\t\t\t\t},\n\t\t\t\tType: SecretTypeConnection,\n\t\t\t\tData: map[string][]byte{},\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := LocalConnectionSecretFor(tc.args.o, tc.args.kind)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"LocalConnectionSecretFor(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConnectionSecretFor(t *testing.T) {\n\tsecretName := \"coolsecret\"\n\n\ttype args struct {\n\t\to    ConnectionSecretOwner\n\t\tkind schema.GroupVersionKind\n\t}\n\n\tcontroller := true\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant *corev1.Secret\n\t}{\n\t\t\"Success\": {\n\t\t\targs: args{\n\t\t\t\to: &fake.MockConnectionSecretOwner{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tNamespace: namespace,\n\t\t\t\t\t\tName:      name,\n\t\t\t\t\t\tUID:       uid,\n\t\t\t\t\t},\n\t\t\t\t\tWriterTo: &xpv2.SecretReference{Namespace: namespace, Name: secretName},\n\t\t\t\t},\n\t\t\t\tkind: MockOwnerGVK,\n\t\t\t},\n\t\t\twant: &corev1.Secret{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tNamespace: namespace,\n\t\t\t\t\tName:      secretName,\n\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{{\n\t\t\t\t\t\tAPIVersion:         MockOwnerGVK.GroupVersion().String(),\n\t\t\t\t\t\tKind:               MockOwnerGVK.Kind,\n\t\t\t\t\t\tName:               name,\n\t\t\t\t\t\tUID:                uid,\n\t\t\t\t\t\tController:         &controller,\n\t\t\t\t\t\tBlockOwnerDeletion: &controller,\n\t\t\t\t\t}},\n\t\t\t\t},\n\t\t\t\tType: SecretTypeConnection,\n\t\t\t\tData: map[string][]byte{},\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := ConnectionSecretFor(tc.args.o, tc.args.kind)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"ConnectionSecretFor(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype MockTyper struct {\n\tGVKs        []schema.GroupVersionKind\n\tUnversioned bool\n\tError       error\n}\n\nfunc (t MockTyper) ObjectKinds(_ runtime.Object) ([]schema.GroupVersionKind, bool, error) {\n\treturn t.GVKs, t.Unversioned, t.Error\n}\n\nfunc (t MockTyper) Recognizes(_ schema.GroupVersionKind) bool { return true }\n\nfunc TestGetKind(t *testing.T) {\n\ttype args struct {\n\t\tobj runtime.Object\n\t\tot  runtime.ObjectTyper\n\t}\n\n\ttype want struct {\n\t\tkind schema.GroupVersionKind\n\t\terr  error\n\t}\n\n\terrBoom := errors.New(\"boom\")\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant want\n\t}{\n\t\t\"KindFound\": {\n\t\t\targs: args{\n\t\t\t\tot: MockTyper{GVKs: []schema.GroupVersionKind{fake.GVK(&fake.Managed{})}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tkind: fake.GVK(&fake.Managed{}),\n\t\t\t},\n\t\t},\n\t\t\"KindError\": {\n\t\t\targs: args{\n\t\t\t\tot: MockTyper{Error: errBoom},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, \"cannot get kind of supplied object\"),\n\t\t\t},\n\t\t},\n\t\t\"KindIsUnversioned\": {\n\t\t\targs: args{\n\t\t\t\tot: MockTyper{Unversioned: true},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"supplied object is unversioned\"),\n\t\t\t},\n\t\t},\n\t\t\"NotEnoughKinds\": {\n\t\t\targs: args{\n\t\t\t\tot: MockTyper{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"supplied object does not have exactly one kind\"),\n\t\t\t},\n\t\t},\n\t\t\"TooManyKinds\": {\n\t\t\targs: args{\n\t\t\t\tot: MockTyper{GVKs: []schema.GroupVersionKind{\n\t\t\t\t\tfake.GVK(&fake.Object{}),\n\t\t\t\t\tfake.GVK(&fake.Managed{}),\n\t\t\t\t}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"supplied object does not have exactly one kind\"),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot, err := GetKind(tc.args.obj, tc.args.ot)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"GetKind(...): -want error, +got error:\\n%s\", diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.kind, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"GetKind(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMustCreateObject(t *testing.T) {\n\ttype args struct {\n\t\tkind schema.GroupVersionKind\n\t\toc   runtime.ObjectCreater\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant runtime.Object\n\t}{\n\t\t\"KindRegistered\": {\n\t\t\targs: args{\n\t\t\t\tkind: fake.GVK(&fake.Managed{}),\n\t\t\t\toc:   fake.SchemeWith(&fake.Managed{}),\n\t\t\t},\n\t\t\twant: &fake.Managed{},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := MustCreateObject(tc.args.kind, tc.args.oc)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"MustCreateObject(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIgnore(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\n\ttype args struct {\n\t\tis  ErrorIs\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"IgnoreError\": {\n\t\t\targs: args{\n\t\t\t\tis:  func(_ error) bool { return true },\n\t\t\t\terr: errBoom,\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"PropagateError\": {\n\t\t\targs: args{\n\t\t\t\tis:  func(_ error) bool { return false },\n\t\t\t\terr: errBoom,\n\t\t\t},\n\t\t\twant: errBoom,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := Ignore(tc.args.is, tc.args.err)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"Ignore(...): -want error, +got error:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIgnoreAny(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\n\ttype args struct {\n\t\tis  []ErrorIs\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"IgnoreError\": {\n\t\t\targs: args{\n\t\t\t\tis:  []ErrorIs{func(_ error) bool { return true }},\n\t\t\t\terr: errBoom,\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"IgnoreErrorArr\": {\n\t\t\targs: args{\n\t\t\t\tis: []ErrorIs{\n\t\t\t\t\tfunc(_ error) bool { return true },\n\t\t\t\t\tfunc(_ error) bool { return false },\n\t\t\t\t},\n\t\t\t\terr: errBoom,\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t\"PropagateError\": {\n\t\t\targs: args{\n\t\t\t\tis:  []ErrorIs{func(_ error) bool { return false }},\n\t\t\t\terr: errBoom,\n\t\t\t},\n\t\t\twant: errBoom,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := IgnoreAny(tc.args.err, tc.args.is...)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"Ignore(...): -want error, +got error:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsConditionTrue(t *testing.T) {\n\tcases := map[string]struct {\n\t\tc    xpv2.Condition\n\t\twant bool\n\t}{\n\t\t\"IsTrue\": {\n\t\t\tc:    xpv2.Condition{Status: corev1.ConditionTrue},\n\t\t\twant: true,\n\t\t},\n\t\t\"IsFalse\": {\n\t\t\tc:    xpv2.Condition{Status: corev1.ConditionFalse},\n\t\t\twant: false,\n\t\t},\n\t\t\"IsUnknown\": {\n\t\t\tc:    xpv2.Condition{Status: corev1.ConditionUnknown},\n\t\t\twant: false,\n\t\t},\n\t\t\"IsUnset\": {\n\t\t\tc:    xpv2.Condition{},\n\t\t\twant: false,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := IsConditionTrue(tc.c)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"IsConditionTrue(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype object struct {\n\truntime.Object\n\tmetav1.ObjectMeta\n}\n\nfunc (o *object) DeepCopyObject() runtime.Object {\n\treturn &object{ObjectMeta: *o.DeepCopy()}\n}\n\nfunc TestIsNotControllable(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\terr    error\n\t\twant   bool\n\t}{\n\t\t\"NilError\": {\n\t\t\treason: \"A nil error does not indicate something is not controllable.\",\n\t\t\terr:    nil,\n\t\t\twant:   false,\n\t\t},\n\t\t\"UnknownError\": {\n\t\t\treason: \"An that doesn't have a 'NotControllable() bool' method does not indicate something is not controllable.\",\n\t\t\terr:    errors.New(\"boom\"),\n\t\t\twant:   false,\n\t\t},\n\t\t\"NotControllableError\": {\n\t\t\treason: \"An that has a 'NotControllable() bool' method indicates something is not controllable.\",\n\t\t\terr:    notControllableError{errors.New(\"boom\")},\n\t\t\twant:   true,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := IsNotControllable(tc.err)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIsNotControllable(...): -want, +got:\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMustBeControllableBy(t *testing.T) {\n\tuid := types.UID(\"very-unique-string\")\n\tcontroller := true\n\n\ttype args struct {\n\t\tctx     context.Context\n\t\tcurrent runtime.Object\n\t\tdesired runtime.Object\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tu      types.UID\n\t\targs   args\n\t\twant   error\n\t}{\n\t\t\"Adoptable\": {\n\t\t\treason: \"A current object with no controller reference may be adopted and controlled\",\n\t\t\tu:      uid,\n\t\t\targs: args{\n\t\t\t\tcurrent: &object{},\n\t\t\t},\n\t\t},\n\t\t\"ControlledBySuppliedUID\": {\n\t\t\treason: \"A current object that is already controlled by the supplied UID is controllable\",\n\t\t\tu:      uid,\n\t\t\targs: args{\n\t\t\t\tcurrent: &object{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{\n\t\t\t\t\tUID:        uid,\n\t\t\t\t\tController: &controller,\n\t\t\t\t}}}},\n\t\t\t},\n\t\t},\n\t\t\"ControlledBySomeoneElse\": {\n\t\t\treason: \"A current object that is already controlled by a different UID is not controllable\",\n\t\t\tu:      uid,\n\t\t\targs: args{\n\t\t\t\tcurrent: &object{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{\n\t\t\t\t\tUID:        types.UID(\"some-other-uid\"),\n\t\t\t\t\tController: &controller,\n\t\t\t\t}}}},\n\t\t\t},\n\t\t\twant: notControllableError{errors.Errorf(\"existing object is not controlled by UID %q\", uid)},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tao := MustBeControllableBy(tc.u)\n\n\t\t\terr := ao(tc.args.ctx, tc.args.current, tc.args.desired)\n\t\t\tif diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nMustBeControllableBy(...)(...): -want error, +got error\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConnectionSecretMustBeControllableBy(t *testing.T) {\n\tuid := types.UID(\"very-unique-string\")\n\tcontroller := true\n\n\ttype args struct {\n\t\tctx     context.Context\n\t\tcurrent runtime.Object\n\t\tdesired runtime.Object\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tu      types.UID\n\t\targs   args\n\t\twant   error\n\t}{\n\t\t\"Adoptable\": {\n\t\t\treason: \"A Secret of SecretTypeConnection with no controller reference may be adopted and controlled\",\n\t\t\tu:      uid,\n\t\t\targs: args{\n\t\t\t\tcurrent: &corev1.Secret{Type: SecretTypeConnection},\n\t\t\t},\n\t\t},\n\t\t\"ControlledBySuppliedUID\": {\n\t\t\treason: \"A Secret of any type that is already controlled by the supplied UID is controllable\",\n\t\t\tu:      uid,\n\t\t\targs: args{\n\t\t\t\tcurrent: &corev1.Secret{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{\n\t\t\t\t\t\tUID:        uid,\n\t\t\t\t\t\tController: &controller,\n\t\t\t\t\t}}},\n\t\t\t\t\tType: corev1.SecretTypeOpaque,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"ControlledBySomeoneElse\": {\n\t\t\treason: \"A Secret of any type that is already controlled by the another UID is not controllable\",\n\t\t\tu:      uid,\n\t\t\targs: args{\n\t\t\t\tcurrent: &corev1.Secret{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{\n\t\t\t\t\t\tUID:        types.UID(\"some-other-uid\"),\n\t\t\t\t\t\tController: &controller,\n\t\t\t\t\t}}},\n\t\t\t\t\tType: SecretTypeConnection,\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: notControllableError{errors.Errorf(\"existing secret is not controlled by UID %q\", uid)},\n\t\t},\n\t\t\"UncontrolledOpaqueSecret\": {\n\t\t\treason: \"A Secret of corev1.SecretTypeOpqaue with no controller is not controllable\",\n\t\t\tu:      uid,\n\t\t\targs: args{\n\t\t\t\tcurrent: &corev1.Secret{Type: corev1.SecretTypeOpaque},\n\t\t\t},\n\t\t\twant: notControllableError{errors.Errorf(\"refusing to modify uncontrolled secret of type %q\", corev1.SecretTypeOpaque)},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tao := ConnectionSecretMustBeControllableBy(tc.u)\n\n\t\t\terr := ao(tc.args.ctx, tc.args.current, tc.args.desired)\n\t\t\tif diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nConnectionSecretMustBeControllableBy(...)(...): -want error, +got error\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAllowUpdateIf(t *testing.T) {\n\ttype args struct {\n\t\tctx     context.Context\n\t\tcurrent runtime.Object\n\t\tdesired runtime.Object\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tfn     func(current, desired runtime.Object) bool\n\t\targs   args\n\t\twant   error\n\t}{\n\t\t\"Allowed\": {\n\t\t\treason: \"No error should be returned when the supplied function returns true\",\n\t\t\tfn:     func(_, _ runtime.Object) bool { return true },\n\t\t\targs: args{\n\t\t\t\tcurrent: &object{},\n\t\t\t},\n\t\t},\n\t\t\"NotAllowed\": {\n\t\t\treason: \"An error that satisfies IsNotAllowed should be returned when the supplied function returns false\",\n\t\t\tfn:     func(_, _ runtime.Object) bool { return false },\n\t\t\targs: args{\n\t\t\t\tcurrent: &object{},\n\t\t\t},\n\t\t\twant: notAllowedError{errors.New(\"update not allowed\")},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tao := AllowUpdateIf(tc.fn)\n\n\t\t\terr := ao(tc.args.ctx, tc.args.current, tc.args.desired)\n\t\t\tif diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nAllowUpdateIf(...)(...): -want error, +got error\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetExternalTags(t *testing.T) {\n\tprovName := \"prov\"\n\tcases := map[string]struct {\n\t\to    Managed\n\t\twant map[string]string\n\t}{\n\t\t\"SuccessfulWithTypedProviderConfig\": {\n\t\t\to: &fake.ModernManaged{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName: name,\n\t\t\t\t},\n\t\t\t\tTypedProviderConfigReferencer: fake.TypedProviderConfigReferencer{\n\t\t\t\t\tRef: &xpv2.ProviderConfigReference{Name: provName, Kind: \"ProviderConfig\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: map[string]string{\n\t\t\t\tExternalResourceTagKeyKind:               strings.ToLower((&fake.Managed{}).GetObjectKind().GroupVersionKind().GroupKind().String()),\n\t\t\t\tExternalResourceTagKeyName:               name,\n\t\t\t\tExternalResourceTagKeyProvider:           provName,\n\t\t\t\tExternalResourceTagKeyProviderConfigKind: \"ProviderConfig\",\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulWithNamespacedObject\": {\n\t\t\to: &fake.ModernManaged{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      name,\n\t\t\t\t\tNamespace: namespace,\n\t\t\t\t},\n\t\t\t\tTypedProviderConfigReferencer: fake.TypedProviderConfigReferencer{\n\t\t\t\t\tRef: &xpv2.ProviderConfigReference{Name: provName, Kind: \"ProviderConfig\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: map[string]string{\n\t\t\t\tExternalResourceTagKeyKind:               strings.ToLower((&fake.Managed{}).GetObjectKind().GroupVersionKind().GroupKind().String()),\n\t\t\t\tExternalResourceTagKeyName:               name,\n\t\t\t\tExternalResourceTagKeyNamespace:          namespace,\n\t\t\t\tExternalResourceTagKeyProvider:           provName,\n\t\t\t\tExternalResourceTagKeyProviderConfigKind: \"ProviderConfig\",\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulWithLegacyProviderConfig\": {\n\t\t\to: &fake.LegacyManaged{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName: name,\n\t\t\t\t},\n\t\t\t\tLegacyProviderConfigReferencer: fake.LegacyProviderConfigReferencer{Ref: &xpv2.Reference{Name: provName}},\n\t\t\t},\n\t\t\twant: map[string]string{\n\t\t\t\tExternalResourceTagKeyKind:     strings.ToLower((&fake.LegacyManaged{}).GetObjectKind().GroupVersionKind().GroupKind().String()),\n\t\t\t\tExternalResourceTagKeyName:     name,\n\t\t\t\tExternalResourceTagKeyProvider: provName,\n\t\t\t},\n\t\t},\n\t\t\"NotLegacyOrModernManaged\": {\n\t\t\to: &fake.Managed{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName: name,\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: map[string]string{\n\t\t\t\tExternalResourceTagKeyKind: strings.ToLower((&fake.Managed{}).GetObjectKind().GroupVersionKind().GroupKind().String()),\n\t\t\t\tExternalResourceTagKeyName: name,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := GetExternalTags(tc.o)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"GetExternalTags(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// single test case => not using tables.\nfunc Test_notControllableError_NotControllable(t *testing.T) {\n\terr := notControllableError{\n\t\terrors.New(\"test-error\"),\n\t}\n\tif !err.NotControllable() {\n\t\tt.Errorf(\"NotControllable(): false\")\n\t}\n}\n\n// single test case => not using tables.\nfunc Test_notAllowedError_NotAllowed(t *testing.T) {\n\terr := notAllowedError{\n\t\terrors.New(\"test-error\"),\n\t}\n\tif !err.NotAllowed() {\n\t\tt.Errorf(\"NotAllowed(): false\")\n\t}\n}\n\nfunc TestIsAPIErrorWrapped(t *testing.T) {\n\ttestCases := map[string]struct {\n\t\terr  error\n\t\twant bool\n\t}{\n\t\t\"NoError\": {\n\t\t\twant: false,\n\t\t},\n\t\t\"NotAPIError\": {\n\t\t\terr:  errors.New(\"test-error\"),\n\t\t\twant: false,\n\t\t},\n\t\t\"APIError\": {\n\t\t\terr:  kerrors.NewNotFound(schema.GroupResource{}, \"test-resource\"),\n\t\t\twant: true,\n\t\t},\n\t\t\"WrappedAPIError\": {\n\t\t\terr: errors.Wrap(\n\t\t\t\tkerrors.NewNotFound(schema.GroupResource{}, \"test-resource\"), \"test-wrapper\"),\n\t\t\twant: true,\n\t\t},\n\t}\n\n\tfor name, tc := range testCases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tif got := IsAPIErrorWrapped(tc.err); got != tc.want {\n\t\t\t\tt.Errorf(\"IsAPIErrorWrapped() = %v, want %v\", got, tc.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNewApplicatorWithRetry(t *testing.T) {\n\ttype args struct {\n\t\tapplicator  Applicator\n\t\tshouldRetry shouldRetryFunc\n\t\tbackoff     *wait.Backoff\n\t}\n\n\ttestCases := map[string]struct {\n\t\targs args\n\t\twant Applicator\n\t}{\n\t\t\"DefaultBackoff\": {\n\t\t\targs: args{},\n\t\t\twant: &ApplicatorWithRetry{\n\t\t\t\tbackoff: retry.DefaultRetry,\n\t\t\t},\n\t\t},\n\t\t\"CustomBackoff\": {\n\t\t\targs: args{\n\t\t\t\tbackoff: &testBackoff,\n\t\t\t},\n\t\t\twant: &ApplicatorWithRetry{\n\t\t\t\tbackoff: testBackoff,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range testCases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tif diff := cmp.Diff(tc.want,\n\t\t\t\tNewApplicatorWithRetry(tc.args.applicator, tc.args.shouldRetry, tc.args.backoff),\n\t\t\t\tcmp.AllowUnexported(ApplicatorWithRetry{})); diff != \"\" {\n\t\t\t\tt.Errorf(\"NewApplicatorWithRetry(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype mockApplicator struct {\n\treturnError bool\n\tcount       uint\n}\n\nfunc (m *mockApplicator) Apply(_ context.Context, _ client.Object, _ ...ApplyOption) error {\n\tm.count++\n\n\tif m.returnError {\n\t\treturn errTest\n\t}\n\n\treturn nil\n}\n\nfunc TestApplicatorWithRetry_Apply(t *testing.T) {\n\ttype fields struct {\n\t\tapplicator  Applicator\n\t\tshouldRetry shouldRetryFunc\n\t\tbackoff     wait.Backoff\n\t}\n\n\ttype args struct {\n\t\tctx  context.Context\n\t\tc    client.Object\n\t\topts []ApplyOption\n\t}\n\n\ttestCases := map[string]struct {\n\t\tfields    fields\n\t\targs      args\n\t\twantErr   error\n\t\twantCount uint\n\t}{\n\t\t\"NoRetry\": {\n\t\t\tfields: fields{\n\t\t\t\tapplicator: &mockApplicator{returnError: true},\n\t\t\t\tshouldRetry: func(_ error) bool {\n\t\t\t\t\treturn false\n\t\t\t\t},\n\t\t\t\tbackoff: wait.Backoff{Steps: testSteps},\n\t\t\t},\n\t\t\targs:      args{},\n\t\t\twantErr:   errTest,\n\t\t\twantCount: 1,\n\t\t},\n\t\t\"ShouldRetry\": {\n\t\t\tfields: fields{\n\t\t\t\tapplicator: &mockApplicator{returnError: true},\n\t\t\t\tshouldRetry: func(_ error) bool {\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\tbackoff: wait.Backoff{Steps: testSteps},\n\t\t\t},\n\t\t\targs:      args{},\n\t\t\twantErr:   errTest,\n\t\t\twantCount: testSteps,\n\t\t},\n\t\t\"NoError\": {\n\t\t\tfields: fields{\n\t\t\t\tapplicator: &mockApplicator{},\n\t\t\t\tshouldRetry: func(_ error) bool {\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\tbackoff: wait.Backoff{Steps: testSteps},\n\t\t\t},\n\t\t\targs:      args{},\n\t\t\twantErr:   nil,\n\t\t\twantCount: 1,\n\t\t},\n\t}\n\n\tfor name, tc := range testCases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tawr := &ApplicatorWithRetry{\n\t\t\t\tApplicator:  tc.fields.applicator,\n\t\t\t\tshouldRetry: tc.fields.shouldRetry,\n\t\t\t\tbackoff:     tc.fields.backoff,\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.wantErr, awr.Apply(tc.args.ctx, tc.args.c, tc.args.opts...), test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Fatalf(\"ApplicatorWithRetry.Apply(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(awr.Applicator.(*mockApplicator).count, tc.wantCount); diff != \"\" {\n\t\t\t\tt.Errorf(\"Retry count mismatch: -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUpdate(t *testing.T) {\n\ttype args struct {\n\t\tfn      func(current, desired runtime.Object)\n\t\tcurrent runtime.Object\n\t\tdesired runtime.Object\n\t}\n\n\ttests := map[string]struct {\n\t\targs args\n\t\twant runtime.Object\n\t}{\n\t\t\"Update\": {\n\t\t\targs: args{\n\t\t\t\tfn: func(current, desired runtime.Object) {\n\t\t\t\t\tc, d := current.(*corev1.Secret), desired.(*corev1.Secret)\n\n\t\t\t\t\tc.StringData = d.StringData\n\t\t\t\t},\n\t\t\t\tcurrent: &corev1.Secret{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"current\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tdesired: &corev1.Secret{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"desired\",\n\t\t\t\t\t},\n\t\t\t\t\tStringData: map[string]string{\n\t\t\t\t\t\t\"key\": \"value\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: &corev1.Secret{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName: \"current\",\n\t\t\t\t},\n\t\t\t\tStringData: map[string]string{\n\t\t\t\t\t\"key\": \"value\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tt := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tif err := UpdateFn(tt.args.fn)(nil, tt.args.current, tt.args.desired); err != nil {\n\t\t\t\tt.Fatalf(\"ApplyOption() = %v, want %v\", err, nil)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tt.want, tt.args.current); diff != \"\" {\n\t\t\t\tt.Errorf(\"UpdateFn updated object mismatch: -want, +got: %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFirstNAndSomeMore(t *testing.T) {\n\ttype args struct {\n\t\tn     int\n\t\tnames []string\n\t}\n\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant string\n\t}{\n\t\t{args: args{n: 3, names: []string{\"a\", \"b\", \"c\", \"d\", \"e\"}}, want: \"a, b, c, and 2 more\"},\n\t\t{args: args{n: 3, names: []string{\"a\", \"b\", \"c\"}}, want: \"a, b, and c\"},\n\t\t{args: args{n: 3, names: []string{\"a\", \"b\"}}, want: \"a, b\"},\n\t\t{args: args{n: 3, names: []string{\"a\"}}, want: \"a\"},\n\t\t{args: args{n: 3, names: []string{}}, want: \"\"},\n\t\t{args: args{n: 3, names: []string{\"a\", \"c\", \"e\", \"b\", \"d\"}}, want: \"a, c, e, and 2 more\"},\n\t\t{args: args{n: 3, names: []string{\"a\", \"b\", \"b\", \"b\", \"d\"}}, want: \"a, b, b, and 2 more\"}, //nolint:dupword // Intentional.\n\t\t{args: args{n: 2, names: []string{\"a\", \"c\", \"e\", \"b\", \"d\"}}, want: \"a, c, and 3 more\"},\n\t\t{args: args{n: 0, names: []string{\"a\", \"c\", \"e\", \"b\", \"d\"}}, want: \"5\"},\n\t\t{args: args{n: -7, names: []string{\"a\", \"c\", \"e\", \"b\", \"d\"}}, want: \"5\"},\n\t\t{args: args{n: 1, names: []string{\"a\", \"c\", \"e\", \"b\", \"d\"}}, want: \"a, and 4 more\"},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := FirstNAndSomeMore(tt.args.n, tt.args.names); got != tt.want {\n\t\t\t\tt.Errorf(\"FirstNAndSomeMore() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/resource/unstructured/claim/claim.go",
    "content": "/*\nCopyright 2020 The Crossplane 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// Package claim contains an unstructured composite resource claim.\npackage claim\n\nimport (\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\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/runtime/schema\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/unstructured/reference\"\n)\n\n// An Option modifies an unstructured composite resource claim.\ntype Option func(*Unstructured)\n\n// WithGroupVersionKind sets the GroupVersionKind of the unstructured composite\n// resource claim.\nfunc WithGroupVersionKind(gvk schema.GroupVersionKind) Option {\n\treturn func(c *Unstructured) {\n\t\tc.SetGroupVersionKind(gvk)\n\t}\n}\n\n// WithConditions returns an Option that sets the supplied conditions on an\n// unstructured composite resource claim.\nfunc WithConditions(c ...xpv2.Condition) Option {\n\treturn func(cr *Unstructured) {\n\t\tcr.SetConditions(c...)\n\t}\n}\n\n// New returns a new unstructured composite resource claim.\nfunc New(opts ...Option) *Unstructured {\n\tc := &Unstructured{Unstructured: unstructured.Unstructured{Object: make(map[string]any)}}\n\tfor _, f := range opts {\n\t\tf(c)\n\t}\n\n\treturn c\n}\n\n// +k8s:deepcopy-gen=true\n// +kubebuilder:object:root=true\n\n// An Unstructured composite resource claim.\ntype Unstructured struct {\n\tunstructured.Unstructured\n}\n\n// GetUnstructured returns the underlying *unstructured.Unstructured.\nfunc (c *Unstructured) GetUnstructured() *unstructured.Unstructured {\n\treturn &c.Unstructured\n}\n\n// GetCompositionSelector of this composite resource claim.\nfunc (c *Unstructured) GetCompositionSelector() *metav1.LabelSelector {\n\tout := &metav1.LabelSelector{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(\"spec.compositionSelector\", out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetCompositionSelector of this composite resource claim.\nfunc (c *Unstructured) SetCompositionSelector(sel *metav1.LabelSelector) {\n\t_ = fieldpath.Pave(c.Object).SetValue(\"spec.compositionSelector\", sel)\n}\n\n// GetCompositionReference of this composite resource claim.\nfunc (c *Unstructured) GetCompositionReference() *corev1.ObjectReference {\n\tout := &corev1.ObjectReference{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(\"spec.compositionRef\", out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetCompositionReference of this composite resource claim.\nfunc (c *Unstructured) SetCompositionReference(ref *corev1.ObjectReference) {\n\t_ = fieldpath.Pave(c.Object).SetValue(\"spec.compositionRef\", ref)\n}\n\n// GetCompositionRevisionReference of this resource claim.\nfunc (c *Unstructured) GetCompositionRevisionReference() *corev1.LocalObjectReference {\n\tout := &corev1.LocalObjectReference{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(\"spec.compositionRevisionRef\", out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetCompositionRevisionReference of this resource claim.\nfunc (c *Unstructured) SetCompositionRevisionReference(ref *corev1.LocalObjectReference) {\n\t_ = fieldpath.Pave(c.Object).SetValue(\"spec.compositionRevisionRef\", ref)\n}\n\n// GetCompositionRevisionSelector of this resource claim.\nfunc (c *Unstructured) GetCompositionRevisionSelector() *metav1.LabelSelector {\n\tout := &metav1.LabelSelector{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(\"spec.compositionRevisionSelector\", out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetCompositionRevisionSelector of this resource claim.\nfunc (c *Unstructured) SetCompositionRevisionSelector(ref *metav1.LabelSelector) {\n\t_ = fieldpath.Pave(c.Object).SetValue(\"spec.compositionRevisionSelector\", ref)\n}\n\n// SetCompositionUpdatePolicy of this resource claim.\nfunc (c *Unstructured) SetCompositionUpdatePolicy(p *xpv2.UpdatePolicy) {\n\t_ = fieldpath.Pave(c.Object).SetValue(\"spec.compositionUpdatePolicy\", p)\n}\n\n// GetCompositionUpdatePolicy of this resource claim.\nfunc (c *Unstructured) GetCompositionUpdatePolicy() *xpv2.UpdatePolicy {\n\tp, err := fieldpath.Pave(c.Object).GetString(\"spec.compositionUpdatePolicy\")\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tout := xpv2.UpdatePolicy(p)\n\n\treturn &out\n}\n\n// SetCompositeDeletePolicy of this resource claim.\nfunc (c *Unstructured) SetCompositeDeletePolicy(p *xpv2.CompositeDeletePolicy) {\n\t_ = fieldpath.Pave(c.Object).SetValue(\"spec.compositeDeletePolicy\", p)\n}\n\n// GetCompositeDeletePolicy of this resource claim.\nfunc (c *Unstructured) GetCompositeDeletePolicy() *xpv2.CompositeDeletePolicy {\n\tp, err := fieldpath.Pave(c.Object).GetString(\"spec.compositeDeletePolicy\")\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tout := xpv2.CompositeDeletePolicy(p)\n\n\treturn &out\n}\n\n// GetResourceReference of this composite resource claim.\nfunc (c *Unstructured) GetResourceReference() *reference.Composite {\n\tout := &reference.Composite{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(\"spec.resourceRef\", out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetResourceReference of this composite resource claim.\nfunc (c *Unstructured) SetResourceReference(ref *reference.Composite) {\n\t_ = fieldpath.Pave(c.Object).SetValue(\"spec.resourceRef\", ref)\n}\n\n// GetReference returns reference to this claim.\nfunc (c *Unstructured) GetReference() *reference.Claim {\n\treturn &reference.Claim{\n\t\tAPIVersion: c.GetAPIVersion(),\n\t\tKind:       c.GetKind(),\n\t\tName:       c.GetName(),\n\t\tNamespace:  c.GetNamespace(),\n\t}\n}\n\n// GetWriteConnectionSecretToReference of this composite resource claim.\nfunc (c *Unstructured) GetWriteConnectionSecretToReference() *xpv2.LocalSecretReference {\n\tout := &xpv2.LocalSecretReference{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(\"spec.writeConnectionSecretToRef\", out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetWriteConnectionSecretToReference of this composite resource claim.\nfunc (c *Unstructured) SetWriteConnectionSecretToReference(ref *xpv2.LocalSecretReference) {\n\t_ = fieldpath.Pave(c.Object).SetValue(\"spec.writeConnectionSecretToRef\", ref)\n}\n\n// GetCondition of this composite resource claim.\nfunc (c *Unstructured) GetCondition(ct xpv2.ConditionType) xpv2.Condition {\n\tconditioned := xpv2.ConditionedStatus{}\n\t// The path is directly `status` because conditions are inline.\n\tif err := fieldpath.Pave(c.Object).GetValueInto(\"status\", &conditioned); err != nil {\n\t\treturn xpv2.Condition{}\n\t}\n\n\treturn conditioned.GetCondition(ct)\n}\n\n// SetConditions of this composite resource claim.\nfunc (c *Unstructured) SetConditions(conditions ...xpv2.Condition) {\n\tconditioned := xpv2.ConditionedStatus{}\n\t// The path is directly `status` because conditions are inline.\n\t_ = fieldpath.Pave(c.Object).GetValueInto(\"status\", &conditioned)\n\tconditioned.SetConditions(conditions...)\n\t_ = fieldpath.Pave(c.Object).SetValue(\"status.conditions\", conditioned.Conditions)\n}\n\n// GetConnectionDetailsLastPublishedTime of this composite resource claim.\nfunc (c *Unstructured) GetConnectionDetailsLastPublishedTime() *metav1.Time {\n\tout := &metav1.Time{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(\"status.connectionDetails.lastPublishedTime\", out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetConnectionDetailsLastPublishedTime of this composite resource claim.\nfunc (c *Unstructured) SetConnectionDetailsLastPublishedTime(t *metav1.Time) {\n\t_ = fieldpath.Pave(c.Object).SetValue(\"status.connectionDetails.lastPublishedTime\", t)\n}\n\n// SetObservedGeneration of this composite resource claim.\nfunc (c *Unstructured) SetObservedGeneration(generation int64) {\n\tstatus := &xpv2.ObservedStatus{}\n\t_ = fieldpath.Pave(c.Object).GetValueInto(\"status\", status)\n\tstatus.SetObservedGeneration(generation)\n\t_ = fieldpath.Pave(c.Object).SetValue(\"status.observedGeneration\", status.ObservedGeneration)\n}\n\n// GetObservedGeneration of this composite resource claim.\nfunc (c *Unstructured) GetObservedGeneration() int64 {\n\tstatus := &xpv2.ObservedStatus{}\n\t_ = fieldpath.Pave(c.Object).GetValueInto(\"status\", status)\n\n\treturn status.GetObservedGeneration()\n}\n"
  },
  {
    "path": "pkg/resource/unstructured/claim/claim_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 claim\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\t\"time\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\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/runtime/schema\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/unstructured/reference\"\n)\n\nvar _ client.Object = &Unstructured{}\n\nfunc TestWithGroupVersionKind(t *testing.T) {\n\tgvk := schema.GroupVersionKind{\n\t\tGroup:   \"g\",\n\t\tVersion: \"v1\",\n\t\tKind:    \"k\",\n\t}\n\tcases := map[string]struct {\n\t\tgvk  schema.GroupVersionKind\n\t\twant *Unstructured\n\t}{\n\t\t\"New\": {\n\t\t\tgvk: gvk,\n\t\t\twant: &Unstructured{\n\t\t\t\tUnstructured: unstructured.Unstructured{\n\t\t\t\t\tObject: map[string]any{\n\t\t\t\t\t\t\"apiVersion\": \"g/v1\",\n\t\t\t\t\t\t\"kind\":       \"k\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := New(WithGroupVersionKind(tc.gvk))\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"New(WithGroupVersionKind(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConditions(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\tu      *Unstructured\n\t\tset    []xpv2.Condition\n\t\tget    xpv2.ConditionType\n\t\twant   xpv2.Condition\n\t}{\n\t\t\"NewCondition\": {\n\t\t\treason: \"It should be possible to set a condition of an empty Unstructured.\",\n\t\t\tu:      New(),\n\t\t\tset:    []xpv2.Condition{xpv2.Available(), xpv2.ReconcileSuccess()},\n\t\t\tget:    xpv2.TypeReady,\n\t\t\twant:   xpv2.Available(),\n\t\t},\n\t\t\"ExistingCondition\": {\n\t\t\treason: \"It should be possible to overwrite a condition that is already set.\",\n\t\t\tu:      New(WithConditions(xpv2.Creating())),\n\t\t\tset:    []xpv2.Condition{xpv2.Available()},\n\t\t\tget:    xpv2.TypeReady,\n\t\t\twant:   xpv2.Available(),\n\t\t},\n\t\t\"WeirdStatus\": {\n\t\t\treason: \"It should not be possible to set a condition when status is not an object.\",\n\t\t\tu: &Unstructured{unstructured.Unstructured{Object: map[string]any{\n\t\t\t\t\"status\": \"wat\",\n\t\t\t}}},\n\t\t\tset:  []xpv2.Condition{xpv2.Available()},\n\t\t\tget:  xpv2.TypeReady,\n\t\t\twant: xpv2.Condition{},\n\t\t},\n\t\t\"WeirdStatusConditions\": {\n\t\t\treason: \"Conditions should be overwritten if they are not an object.\",\n\t\t\tu: &Unstructured{unstructured.Unstructured{Object: map[string]any{\n\t\t\t\t\"status\": map[string]any{\n\t\t\t\t\t\"conditions\": \"wat\",\n\t\t\t\t},\n\t\t\t}}},\n\t\t\tset:  []xpv2.Condition{xpv2.Available()},\n\t\t\tget:  xpv2.TypeReady,\n\t\t\twant: xpv2.Available(),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetConditions(tc.set...)\n\n\t\t\tgot := tc.u.GetCondition(tc.get)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nu.GetCondition(%s): -want, +got:\\n%s\", tc.reason, tc.get, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompositionSelector(t *testing.T) {\n\tsel := &metav1.LabelSelector{MatchLabels: map[string]string{\"cool\": \"very\"}}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *metav1.LabelSelector\n\t\twant *metav1.LabelSelector\n\t}{\n\t\t\"NewSel\": {\n\t\t\tu:    New(),\n\t\t\tset:  sel,\n\t\t\twant: sel,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetCompositionSelector(tc.set)\n\n\t\t\tgot := tc.u.GetCompositionSelector()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetCompositionSelector(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompositionReference(t *testing.T) {\n\tref := &corev1.ObjectReference{Namespace: \"ns\", Name: \"cool\"}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *corev1.ObjectReference\n\t\twant *corev1.ObjectReference\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(),\n\t\t\tset:  ref,\n\t\t\twant: ref,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetCompositionReference(tc.set)\n\n\t\t\tgot := tc.u.GetCompositionReference()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetCompositionReference(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompositionRevisionReference(t *testing.T) {\n\tref := &corev1.LocalObjectReference{Name: \"cool\"}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *corev1.LocalObjectReference\n\t\twant *corev1.LocalObjectReference\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(),\n\t\t\tset:  ref,\n\t\t\twant: ref,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetCompositionRevisionReference(tc.set)\n\n\t\t\tgot := tc.u.GetCompositionRevisionReference()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetCompositionRevisionReference(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompositionRevisionSelector(t *testing.T) {\n\tsel := &metav1.LabelSelector{MatchLabels: map[string]string{\"cool\": \"very\"}}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *metav1.LabelSelector\n\t\twant *metav1.LabelSelector\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(),\n\t\t\tset:  sel,\n\t\t\twant: sel,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetCompositionRevisionSelector(tc.set)\n\n\t\t\tgot := tc.u.GetCompositionRevisionSelector()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetCompositionRevisionSelector(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompositionUpdatePolicy(t *testing.T) {\n\tp := xpv2.UpdateManual\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *xpv2.UpdatePolicy\n\t\twant *xpv2.UpdatePolicy\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(),\n\t\t\tset:  &p,\n\t\t\twant: &p,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetCompositionUpdatePolicy(tc.set)\n\n\t\t\tgot := tc.u.GetCompositionUpdatePolicy()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetCompositionUpdatePolicy(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompositeDeletePolicy(t *testing.T) {\n\tp := xpv2.CompositeDeleteBackground\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *xpv2.CompositeDeletePolicy\n\t\twant *xpv2.CompositeDeletePolicy\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(),\n\t\t\tset:  &p,\n\t\t\twant: &p,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetCompositeDeletePolicy(tc.set)\n\n\t\t\tgot := tc.u.GetCompositeDeletePolicy()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetCompositeDeletePolicy(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestResourceReference(t *testing.T) {\n\tref := &reference.Composite{Name: \"cool\"}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *reference.Composite\n\t\twant *reference.Composite\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(),\n\t\t\tset:  ref,\n\t\t\twant: ref,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetResourceReference(tc.set)\n\n\t\t\tgot := tc.u.GetResourceReference()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetResourceReference(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestClaimReference(t *testing.T) {\n\tref := &reference.Claim{Namespace: \"ns\", Name: \"cool\", APIVersion: \"foo.com/v1\", Kind: \"Foo\"}\n\tu := &Unstructured{}\n\tu.SetName(ref.Name)\n\tu.SetNamespace(ref.Namespace)\n\tu.SetAPIVersion(ref.APIVersion)\n\tu.SetKind(ref.Kind)\n\n\tgot := u.GetReference()\n\tif diff := cmp.Diff(ref, got); diff != \"\" {\n\t\tt.Errorf(\"\\nu.GetClaimReference(): -want, +got:\\n%s\", diff)\n\t}\n}\n\nfunc TestWriteConnectionSecretToReference(t *testing.T) {\n\tref := &xpv2.LocalSecretReference{Name: \"cool\"}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *xpv2.LocalSecretReference\n\t\twant *xpv2.LocalSecretReference\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(),\n\t\t\tset:  ref,\n\t\t\twant: ref,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetWriteConnectionSecretToReference(tc.set)\n\n\t\t\tgot := tc.u.GetWriteConnectionSecretToReference()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetWriteConnectionSecretToReference(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConnectionDetailsLastPublishedTime(t *testing.T) {\n\tnow := &metav1.Time{Time: time.Now()}\n\n\t// The timestamp loses a little resolution when round-tripped through JSON\n\t// encoding.\n\tlores := func(t *metav1.Time) *metav1.Time {\n\t\tout := &metav1.Time{}\n\t\tj, _ := json.Marshal(t) //nolint:errchkjson // No encoding issue in practice.\n\t\t_ = json.Unmarshal(j, out)\n\n\t\treturn out\n\t}\n\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *metav1.Time\n\t\twant *metav1.Time\n\t}{\n\t\t\"NewTime\": {\n\t\t\tu:    New(),\n\t\t\tset:  now,\n\t\t\twant: lores(now),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetConnectionDetailsLastPublishedTime(tc.set)\n\n\t\t\tgot := tc.u.GetConnectionDetailsLastPublishedTime()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetConnectionDetailsLastPublishedTime(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestObservedGeneration(t *testing.T) {\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\twant int64\n\t}{\n\t\t\"Set\": {\n\t\t\tu: New(func(u *Unstructured) {\n\t\t\t\tu.SetObservedGeneration(123)\n\t\t\t}),\n\t\t\twant: 123,\n\t\t},\n\t\t\"NotFound\": {\n\t\t\tu: New(),\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := tc.u.GetObservedGeneration()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetObservedGeneration(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/resource/unstructured/claim/zz_generated.deepcopy.go",
    "content": "//go:build !ignore_autogenerated\n\n/*\nCopyright 2025 The Crossplane 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 controller-gen. DO NOT EDIT.\n\npackage claim\n\nimport (\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n)\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *Unstructured) DeepCopyInto(out *Unstructured) {\n\t*out = *in\n\tin.Unstructured.DeepCopyInto(&out.Unstructured)\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Unstructured.\nfunc (in *Unstructured) DeepCopy() *Unstructured {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(Unstructured)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.\nfunc (in *Unstructured) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/resource/unstructured/client.go",
    "content": "/*\nCopyright 2020 The Crossplane 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// Package unstructured contains utilities unstructured Kubernetes objects.\npackage unstructured\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"k8s.io/apimachinery/pkg/api/meta\"\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\"sigs.k8s.io/controller-runtime/pkg/client\"\n)\n\n// Wrapper returns the underlying *unstructured.Unstructured.\ntype Wrapper interface {\n\tGetUnstructured() *unstructured.Unstructured\n}\n\n// ListWrapper allows the *unstructured.UnstructuredList to be accessed.\ntype ListWrapper interface {\n\tGetUnstructuredList() *unstructured.UnstructuredList\n}\n\n// NewClient returns a client.Client that will operate on the underlying\n// *unstructured.Unstructured if the object satisfies the Wrapper or ListWrapper\n// interfaces. It relies on *unstructured.Unstructured instead of simpler\n// map[string]any to avoid unnecessary copying.\nfunc NewClient(c client.Client) *WrapperClient {\n\treturn &WrapperClient{kube: c}\n}\n\n// A WrapperClient is a client.Client that will operate on the underlying\n// *unstructured.Unstructured if the object satisfies the Wrapper or ListWrapper\n// interfaces.\ntype WrapperClient struct {\n\tkube client.Client\n}\n\n// Get retrieves an obj for the given object key from the Kubernetes Cluster.\n// obj must be a struct pointer so that obj can be updated with the response\n// returned by the Server.\nfunc (c *WrapperClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {\n\tif u, ok := obj.(Wrapper); ok {\n\t\treturn c.kube.Get(ctx, key, u.GetUnstructured(), opts...)\n\t}\n\n\treturn c.kube.Get(ctx, key, obj, opts...)\n}\n\n// List retrieves list of objects for a given namespace and list options. On a\n// successful call, Items field in the list will be populated with the\n// result returned from the server.\nfunc (c *WrapperClient) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {\n\tif u, ok := list.(ListWrapper); ok {\n\t\treturn c.kube.List(ctx, u.GetUnstructuredList(), opts...)\n\t}\n\n\treturn c.kube.List(ctx, list, opts...)\n}\n\n// Create saves the object obj in the Kubernetes cluster.\nfunc (c *WrapperClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {\n\tif u, ok := obj.(Wrapper); ok {\n\t\treturn c.kube.Create(ctx, u.GetUnstructured(), opts...)\n\t}\n\n\treturn c.kube.Create(ctx, obj, opts...)\n}\n\n// Delete deletes the given obj from Kubernetes cluster.\nfunc (c *WrapperClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {\n\tif u, ok := obj.(Wrapper); ok {\n\t\treturn c.kube.Delete(ctx, u.GetUnstructured(), opts...)\n\t}\n\n\treturn c.kube.Delete(ctx, obj, opts...)\n}\n\n// Update updates the given obj in the Kubernetes cluster. obj must be a\n// struct pointer so that obj can be updated with the content returned by the Server.\nfunc (c *WrapperClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {\n\tif u, ok := obj.(Wrapper); ok {\n\t\treturn c.kube.Update(ctx, u.GetUnstructured(), opts...)\n\t}\n\n\treturn c.kube.Update(ctx, obj, opts...)\n}\n\n// Patch patches the given obj in the Kubernetes cluster. obj must be a\n// struct pointer so that obj can be updated with the content returned by the Server.\nfunc (c *WrapperClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {\n\tif u, ok := obj.(Wrapper); ok {\n\t\treturn c.kube.Patch(ctx, u.GetUnstructured(), patch, opts...)\n\t}\n\n\treturn c.kube.Patch(ctx, obj, patch, opts...)\n}\n\n// Apply applies the given apply configuration to the Kubernetes cluster.\nfunc (c *WrapperClient) Apply(ctx context.Context, config runtime.ApplyConfiguration, opts ...client.ApplyOption) error {\n\treturn c.kube.Apply(ctx, config, opts...)\n}\n\n// DeleteAllOf deletes all objects of the given type matching the given options.\nfunc (c *WrapperClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error {\n\tif u, ok := obj.(Wrapper); ok {\n\t\treturn c.kube.DeleteAllOf(ctx, u.GetUnstructured(), opts...)\n\t}\n\n\treturn c.kube.DeleteAllOf(ctx, obj, opts...)\n}\n\n// Status returns a client for the Status subresource.\nfunc (c *WrapperClient) Status() client.StatusWriter {\n\treturn &wrapperStatusClient{\n\t\tkube: c.kube.Status(),\n\t}\n}\n\n// SubResource returns the underlying client's SubResource client, unwrapped.\nfunc (c *WrapperClient) SubResource(subResource string) client.SubResourceClient {\n\t// TODO(negz): Is there anything to wrap here?\n\treturn c.kube.SubResource(subResource)\n}\n\n// Scheme returns the scheme this client is using.\nfunc (c *WrapperClient) Scheme() *runtime.Scheme {\n\treturn c.kube.Scheme()\n}\n\n// RESTMapper returns the rest this client is using.\nfunc (c *WrapperClient) RESTMapper() meta.RESTMapper {\n\treturn c.kube.RESTMapper()\n}\n\n// GroupVersionKindFor returns the GVK for the given obj.\nfunc (c *WrapperClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {\n\tif u, ok := obj.(Wrapper); ok {\n\t\treturn c.kube.GroupVersionKindFor(u.GetUnstructured())\n\t}\n\n\treturn c.kube.GroupVersionKindFor(obj)\n}\n\n// IsObjectNamespaced checks whether the object is namespaced.\nfunc (c *WrapperClient) IsObjectNamespaced(obj runtime.Object) (bool, error) {\n\tif u, ok := obj.(Wrapper); ok {\n\t\treturn c.kube.IsObjectNamespaced(u.GetUnstructured())\n\t}\n\n\treturn c.kube.IsObjectNamespaced(obj)\n}\n\ntype wrapperStatusClient struct {\n\tkube client.StatusWriter\n}\n\n// Create creates the fields corresponding to the status subresource for the\n// given obj. obj must be a struct pointer so that obj can be updated\n// with the content returned by the Server.\nfunc (c *wrapperStatusClient) Create(ctx context.Context, obj, subResource client.Object, opts ...client.SubResourceCreateOption) error {\n\t// TODO(negz): Could subResource be wrapped?\n\tif u, ok := obj.(Wrapper); ok {\n\t\treturn c.kube.Create(ctx, u.GetUnstructured(), subResource, opts...)\n\t}\n\n\treturn c.kube.Create(ctx, obj, subResource, opts...)\n}\n\n// Update updates the fields corresponding to the status subresource for the\n// given obj. obj must be a struct pointer so that obj can be updated\n// with the content returned by the Server.\nfunc (c *wrapperStatusClient) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {\n\tif u, ok := obj.(Wrapper); ok {\n\t\treturn c.kube.Update(ctx, u.GetUnstructured(), opts...)\n\t}\n\n\treturn c.kube.Update(ctx, obj, opts...)\n}\n\n// Patch patches the given object's subresource. obj must be a struct\n// pointer so that obj can be updated with the content returned by the\n// Server.\nfunc (c *wrapperStatusClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error {\n\tif u, ok := obj.(Wrapper); ok {\n\t\treturn c.kube.Patch(ctx, u.GetUnstructured(), patch, opts...)\n\t}\n\n\treturn c.kube.Patch(ctx, obj, patch, opts...)\n}\n\n// Apply applies the given object's subresource. obj must be a struct\n// pointer so that obj can be updated with the content returned by the\n// Server. Returns an error if an unstructured object is used due to\n// how the underlying client works. This method is only added to\n// satisfy the client.SubResourceWriter interface.\nfunc (c *wrapperStatusClient) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.SubResourceApplyOption) error {\n\tif u, ok := obj.(Wrapper); ok {\n\t\treturn fmt.Errorf(\"cannot apply status for unstructured %T; use a typed ApplyConfiguration or Patch with ApplyPatchType\", u)\n\t}\n\n\treturn c.kube.Apply(ctx, obj, opts...)\n}\n"
  },
  {
    "path": "pkg/resource/unstructured/client_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 unstructured\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nvar (\n\tnameWrapped   = \"wrapped\"\n\tnameUnwrapped = \"unwrapped\"\n\n\terrWrapped   = errors.New(\"unexpected Wrapped object\")\n\terrUnwrapped = errors.New(\"unexpected Unwrapped object\")\n)\n\nvar _ client.Client = &WrapperClient{}\n\ntype Wrapped struct{ client.Object }\n\nfunc (w *Wrapped) GetUnstructured() *unstructured.Unstructured {\n\treturn &unstructured.Unstructured{Object: map[string]any{\n\t\t\"metadata\": map[string]any{\n\t\t\t\"name\": nameWrapped,\n\t\t},\n\t}}\n}\n\nfunc NewWrapped() *Wrapped { return &Wrapped{} }\n\ntype WrappedList struct{ client.ObjectList }\n\nfunc (w *WrappedList) GetUnstructuredList() *unstructured.UnstructuredList {\n\tu := NewWrapped().GetUnstructured()\n\treturn &unstructured.UnstructuredList{Items: []unstructured.Unstructured{*u}}\n}\n\nfunc NewWrappedList() *WrappedList { return &WrappedList{} }\n\nfunc NewUnwrapped() *unstructured.Unstructured {\n\treturn &unstructured.Unstructured{Object: map[string]any{\n\t\t\"metadata\": map[string]any{\n\t\t\t\"name\": nameUnwrapped,\n\t\t},\n\t}}\n}\n\nfunc NewUnwrappedList() *unstructured.UnstructuredList {\n\tu := NewUnwrapped()\n\treturn &unstructured.UnstructuredList{Items: []unstructured.Unstructured{*u}}\n}\n\nfunc TestGet(t *testing.T) {\n\ttype args struct {\n\t\tctx context.Context\n\t\tkey client.ObjectKey\n\t\tobj client.Object\n\t}\n\n\tcases := map[string]struct {\n\t\tc    client.Client\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"Unwrapped\": {\n\t\t\tc: &test.MockClient{MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameUnwrapped {\n\t\t\t\t\treturn errWrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewUnwrapped()},\n\t\t},\n\t\t\"Wrapped\": {\n\t\t\tc: &test.MockClient{MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameWrapped {\n\t\t\t\t\treturn errUnwrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewWrapped()},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tc := NewClient(tc.c)\n\n\t\t\tgot := c.Get(tc.args.ctx, tc.args.key, tc.args.obj)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nc.Get(...): -want error, +got error:\\n %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestList(t *testing.T) {\n\ttype args struct {\n\t\tctx context.Context\n\t\tobj client.ObjectList\n\t}\n\n\tcases := map[string]struct {\n\t\tc    client.Client\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"Unwrapped\": {\n\t\t\tc: &test.MockClient{MockList: test.NewMockListFn(nil, func(obj client.ObjectList) error {\n\t\t\t\tu := &obj.(*unstructured.UnstructuredList).Items[0]\n\t\t\t\tif u.GetName() != nameUnwrapped {\n\t\t\t\t\treturn errWrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewUnwrappedList()},\n\t\t},\n\t\t\"Wrapped\": {\n\t\t\tc: &test.MockClient{MockList: test.NewMockListFn(nil, func(obj client.ObjectList) error {\n\t\t\t\tu := &obj.(*unstructured.UnstructuredList).Items[0]\n\t\t\t\tif u.GetName() != nameWrapped {\n\t\t\t\t\treturn errUnwrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewWrappedList()},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tc := NewClient(tc.c)\n\n\t\t\tgot := c.List(tc.args.ctx, tc.args.obj)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nc.List(...): -want error, +got error:\\n %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCreate(t *testing.T) {\n\ttype args struct {\n\t\tctx context.Context\n\t\tobj client.Object\n\t}\n\n\tcases := map[string]struct {\n\t\tc    client.Client\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"Unwrapped\": {\n\t\t\tc: &test.MockClient{MockCreate: test.NewMockCreateFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameUnwrapped {\n\t\t\t\t\treturn errWrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewUnwrapped()},\n\t\t},\n\t\t\"Wrapped\": {\n\t\t\tc: &test.MockClient{MockCreate: test.NewMockCreateFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameWrapped {\n\t\t\t\t\treturn errUnwrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewWrapped()},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tc := NewClient(tc.c)\n\n\t\t\tgot := c.Create(tc.args.ctx, tc.args.obj)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nc.Create(...): -want error, +got error:\\n %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDelete(t *testing.T) {\n\ttype args struct {\n\t\tctx context.Context\n\t\tobj client.Object\n\t}\n\n\tcases := map[string]struct {\n\t\tc    client.Client\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"Unwrapped\": {\n\t\t\tc: &test.MockClient{MockDelete: test.NewMockDeleteFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameUnwrapped {\n\t\t\t\t\treturn errWrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewUnwrapped()},\n\t\t},\n\t\t\"Wrapped\": {\n\t\t\tc: &test.MockClient{MockDelete: test.NewMockDeleteFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameWrapped {\n\t\t\t\t\treturn errUnwrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewWrapped()},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tc := NewClient(tc.c)\n\n\t\t\tgot := c.Delete(tc.args.ctx, tc.args.obj)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nc.Delete(...): -want error, +got error:\\n %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUpdate(t *testing.T) {\n\ttype args struct {\n\t\tctx context.Context\n\t\tobj client.Object\n\t}\n\n\tcases := map[string]struct {\n\t\tc    client.Client\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"Unwrapped\": {\n\t\t\tc: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameUnwrapped {\n\t\t\t\t\treturn errWrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewUnwrapped()},\n\t\t},\n\t\t\"Wrapped\": {\n\t\t\tc: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameWrapped {\n\t\t\t\t\treturn errUnwrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewWrapped()},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tc := NewClient(tc.c)\n\n\t\t\tgot := c.Update(tc.args.ctx, tc.args.obj)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nc.Update(...): -want error, +got error:\\n %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestPatch(t *testing.T) {\n\ttype args struct {\n\t\tctx   context.Context\n\t\tobj   client.Object\n\t\tpatch client.Patch\n\t}\n\n\tcases := map[string]struct {\n\t\tc    client.Client\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"Unwrapped\": {\n\t\t\tc: &test.MockClient{MockPatch: test.NewMockPatchFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameUnwrapped {\n\t\t\t\t\treturn errWrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewUnwrapped()},\n\t\t},\n\t\t\"Wrapped\": {\n\t\t\tc: &test.MockClient{MockPatch: test.NewMockPatchFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameWrapped {\n\t\t\t\t\treturn errUnwrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewWrapped()},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tc := NewClient(tc.c)\n\n\t\t\tgot := c.Patch(tc.args.ctx, tc.args.obj, tc.args.patch)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nc.Patch(...): -want error, +got error:\\n %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDeleteAllOf(t *testing.T) {\n\ttype args struct {\n\t\tctx context.Context\n\t\tobj client.Object\n\t}\n\n\tcases := map[string]struct {\n\t\tc    client.Client\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"Unwrapped\": {\n\t\t\tc: &test.MockClient{MockDeleteAllOf: test.NewMockDeleteAllOfFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameUnwrapped {\n\t\t\t\t\treturn errWrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewUnwrapped()},\n\t\t},\n\t\t\"Wrapped\": {\n\t\t\tc: &test.MockClient{MockDeleteAllOf: test.NewMockDeleteAllOfFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameWrapped {\n\t\t\t\t\treturn errUnwrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewWrapped()},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tc := NewClient(tc.c)\n\n\t\t\tgot := c.DeleteAllOf(tc.args.ctx, tc.args.obj)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nc.DeleteAllOf(...): -want error, +got error:\\n %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestStatusCreate(t *testing.T) {\n\ttype args struct {\n\t\tctx context.Context\n\t\tobj client.Object\n\t\tsub client.Object\n\t}\n\n\tcases := map[string]struct {\n\t\tc    client.Client\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"Unwrapped\": {\n\t\t\tc: &test.MockClient{MockStatusCreate: test.NewMockSubResourceCreateFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameUnwrapped {\n\t\t\t\t\treturn errWrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewUnwrapped()},\n\t\t},\n\t\t\"Wrapped\": {\n\t\t\tc: &test.MockClient{MockStatusCreate: test.NewMockSubResourceCreateFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameWrapped {\n\t\t\t\t\treturn errUnwrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewWrapped()},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tc := NewClient(tc.c)\n\n\t\t\tgot := c.Status().Create(tc.args.ctx, tc.args.obj, tc.args.sub)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nc.Status().Create(...): -want error, +got error:\\n %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestStatusUpdate(t *testing.T) {\n\ttype args struct {\n\t\tctx context.Context\n\t\tobj client.Object\n\t}\n\n\tcases := map[string]struct {\n\t\tc    client.Client\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"Unwrapped\": {\n\t\t\tc: &test.MockClient{MockStatusUpdate: test.NewMockSubResourceUpdateFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameUnwrapped {\n\t\t\t\t\treturn errWrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewUnwrapped()},\n\t\t},\n\t\t\"Wrapped\": {\n\t\t\tc: &test.MockClient{MockStatusUpdate: test.NewMockSubResourceUpdateFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameWrapped {\n\t\t\t\t\treturn errUnwrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewWrapped()},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tc := NewClient(tc.c)\n\n\t\t\tgot := c.Status().Update(tc.args.ctx, tc.args.obj)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nc.Status().Update(...): -want error, +got error:\\n %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestStatusPatch(t *testing.T) {\n\ttype args struct {\n\t\tctx   context.Context\n\t\tobj   client.Object\n\t\tpatch client.Patch\n\t}\n\n\tcases := map[string]struct {\n\t\tc    client.Client\n\t\targs args\n\t\twant error\n\t}{\n\t\t\"Unwrapped\": {\n\t\t\tc: &test.MockClient{MockStatusPatch: test.NewMockSubResourcePatchFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameUnwrapped {\n\t\t\t\t\treturn errWrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewUnwrapped()},\n\t\t},\n\t\t\"Wrapped\": {\n\t\t\tc: &test.MockClient{MockStatusPatch: test.NewMockSubResourcePatchFn(nil, func(obj client.Object) error {\n\t\t\t\tif obj.(metav1.Object).GetName() != nameWrapped {\n\t\t\t\t\treturn errUnwrapped\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t})},\n\t\t\targs: args{obj: NewWrapped()},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tc := NewClient(tc.c)\n\n\t\t\tgot := c.Status().Patch(tc.args.ctx, tc.args.obj, tc.args.patch)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nc.StatusPatch(...): -want error, +got error:\\n %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/resource/unstructured/composed/composed.go",
    "content": "/*\nCopyright 2020 The Crossplane 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// Package composed contains an unstructured composed resource.\npackage composed\n\nimport (\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath\"\n)\n\n// An Option modifies an unstructured composed resource.\ntype Option func(resource *Unstructured)\n\n// FromReference returns an Option that propagates the metadata in the supplied\n// reference to an unstructured composed resource.\nfunc FromReference(ref corev1.ObjectReference) Option {\n\treturn func(cr *Unstructured) {\n\t\tcr.SetGroupVersionKind(ref.GroupVersionKind())\n\t\tcr.SetName(ref.Name)\n\t\tcr.SetNamespace(ref.Namespace)\n\t\tcr.SetUID(ref.UID)\n\t}\n}\n\n// WithConditions returns an Option that sets the supplied conditions on an\n// unstructured composed resource.\nfunc WithConditions(c ...xpv2.Condition) Option {\n\treturn func(cr *Unstructured) {\n\t\tcr.SetConditions(c...)\n\t}\n}\n\n// New returns a new unstructured composed resource.\nfunc New(opts ...Option) *Unstructured {\n\tcr := &Unstructured{unstructured.Unstructured{Object: make(map[string]any)}}\n\tfor _, f := range opts {\n\t\tf(cr)\n\t}\n\n\treturn cr\n}\n\n// +k8s:deepcopy-gen=true\n// +kubebuilder:object:root=true\n\n// An Unstructured composed resource.\ntype Unstructured struct {\n\tunstructured.Unstructured\n}\n\n// GetUnstructured returns the underlying *unstructured.Unstructured.\nfunc (cr *Unstructured) GetUnstructured() *unstructured.Unstructured {\n\treturn &cr.Unstructured\n}\n\n// GetCondition of this Composed resource.\nfunc (cr *Unstructured) GetCondition(ct xpv2.ConditionType) xpv2.Condition {\n\tconditioned := xpv2.ConditionedStatus{}\n\t// The path is directly `status` because conditions are inline.\n\tif err := fieldpath.Pave(cr.Object).GetValueInto(\"status\", &conditioned); err != nil {\n\t\treturn xpv2.Condition{}\n\t}\n\n\treturn conditioned.GetCondition(ct)\n}\n\n// SetConditions of this Composed resource.\nfunc (cr *Unstructured) SetConditions(c ...xpv2.Condition) {\n\tconditioned := xpv2.ConditionedStatus{}\n\t// The path is directly `status` because conditions are inline.\n\t_ = fieldpath.Pave(cr.Object).GetValueInto(\"status\", &conditioned)\n\tconditioned.SetConditions(c...)\n\t_ = fieldpath.Pave(cr.Object).SetValue(\"status.conditions\", conditioned.Conditions)\n}\n\n// GetWriteConnectionSecretToReference of this Composed resource.\nfunc (cr *Unstructured) GetWriteConnectionSecretToReference() *xpv2.SecretReference {\n\tout := &xpv2.SecretReference{}\n\tif err := fieldpath.Pave(cr.Object).GetValueInto(\"spec.writeConnectionSecretToRef\", out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetWriteConnectionSecretToReference of this Composed resource.\nfunc (cr *Unstructured) SetWriteConnectionSecretToReference(r *xpv2.SecretReference) {\n\t_ = fieldpath.Pave(cr.Object).SetValue(\"spec.writeConnectionSecretToRef\", r)\n}\n\n// OwnedBy returns true if the supplied UID is an owner of the composed.\nfunc (cr *Unstructured) OwnedBy(u types.UID) bool {\n\tfor _, owner := range cr.GetOwnerReferences() {\n\t\tif owner.UID == u {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// RemoveOwnerRef removes the supplied UID from the composed resource's owner.\nfunc (cr *Unstructured) RemoveOwnerRef(u types.UID) {\n\trefs := cr.GetOwnerReferences()\n\tfor i := range refs {\n\t\tif refs[i].UID == u {\n\t\t\tcr.SetOwnerReferences(append(refs[:i], refs[i+1:]...))\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// An ListOption modifies an unstructured list of composed resource.\ntype ListOption func(*UnstructuredList)\n\n// FromReferenceToList returns a ListOption that propagates the metadata in the\n// supplied reference to an unstructured list composed resource.\nfunc FromReferenceToList(ref corev1.ObjectReference) ListOption {\n\treturn func(list *UnstructuredList) {\n\t\tlist.SetAPIVersion(ref.APIVersion)\n\t\tlist.SetKind(ref.Kind + \"List\")\n\t}\n}\n\n// NewList returns a new unstructured list of composed resources.\nfunc NewList(opts ...ListOption) *UnstructuredList {\n\tcr := &UnstructuredList{unstructured.UnstructuredList{Object: make(map[string]any)}}\n\tfor _, f := range opts {\n\t\tf(cr)\n\t}\n\n\treturn cr\n}\n\n// An UnstructuredList of composed resources.\ntype UnstructuredList struct {\n\tunstructured.UnstructuredList\n}\n\n// GetUnstructuredList returns the underlying *unstructured.Unstructured.\nfunc (cr *UnstructuredList) GetUnstructuredList() *unstructured.UnstructuredList {\n\treturn &cr.UnstructuredList\n}\n\n// SetObservedGeneration of this composite resource claim.\nfunc (cr *Unstructured) SetObservedGeneration(generation int64) {\n\tstatus := &xpv2.ObservedStatus{}\n\t_ = fieldpath.Pave(cr.Object).GetValueInto(\"status\", status)\n\tstatus.SetObservedGeneration(generation)\n\t_ = fieldpath.Pave(cr.Object).SetValue(\"status.observedGeneration\", status.ObservedGeneration)\n}\n\n// GetObservedGeneration of this composite resource claim.\nfunc (cr *Unstructured) GetObservedGeneration() int64 {\n\tstatus := &xpv2.ObservedStatus{}\n\t_ = fieldpath.Pave(cr.Object).GetValueInto(\"status\", status)\n\n\treturn status.GetObservedGeneration()\n}\n"
  },
  {
    "path": "pkg/resource/unstructured/composed/composed_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 composed\n\nimport (\n\t\"testing\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n)\n\nvar _ client.Object = &Unstructured{}\n\nfunc TestFromReference(t *testing.T) {\n\tref := corev1.ObjectReference{\n\t\tAPIVersion: \"a/v1\",\n\t\tKind:       \"k\",\n\t\tNamespace:  \"ns\",\n\t\tName:       \"name\",\n\t}\n\tcases := map[string]struct {\n\t\tref  corev1.ObjectReference\n\t\twant *Unstructured\n\t}{\n\t\t\"New\": {\n\t\t\tref: ref,\n\t\t\twant: &Unstructured{\n\t\t\t\tUnstructured: unstructured.Unstructured{\n\t\t\t\t\tObject: map[string]any{\n\t\t\t\t\t\t\"apiVersion\": \"a/v1\",\n\t\t\t\t\t\t\"kind\":       \"k\",\n\t\t\t\t\t\t\"metadata\": map[string]any{\n\t\t\t\t\t\t\t\"name\":      \"name\",\n\t\t\t\t\t\t\t\"namespace\": \"ns\",\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}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := New(FromReference(tc.ref))\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"New(FromReference(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConditions(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\tu      *Unstructured\n\t\tset    []xpv2.Condition\n\t\tget    xpv2.ConditionType\n\t\twant   xpv2.Condition\n\t}{\n\t\t\"NewCondition\": {\n\t\t\treason: \"It should be possible to set a condition of an empty Unstructured.\",\n\t\t\tu:      New(),\n\t\t\tset:    []xpv2.Condition{xpv2.Available(), xpv2.ReconcileSuccess()},\n\t\t\tget:    xpv2.TypeReady,\n\t\t\twant:   xpv2.Available(),\n\t\t},\n\t\t\"ExistingCondition\": {\n\t\t\treason: \"It should be possible to overwrite a condition that is already set.\",\n\t\t\tu:      New(WithConditions(xpv2.Creating())),\n\t\t\tset:    []xpv2.Condition{xpv2.Available()},\n\t\t\tget:    xpv2.TypeReady,\n\t\t\twant:   xpv2.Available(),\n\t\t},\n\t\t\"WeirdStatus\": {\n\t\t\treason: \"It should not be possible to set a condition when status is not an object.\",\n\t\t\tu: &Unstructured{unstructured.Unstructured{Object: map[string]any{\n\t\t\t\t\"status\": \"wat\",\n\t\t\t}}},\n\t\t\tset:  []xpv2.Condition{xpv2.Available()},\n\t\t\tget:  xpv2.TypeReady,\n\t\t\twant: xpv2.Condition{},\n\t\t},\n\t\t\"WeirdStatusConditions\": {\n\t\t\treason: \"Conditions should be overwritten if they are not an object.\",\n\t\t\tu: &Unstructured{unstructured.Unstructured{Object: map[string]any{\n\t\t\t\t\"status\": map[string]any{\n\t\t\t\t\t\"conditions\": \"wat\",\n\t\t\t\t},\n\t\t\t}}},\n\t\t\tset:  []xpv2.Condition{xpv2.Available()},\n\t\t\tget:  xpv2.TypeReady,\n\t\t\twant: xpv2.Available(),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetConditions(tc.set...)\n\n\t\t\tgot := tc.u.GetCondition(tc.get)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nu.GetCondition(%s): -want, +got:\\n%s\", tc.reason, tc.get, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestWriteConnectionSecretToReference(t *testing.T) {\n\tref := &xpv2.SecretReference{Namespace: \"ns\", Name: \"cool\"}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *xpv2.SecretReference\n\t\twant *xpv2.SecretReference\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(),\n\t\t\tset:  ref,\n\t\t\twant: ref,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetWriteConnectionSecretToReference(tc.set)\n\n\t\t\tgot := tc.u.GetWriteConnectionSecretToReference()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetWriteConnectionSecretToReference(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestObservedGeneration(t *testing.T) {\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\twant int64\n\t}{\n\t\t\"Set\": {\n\t\t\tu: New(func(u *Unstructured) {\n\t\t\t\tu.SetObservedGeneration(123)\n\t\t\t}),\n\t\t\twant: 123,\n\t\t},\n\t\t\"NotFound\": {\n\t\t\tu: New(),\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := tc.u.GetObservedGeneration()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetObservedGeneration(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/resource/unstructured/composed/zz_generated.deepcopy.go",
    "content": "//go:build !ignore_autogenerated\n\n/*\nCopyright 2025 The Crossplane 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 controller-gen. DO NOT EDIT.\n\npackage composed\n\nimport (\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n)\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *Unstructured) DeepCopyInto(out *Unstructured) {\n\t*out = *in\n\tin.Unstructured.DeepCopyInto(&out.Unstructured)\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Unstructured.\nfunc (in *Unstructured) DeepCopy() *Unstructured {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(Unstructured)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.\nfunc (in *Unstructured) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/resource/unstructured/composite/composite.go",
    "content": "/*\nCopyright 2020 The Crossplane 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// Package composite contains an unstructured composite resource.\npackage composite\n\nimport (\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\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/runtime/schema\"\n\t\"k8s.io/utils/ptr\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/unstructured/reference\"\n)\n\n// Schema specifies the schema version of a composite resource's Crossplane\n// machinery fields.\ntype Schema int\n\nconst (\n\t// SchemaModern indicates a modern Namespaced or Cluster scope composite\n\t// resource. Modern composite resources nest all Crossplane machinery fields\n\t// under spec.crossplane and status.crossplane, and can't be claimed.\n\tSchemaModern Schema = iota\n\n\t// SchemaLegacy indicates a LegacyCluster scope composite resource. Legacy\n\t// composite resources don't nest Crossplane machinery fields - they're set\n\t// directly under spec and status. Legacy composite resources can be claimed.\n\tSchemaLegacy\n)\n\n// An Option modifies an unstructured composite resource.\ntype Option func(*Unstructured)\n\n// WithGroupVersionKind sets the GroupVersionKind of the composite resource.\nfunc WithGroupVersionKind(gvk schema.GroupVersionKind) Option {\n\treturn func(c *Unstructured) {\n\t\tc.SetGroupVersionKind(gvk)\n\t}\n}\n\n// WithConditions sets the supplied conditions on the composite resource.\nfunc WithConditions(c ...xpv2.Condition) Option {\n\treturn func(cr *Unstructured) {\n\t\tcr.SetConditions(c...)\n\t}\n}\n\n// WithSchema sets the schema of the composite resource.\nfunc WithSchema(s Schema) Option {\n\treturn func(c *Unstructured) {\n\t\tc.Schema = s\n\t}\n}\n\n// New returns a new unstructured composite resource.\nfunc New(opts ...Option) *Unstructured {\n\tc := &Unstructured{Unstructured: unstructured.Unstructured{Object: make(map[string]any)}}\n\tfor _, f := range opts {\n\t\tf(c)\n\t}\n\n\treturn c\n}\n\n// +k8s:deepcopy-gen=true\n// +kubebuilder:object:root=true\n\n// An Unstructured composite resource.\ntype Unstructured struct {\n\tunstructured.Unstructured\n\n\tSchema Schema\n}\n\n// GetUnstructured returns the underlying *unstructured.Unstructured.\nfunc (c *Unstructured) GetUnstructured() *unstructured.Unstructured {\n\treturn &c.Unstructured\n}\n\n// GetCompositionSelector of this composite resource.\nfunc (c *Unstructured) GetCompositionSelector() *metav1.LabelSelector {\n\tpath := \"spec.crossplane.compositionSelector\"\n\tif c.Schema == SchemaLegacy {\n\t\tpath = \"spec.compositionSelector\"\n\t}\n\n\tout := &metav1.LabelSelector{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetCompositionSelector of this composite resource.\nfunc (c *Unstructured) SetCompositionSelector(sel *metav1.LabelSelector) {\n\tpath := \"spec.crossplane.compositionSelector\"\n\tif c.Schema == SchemaLegacy {\n\t\tpath = \"spec.compositionSelector\"\n\t}\n\n\t_ = fieldpath.Pave(c.Object).SetValue(path, sel)\n}\n\n// GetCompositionReference of this composite resource.\nfunc (c *Unstructured) GetCompositionReference() *corev1.ObjectReference {\n\tpath := \"spec.crossplane.compositionRef\"\n\tif c.Schema == SchemaLegacy {\n\t\tpath = \"spec.compositionRef\"\n\t}\n\n\tout := &corev1.ObjectReference{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetCompositionReference of this composite resource.\nfunc (c *Unstructured) SetCompositionReference(ref *corev1.ObjectReference) {\n\tpath := \"spec.crossplane.compositionRef\"\n\tif c.Schema == SchemaLegacy {\n\t\tpath = \"spec.compositionRef\"\n\t}\n\n\t_ = fieldpath.Pave(c.Object).SetValue(path, ref)\n}\n\n// GetCompositionRevisionReference of this composite resource.\nfunc (c *Unstructured) GetCompositionRevisionReference() *corev1.LocalObjectReference {\n\tpath := \"spec.crossplane.compositionRevisionRef\"\n\tif c.Schema == SchemaLegacy {\n\t\tpath = \"spec.compositionRevisionRef\"\n\t}\n\n\tout := &corev1.LocalObjectReference{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetCompositionRevisionReference of this composite resource.\nfunc (c *Unstructured) SetCompositionRevisionReference(ref *corev1.LocalObjectReference) {\n\tpath := \"spec.crossplane.compositionRevisionRef\"\n\tif c.Schema == SchemaLegacy {\n\t\tpath = \"spec.compositionRevisionRef\"\n\t}\n\n\t_ = fieldpath.Pave(c.Object).SetValue(path, ref)\n}\n\n// GetCompositionRevisionSelector of this resource claim.\nfunc (c *Unstructured) GetCompositionRevisionSelector() *metav1.LabelSelector {\n\tpath := \"spec.crossplane.compositionRevisionSelector\"\n\tif c.Schema == SchemaLegacy {\n\t\tpath = \"spec.compositionRevisionSelector\"\n\t}\n\n\tout := &metav1.LabelSelector{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetCompositionRevisionSelector of this resource claim.\nfunc (c *Unstructured) SetCompositionRevisionSelector(sel *metav1.LabelSelector) {\n\tpath := \"spec.crossplane.compositionRevisionSelector\"\n\tif c.Schema == SchemaLegacy {\n\t\tpath = \"spec.compositionRevisionSelector\"\n\t}\n\n\t_ = fieldpath.Pave(c.Object).SetValue(path, sel)\n}\n\n// SetCompositionUpdatePolicy of this composite resource.\nfunc (c *Unstructured) SetCompositionUpdatePolicy(p *xpv2.UpdatePolicy) {\n\tpath := \"spec.crossplane.compositionUpdatePolicy\"\n\tif c.Schema == SchemaLegacy {\n\t\tpath = \"spec.compositionUpdatePolicy\"\n\t}\n\n\t_ = fieldpath.Pave(c.Object).SetValue(path, p)\n}\n\n// GetCompositionUpdatePolicy of this composite resource.\nfunc (c *Unstructured) GetCompositionUpdatePolicy() *xpv2.UpdatePolicy {\n\tpath := \"spec.crossplane.compositionUpdatePolicy\"\n\tif c.Schema == SchemaLegacy {\n\t\tpath = \"spec.compositionUpdatePolicy\"\n\t}\n\n\tp, err := fieldpath.Pave(c.Object).GetString(path)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tout := xpv2.UpdatePolicy(p)\n\n\treturn &out\n}\n\n// GetClaimReference of this composite resource.\nfunc (c *Unstructured) GetClaimReference() *reference.Claim {\n\t// Only legacy XRs support claims.\n\tif c.Schema != SchemaLegacy {\n\t\treturn nil\n\t}\n\n\tout := &reference.Claim{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(\"spec.claimRef\", out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetClaimReference of this composite resource.\nfunc (c *Unstructured) SetClaimReference(ref *reference.Claim) {\n\t// Only legacy XRs support claims.\n\tif c.Schema != SchemaLegacy {\n\t\treturn\n\t}\n\n\t_ = fieldpath.Pave(c.Object).SetValue(\"spec.claimRef\", ref)\n}\n\n// GetResourceReferences of this composite resource.\nfunc (c *Unstructured) GetResourceReferences() []corev1.ObjectReference {\n\tpath := \"spec.crossplane.resourceRefs\"\n\tif c.Schema == SchemaLegacy {\n\t\tpath = \"spec.resourceRefs\"\n\t}\n\n\tout := &[]corev1.ObjectReference{}\n\t_ = fieldpath.Pave(c.Object).GetValueInto(path, out)\n\n\treturn *out\n}\n\n// SetResourceReferences of this composite resource.\nfunc (c *Unstructured) SetResourceReferences(refs []corev1.ObjectReference) {\n\tpath := \"spec.crossplane.resourceRefs\"\n\tif c.Schema == SchemaLegacy {\n\t\tpath = \"spec.resourceRefs\"\n\t}\n\n\tempty := corev1.ObjectReference{}\n\n\tfiltered := make([]corev1.ObjectReference, 0, len(refs))\n\tfor _, ref := range refs {\n\t\t// TODO(negz): Ask muvaf to explain what this is working around. :)\n\t\t// TODO(muvaf): temporary workaround.\n\t\tif ref.String() == empty.String() {\n\t\t\tcontinue\n\t\t}\n\n\t\tfiltered = append(filtered, ref)\n\t}\n\n\t_ = fieldpath.Pave(c.Object).SetValue(path, filtered)\n}\n\n// GetReference returns reference to this composite.\nfunc (c *Unstructured) GetReference() *reference.Composite {\n\tref := &reference.Composite{\n\t\tAPIVersion: c.GetAPIVersion(),\n\t\tKind:       c.GetKind(),\n\t\tName:       c.GetName(),\n\t}\n\n\tif c.GetNamespace() != \"\" {\n\t\tref.Namespace = ptr.To(c.GetNamespace())\n\t}\n\n\treturn ref\n}\n\n// TODO(negz): Ideally we'd use LocalSecretReference for namespaced XRs. As is\n// we'll return a SecretReference with an empty namespace if the XR doesn't\n// actually have a spec.crossplane.writeConnectionSecretToRef.namespace field.\n\n// GetWriteConnectionSecretToReference of this composite resource.\nfunc (c *Unstructured) GetWriteConnectionSecretToReference() *xpv2.SecretReference {\n\t// Only legacy XRs support connection secrets.\n\tif c.Schema != SchemaLegacy {\n\t\treturn nil\n\t}\n\n\tout := &xpv2.SecretReference{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(\"spec.writeConnectionSecretToRef\", out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetWriteConnectionSecretToReference of this composite resource.\nfunc (c *Unstructured) SetWriteConnectionSecretToReference(ref *xpv2.SecretReference) {\n\t// Only legacy XRs support connection secrets.\n\tif c.Schema != SchemaLegacy {\n\t\treturn\n\t}\n\n\t_ = fieldpath.Pave(c.Object).SetValue(\"spec.writeConnectionSecretToRef\", ref)\n}\n\n// GetCondition of this composite resource.\nfunc (c *Unstructured) GetCondition(ct xpv2.ConditionType) xpv2.Condition {\n\tconditioned := xpv2.ConditionedStatus{}\n\t// The path is directly `status` because conditions are inline.\n\tif err := fieldpath.Pave(c.Object).GetValueInto(\"status\", &conditioned); err != nil {\n\t\treturn xpv2.Condition{}\n\t}\n\n\treturn conditioned.GetCondition(ct)\n}\n\n// SetConditions of this composite resource.\nfunc (c *Unstructured) SetConditions(conditions ...xpv2.Condition) {\n\tconditioned := xpv2.ConditionedStatus{}\n\t// The path is directly `status` because conditions are inline.\n\t_ = fieldpath.Pave(c.Object).GetValueInto(\"status\", &conditioned)\n\tconditioned.SetConditions(conditions...)\n\t_ = fieldpath.Pave(c.Object).SetValue(\"status.conditions\", conditioned.Conditions)\n}\n\n// GetConditions of this composite resource.\nfunc (c *Unstructured) GetConditions() []xpv2.Condition {\n\tconditioned := xpv2.ConditionedStatus{}\n\t// The path is directly `status` because conditions are inline.\n\t_ = fieldpath.Pave(c.Object).GetValueInto(\"status\", &conditioned)\n\n\treturn conditioned.Conditions\n}\n\n// GetConnectionDetailsLastPublishedTime of this composite resource.\nfunc (c *Unstructured) GetConnectionDetailsLastPublishedTime() *metav1.Time {\n\t// Only legacy XRs support connection details.\n\tif c.Schema != SchemaLegacy {\n\t\treturn nil\n\t}\n\n\tout := &metav1.Time{}\n\tif err := fieldpath.Pave(c.Object).GetValueInto(\"status.connectionDetails.lastPublishedTime\", out); err != nil {\n\t\treturn nil\n\t}\n\n\treturn out\n}\n\n// SetConnectionDetailsLastPublishedTime of this composite resource.\nfunc (c *Unstructured) SetConnectionDetailsLastPublishedTime(t *metav1.Time) {\n\t// Only legacy XRs support connection details.\n\tif c.Schema != SchemaLegacy {\n\t\treturn\n\t}\n\n\t_ = fieldpath.Pave(c.Object).SetValue(\"status.connectionDetails.lastPublishedTime\", t)\n}\n\n// SetObservedGeneration of this composite resource claim.\nfunc (c *Unstructured) SetObservedGeneration(generation int64) {\n\tstatus := &xpv2.ObservedStatus{}\n\t_ = fieldpath.Pave(c.Object).GetValueInto(\"status\", status)\n\tstatus.SetObservedGeneration(generation)\n\t_ = fieldpath.Pave(c.Object).SetValue(\"status.observedGeneration\", status.ObservedGeneration)\n}\n\n// GetObservedGeneration of this composite resource claim.\nfunc (c *Unstructured) GetObservedGeneration() int64 {\n\tstatus := &xpv2.ObservedStatus{}\n\t_ = fieldpath.Pave(c.Object).GetValueInto(\"status\", status)\n\n\treturn status.GetObservedGeneration()\n}\n\n// SetLastHandledReconcileAt of this composite resource.\nfunc (c *Unstructured) SetLastHandledReconcileAt(token string) {\n\t_ = fieldpath.Pave(c.Object).SetValue(\"status.lastHandledReconcileAt\", token)\n}\n\n// GetLastHandledReconcileAt of this composite resource.\nfunc (c *Unstructured) GetLastHandledReconcileAt() string {\n\tv, err := fieldpath.Pave(c.Object).GetString(\"status.lastHandledReconcileAt\")\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn v\n}\n\n// SetClaimConditionTypes of this composite resource. You cannot set system\n// condition types such as Ready, Synced or Healthy as claim conditions.\nfunc (c *Unstructured) SetClaimConditionTypes(in ...xpv2.ConditionType) error {\n\t// Only legacy XRs support claims.\n\tif c.Schema != SchemaLegacy {\n\t\treturn nil\n\t}\n\n\tts := c.GetClaimConditionTypes()\n\n\tm := make(map[xpv2.ConditionType]bool, len(ts))\n\tfor _, t := range ts {\n\t\tm[t] = true\n\t}\n\n\tfor _, t := range in {\n\t\tif xpv2.IsSystemConditionType(t) {\n\t\t\treturn errors.Errorf(\"cannot set system condition %s as a claim condition\", t)\n\t\t}\n\n\t\tif m[t] {\n\t\t\tcontinue\n\t\t}\n\n\t\tm[t] = true\n\t\tts = append(ts, t)\n\t}\n\n\t_ = fieldpath.Pave(c.Object).SetValue(\"status.claimConditionTypes\", ts)\n\n\treturn nil\n}\n\n// GetClaimConditionTypes of this composite resource.\nfunc (c *Unstructured) GetClaimConditionTypes() []xpv2.ConditionType {\n\t// Only legacy XRs support claims.\n\tif c.Schema != SchemaLegacy {\n\t\treturn nil\n\t}\n\n\tcs := []xpv2.ConditionType{}\n\t_ = fieldpath.Pave(c.Object).GetValueInto(\"status.claimConditionTypes\", &cs)\n\n\treturn cs\n}\n"
  },
  {
    "path": "pkg/resource/unstructured/composite/composite_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 composite\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\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/runtime/schema\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource/unstructured/reference\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nvar _ client.Object = &Unstructured{}\n\nfunc TestWithGroupVersionKind(t *testing.T) {\n\tgvk := schema.GroupVersionKind{\n\t\tGroup:   \"g\",\n\t\tVersion: \"v1\",\n\t\tKind:    \"k\",\n\t}\n\tcases := map[string]struct {\n\t\tgvk  schema.GroupVersionKind\n\t\twant *Unstructured\n\t}{\n\t\t\"New\": {\n\t\t\tgvk: gvk,\n\t\t\twant: &Unstructured{\n\t\t\t\tUnstructured: unstructured.Unstructured{\n\t\t\t\t\tObject: map[string]any{\n\t\t\t\t\t\t\"apiVersion\": \"g/v1\",\n\t\t\t\t\t\t\"kind\":       \"k\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := New(WithGroupVersionKind(tc.gvk))\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"New(WithGroupVersionKind(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConditions(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason  string\n\t\tu       *Unstructured\n\t\tset     []xpv2.Condition\n\t\tget     xpv2.ConditionType\n\t\twant    xpv2.Condition\n\t\twantAll []xpv2.Condition\n\t}{\n\t\t\"NewCondition\": {\n\t\t\treason:  \"It should be possible to set a condition of an empty Unstructured.\",\n\t\t\tu:       New(),\n\t\t\tset:     []xpv2.Condition{xpv2.Available(), xpv2.ReconcileSuccess()},\n\t\t\tget:     xpv2.TypeReady,\n\t\t\twant:    xpv2.Available(),\n\t\t\twantAll: []xpv2.Condition{xpv2.Available(), xpv2.ReconcileSuccess()},\n\t\t},\n\t\t\"ExistingCondition\": {\n\t\t\treason:  \"It should be possible to overwrite a condition that is already set.\",\n\t\t\tu:       New(WithConditions(xpv2.Creating())),\n\t\t\tset:     []xpv2.Condition{xpv2.Available()},\n\t\t\tget:     xpv2.TypeReady,\n\t\t\twant:    xpv2.Available(),\n\t\t\twantAll: []xpv2.Condition{xpv2.Available()},\n\t\t},\n\t\t\"WeirdStatus\": {\n\t\t\treason: \"It should not be possible to set a condition when status is not an object.\",\n\t\t\tu: &Unstructured{Unstructured: unstructured.Unstructured{Object: map[string]any{\n\t\t\t\t\"status\": \"wat\",\n\t\t\t}}},\n\t\t\tset:     []xpv2.Condition{xpv2.Available()},\n\t\t\tget:     xpv2.TypeReady,\n\t\t\twant:    xpv2.Condition{},\n\t\t\twantAll: nil,\n\t\t},\n\t\t\"WeirdStatusConditions\": {\n\t\t\treason: \"Conditions should be overwritten if they are not an object.\",\n\t\t\tu: &Unstructured{Unstructured: unstructured.Unstructured{Object: map[string]any{\n\t\t\t\t\"status\": map[string]any{\n\t\t\t\t\t\"conditions\": \"wat\",\n\t\t\t\t},\n\t\t\t}}},\n\t\t\tset:     []xpv2.Condition{xpv2.Available()},\n\t\t\tget:     xpv2.TypeReady,\n\t\t\twant:    xpv2.Available(),\n\t\t\twantAll: []xpv2.Condition{xpv2.Available()},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetConditions(tc.set...)\n\n\t\t\tgot := tc.u.GetCondition(tc.get)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nu.GetCondition(%s): -want, +got:\\n%s\", tc.reason, tc.get, diff)\n\t\t\t}\n\n\t\t\tgotAll := tc.u.GetConditions()\n\t\t\tif diff := cmp.Diff(tc.wantAll, gotAll); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nu.GetConditions(): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestClaimConditionTypes(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason  string\n\t\tu       *Unstructured\n\t\tset     []xpv2.ConditionType\n\t\twant    []xpv2.ConditionType\n\t\twantErr error\n\t}{\n\t\t\"CannotSetSystemConditionTypes\": {\n\t\t\treason: \"Claim conditions API should fail to set conditions if a system condition is detected.\",\n\t\t\tu:      New(WithSchema(SchemaLegacy)),\n\t\t\tset: []xpv2.ConditionType{\n\t\t\t\txpv2.ConditionType(\"DatabaseReady\"),\n\t\t\t\txpv2.ConditionType(\"NetworkReady\"),\n\t\t\t\t// system condition\n\t\t\t\txpv2.ConditionType(\"Ready\"),\n\t\t\t},\n\t\t\twant:    []xpv2.ConditionType{},\n\t\t\twantErr: errors.New(\"cannot set system condition Ready as a claim condition\"),\n\t\t},\n\t\t\"SetSingleCustomConditionType\": {\n\t\t\treason: \"Claim condition API should work with a single custom condition type.\",\n\t\t\tu:      New(WithSchema(SchemaLegacy)),\n\t\t\tset:    []xpv2.ConditionType{xpv2.ConditionType(\"DatabaseReady\")},\n\t\t\twant:   []xpv2.ConditionType{xpv2.ConditionType(\"DatabaseReady\")},\n\t\t},\n\t\t\"SetMultipleCustomConditionTypes\": {\n\t\t\treason: \"Claim condition API should work with multiple custom condition types.\",\n\t\t\tu:      New(WithSchema(SchemaLegacy)),\n\t\t\tset:    []xpv2.ConditionType{xpv2.ConditionType(\"DatabaseReady\"), xpv2.ConditionType(\"NetworkReady\")},\n\t\t\twant:   []xpv2.ConditionType{xpv2.ConditionType(\"DatabaseReady\"), xpv2.ConditionType(\"NetworkReady\")},\n\t\t},\n\t\t\"SetMultipleOfTheSameCustomConditionTypes\": {\n\t\t\treason: \"Claim condition API not add more than one of the same condition.\",\n\t\t\tu:      New(WithSchema(SchemaLegacy)),\n\t\t\tset:    []xpv2.ConditionType{xpv2.ConditionType(\"DatabaseReady\"), xpv2.ConditionType(\"DatabaseReady\")},\n\t\t\twant:   []xpv2.ConditionType{xpv2.ConditionType(\"DatabaseReady\")},\n\t\t},\n\t\t\"WeirdStatus\": {\n\t\t\treason: \"It should not be possible to set a condition when status is not an object.\",\n\t\t\tu: &Unstructured{\n\t\t\t\tUnstructured: unstructured.Unstructured{Object: map[string]any{\n\t\t\t\t\t\"status\": \"wat\",\n\t\t\t\t}},\n\t\t\t\tSchema: SchemaLegacy,\n\t\t\t},\n\t\t\tset:  []xpv2.ConditionType{xpv2.ConditionType(\"DatabaseReady\")},\n\t\t\twant: []xpv2.ConditionType{},\n\t\t},\n\t\t\"WeirdStatusClaimConditionTypes\": {\n\t\t\treason: \"Claim conditions should be overwritten if they are not an object.\",\n\t\t\tu: &Unstructured{\n\t\t\t\tUnstructured: unstructured.Unstructured{Object: map[string]any{\n\t\t\t\t\t\"status\": map[string]any{\n\t\t\t\t\t\t\"claimConditionTypes\": \"wat\",\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t\tSchema: SchemaLegacy,\n\t\t\t},\n\t\t\tset:  []xpv2.ConditionType{xpv2.ConditionType(\"DatabaseReady\")},\n\t\t\twant: []xpv2.ConditionType{xpv2.ConditionType(\"DatabaseReady\")},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgotErr := tc.u.SetClaimConditionTypes(tc.set...)\n\t\t\tif diff := cmp.Diff(tc.wantErr, gotErr, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nu.SetClaimConditionTypes(): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tgot := tc.u.GetClaimConditionTypes()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nu.GetClaimConditionTypes(): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompositionSelector(t *testing.T) {\n\tsel := &metav1.LabelSelector{MatchLabels: map[string]string{\"cool\": \"very\"}}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *metav1.LabelSelector\n\t\twant *metav1.LabelSelector\n\t}{\n\t\t\"NewSel\": {\n\t\t\tu:    New(),\n\t\t\tset:  sel,\n\t\t\twant: sel,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetCompositionSelector(tc.set)\n\n\t\t\tgot := tc.u.GetCompositionSelector()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetCompositionSelector(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompositionReference(t *testing.T) {\n\tref := &corev1.ObjectReference{Namespace: \"ns\", Name: \"cool\"}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *corev1.ObjectReference\n\t\twant *corev1.ObjectReference\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(),\n\t\t\tset:  ref,\n\t\t\twant: ref,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetCompositionReference(tc.set)\n\n\t\t\tgot := tc.u.GetCompositionReference()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetCompositionReference(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompositionRevisionReference(t *testing.T) {\n\tref := &corev1.LocalObjectReference{Name: \"cool\"}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *corev1.LocalObjectReference\n\t\twant *corev1.LocalObjectReference\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(),\n\t\t\tset:  ref,\n\t\t\twant: ref,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetCompositionRevisionReference(tc.set)\n\n\t\t\tgot := tc.u.GetCompositionRevisionReference()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetCompositionRevisionReference(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompositionRevisionSelector(t *testing.T) {\n\tsel := &metav1.LabelSelector{MatchLabels: map[string]string{\"cool\": \"very\"}}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *metav1.LabelSelector\n\t\twant *metav1.LabelSelector\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(),\n\t\t\tset:  sel,\n\t\t\twant: sel,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetCompositionRevisionSelector(tc.set)\n\n\t\t\tgot := tc.u.GetCompositionRevisionSelector()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetCompositionRevisionSelector(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompositionUpdatePolicy(t *testing.T) {\n\tp := xpv2.UpdateManual\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *xpv2.UpdatePolicy\n\t\twant *xpv2.UpdatePolicy\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(),\n\t\t\tset:  &p,\n\t\t\twant: &p,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetCompositionUpdatePolicy(tc.set)\n\n\t\t\tgot := tc.u.GetCompositionUpdatePolicy()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetCompositionUpdatePolicy(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestClaimReference(t *testing.T) {\n\tref := &reference.Claim{Namespace: \"ns\", Name: \"cool\", APIVersion: \"acme.com/v1\", Kind: \"Foo\"}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *reference.Claim\n\t\twant *reference.Claim\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(WithSchema(SchemaLegacy)),\n\t\t\tset:  ref,\n\t\t\twant: ref,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetClaimReference(tc.set)\n\n\t\t\tgot := tc.u.GetClaimReference()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetClaimReference(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestResourceReferences(t *testing.T) {\n\tref := corev1.ObjectReference{Namespace: \"ns\", Name: \"cool\"}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  []corev1.ObjectReference\n\t\twant []corev1.ObjectReference\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(),\n\t\t\tset:  []corev1.ObjectReference{ref},\n\t\t\twant: []corev1.ObjectReference{ref},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetResourceReferences(tc.set)\n\n\t\t\tgot := tc.u.GetResourceReferences()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetResourceReferences(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestWriteConnectionSecretToReference(t *testing.T) {\n\tref := &xpv2.SecretReference{Namespace: \"ns\", Name: \"cool\"}\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *xpv2.SecretReference\n\t\twant *xpv2.SecretReference\n\t}{\n\t\t\"NewRef\": {\n\t\t\tu:    New(WithSchema(SchemaLegacy)),\n\t\t\tset:  ref,\n\t\t\twant: ref,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetWriteConnectionSecretToReference(tc.set)\n\n\t\t\tgot := tc.u.GetWriteConnectionSecretToReference()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetWriteConnectionSecretToReference(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConnectionDetailsLastPublishedTime(t *testing.T) {\n\tnow := &metav1.Time{Time: time.Now()}\n\n\t// The timestamp loses a little resolution when round-tripped through JSON\n\t// encoding.\n\tlores := func(t *metav1.Time) *metav1.Time {\n\t\tout := &metav1.Time{}\n\t\tj, _ := json.Marshal(t) //nolint:errchkjson // No encoding error in practice.\n\t\t_ = json.Unmarshal(j, out)\n\n\t\treturn out\n\t}\n\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\tset  *metav1.Time\n\t\twant *metav1.Time\n\t}{\n\t\t\"NewTimeLegacy\": {\n\t\t\tu:    New(WithSchema(SchemaLegacy)),\n\t\t\tset:  now,\n\t\t\twant: lores(now),\n\t\t},\n\t\t\"NewTimeModern\": {\n\t\t\tu:    New(WithSchema(SchemaModern)),\n\t\t\tset:  now,\n\t\t\twant: nil, // modern schema doesn't support connection details\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ttc.u.SetConnectionDetailsLastPublishedTime(tc.set)\n\n\t\t\tgot := tc.u.GetConnectionDetailsLastPublishedTime()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetConnectionDetailsLastPublishedTime(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestObservedGeneration(t *testing.T) {\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\twant int64\n\t}{\n\t\t\"Set\": {\n\t\t\tu: New(func(u *Unstructured) {\n\t\t\t\tu.SetObservedGeneration(123)\n\t\t\t}),\n\t\t\twant: 123,\n\t\t},\n\t\t\"NotFound\": {\n\t\t\tu: New(),\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := tc.u.GetObservedGeneration()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetObservedGeneration(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLastHandledReconcileAt(t *testing.T) {\n\tcases := map[string]struct {\n\t\tu    *Unstructured\n\t\twant string\n\t}{\n\t\t\"Set\": {\n\t\t\tu: New(func(u *Unstructured) {\n\t\t\t\tu.SetLastHandledReconcileAt(\"2024-01-15T10:30:00Z\")\n\t\t\t}),\n\t\t\twant: \"2024-01-15T10:30:00Z\",\n\t\t},\n\t\t\"NotFound\": {\n\t\t\tu: New(),\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := tc.u.GetLastHandledReconcileAt()\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\nu.GetLastHandledReconcileAt(): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/resource/unstructured/composite/zz_generated.deepcopy.go",
    "content": "//go:build !ignore_autogenerated\n\n/*\nCopyright 2025 The Crossplane 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 controller-gen. DO NOT EDIT.\n\npackage composite\n\nimport (\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n)\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *Unstructured) DeepCopyInto(out *Unstructured) {\n\t*out = *in\n\tin.Unstructured.DeepCopyInto(&out.Unstructured)\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Unstructured.\nfunc (in *Unstructured) DeepCopy() *Unstructured {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(Unstructured)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.\nfunc (in *Unstructured) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/resource/unstructured/generate.go",
    "content": "//go:build generate\n\n/*\nCopyright 2019 The Crossplane 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// NOTE(negz): See the below link for details on what is happening here.\n// https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module\n\n// Generate deepcopy methodsets\n//go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=../../../hack/boilerplate.go.txt paths=./...\n\npackage unstructured\n\nimport (\n\t_ \"sigs.k8s.io/controller-tools/cmd/controller-gen\" //nolint:typecheck\n)\n"
  },
  {
    "path": "pkg/resource/unstructured/reference/reference.go",
    "content": "/*\nCopyright 2024 The Crossplane 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// Package reference contains references to resources.\npackage reference\n\nimport (\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// A Claim is a reference to a claim.\ntype Claim struct {\n\t// APIVersion of the referenced claim.\n\tAPIVersion string `json:\"apiVersion\"`\n\n\t// Kind of the referenced claim.\n\tKind string `json:\"kind\"`\n\n\t// Name of the referenced claim.\n\tName string `json:\"name\"`\n\n\t// Namespace of the referenced claim.\n\tNamespace string `json:\"namespace\"`\n}\n\n// A Composite is a reference to a composite.\ntype Composite struct {\n\t// APIVersion of the referenced composite.\n\tAPIVersion string `json:\"apiVersion\"`\n\n\t// Kind of the referenced composite.\n\tKind string `json:\"kind\"`\n\n\t// Name of the referenced composite.\n\tName string `json:\"name\"`\n\n\t// Namespace of the referenced composite.\n\tNamespace *string `json:\"namespace,omitempty\"`\n}\n\n// GroupVersionKind returns the GroupVersionKind of the claim reference.\nfunc (c *Claim) GroupVersionKind() schema.GroupVersionKind {\n\treturn schema.FromAPIVersionAndKind(c.APIVersion, c.Kind)\n}\n\n// GroupVersionKind returns the GroupVersionKind of the composite reference.\nfunc (c *Composite) GroupVersionKind() schema.GroupVersionKind {\n\treturn schema.FromAPIVersionAndKind(c.APIVersion, c.Kind)\n}\n"
  },
  {
    "path": "pkg/statemetrics/mr_state_metrics.go",
    "content": "/*\nCopyright 2024 The Crossplane 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 statemetrics\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"time\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client/apiutil\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/logging\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/resource\"\n)\n\n// MRStateMetrics holds Prometheus metrics for managed resources.\ntype MRStateMetrics struct {\n\tExists *prometheus.GaugeVec\n\tReady  *prometheus.GaugeVec\n\tSynced *prometheus.GaugeVec\n}\n\n// NewMRStateMetrics returns a new MRStateMetrics.\nfunc NewMRStateMetrics() *MRStateMetrics {\n\treturn &MRStateMetrics{\n\t\tExists: prometheus.NewGaugeVec(prometheus.GaugeOpts{\n\t\t\tSubsystem: subSystem,\n\t\t\tName:      \"managed_resource_exists\",\n\t\t\tHelp:      \"The number of managed resources that exist\",\n\t\t}, []string{\"gvk\"}),\n\t\tReady: prometheus.NewGaugeVec(prometheus.GaugeOpts{\n\t\t\tSubsystem: subSystem,\n\t\t\tName:      \"managed_resource_ready\",\n\t\t\tHelp:      \"The number of managed resources in Ready=True state\",\n\t\t}, []string{\"gvk\"}),\n\t\tSynced: prometheus.NewGaugeVec(prometheus.GaugeOpts{\n\t\t\tSubsystem: subSystem,\n\t\t\tName:      \"managed_resource_synced\",\n\t\t\tHelp:      \"The number of managed resources in Synced=True state\",\n\t\t}, []string{\"gvk\"}),\n\t}\n}\n\n// Describe sends the super-set of all possible descriptors of metrics\n// collected by this Collector to the provided channel and returns once\n// the last descriptor has been sent.\nfunc (r *MRStateMetrics) Describe(ch chan<- *prometheus.Desc) {\n\tr.Exists.Describe(ch)\n\tr.Ready.Describe(ch)\n\tr.Synced.Describe(ch)\n}\n\n// Collect is called by the Prometheus registry when collecting\n// metrics. The implementation sends each collected metric via the\n// provided channel and returns once the last metric has been sent.\nfunc (r *MRStateMetrics) Collect(ch chan<- prometheus.Metric) {\n\tr.Exists.Collect(ch)\n\tr.Ready.Collect(ch)\n\tr.Synced.Collect(ch)\n}\n\n// A MRStateRecorder records the state of managed resources.\ntype MRStateRecorder struct {\n\tclient      client.Client\n\tlog         logging.Logger\n\tinterval    time.Duration\n\tmanagedList resource.ManagedList\n\n\tmetrics *MRStateMetrics\n}\n\n// NewMRStateRecorder returns a new MRStateRecorder which records the state of managed resources.\nfunc NewMRStateRecorder(c client.Client, log logging.Logger, metrics *MRStateMetrics, managedList resource.ManagedList, interval time.Duration) *MRStateRecorder {\n\treturn &MRStateRecorder{\n\t\tclient:      c,\n\t\tlog:         log,\n\t\tmetrics:     metrics,\n\t\tmanagedList: managedList,\n\t\tinterval:    interval,\n\t}\n}\n\n// Record records the state of managed resources.\nfunc (r *MRStateRecorder) Record(ctx context.Context) error {\n\tif err := r.client.List(ctx, r.managedList); err != nil {\n\t\treturn errors.Wrap(err, \"failed to list managed resources\")\n\t}\n\n\tlabels, err := r.getLabels()\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"failed to get labels\")\n\t}\n\n\tmrs := r.managedList.GetItems()\n\tr.metrics.Exists.With(labels).Set(float64(len(mrs)))\n\n\tvar numReady, numSynced float64 = 0, 0\n\n\tfor _, o := range mrs {\n\t\tif o.GetCondition(xpv2.TypeReady).Status == corev1.ConditionTrue {\n\t\t\tnumReady++\n\t\t}\n\n\t\tif o.GetCondition(xpv2.TypeSynced).Status == corev1.ConditionTrue {\n\t\t\tnumSynced++\n\t\t}\n\t}\n\n\tr.metrics.Ready.With(labels).Set(numReady)\n\tr.metrics.Synced.With(labels).Set(numSynced)\n\n\treturn nil\n}\n\n// Start records state of managed resources with given interval.\nfunc (r *MRStateRecorder) Start(ctx context.Context) error {\n\tticker := time.NewTicker(r.interval)\n\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\t\tif err := r.Record(ctx); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase <-ctx.Done():\n\t\t\tticker.Stop()\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nfunc (r *MRStateRecorder) getLabels() (prometheus.Labels, error) {\n\tgvk, err := apiutil.GVKForObject(r.managedList, r.client.Scheme())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Remove \"List\" to get object kind.\n\tres := strings.Replace(gvk.String(), \"List\", \"\", 1)\n\n\treturn prometheus.Labels{\"gvk\": res}, nil\n}\n"
  },
  {
    "path": "pkg/statemetrics/state_recorder.go",
    "content": "/*\nCopyright 2024 The Crossplane 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\thttp://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// Package statemetrics contains utilities for recording Crossplane resource state metrics.\npackage statemetrics\n\nimport (\n\t\"context\"\n\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nconst subSystem = \"crossplane\"\n\n// A StateRecorder records the state of given GroupVersionKind.\ntype StateRecorder interface {\n\tRecord(ctx context.Context, gvk schema.GroupVersionKind)\n\tStart(ctx context.Context) error\n}\n\n// A NopStateRecorder does nothing.\ntype NopStateRecorder struct{}\n\n// NewNopStateRecorder returns a NopStateRecorder that does nothing.\nfunc NewNopStateRecorder() *NopStateRecorder {\n\treturn &NopStateRecorder{}\n}\n\n// Record does nothing.\nfunc (r *NopStateRecorder) Record(_ context.Context, _ schema.GroupVersionKind) {}\n\n// Start does nothing.\nfunc (r *NopStateRecorder) Start(_ context.Context) error { return nil }\n"
  },
  {
    "path": "pkg/test/cmp.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 test\n\nimport (\n\t\"reflect\"\n\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n)\n\n// EquateErrors returns true if the supplied errors are of the same type and\n// produce identical strings. This mirrors the error comparison behaviour of\n// https://github.com/go-test/deep, which most Crossplane tests targeted before\n// we switched to go-cmp.\n//\n// This differs from cmpopts.EquateErrors, which does not test for error strings\n// and instead returns whether one error 'is' (in the errors.Is sense) the\n// other.\nfunc EquateErrors() cmp.Option {\n\treturn cmp.Comparer(func(a, b error) bool {\n\t\tif a == nil || b == nil {\n\t\t\treturn a == nil && b == nil\n\t\t}\n\n\t\tav := reflect.ValueOf(a)\n\n\t\tbv := reflect.ValueOf(b)\n\t\tif av.Type() != bv.Type() {\n\t\t\treturn false\n\t\t}\n\n\t\treturn a.Error() == b.Error()\n\t})\n}\n\n// EquateConditions sorts any slices of Condition before comparing them.\nfunc EquateConditions() cmp.Option {\n\treturn cmpopts.SortSlices(func(i, j xpv2.Condition) bool { return i.Type < j.Type })\n}\n"
  },
  {
    "path": "pkg/test/doc.go",
    "content": "/*\nCopyright 2019 The Crossplane 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// Package test implements utilities that can be used to test Kubernetes\n// controllers that reconcile Crossplane resources.\npackage test\n"
  },
  {
    "path": "pkg/test/fake.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 test\n\nimport (\n\t\"context\"\n\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n)\n\nvar _ client.Client = &MockClient{}\n\n// A MockGetFn is used to mock client.Client's Get implementation.\ntype MockGetFn func(ctx context.Context, key client.ObjectKey, obj client.Object) error\n\n// A MockListFn is used to mock client.Client's List implementation.\ntype MockListFn func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error\n\n// A MockCreateFn is used to mock client.Client's Create implementation.\ntype MockCreateFn func(ctx context.Context, obj client.Object, opts ...client.CreateOption) error\n\n// A MockDeleteFn is used to mock client.Client's Delete implementation.\ntype MockDeleteFn func(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error\n\n// A MockDeleteAllOfFn is used to mock client.Client's Delete implementation.\ntype MockDeleteAllOfFn func(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error\n\n// A MockUpdateFn is used to mock client.Client's Update implementation.\ntype MockUpdateFn func(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error\n\n// A MockPatchFn is used to mock client.Client's Patch implementation.\ntype MockPatchFn func(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error\n\n// A MockApplyFn is used to mock client.Client's Apply implementation.\ntype MockApplyFn func(ctx context.Context, config runtime.ApplyConfiguration, opts ...client.ApplyOption) error\n\n// A MockSubResourceGetFn is used to mock client.SubResourceClient's get implementation.\ntype MockSubResourceGetFn func(ctx context.Context, obj, subResource client.Object, opts ...client.SubResourceGetOption) error\n\n// A MockSubResourceCreateFn is used to mock client.SubResourceClient's create implementation.\ntype MockSubResourceCreateFn func(ctx context.Context, obj, subResource client.Object, opts ...client.SubResourceCreateOption) error\n\n// A MockSubResourceUpdateFn is used to mock client.SubResourceClient's update implementation.\ntype MockSubResourceUpdateFn func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error\n\n// A MockSubResourcePatchFn is used to mock client.SubResourceClient's patch implementation.\ntype MockSubResourcePatchFn func(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error\n\n// A MockSubResourceApplyFn is used to mock client.SubResourceClient's apply implementation.\ntype MockSubResourceApplyFn func(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.SubResourceApplyOption) error\n\n// A MockSchemeFn is used to mock client.Client's Scheme implementation.\ntype MockSchemeFn func() *runtime.Scheme\n\n// A MockGroupVersionKindForFn is used to mock client.Client's GroupVersionKindFor implementation.\ntype MockGroupVersionKindForFn func(runtime.Object) (schema.GroupVersionKind, error)\n\n// A MockIsObjectNamespacedFn is used to mock client.Client's IsObjectNamespaced implementation.\ntype MockIsObjectNamespacedFn func(runtime.Object) (bool, error)\n\n// An ObjectFn operates on the supplied Object. You might use an ObjectFn to\n// test or update the contents of an Object.\ntype ObjectFn func(obj client.Object) error\n\n// ApplyFn operates on the supplied ApplyConfiguration. You might use an ApplyFn to\n// test or update the contents of an ApplyConfiguration object.\ntype ApplyFn func(obj runtime.ApplyConfiguration) error\n\n// An RuntimeObjectFn operates on the supplied Object. You might use an RuntimeObjectFn to\n// test or update the contents of an runtime.Object.\ntype RuntimeObjectFn func(obj runtime.Object) error\n\n// An ObjectListFn operates on the supplied ObjectList. You might use an\n// ObjectListFn to test or update the contents of an ObjectList.\ntype ObjectListFn func(obj client.ObjectList) error\n\n// NewMockGetFn returns a MockGetFn that returns the supplied error.\nfunc NewMockGetFn(err error, ofn ...ObjectFn) MockGetFn {\n\treturn func(_ context.Context, _ client.ObjectKey, obj client.Object) error {\n\t\tfor _, fn := range ofn {\n\t\t\tif err := fn(obj); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn err\n\t}\n}\n\n// NewMockListFn returns a MockListFn that returns the supplied error.\nfunc NewMockListFn(err error, ofn ...ObjectListFn) MockListFn {\n\treturn func(_ context.Context, obj client.ObjectList, _ ...client.ListOption) error {\n\t\tfor _, fn := range ofn {\n\t\t\tif err := fn(obj); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn err\n\t}\n}\n\n// NewMockCreateFn returns a MockCreateFn that returns the supplied error.\nfunc NewMockCreateFn(err error, ofn ...ObjectFn) MockCreateFn {\n\treturn func(_ context.Context, obj client.Object, _ ...client.CreateOption) error {\n\t\tfor _, fn := range ofn {\n\t\t\tif err := fn(obj); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn err\n\t}\n}\n\n// NewMockDeleteFn returns a MockDeleteFn that returns the supplied error.\nfunc NewMockDeleteFn(err error, ofn ...ObjectFn) MockDeleteFn {\n\treturn func(_ context.Context, obj client.Object, _ ...client.DeleteOption) error {\n\t\tfor _, fn := range ofn {\n\t\t\tif err := fn(obj); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn err\n\t}\n}\n\n// NewMockDeleteAllOfFn returns a MockDeleteAllOfFn that returns the supplied error.\nfunc NewMockDeleteAllOfFn(err error, ofn ...ObjectFn) MockDeleteAllOfFn {\n\treturn func(_ context.Context, obj client.Object, _ ...client.DeleteAllOfOption) error {\n\t\tfor _, fn := range ofn {\n\t\t\tif err := fn(obj); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn err\n\t}\n}\n\n// NewMockUpdateFn returns a MockUpdateFn that returns the supplied error.\nfunc NewMockUpdateFn(err error, ofn ...ObjectFn) MockUpdateFn {\n\treturn func(_ context.Context, obj client.Object, _ ...client.UpdateOption) error {\n\t\tfor _, fn := range ofn {\n\t\t\tif err := fn(obj); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn err\n\t}\n}\n\n// NewMockPatchFn returns a MockPatchFn that returns the supplied error.\nfunc NewMockPatchFn(err error, ofn ...ObjectFn) MockPatchFn {\n\treturn func(_ context.Context, obj client.Object, _ client.Patch, _ ...client.PatchOption) error {\n\t\tfor _, fn := range ofn {\n\t\t\tif err := fn(obj); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn err\n\t}\n}\n\n// NewMockApplyFn returns a MockApplyFn that returns the supplied error.\nfunc NewMockApplyFn(err error, afn ...ApplyFn) MockApplyFn {\n\treturn func(_ context.Context, obj runtime.ApplyConfiguration, _ ...client.ApplyOption) error {\n\t\tfor _, fn := range afn {\n\t\t\tif fnErr := fn(obj); fnErr != nil {\n\t\t\t\treturn fnErr\n\t\t\t}\n\t\t}\n\n\t\treturn err\n\t}\n}\n\n// NewMockSubResourceCreateFn returns a MockSubResourceCreateFn that returns the supplied error.\nfunc NewMockSubResourceCreateFn(err error, ofn ...ObjectFn) MockSubResourceCreateFn {\n\treturn func(_ context.Context, obj, _ client.Object, _ ...client.SubResourceCreateOption) error {\n\t\tfor _, fn := range ofn {\n\t\t\tif err := fn(obj); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn err\n\t}\n}\n\n// NewMockSubResourceUpdateFn returns a MockSubResourceUpdateFn that returns the supplied error.\nfunc NewMockSubResourceUpdateFn(err error, ofn ...ObjectFn) MockSubResourceUpdateFn {\n\treturn func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {\n\t\tfor _, fn := range ofn {\n\t\t\tif err := fn(obj); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn err\n\t}\n}\n\n// NewMockSubResourcePatchFn returns a MockSubResourcePatchFn that returns the supplied error.\nfunc NewMockSubResourcePatchFn(err error, ofn ...ObjectFn) MockSubResourcePatchFn {\n\treturn func(_ context.Context, obj client.Object, _ client.Patch, _ ...client.SubResourcePatchOption) error {\n\t\tfor _, fn := range ofn {\n\t\t\tif err := fn(obj); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn err\n\t}\n}\n\n// NewMockSchemeFn returns a MockSchemeFn that returns the scheme.\nfunc NewMockSchemeFn(scheme *runtime.Scheme) MockSchemeFn {\n\treturn func() *runtime.Scheme {\n\t\treturn scheme\n\t}\n}\n\n// NewMockGroupVersionKindForFn returns a MockGroupVersionKindForFn that returns the supplied GVK and error.\nfunc NewMockGroupVersionKindForFn(err error, gvk schema.GroupVersionKind, rofn ...RuntimeObjectFn) MockGroupVersionKindForFn {\n\treturn func(obj runtime.Object) (schema.GroupVersionKind, error) {\n\t\tfor _, fn := range rofn {\n\t\t\tif err := fn(obj); err != nil {\n\t\t\t\treturn gvk, err\n\t\t\t}\n\t\t}\n\n\t\treturn gvk, err\n\t}\n}\n\n// NewMockIsObjectNamespacedFn returns a MockGroupVersionKindForFn that returns the supplied GVK and error.\nfunc NewMockIsObjectNamespacedFn(err error, isNamespaced bool, rofn ...RuntimeObjectFn) MockIsObjectNamespacedFn {\n\treturn func(obj runtime.Object) (bool, error) {\n\t\tfor _, fn := range rofn {\n\t\t\tif err := fn(obj); err != nil {\n\t\t\t\treturn isNamespaced, err\n\t\t\t}\n\t\t}\n\n\t\treturn isNamespaced, err\n\t}\n}\n\n// MockClient implements controller-runtime's Client interface, allowing each\n// method to be overridden for testing. The controller-runtime provides a fake\n// client, but it is has surprising side effects (e.g. silently calling\n// os.Exit(1)) and does not allow us control over the errors it returns.\ntype MockClient struct {\n\tMockGet         MockGetFn\n\tMockList        MockListFn\n\tMockCreate      MockCreateFn\n\tMockDelete      MockDeleteFn\n\tMockDeleteAllOf MockDeleteAllOfFn\n\tMockUpdate      MockUpdateFn\n\tMockPatch       MockPatchFn\n\tMockApply       MockApplyFn\n\n\tMockStatusCreate MockSubResourceCreateFn\n\tMockStatusUpdate MockSubResourceUpdateFn\n\tMockStatusPatch  MockSubResourcePatchFn\n\n\tMockSubResourceGet    MockSubResourceGetFn\n\tMockSubResourceCreate MockSubResourceCreateFn\n\tMockSubResourceUpdate MockSubResourceUpdateFn\n\tMockSubResourcePatch  MockSubResourcePatchFn\n\n\tMockScheme              MockSchemeFn\n\tMockGroupVersionKindFor MockGroupVersionKindForFn\n\tMockIsObjectNamespaced  MockIsObjectNamespacedFn\n}\n\n// NewMockClient returns a MockClient that does nothing when its methods are\n// called.\nfunc NewMockClient() *MockClient {\n\treturn &MockClient{\n\t\tMockGet:         NewMockGetFn(nil),\n\t\tMockList:        NewMockListFn(nil),\n\t\tMockCreate:      NewMockCreateFn(nil),\n\t\tMockDelete:      NewMockDeleteFn(nil),\n\t\tMockDeleteAllOf: NewMockDeleteAllOfFn(nil),\n\t\tMockUpdate:      NewMockUpdateFn(nil),\n\t\tMockPatch:       NewMockPatchFn(nil),\n\t\tMockApply:       NewMockApplyFn(nil),\n\n\t\tMockStatusUpdate: NewMockSubResourceUpdateFn(nil),\n\t\tMockStatusPatch:  NewMockSubResourcePatchFn(nil),\n\n\t\tMockScheme:              NewMockSchemeFn(nil),\n\t\tMockGroupVersionKindFor: NewMockGroupVersionKindForFn(nil, schema.GroupVersionKind{}),\n\t\tMockIsObjectNamespaced:  NewMockIsObjectNamespacedFn(nil, false),\n\t}\n}\n\n// Get calls MockClient's MockGet function.\nfunc (c *MockClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, _ ...client.GetOption) error {\n\treturn c.MockGet(ctx, key, obj)\n}\n\n// List calls MockClient's MockList function.\nfunc (c *MockClient) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {\n\treturn c.MockList(ctx, list, opts...)\n}\n\n// Create calls MockClient's MockCreate function.\nfunc (c *MockClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {\n\treturn c.MockCreate(ctx, obj, opts...)\n}\n\n// Delete calls MockClient's MockDelete function.\nfunc (c *MockClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {\n\treturn c.MockDelete(ctx, obj, opts...)\n}\n\n// DeleteAllOf calls MockClient's DeleteAllOf function.\nfunc (c *MockClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error {\n\treturn c.MockDeleteAllOf(ctx, obj, opts...)\n}\n\n// Update calls MockClient's MockUpdate function.\nfunc (c *MockClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {\n\treturn c.MockUpdate(ctx, obj, opts...)\n}\n\n// Patch calls MockClient's MockPatch function.\nfunc (c *MockClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {\n\treturn c.MockPatch(ctx, obj, patch, opts...)\n}\n\n// Apply calls MockClient's MockApply function.\nfunc (c *MockClient) Apply(ctx context.Context, config runtime.ApplyConfiguration, opts ...client.ApplyOption) error {\n\treturn c.MockApply(ctx, config, opts...)\n}\n\n// Status returns status writer for status sub-resource.\nfunc (c *MockClient) Status() client.SubResourceWriter {\n\treturn &MockSubResourceClient{\n\t\tMockCreate: c.MockStatusCreate,\n\t\tMockUpdate: c.MockStatusUpdate,\n\t\tMockPatch:  c.MockStatusPatch,\n\t}\n}\n\n// SubResource is unimplemented. It panics if called.\nfunc (c *MockClient) SubResource(_ string) client.SubResourceClient {\n\treturn &MockSubResourceClient{\n\t\tMockGet:    c.MockSubResourceGet,\n\t\tMockCreate: c.MockSubResourceCreate,\n\t\tMockUpdate: c.MockSubResourceUpdate,\n\t\tMockPatch:  c.MockSubResourcePatch,\n\t}\n}\n\n// RESTMapper returns the REST mapper.\nfunc (c *MockClient) RESTMapper() meta.RESTMapper {\n\treturn nil\n}\n\n// Scheme calls MockClient's MockScheme function.\nfunc (c *MockClient) Scheme() *runtime.Scheme {\n\treturn c.MockScheme()\n}\n\n// GroupVersionKindFor calls MockClient's MockGroupVersionKindFor function.\nfunc (c *MockClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {\n\treturn c.MockGroupVersionKindFor(obj)\n}\n\n// IsObjectNamespaced calls MockClient's MockIsObjectNamespaced function.\nfunc (c *MockClient) IsObjectNamespaced(obj runtime.Object) (bool, error) {\n\treturn c.MockIsObjectNamespaced(obj)\n}\n\n// MockSubResourceClient provides mock functionality for status sub-resource.\ntype MockSubResourceClient struct {\n\tMockGet    MockSubResourceGetFn\n\tMockCreate MockSubResourceCreateFn\n\tMockUpdate MockSubResourceUpdateFn\n\tMockPatch  MockSubResourcePatchFn\n\tMockApply  MockSubResourceApplyFn\n}\n\n// Get a sub-resource.\nfunc (m *MockSubResourceClient) Get(ctx context.Context, obj, subResource client.Object, opts ...client.SubResourceGetOption) error {\n\treturn m.MockGet(ctx, obj, subResource, opts...)\n}\n\n// Create a sub-resource.\nfunc (m *MockSubResourceClient) Create(ctx context.Context, obj, subResource client.Object, opts ...client.SubResourceCreateOption) error {\n\treturn m.MockCreate(ctx, obj, subResource, opts...)\n}\n\n// Update a sub-resource.\nfunc (m *MockSubResourceClient) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {\n\treturn m.MockUpdate(ctx, obj, opts...)\n}\n\n// Patch a sub-resource.\nfunc (m *MockSubResourceClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error {\n\treturn m.MockPatch(ctx, obj, patch, opts...)\n}\n\n// Apply a sub-resource.\nfunc (m *MockSubResourceClient) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.SubResourceApplyOption) error {\n\treturn m.MockApply(ctx, obj, opts...)\n}\n"
  },
  {
    "path": "pkg/test/retry.go",
    "content": "/*\nCopyright 2019 The Crossplane 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 test\n\nimport (\n\t\"time\"\n\n\t\"k8s.io/apimachinery/pkg/util/wait\"\n)\n\n// DefaultRetry is the recommended retry parameters for unit testing scenarios\n// where a condition is being tested multiple times before it is expected to\n// succeed.\n//\n//nolint:gochecknoglobals // We treat this as a constant.\nvar DefaultRetry = wait.Backoff{\n\tSteps:    500,\n\tDuration: 10 * time.Millisecond,\n\tFactor:   1.0,\n\tJitter:   0.1,\n}\n"
  },
  {
    "path": "pkg/version/fake/mocks.go",
    "content": "/*\nCopyright 2020 The Crossplane 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// Package fake contains semantic version mocks.\npackage fake\n\nimport (\n\t\"github.com/Masterminds/semver/v3\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/version\"\n)\n\nvar _ version.Operations = &MockVersioner{}\n\n// MockVersioner provides mock version operations.\ntype MockVersioner struct {\n\tMockGetVersionString func() string\n\tMockGetSemVer        func() (*semver.Version, error)\n\tMockInConstraints    func() (bool, error)\n}\n\n// NewMockGetVersionStringFn creates new MockGetVersionString function for MockVersioner.\nfunc NewMockGetVersionStringFn(s string) func() string {\n\treturn func() string { return s }\n}\n\n// NewMockGetSemVerFn creates new MockGetSemver function for MockVersioner.\nfunc NewMockGetSemVerFn(s *semver.Version, err error) func() (*semver.Version, error) {\n\treturn func() (*semver.Version, error) { return s, err }\n}\n\n// NewMockInConstraintsFn creates new MockInConstraintsString function for MockVersioner.\nfunc NewMockInConstraintsFn(b bool, err error) func() (bool, error) {\n\treturn func() (bool, error) { return b, err }\n}\n\n// GetVersionString calls the underlying MockGetVersionString.\nfunc (m *MockVersioner) GetVersionString() string {\n\treturn m.MockGetVersionString()\n}\n\n// GetSemVer calls the underlying MockGetSemVer.\nfunc (m *MockVersioner) GetSemVer() (*semver.Version, error) {\n\treturn m.MockGetSemVer()\n}\n\n// InConstraints calls the underlying MockInConstraints.\nfunc (m *MockVersioner) InConstraints(_ string) (bool, error) {\n\treturn m.MockInConstraints()\n}\n"
  },
  {
    "path": "pkg/version/version.go",
    "content": "/*\nCopyright 2020 The Crossplane 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// Package version contains utilities for working with semantic versions.\npackage version\n\nimport (\n\t\"github.com/Masterminds/semver/v3\"\n)\n\nvar version string\n\n// Operations provides semantic version operations.\ntype Operations interface {\n\tGetVersionString() string\n\tGetSemVer() (*semver.Version, error)\n\tInConstraints(c string) (bool, error)\n}\n\n// Versioner provides semantic version operations.\ntype Versioner struct {\n\tversion string\n}\n\n// New creates a new versioner.\nfunc New() *Versioner {\n\treturn &Versioner{\n\t\tversion: version,\n\t}\n}\n\n// GetVersionString returns the current Crossplane version as string.\nfunc (v *Versioner) GetVersionString() string {\n\treturn v.version\n}\n\n// GetSemVer returns the current Crossplane version as a semantic version.\nfunc (v *Versioner) GetSemVer() (*semver.Version, error) {\n\treturn semver.NewVersion(v.version)\n}\n\n// InConstraints is a helper function that checks if the current Crossplane\n// version is in the semantic version constraints.\nfunc (v *Versioner) InConstraints(c string) (bool, error) {\n\tver, err := v.GetSemVer()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tconstraint, err := semver.NewConstraint(c)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn constraint.Check(ver), nil\n}\n"
  },
  {
    "path": "pkg/version/version_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 version\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nfunc TestInRange(t *testing.T) {\n\ttype args struct {\n\t\tversion string\n\t\tr       string\n\t}\n\n\ttype want struct {\n\t\tis  bool\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"ValidInRange\": {\n\t\t\treason: \"Should return true when a valid semantic version is in a valid range.\",\n\t\t\targs: args{\n\t\t\t\tversion: \"v0.13.0\",\n\t\t\t\tr:       \">0.12.0\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tis: true,\n\t\t\t},\n\t\t},\n\t\t\"ValidNotInRange\": {\n\t\t\treason: \"Should return false when a valid semantic version is not in a valid range.\",\n\t\t\targs: args{\n\t\t\t\tversion: \"v0.13.0\",\n\t\t\t\tr:       \">0.13.0\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tis: false,\n\t\t\t},\n\t\t},\n\t\t\"InvalidVersion\": {\n\t\t\treason: \"Should return error when version is invalid.\",\n\t\t\targs: args{\n\t\t\t\tversion: \"v0a.13.0\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"invalid semantic version\"),\n\t\t\t},\n\t\t},\n\t\t\"InvalidRange\": {\n\t\t\treason: \"Should return error when range is invalid.\",\n\t\t\targs: args{\n\t\t\t\tversion: \"v0.13.0\",\n\t\t\t\tr:       \">a2\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"improper constraint: >a2\"),\n\t\t\t},\n\t\t},\n\t\t\"ValidSpaceSeparatedRange\": {\n\t\t\treason: \"Should return true if version is within a valid space separated ranged constraint\",\n\t\t\targs: args{\n\t\t\t\tversion: \"v2.13.0\",\n\t\t\t\tr:       \">=v2.0.0 <v5.0.0\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tis: true,\n\t\t\t},\n\t\t},\n\t\t\"ValidCommaSeparatedRangedConstraint\": {\n\t\t\treason: \"Should return true if version is within a valid comma-separated ranged constraint\",\n\t\t\targs: args{\n\t\t\t\tversion: \"v2.13.0\",\n\t\t\t\tr:       \">=v2.0.0,<v5.0.0\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tis: true,\n\t\t\t},\n\t\t},\n\t\t\"InvalidSpaceSeparatedRange\": {\n\t\t\treason: \"Should return error if the ranged constraint is invalid\",\n\t\t\targs: args{\n\t\t\t\tversion: \"v1.13.0\",\n\t\t\t\tr:       \">=v2.0.0 >v5a.0.0\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.New(\"improper constraint: >=v2.0.0 >v5a.0.0\"),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tversion = tc.args.version\n\n\t\t\tis, err := New().InConstraints(tc.args.r)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nInRange(...): -want err, +got err:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.is, is, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nInRange(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/webhook/mutator.go",
    "content": "/*\nCopyright 2022 The Crossplane 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\thttp://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// Package webhook contains utilities for building Kubernetes webhooks.\npackage webhook\n\nimport (\n\t\"context\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n)\n\n// WithMutationFns allows you to initiate the mutator with given list of mutator\n// functions.\nfunc WithMutationFns(fns ...MutateFn) MutatorOption {\n\treturn func(m *Mutator) {\n\t\tm.MutationChain = fns\n\t}\n}\n\n// MutatorOption configures given Mutator.\ntype MutatorOption func(*Mutator)\n\n// MutateFn is a single mutating function that can be used by Mutator.\ntype MutateFn func(ctx context.Context, obj runtime.Object) error\n\n// NewMutator returns a new instance of Mutator that can be used as CustomDefaulter.\nfunc NewMutator(opts ...MutatorOption) *Mutator {\n\tm := &Mutator{\n\t\tMutationChain: []MutateFn{},\n\t}\n\tfor _, f := range opts {\n\t\tf(m)\n\t}\n\n\treturn m\n}\n\n// Mutator satisfies CustomDefaulter interface with an ordered MutateFn list.\ntype Mutator struct {\n\tMutationChain []MutateFn\n}\n\n// Default executes the MutatorFns in given order. Its name might sound misleading\n// since defaulting seems to be the first use case used by controller-runtime\n// but MutatorFns can make any changes on given resource.\nfunc (m *Mutator) Default(ctx context.Context, obj runtime.Object) error {\n\tfor _, f := range m.MutationChain {\n\t\tif err := f(ctx, obj); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/webhook/mutator_test.go",
    "content": "/*\nCopyright 2022 The Crossplane 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\thttp://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 webhook\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/webhook\"\n\t\"sigs.k8s.io/controller-runtime/pkg/webhook/admission\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nvar (\n\t// Mutator has to satisfy CustomDefaulter interface so that it can be used by\n\t// controller-runtime Manager.\n\t_ webhook.CustomDefaulter             = &Mutator{} //nolint:staticcheck // Testing deprecated interface for backwards compatibility.\n\t_ admission.Defaulter[runtime.Object] = &Mutator{}\n)\n\nfunc TestDefault(t *testing.T) {\n\ttype args struct {\n\t\tobj runtime.Object\n\t\tfns []MutateFn\n\t}\n\n\ttype want struct {\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs\n\t\twant\n\t}{\n\t\t\"Success\": {\n\t\t\treason: \"Functions without errors should be executed successfully\",\n\t\t\targs: args{\n\t\t\t\tfns: []MutateFn{\n\t\t\t\t\tfunc(_ context.Context, _ runtime.Object) error {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"Failure\": {\n\t\t\treason: \"Functions with errors should return with error\",\n\t\t\targs: args{\n\t\t\t\tfns: []MutateFn{\n\t\t\t\t\tfunc(_ context.Context, _ runtime.Object) error {\n\t\t\t\t\t\treturn errBoom\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errBoom,\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tv := NewMutator(WithMutationFns(tc.fns...))\n\n\t\t\terr := v.Default(context.TODO(), tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nDefault(...): -want, +got\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/webhook/validator.go",
    "content": "/*\nCopyright 2022 The Crossplane 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\thttp://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 webhook\n\nimport (\n\t\"context\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/webhook/admission\"\n)\n\n// WithValidateCreationFns initializes the Validator with given set of creation\n// validation functions.\nfunc WithValidateCreationFns(fns ...ValidateCreateFn) ValidatorOption {\n\treturn func(v *Validator) {\n\t\tv.CreationChain = fns\n\t}\n}\n\n// WithValidateUpdateFns initializes the Validator with given set of update\n// validation functions.\nfunc WithValidateUpdateFns(fns ...ValidateUpdateFn) ValidatorOption {\n\treturn func(v *Validator) {\n\t\tv.UpdateChain = fns\n\t}\n}\n\n// WithValidateDeletionFns initializes the Validator with given set of deletion\n// validation functions.\nfunc WithValidateDeletionFns(fns ...ValidateDeleteFn) ValidatorOption {\n\treturn func(v *Validator) {\n\t\tv.DeletionChain = fns\n\t}\n}\n\n// ValidatorOption allows you to configure given Validator.\ntype ValidatorOption func(*Validator)\n\n// ValidateCreateFn is function type for creation validation.\ntype ValidateCreateFn func(ctx context.Context, obj runtime.Object) (admission.Warnings, error)\n\n// ValidateUpdateFn is function type for update validation.\ntype ValidateUpdateFn func(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error)\n\n// ValidateDeleteFn is function type for deletion validation.\ntype ValidateDeleteFn func(ctx context.Context, obj runtime.Object) (admission.Warnings, error)\n\n// NewValidator returns a new Validator with no-op defaults.\nfunc NewValidator(opts ...ValidatorOption) *Validator {\n\tvc := &Validator{\n\t\tCreationChain: []ValidateCreateFn{},\n\t\tUpdateChain:   []ValidateUpdateFn{},\n\t\tDeletionChain: []ValidateDeleteFn{},\n\t}\n\tfor _, f := range opts {\n\t\tf(vc)\n\t}\n\n\treturn vc\n}\n\n// Validator runs the given validation chains in order.\ntype Validator struct {\n\tCreationChain []ValidateCreateFn\n\tUpdateChain   []ValidateUpdateFn\n\tDeletionChain []ValidateDeleteFn\n}\n\n// ValidateCreate runs functions in creation chain in order.\nfunc (vc *Validator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {\n\twarnings := []string{}\n\n\tfor _, f := range vc.CreationChain {\n\t\twarns, err := f(ctx, obj)\n\t\tif err != nil {\n\t\t\treturn append(warnings, warns...), err\n\t\t}\n\n\t\twarnings = append(warnings, warns...)\n\t}\n\n\treturn warnings, nil\n}\n\n// ValidateUpdate runs functions in update chain in order.\nfunc (vc *Validator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {\n\twarnings := []string{}\n\n\tfor _, f := range vc.UpdateChain {\n\t\twarns, err := f(ctx, oldObj, newObj)\n\t\tif err != nil {\n\t\t\treturn append(warnings, warns...), err\n\t\t}\n\n\t\twarnings = append(warnings, warns...)\n\t}\n\n\treturn warnings, nil\n}\n\n// ValidateDelete runs functions in deletion chain in order.\nfunc (vc *Validator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {\n\twarnings := []string{}\n\n\tfor _, f := range vc.DeletionChain {\n\t\twarns, err := f(ctx, obj)\n\t\tif err != nil {\n\t\t\treturn append(warnings, warns...), err\n\t\t}\n\n\t\twarnings = append(warnings, warns...)\n\t}\n\n\treturn warnings, nil\n}\n"
  },
  {
    "path": "pkg/webhook/validator_test.go",
    "content": "/*\nCopyright 2022 The Crossplane 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\thttp://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 webhook\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/webhook\"\n\t\"sigs.k8s.io/controller-runtime/pkg/webhook/admission\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nvar (\n\t// Validator has to satisfy CustomValidator interface so that it can be used by\n\t// controller-runtime Manager.\n\t_ webhook.CustomValidator             = &Validator{} //nolint:staticcheck // Testing deprecated interface for backwards compatibility.\n\t_ admission.Validator[runtime.Object] = &Validator{}\n)\n\nvar (\n\terrBoom  = errors.New(\"boom\")\n\twarnings = []string{\"warning\"}\n)\n\nfunc TestValidateCreate(t *testing.T) {\n\ttype args struct {\n\t\tobj runtime.Object\n\t\tfns []ValidateCreateFn\n\t}\n\n\ttype want struct {\n\t\terr      error\n\t\twarnings admission.Warnings\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs\n\t\twant\n\t}{\n\t\t\"Success\": {\n\t\t\treason: \"Functions without errors and warnings should be executed successfully\",\n\t\t\targs: args{\n\t\t\t\tfns: []ValidateCreateFn{\n\t\t\t\t\tfunc(_ context.Context, _ runtime.Object) (admission.Warnings, error) {\n\t\t\t\t\t\treturn nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessWithWarnings\": {\n\t\t\treason: \"Functions with warnings but without errors should be executed successfully\",\n\t\t\targs: args{\n\t\t\t\tfns: []ValidateCreateFn{\n\t\t\t\t\tfunc(_ context.Context, _ runtime.Object) (admission.Warnings, error) {\n\t\t\t\t\t\treturn warnings, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\twarnings: warnings,\n\t\t\t},\n\t\t},\n\t\t\"Failure\": {\n\t\t\treason: \"Functions with errors and without warnings should return with error\",\n\t\t\targs: args{\n\t\t\t\tfns: []ValidateCreateFn{\n\t\t\t\t\tfunc(_ context.Context, _ runtime.Object) (admission.Warnings, error) {\n\t\t\t\t\t\treturn nil, errBoom\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errBoom,\n\t\t\t},\n\t\t},\n\t\t\"FailureWithWarnings\": {\n\t\t\treason: \"Functions with errors and warnings should return with error\",\n\t\t\targs: args{\n\t\t\t\tfns: []ValidateCreateFn{\n\t\t\t\t\tfunc(_ context.Context, _ runtime.Object) (admission.Warnings, error) {\n\t\t\t\t\t\treturn warnings, errBoom\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\twarnings: warnings,\n\t\t\t\terr:      errBoom,\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tv := NewValidator(WithValidateCreationFns(tc.fns...))\n\n\t\t\twarn, err := v.ValidateCreate(context.TODO(), tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nValidateCreate(...): -want error, +got error\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.warnings, warn, cmpopts.EquateEmpty()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nValidateCreate(...): -want warnings, +got warnings\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestValidateUpdate(t *testing.T) {\n\ttype args struct {\n\t\toldObj runtime.Object\n\t\tnewObj runtime.Object\n\t\tfns    []ValidateUpdateFn\n\t}\n\n\ttype want struct {\n\t\terr      error\n\t\twarnings admission.Warnings\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs\n\t\twant\n\t}{\n\t\t\"Success\": {\n\t\t\treason: \"Functions without errors should be executed successfully\",\n\t\t\targs: args{\n\t\t\t\tfns: []ValidateUpdateFn{\n\t\t\t\t\tfunc(_ context.Context, _, _ runtime.Object) (admission.Warnings, error) {\n\t\t\t\t\t\treturn nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessWithWarnings\": {\n\t\t\treason: \"Functions without errors but with warnings should be executed successfully with warnings\",\n\t\t\targs: args{\n\t\t\t\tfns: []ValidateUpdateFn{\n\t\t\t\t\tfunc(_ context.Context, _, _ runtime.Object) (admission.Warnings, error) {\n\t\t\t\t\t\treturn warnings, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\twarnings: warnings,\n\t\t\t},\n\t\t},\n\t\t\"Failure\": {\n\t\t\treason: \"Functions with errors should return with error\",\n\t\t\targs: args{\n\t\t\t\tfns: []ValidateUpdateFn{\n\t\t\t\t\tfunc(_ context.Context, _, _ runtime.Object) (admission.Warnings, error) {\n\t\t\t\t\t\treturn nil, errBoom\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errBoom,\n\t\t\t},\n\t\t},\n\t\t\"FailureWithWarnings\": {\n\t\t\treason: \"Functions with errors and warnings should return with error and warning\",\n\t\t\targs: args{\n\t\t\t\tfns: []ValidateUpdateFn{\n\t\t\t\t\tfunc(_ context.Context, _, _ runtime.Object) (admission.Warnings, error) {\n\t\t\t\t\t\treturn warnings, errBoom\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr:      errBoom,\n\t\t\t\twarnings: warnings,\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tv := NewValidator(WithValidateUpdateFns(tc.fns...))\n\n\t\t\twarn, err := v.ValidateUpdate(context.TODO(), tc.oldObj, tc.newObj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nValidateUpdate(...): -want error, +got error\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.warnings, warn, cmpopts.EquateEmpty()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nValidateUpdate(...): -want warnings, +got warnings\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestValidateDelete(t *testing.T) {\n\ttype args struct {\n\t\tobj runtime.Object\n\t\tfns []ValidateDeleteFn\n\t}\n\n\ttype want struct {\n\t\terr      error\n\t\twarnings admission.Warnings\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs\n\t\twant\n\t}{\n\t\t\"Success\": {\n\t\t\treason: \"Functions without errors should be executed successfully\",\n\t\t\targs: args{\n\t\t\t\tfns: []ValidateDeleteFn{\n\t\t\t\t\tfunc(_ context.Context, _ runtime.Object) (admission.Warnings, error) {\n\t\t\t\t\t\treturn nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessWithWarnings\": {\n\t\t\treason: \"Functions without errors but with warnings should be executed successfully\",\n\t\t\targs: args{\n\t\t\t\tfns: []ValidateDeleteFn{\n\t\t\t\t\tfunc(_ context.Context, _ runtime.Object) (admission.Warnings, error) {\n\t\t\t\t\t\treturn warnings, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\twarnings: warnings,\n\t\t\t},\n\t\t},\n\t\t\"Failure\": {\n\t\t\treason: \"Functions with errors should return with error\",\n\t\t\targs: args{\n\t\t\t\tfns: []ValidateDeleteFn{\n\t\t\t\t\tfunc(_ context.Context, _ runtime.Object) (admission.Warnings, error) {\n\t\t\t\t\t\treturn nil, errBoom\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errBoom,\n\t\t\t},\n\t\t},\n\t\t\"FailureWithWarnings\": {\n\t\t\treason: \"Functions with errors and warnings should return with error and warnings\",\n\t\t\targs: args{\n\t\t\t\tfns: []ValidateDeleteFn{\n\t\t\t\t\tfunc(_ context.Context, _ runtime.Object) (admission.Warnings, error) {\n\t\t\t\t\t\treturn warnings, errBoom\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr:      errBoom,\n\t\t\t\twarnings: warnings,\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tv := NewValidator(WithValidateDeletionFns(tc.fns...))\n\n\t\t\twarn, err := v.ValidateDelete(context.TODO(), tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nValidateDelete(...): -want error, +got error\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.warnings, warn, cmpopts.EquateEmpty()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nValidateDelete(...): -want warnings, +got warnings\\n%s\\n\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/xcrd/composite.go",
    "content": "/*\nCopyright 2021 The Crossplane 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 xcrd\n\nimport (\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n)\n\n// Annotation keys.\nconst (\n\t// AnnotationKeyCompositionResourceName is the name of the composite resource as described from a composition.\n\tAnnotationKeyCompositionResourceName = \"crossplane.io/composition-resource-name\"\n)\n\n// SetCompositionResourceName sets the name of the composition template used to\n// reconcile a composed resource as an annotation.\nfunc SetCompositionResourceName(o metav1.Object, n string) {\n\tmeta.AddAnnotations(o, map[string]string{AnnotationKeyCompositionResourceName: n})\n}\n\n// GetCompositionResourceName gets the name of the composition template used to\n// reconcile a composed resource from its annotations.\nfunc GetCompositionResourceName(o metav1.Object) string {\n\treturn o.GetAnnotations()[AnnotationKeyCompositionResourceName]\n}\n"
  },
  {
    "path": "pkg/xcrd/crd.go",
    "content": "/*\nCopyright 2020 The Crossplane 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// Package xcrd generates CustomResourceDefinitions from Crossplane definitions.\n//\n// v1.JSONSchemaProps is incompatible with controller-tools (as of 0.2.4)\n// because it is missing JSON tags and uses float64, which is a disallowed type.\n// We thus copy the entire struct as CRDSpecTemplate. See the below issue:\n// https://github.com/kubernetes-sigs/controller-tools/issues/291\npackage xcrd\n\nimport (\n\t\"encoding/json\"\n\t\"maps\"\n\n\tv1 \"github.com/crossplane/crossplane/apis/v2/apiextensions/v1\"\n\textv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/utils/ptr\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n)\n\n// Category names for generated claim and composite CRDs.\nconst (\n\tCategoryClaim     = \"claim\"\n\tCategoryComposite = \"composite\"\n)\n\nconst (\n\terrFmtGenCrd                   = \"cannot generate CRD for %q %q\"\n\terrParseValidation             = \"cannot parse validation schema\"\n\terrInvalidClaimNames           = \"invalid resource claim names\"\n\terrMissingClaimNames           = \"missing names\"\n\terrFmtConflictingClaimName     = \"%q conflicts with composite resource name\"\n\terrCustomResourceValidationNil = \"custom resource validation cannot be nil\"\n)\n\n// ForCompositeResource derives the CustomResourceDefinition for a composite\n// resource from the supplied CompositeResourceDefinition.\nfunc ForCompositeResource(xrd *v1.CompositeResourceDefinition) (*extv1.CustomResourceDefinition, error) {\n\tcrd := &extv1.CustomResourceDefinition{\n\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\tGroup:      xrd.Spec.Group,\n\t\t\tNames:      xrd.Spec.Names,\n\t\t\tVersions:   make([]extv1.CustomResourceDefinitionVersion, len(xrd.Spec.Versions)),\n\t\t\tConversion: xrd.Spec.Conversion,\n\t\t},\n\t}\n\n\tcrd.SetName(xrd.GetName())\n\tsetCrdMetadata(crd, xrd)\n\tcrd.SetOwnerReferences([]metav1.OwnerReference{meta.AsController(\n\t\tmeta.TypedReferenceTo(xrd, v1.CompositeResourceDefinitionGroupVersionKind),\n\t)})\n\n\tscope := ptr.Deref(xrd.Spec.Scope, v1.CompositeResourceScopeLegacyCluster)\n\tswitch scope {\n\tcase v1.CompositeResourceScopeNamespaced:\n\t\tcrd.Spec.Scope = extv1.NamespaceScoped\n\tcase v1.CompositeResourceScopeCluster:\n\t\tcrd.Spec.Scope = extv1.ClusterScoped\n\tcase v1.CompositeResourceScopeLegacyCluster:\n\t\tcrd.Spec.Scope = extv1.ClusterScoped\n\t}\n\n\tcrd.Spec.Names.Categories = append(crd.Spec.Names.Categories, CategoryComposite)\n\n\t// The composite name is used as a label value, so we must ensure it is not\n\t// longer.\n\tconst maxCompositeNameLength = 63\n\n\tfor i, vr := range xrd.Spec.Versions {\n\t\tcrdv, err := genCrdVersion(vr, maxCompositeNameLength)\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrapf(err, errFmtGenCrd, \"Composite Resource\", xrd.Name)\n\t\t}\n\n\t\tcrdv.AdditionalPrinterColumns = append(crdv.AdditionalPrinterColumns, CompositeResourcePrinterColumns(scope)...)\n\n\t\tprops := CompositeResourceSpecProps(scope, xrd.Spec.DefaultCompositionUpdatePolicy)\n\t\tmaps.Copy(crdv.Schema.OpenAPIV3Schema.Properties[\"spec\"].Properties, props)\n\n\t\tprops = CompositeResourceStatusProps(scope)\n\t\tmaps.Copy(crdv.Schema.OpenAPIV3Schema.Properties[\"status\"].Properties, props)\n\n\t\tcrd.Spec.Versions[i] = *crdv\n\t}\n\n\treturn crd, nil\n}\n\n// ForCompositeResourceClaim derives the CustomResourceDefinition for a\n// composite resource claim from the supplied CompositeResourceDefinition.\nfunc ForCompositeResourceClaim(xrd *v1.CompositeResourceDefinition) (*extv1.CustomResourceDefinition, error) {\n\tif err := validateClaimNames(xrd); err != nil {\n\t\treturn nil, errors.Wrap(err, errInvalidClaimNames)\n\t}\n\n\tcrd := &extv1.CustomResourceDefinition{\n\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\tScope:      extv1.NamespaceScoped,\n\t\t\tGroup:      xrd.Spec.Group,\n\t\t\tNames:      *xrd.Spec.ClaimNames,\n\t\t\tVersions:   make([]extv1.CustomResourceDefinitionVersion, len(xrd.Spec.Versions)),\n\t\t\tConversion: xrd.Spec.Conversion,\n\t\t},\n\t}\n\n\tcrd.SetName(xrd.Spec.ClaimNames.Plural + \".\" + xrd.Spec.Group)\n\tsetCrdMetadata(crd, xrd)\n\tcrd.SetOwnerReferences([]metav1.OwnerReference{meta.AsController(\n\t\tmeta.TypedReferenceTo(xrd, v1.CompositeResourceDefinitionGroupVersionKind),\n\t)})\n\n\tcrd.Spec.Names.Categories = append(crd.Spec.Names.Categories, CategoryClaim)\n\n\t// 63 because the names are used as label values. We don't put 63-6\n\t// (generateName suffix length) here because the name generator shortens\n\t// the base to 57 automatically before appending the suffix.\n\tconst maxClaimNameLength = 63\n\n\tfor i, vr := range xrd.Spec.Versions {\n\t\tcrdv, err := genCrdVersion(vr, maxClaimNameLength)\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrapf(err, errFmtGenCrd, \"Composite Resource Claim\", xrd.Name)\n\t\t}\n\n\t\tcrdv.AdditionalPrinterColumns = append(crdv.AdditionalPrinterColumns, CompositeResourceClaimPrinterColumns()...)\n\n\t\tprops := CompositeResourceClaimSpecProps(xrd.Spec.DefaultCompositeDeletePolicy)\n\t\tmaps.Copy(crdv.Schema.OpenAPIV3Schema.Properties[\"spec\"].Properties, props)\n\t\t// TODO(negz): This means claims will have status.claimConditionTypes.\n\t\t// I think that's a bug - only XRs should have that field.\n\t\tprops = CompositeResourceStatusProps(v1.CompositeResourceScopeLegacyCluster)\n\t\tmaps.Copy(crdv.Schema.OpenAPIV3Schema.Properties[\"status\"].Properties, props)\n\n\t\tcrd.Spec.Versions[i] = *crdv\n\t}\n\n\treturn crd, nil\n}\n\nfunc genCrdVersion(vr v1.CompositeResourceDefinitionVersion, maxNameLength int64) (*extv1.CustomResourceDefinitionVersion, error) {\n\tcrdv := extv1.CustomResourceDefinitionVersion{\n\t\tName:                     vr.Name,\n\t\tServed:                   vr.Served,\n\t\tStorage:                  vr.Referenceable,\n\t\tDeprecated:               ptr.Deref(vr.Deprecated, false),\n\t\tDeprecationWarning:       vr.DeprecationWarning,\n\t\tAdditionalPrinterColumns: vr.AdditionalPrinterColumns,\n\t\tSchema: &extv1.CustomResourceValidation{\n\t\t\tOpenAPIV3Schema: BaseProps(),\n\t\t},\n\t\tSubresources: &extv1.CustomResourceSubresources{\n\t\t\tStatus: &extv1.CustomResourceSubresourceStatus{},\n\t\t},\n\t}\n\n\ts, err := parseSchema(vr.Schema)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, errParseValidation)\n\t}\n\n\tif s == nil {\n\t\treturn nil, errors.New(errCustomResourceValidationNil)\n\t}\n\n\tcrdv.Schema.OpenAPIV3Schema.Description = s.Description\n\tcrdv.Schema.OpenAPIV3Schema.XValidations = s.XValidations\n\n\tmaxLength := maxNameLength\n\tif old := s.Properties[\"metadata\"].Properties[\"name\"].MaxLength; old != nil && *old < maxLength {\n\t\tmaxLength = *old\n\t}\n\n\txName := crdv.Schema.OpenAPIV3Schema.Properties[\"metadata\"].Properties[\"name\"]\n\txName.MaxLength = ptr.To(maxLength)\n\txName.Type = \"string\"\n\txMetaData := crdv.Schema.OpenAPIV3Schema.Properties[\"metadata\"]\n\txMetaData.Properties = map[string]extv1.JSONSchemaProps{\"name\": xName}\n\tcrdv.Schema.OpenAPIV3Schema.Properties[\"metadata\"] = xMetaData\n\n\txSpec := s.Properties[\"spec\"]\n\tcSpec := crdv.Schema.OpenAPIV3Schema.Properties[\"spec\"]\n\tcSpec.Required = append(cSpec.Required, xSpec.Required...)\n\tcSpec.XPreserveUnknownFields = xSpec.XPreserveUnknownFields\n\tcSpec.XValidations = append(cSpec.XValidations, xSpec.XValidations...)\n\tcSpec.OneOf = append(cSpec.OneOf, xSpec.OneOf...)\n\n\tcSpec.Description = xSpec.Description\n\tmaps.Copy(cSpec.Properties, xSpec.Properties)\n\n\tcrdv.Schema.OpenAPIV3Schema.Properties[\"spec\"] = cSpec\n\n\txStatus := s.Properties[\"status\"]\n\tcStatus := crdv.Schema.OpenAPIV3Schema.Properties[\"status\"]\n\tcStatus.Required = xStatus.Required\n\tcStatus.XValidations = xStatus.XValidations\n\tcStatus.Description = xStatus.Description\n\n\tcStatus.OneOf = xStatus.OneOf\n\tmaps.Copy(cStatus.Properties, xStatus.Properties)\n\n\tcrdv.Schema.OpenAPIV3Schema.Properties[\"status\"] = cStatus\n\n\tif vr.Subresources != nil && vr.Subresources.Scale != nil {\n\t\tcrdv.Subresources.Scale = vr.Subresources.Scale\n\t}\n\n\treturn &crdv, nil\n}\n\nfunc validateClaimNames(d *v1.CompositeResourceDefinition) error {\n\tif d.Spec.ClaimNames == nil {\n\t\treturn errors.New(errMissingClaimNames)\n\t}\n\n\tif n := d.Spec.ClaimNames.Kind; n == d.Spec.Names.Kind {\n\t\treturn errors.Errorf(errFmtConflictingClaimName, n)\n\t}\n\n\tif n := d.Spec.ClaimNames.Plural; n == d.Spec.Names.Plural {\n\t\treturn errors.Errorf(errFmtConflictingClaimName, n)\n\t}\n\n\tif n := d.Spec.ClaimNames.Singular; n != \"\" && n == d.Spec.Names.Singular {\n\t\treturn errors.Errorf(errFmtConflictingClaimName, n)\n\t}\n\n\tif n := d.Spec.ClaimNames.ListKind; n != \"\" && n == d.Spec.Names.ListKind {\n\t\treturn errors.Errorf(errFmtConflictingClaimName, n)\n\t}\n\n\treturn nil\n}\n\nfunc parseSchema(v *v1.CompositeResourceValidation) (*extv1.JSONSchemaProps, error) {\n\tif v == nil {\n\t\treturn nil, nil\n\t}\n\n\ts := &extv1.JSONSchemaProps{}\n\tif err := json.Unmarshal(v.OpenAPIV3Schema.Raw, s); err != nil {\n\t\treturn nil, errors.Wrap(err, errParseValidation)\n\t}\n\n\treturn s, nil\n}\n\n// setCrdMetadata sets the labels and annotations on the CRD.\nfunc setCrdMetadata(crd *extv1.CustomResourceDefinition, xrd *v1.CompositeResourceDefinition) *extv1.CustomResourceDefinition {\n\tcrd.SetLabels(xrd.GetLabels())\n\n\tif xrd.Spec.Metadata != nil {\n\t\tif xrd.Spec.Metadata.Labels != nil {\n\t\t\tinheritedLabels := crd.GetLabels()\n\t\t\tif inheritedLabels == nil {\n\t\t\t\tinheritedLabels = map[string]string{}\n\t\t\t}\n\n\t\t\tmaps.Copy(inheritedLabels, xrd.Spec.Metadata.Labels)\n\n\t\t\tcrd.SetLabels(inheritedLabels)\n\t\t}\n\n\t\tif xrd.Spec.Metadata.Annotations != nil {\n\t\t\tcrd.SetAnnotations(xrd.Spec.Metadata.Annotations)\n\t\t}\n\t}\n\n\treturn crd\n}\n\n// IsEstablished is a helper function to check whether api-server is ready\n// to accept the instances of registered CRD.\nfunc IsEstablished(s extv1.CustomResourceDefinitionStatus) bool {\n\tfor _, c := range s.Conditions {\n\t\tif c.Type == extv1.Established {\n\t\t\treturn c.Status == extv1.ConditionTrue\n\t\t}\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "pkg/xcrd/crd_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 xcrd\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\tv1 \"github.com/crossplane/crossplane/apis/v2/apiextensions/v1\"\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\t\"github.com/google/go-cmp/cmp\"\n\textv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\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/utils/ptr\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/meta\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nvar (\n\tname        = \"coolcomposites.example.org\"\n\tlabels      = map[string]string{\"cool\": \"very\"}\n\tannotations = map[string]string{\"example.org/cool\": \"very\"}\n\n\tgroup    = \"example.org\"\n\tversion  = \"v1\"\n\tkind     = \"CoolComposite\"\n\tlistKind = \"CoolCompositeList\"\n\tsingular = \"coolcomposite\"\n\tplural   = \"coolcomposites\"\n\n\td = &v1.CompositeResourceDefinition{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:        name,\n\t\t\tLabels:      labels,\n\t\t\tAnnotations: annotations,\n\t\t\tUID:         types.UID(\"you-you-eye-dee\"),\n\t\t},\n\t\tSpec: v1.CompositeResourceDefinitionSpec{\n\t\t\tGroup: group,\n\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\tPlural:   plural,\n\t\t\t\tSingular: singular,\n\t\t\t\tKind:     kind,\n\t\t\t\tListKind: listKind,\n\t\t\t},\n\t\t\tVersions: []v1.CompositeResourceDefinitionVersion{{\n\t\t\t\tName:          version,\n\t\t\t\tReferenceable: true,\n\t\t\t\tServed:        true,\n\t\t\t}},\n\t\t},\n\t}\n\n\tschema = `\n{\n\t\"required\": [\n\t\t\"spec\"\n\t],\n\t\"properties\": {\n\t\t\"spec\": {\n\t\t\t\"description\": \"Specification of the resource.\",\n\t\t\t\"required\": [\n\t\t\t\t\"storageGB\",\n\t\t\t\t\"engineVersion\"\n\t\t\t],\n\t\t\t\"properties\": {\n\t\t\t\t\"engineVersion\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"5.6\",\n\t\t\t\t\t\t\"5.7\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"storageGB\": {\n\t\t\t\t\t\"type\": \"integer\",\n\t\t\t\t\t\"description\": \"Pretend this is useful.\"\n\t\t\t\t},\n\t\t\t\t\"someField\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Pretend this is useful.\"\n\t\t\t\t},\n\t\t\t\t\"someOtherField\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Pretend this is useful.\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"x-kubernetes-validations\": [\n\t\t\t\t{\n\t\t\t\t\t\"message\": \"Cannot change engine version\",\n\t\t\t\t\t\"rule\": \"self.engineVersion == oldSelf.engineVersion\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"type\": \"object\",\n\t\t\t\"oneOf\": [\n\t\t\t\t{\n\t\t\t\t\t\"required\": [\"someField\"]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"required\": [\"someOtherField\"]\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t\"status\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"phase\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"something\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"x-kubernetes-validations\": [\n\t\t\t\t{\n\t\t\t\t\t\"message\": \"Phase is required once set\",\n\t\t\t\t\t\"rule\": \"!has(oldSelf.phase) || has(self.phase)\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"oneOf\": [\n\t\t\t\t{ \"required\": [\"phase\"] },\n\t\t\t\t{ \"required\": [\"something\"] }\n\t\t\t],\n\t\t\t\"type\": \"object\",\n\t\t\t\"description\": \"Status of the resource.\"\n\t\t}\n\t},\n\t\"type\": \"object\",\n\t\"description\": \"What the resource is for.\"\n}`\n)\n\nfunc TestIsEstablished(t *testing.T) {\n\tcases := map[string]struct {\n\t\ts    extv1.CustomResourceDefinitionStatus\n\t\twant bool\n\t}{\n\t\t\"IsEstablished\": {\n\t\t\ts: extv1.CustomResourceDefinitionStatus{\n\t\t\t\tConditions: []extv1.CustomResourceDefinitionCondition{{\n\t\t\t\t\tType:   extv1.Established,\n\t\t\t\t\tStatus: extv1.ConditionTrue,\n\t\t\t\t}},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"IsNotEstablished\": {\n\t\t\ts:    extv1.CustomResourceDefinitionStatus{},\n\t\t\twant: false,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := IsEstablished(tc.s)\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"IsEstablished(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestForCompositeResource(t *testing.T) {\n\tdefaultCompositionUpdatePolicy := xpv2.UpdatePolicy(\"Automatic\")\n\n\ttype args struct {\n\t\txrd *v1.CompositeResourceDefinition\n\t\tv   *v1.CompositeResourceValidation\n\t}\n\n\ttype want struct {\n\t\tc   *extv1.CustomResourceDefinition\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"Namespaced\": {\n\t\t\treason: \"A CRD should be generated from a modern CompositeResourceDefinitionVersion of a namespaced XR.\",\n\t\t\targs: args{\n\t\t\t\txrd: &v1.CompositeResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:        name,\n\t\t\t\t\t\tLabels:      labels,\n\t\t\t\t\t\tAnnotations: annotations,\n\t\t\t\t\t\tUID:         types.UID(\"you-you-eye-dee\"),\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{\n\t\t\t\t\t\tScope: ptr.To(v1.CompositeResourceScopeNamespaced),\n\t\t\t\t\t\tGroup: group,\n\t\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tPlural:   plural,\n\t\t\t\t\t\t\tSingular: singular,\n\t\t\t\t\t\t\tKind:     kind,\n\t\t\t\t\t\t\tListKind: listKind,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVersions: []v1.CompositeResourceDefinitionVersion{{\n\t\t\t\t\t\t\tName:          version,\n\t\t\t\t\t\t\tReferenceable: true,\n\t\t\t\t\t\t\tServed:        true,\n\t\t\t\t\t\t}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tv: &v1.CompositeResourceValidation{\n\t\t\t\t\tOpenAPIV3Schema: runtime.RawExtension{Raw: []byte(schema)},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tc: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:   name,\n\t\t\t\t\t\tLabels: labels,\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{\n\t\t\t\t\t\t\tmeta.AsController(meta.TypedReferenceTo(d, v1.CompositeResourceDefinitionGroupVersionKind)),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: group,\n\t\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tPlural:     plural,\n\t\t\t\t\t\t\tSingular:   singular,\n\t\t\t\t\t\t\tKind:       kind,\n\t\t\t\t\t\t\tListKind:   listKind,\n\t\t\t\t\t\t\tCategories: []string{CategoryComposite},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tScope: extv1.NamespaceScoped,\n\t\t\t\t\t\tVersions: []extv1.CustomResourceDefinitionVersion{{\n\t\t\t\t\t\t\tName:    version,\n\t\t\t\t\t\t\tServed:  true,\n\t\t\t\t\t\t\tStorage: true,\n\t\t\t\t\t\t\tSubresources: &extv1.CustomResourceSubresources{\n\t\t\t\t\t\t\t\tStatus: &extv1.CustomResourceSubresourceStatus{},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tAdditionalPrinterColumns: []extv1.CustomResourceColumnDefinition{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"SYNCED\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"READY\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.crossplane.compositionRef.name\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITIONREVISION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.crossplane.compositionRevisionRef.name\",\n\t\t\t\t\t\t\t\t\tPriority: 1,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"AGE\",\n\t\t\t\t\t\t\t\t\tType:     \"date\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tSchema: &extv1.CustomResourceValidation{\n\t\t\t\t\t\t\t\tOpenAPIV3Schema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\tDescription: \"What the resource is for.\",\n\t\t\t\t\t\t\t\t\tRequired:    []string{\"spec\"},\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"kind\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"metadata\": {\n\t\t\t\t\t\t\t\t\t\t\t// NOTE(muvaf): api-server takes care of validating\n\t\t\t\t\t\t\t\t\t\t\t// metadata.\n\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tMaxLength: ptr.To[int64](63),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired:    []string{\"storageGB\", \"engineVersion\"},\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Specification of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"storageGB\": {Type: \"integer\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"engineVersion\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.6\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.7\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someField\":      {Type: \"string\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someOtherField\": {Type: \"string\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"crossplane\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription: \"Configures how Crossplane will reconcile this composite resource\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionUpdatePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"resourceRefs\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"atomic\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Cannot change engine version\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"self.engineVersion == oldSelf.engineVersion\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someField\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someOtherField\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"status\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Status of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"phase\":     {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"something\": {Type: \"string\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t\"conditions\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription:  \"Conditions of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:         \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType:    ptr.To(\"map\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListMapKeys: []string{\"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"lastTransitionTime\", \"reason\", \"status\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastTransitionTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"message\":            {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"reason\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"status\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\":               {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"observedGeneration\": {Type: \"integer\", Format: \"int64\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Phase is required once set\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"!has(oldSelf.phase) || has(self.phase)\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"phase\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"something\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\"Legacy\": {\n\t\t\treason: \"A CRD should be generated from a legacy CompositeResourceDefinitionVersion.\",\n\t\t\targs: args{\n\t\t\t\tv: &v1.CompositeResourceValidation{\n\t\t\t\t\tOpenAPIV3Schema: runtime.RawExtension{Raw: []byte(schema)},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tc: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:   name,\n\t\t\t\t\t\tLabels: labels,\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{\n\t\t\t\t\t\t\tmeta.AsController(meta.TypedReferenceTo(d, v1.CompositeResourceDefinitionGroupVersionKind)),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: group,\n\t\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tPlural:     plural,\n\t\t\t\t\t\t\tSingular:   singular,\n\t\t\t\t\t\t\tKind:       kind,\n\t\t\t\t\t\t\tListKind:   listKind,\n\t\t\t\t\t\t\tCategories: []string{CategoryComposite},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tScope: extv1.ClusterScoped,\n\t\t\t\t\t\tVersions: []extv1.CustomResourceDefinitionVersion{{\n\t\t\t\t\t\t\tName:    version,\n\t\t\t\t\t\t\tServed:  true,\n\t\t\t\t\t\t\tStorage: true,\n\t\t\t\t\t\t\tSubresources: &extv1.CustomResourceSubresources{\n\t\t\t\t\t\t\t\tStatus: &extv1.CustomResourceSubresourceStatus{},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tAdditionalPrinterColumns: []extv1.CustomResourceColumnDefinition{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"SYNCED\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"READY\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.compositionRef.name\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITIONREVISION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.compositionRevisionRef.name\",\n\t\t\t\t\t\t\t\t\tPriority: 1,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"AGE\",\n\t\t\t\t\t\t\t\t\tType:     \"date\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tSchema: &extv1.CustomResourceValidation{\n\t\t\t\t\t\t\t\tOpenAPIV3Schema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\tDescription: \"What the resource is for.\",\n\t\t\t\t\t\t\t\t\tRequired:    []string{\"spec\"},\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"kind\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"metadata\": {\n\t\t\t\t\t\t\t\t\t\t\t// NOTE(muvaf): api-server takes care of validating\n\t\t\t\t\t\t\t\t\t\t\t// metadata.\n\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tMaxLength: ptr.To[int64](63),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired:    []string{\"storageGB\", \"engineVersion\"},\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Specification of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t// From CRDSpecTemplate.Validation\n\t\t\t\t\t\t\t\t\t\t\t\t\"storageGB\": {Type: \"integer\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"engineVersion\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.6\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.7\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someField\":      {Type: \"string\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someOtherField\": {Type: \"string\", Description: \"Pretend this is useful.\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceSpecProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionUpdatePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\", \"namespace\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"resourceRefs\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"atomic\"),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"writeConnectionSecretToRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\", \"namespace\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":      {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Cannot change engine version\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"self.engineVersion == oldSelf.engineVersion\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someField\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someOtherField\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"status\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Status of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"phase\":     {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"something\": {Type: \"string\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceStatusProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"conditions\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription:  \"Conditions of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:         \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType:    ptr.To(\"map\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListMapKeys: []string{\"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"lastTransitionTime\", \"reason\", \"status\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastTransitionTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"message\":            {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"reason\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"status\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\":               {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"observedGeneration\": {Type: \"integer\", Format: \"int64\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimConditionTypes\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"set\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"connectionDetails\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastPublishedTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Phase is required once set\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"!has(oldSelf.phase) || has(self.phase)\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"phase\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"something\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\"DefaultCompositionUpdatePolicyIsSet\": {\n\t\t\treason: \"A CRD should be generated from a CompositeResourceDefinitionVersion.\",\n\t\t\targs: args{\n\t\t\t\txrd: &v1.CompositeResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:        name,\n\t\t\t\t\t\tLabels:      labels,\n\t\t\t\t\t\tAnnotations: annotations,\n\t\t\t\t\t\tUID:         types.UID(\"you-you-eye-dee\"),\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: group,\n\t\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tPlural:   plural,\n\t\t\t\t\t\t\tSingular: singular,\n\t\t\t\t\t\t\tKind:     kind,\n\t\t\t\t\t\t\tListKind: listKind,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVersions: []v1.CompositeResourceDefinitionVersion{{\n\t\t\t\t\t\t\tName:          version,\n\t\t\t\t\t\t\tReferenceable: true,\n\t\t\t\t\t\t\tServed:        true,\n\t\t\t\t\t\t}},\n\t\t\t\t\t\tDefaultCompositionUpdatePolicy: &defaultCompositionUpdatePolicy,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tv: &v1.CompositeResourceValidation{\n\t\t\t\t\tOpenAPIV3Schema: runtime.RawExtension{Raw: []byte(schema)},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tc: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:   name,\n\t\t\t\t\t\tLabels: labels,\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{\n\t\t\t\t\t\t\tmeta.AsController(meta.TypedReferenceTo(d, v1.CompositeResourceDefinitionGroupVersionKind)),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: group,\n\t\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tPlural:     plural,\n\t\t\t\t\t\t\tSingular:   singular,\n\t\t\t\t\t\t\tKind:       kind,\n\t\t\t\t\t\t\tListKind:   listKind,\n\t\t\t\t\t\t\tCategories: []string{CategoryComposite},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tScope: extv1.ClusterScoped,\n\t\t\t\t\t\tVersions: []extv1.CustomResourceDefinitionVersion{{\n\t\t\t\t\t\t\tName:    version,\n\t\t\t\t\t\t\tServed:  true,\n\t\t\t\t\t\t\tStorage: true,\n\t\t\t\t\t\t\tSubresources: &extv1.CustomResourceSubresources{\n\t\t\t\t\t\t\t\tStatus: &extv1.CustomResourceSubresourceStatus{},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tAdditionalPrinterColumns: []extv1.CustomResourceColumnDefinition{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"SYNCED\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"READY\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.compositionRef.name\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITIONREVISION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.compositionRevisionRef.name\",\n\t\t\t\t\t\t\t\t\tPriority: 1,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"AGE\",\n\t\t\t\t\t\t\t\t\tType:     \"date\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tSchema: &extv1.CustomResourceValidation{\n\t\t\t\t\t\t\t\tOpenAPIV3Schema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\tDescription: \"What the resource is for.\",\n\t\t\t\t\t\t\t\t\tRequired:    []string{\"spec\"},\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"kind\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"metadata\": {\n\t\t\t\t\t\t\t\t\t\t\t// NOTE(muvaf): api-server takes care of validating\n\t\t\t\t\t\t\t\t\t\t\t// metadata.\n\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tMaxLength: ptr.To[int64](63),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired:    []string{\"storageGB\", \"engineVersion\"},\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Specification of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t// From CRDSpecTemplate.Validation\n\t\t\t\t\t\t\t\t\t\t\t\t\"storageGB\": {Type: \"integer\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"engineVersion\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.6\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.7\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someField\":      {Type: \"string\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someOtherField\": {Type: \"string\", Description: \"Pretend this is useful.\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceSpecProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionUpdatePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:    \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tDefault: &extv1.JSON{Raw: fmt.Appendf(nil, \"\\\"%s\\\"\", defaultCompositionUpdatePolicy)},\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\", \"namespace\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"resourceRefs\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"atomic\"),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"writeConnectionSecretToRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\", \"namespace\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":      {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Cannot change engine version\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"self.engineVersion == oldSelf.engineVersion\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someField\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someOtherField\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"status\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Status of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"phase\":     {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"something\": {Type: \"string\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceStatusProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"conditions\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription:  \"Conditions of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:         \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType:    ptr.To(\"map\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListMapKeys: []string{\"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"lastTransitionTime\", \"reason\", \"status\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastTransitionTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"message\":            {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"reason\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"status\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\":               {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"observedGeneration\": {Type: \"integer\", Format: \"int64\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimConditionTypes\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"set\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"connectionDetails\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastPublishedTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Phase is required once set\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"!has(oldSelf.phase) || has(self.phase)\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"phase\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"something\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\"EmptyOpenAPIV3Schema\": {\n\t\t\treason: \"A CRD should be generated from a CompositeResourceDefinitionVersion when schema is empty.\",\n\t\t\targs: args{\n\t\t\t\tv: &v1.CompositeResourceValidation{\n\t\t\t\t\tOpenAPIV3Schema: runtime.RawExtension{Raw: []byte(`{}`)},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tc: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:   name,\n\t\t\t\t\t\tLabels: labels,\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{\n\t\t\t\t\t\t\tmeta.AsController(meta.TypedReferenceTo(d, v1.CompositeResourceDefinitionGroupVersionKind)),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: group,\n\t\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tPlural:     plural,\n\t\t\t\t\t\t\tSingular:   singular,\n\t\t\t\t\t\t\tKind:       kind,\n\t\t\t\t\t\t\tListKind:   listKind,\n\t\t\t\t\t\t\tCategories: []string{CategoryComposite},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tScope: extv1.ClusterScoped,\n\t\t\t\t\t\tVersions: []extv1.CustomResourceDefinitionVersion{{\n\t\t\t\t\t\t\tName:    version,\n\t\t\t\t\t\t\tServed:  true,\n\t\t\t\t\t\t\tStorage: true,\n\t\t\t\t\t\t\tSubresources: &extv1.CustomResourceSubresources{\n\t\t\t\t\t\t\t\tStatus: &extv1.CustomResourceSubresourceStatus{},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tAdditionalPrinterColumns: []extv1.CustomResourceColumnDefinition{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"SYNCED\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"READY\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.compositionRef.name\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITIONREVISION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.compositionRevisionRef.name\",\n\t\t\t\t\t\t\t\t\tPriority: 1,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"AGE\",\n\t\t\t\t\t\t\t\t\tType:     \"date\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tSchema: &extv1.CustomResourceValidation{\n\t\t\t\t\t\t\t\tOpenAPIV3Schema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\tDescription: \"\",\n\t\t\t\t\t\t\t\t\tRequired:    []string{\"spec\"},\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"kind\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"metadata\": {\n\t\t\t\t\t\t\t\t\t\t\t// NOTE(muvaf): api-server takes care of validating\n\t\t\t\t\t\t\t\t\t\t\t// metadata.\n\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tMaxLength: ptr.To[int64](63),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceSpecProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionUpdatePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\", \"namespace\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"resourceRefs\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"atomic\"),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"writeConnectionSecretToRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\", \"namespace\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":      {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"status\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceStatusProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"conditions\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription:  \"Conditions of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:         \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType:    ptr.To(\"map\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListMapKeys: []string{\"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"lastTransitionTime\", \"reason\", \"status\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastTransitionTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"message\":            {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"reason\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"status\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\":               {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"observedGeneration\": {Type: \"integer\", Format: \"int64\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimConditionTypes\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"set\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"connectionDetails\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastPublishedTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\"RestrictingNameLength\": {\n\t\t\treason: \"A CRD should be generated from a CompositeResourceDefinitionVersion.\",\n\t\t\targs: args{\n\t\t\t\tv: &v1.CompositeResourceValidation{\n\t\t\t\t\tOpenAPIV3Schema: runtime.RawExtension{Raw: []byte(strings.Replace(schema, `\"spec\":`, `\"metadata\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"maxLength\":10}}},\"spec\":`, 1))},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tc: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:   name,\n\t\t\t\t\t\tLabels: labels,\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{\n\t\t\t\t\t\t\tmeta.AsController(meta.TypedReferenceTo(d, v1.CompositeResourceDefinitionGroupVersionKind)),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: group,\n\t\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tPlural:     plural,\n\t\t\t\t\t\t\tSingular:   singular,\n\t\t\t\t\t\t\tKind:       kind,\n\t\t\t\t\t\t\tListKind:   listKind,\n\t\t\t\t\t\t\tCategories: []string{CategoryComposite},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tScope: extv1.ClusterScoped,\n\t\t\t\t\t\tVersions: []extv1.CustomResourceDefinitionVersion{{\n\t\t\t\t\t\t\tName:    version,\n\t\t\t\t\t\t\tServed:  true,\n\t\t\t\t\t\t\tStorage: true,\n\t\t\t\t\t\t\tSubresources: &extv1.CustomResourceSubresources{\n\t\t\t\t\t\t\t\tStatus: &extv1.CustomResourceSubresourceStatus{},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tAdditionalPrinterColumns: []extv1.CustomResourceColumnDefinition{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"SYNCED\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"READY\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.compositionRef.name\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITIONREVISION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.compositionRevisionRef.name\",\n\t\t\t\t\t\t\t\t\tPriority: 1,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"AGE\",\n\t\t\t\t\t\t\t\t\tType:     \"date\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tSchema: &extv1.CustomResourceValidation{\n\t\t\t\t\t\t\t\tOpenAPIV3Schema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\tDescription: \"What the resource is for.\",\n\t\t\t\t\t\t\t\t\tRequired:    []string{\"spec\"},\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"kind\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"metadata\": {\n\t\t\t\t\t\t\t\t\t\t\t// NOTE(muvaf): api-server takes care of validating\n\t\t\t\t\t\t\t\t\t\t\t// metadata.\n\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tMaxLength: ptr.To[int64](10),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired:    []string{\"storageGB\", \"engineVersion\"},\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Specification of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t// From CRDSpecTemplate.Validation\n\t\t\t\t\t\t\t\t\t\t\t\t\"storageGB\": {Type: \"integer\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"engineVersion\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.6\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.7\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someField\":      {Type: \"string\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someOtherField\": {Type: \"string\", Description: \"Pretend this is useful.\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceSpecProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionUpdatePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\", \"namespace\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"resourceRefs\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"atomic\"),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"writeConnectionSecretToRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\", \"namespace\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":      {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Cannot change engine version\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"self.engineVersion == oldSelf.engineVersion\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someField\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someOtherField\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"status\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Status of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"phase\":     {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"something\": {Type: \"string\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceStatusProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"conditions\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription:  \"Conditions of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:         \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType:    ptr.To(\"map\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListMapKeys: []string{\"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"lastTransitionTime\", \"reason\", \"status\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastTransitionTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"message\":            {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"reason\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"status\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\":               {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"observedGeneration\": {Type: \"integer\", Format: \"int64\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimConditionTypes\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"set\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"connectionDetails\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastPublishedTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Phase is required once set\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"!has(oldSelf.phase) || has(self.phase)\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"phase\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"something\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\"WeaklyRestrictingNameLength\": {\n\t\t\treason: \"A CRD should be generated from a CompositeResourceDefinitionVersion.\",\n\t\t\targs: args{\n\t\t\t\tv: &v1.CompositeResourceValidation{\n\t\t\t\t\tOpenAPIV3Schema: runtime.RawExtension{Raw: []byte(strings.Replace(schema, `\"spec\":`, `\"metadata\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"maxLength\":100}}},\"spec\":`, 1))},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tc: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:   name,\n\t\t\t\t\t\tLabels: labels,\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{\n\t\t\t\t\t\t\tmeta.AsController(meta.TypedReferenceTo(d, v1.CompositeResourceDefinitionGroupVersionKind)),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: group,\n\t\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tPlural:     plural,\n\t\t\t\t\t\t\tSingular:   singular,\n\t\t\t\t\t\t\tKind:       kind,\n\t\t\t\t\t\t\tListKind:   listKind,\n\t\t\t\t\t\t\tCategories: []string{CategoryComposite},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tScope: extv1.ClusterScoped,\n\t\t\t\t\t\tVersions: []extv1.CustomResourceDefinitionVersion{{\n\t\t\t\t\t\t\tName:    version,\n\t\t\t\t\t\t\tServed:  true,\n\t\t\t\t\t\t\tStorage: true,\n\t\t\t\t\t\t\tSubresources: &extv1.CustomResourceSubresources{\n\t\t\t\t\t\t\t\tStatus: &extv1.CustomResourceSubresourceStatus{},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tAdditionalPrinterColumns: []extv1.CustomResourceColumnDefinition{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"SYNCED\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"READY\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.compositionRef.name\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITIONREVISION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.compositionRevisionRef.name\",\n\t\t\t\t\t\t\t\t\tPriority: 1,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"AGE\",\n\t\t\t\t\t\t\t\t\tType:     \"date\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tSchema: &extv1.CustomResourceValidation{\n\t\t\t\t\t\t\t\tOpenAPIV3Schema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\tDescription: \"What the resource is for.\",\n\t\t\t\t\t\t\t\t\tRequired:    []string{\"spec\"},\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"kind\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"metadata\": {\n\t\t\t\t\t\t\t\t\t\t\t// NOTE(muvaf): api-server takes care of validating\n\t\t\t\t\t\t\t\t\t\t\t// metadata.\n\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tMaxLength: ptr.To[int64](63),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired:    []string{\"storageGB\", \"engineVersion\"},\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Specification of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t// From CRDSpecTemplate.Validation\n\t\t\t\t\t\t\t\t\t\t\t\t\"storageGB\": {Type: \"integer\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"engineVersion\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.6\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.7\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someField\":      {Type: \"string\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someOtherField\": {Type: \"string\", Description: \"Pretend this is useful.\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceSpecProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionUpdatePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\", \"namespace\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"resourceRefs\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"atomic\"),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"writeConnectionSecretToRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\", \"namespace\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":      {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Cannot change engine version\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"self.engineVersion == oldSelf.engineVersion\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someField\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someOtherField\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"status\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Status of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"phase\":     {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"something\": {Type: \"string\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceStatusProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"conditions\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription:  \"Conditions of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:         \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType:    ptr.To(\"map\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListMapKeys: []string{\"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"lastTransitionTime\", \"reason\", \"status\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastTransitionTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"message\":            {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"reason\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"status\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\":               {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"observedGeneration\": {Type: \"integer\", Format: \"int64\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimConditionTypes\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"set\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"connectionDetails\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastPublishedTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Phase is required once set\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"!has(oldSelf.phase) || has(self.phase)\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"phase\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"something\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\"NilCompositeResourceValidation\": {\n\t\t\treason: \"Error should be returned if composite resource validation is nil.\",\n\t\t\targs: args{\n\t\t\t\tv: nil,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errors.New(errCustomResourceValidationNil), fmt.Sprintf(errFmtGenCrd, \"Composite Resource\", name)),\n\t\t\t\tc:   nil,\n\t\t\t},\n\t\t},\n\t\t\"PreserveUnknownFieldsInSpec\": {\n\t\t\treason: \"A CRD should set PreserveUnknownFields based on the XRD PreserveUnknownFields.\",\n\t\t\targs: args{\n\t\t\t\tv: &v1.CompositeResourceValidation{\n\t\t\t\t\tOpenAPIV3Schema: runtime.RawExtension{Raw: []byte(strings.Replace(schema, `\"spec\": {`, `\"spec\": { \"x-kubernetes-preserve-unknown-fields\": true,`, 1))},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tc: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:   name,\n\t\t\t\t\t\tLabels: labels,\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{\n\t\t\t\t\t\t\tmeta.AsController(meta.TypedReferenceTo(d, v1.CompositeResourceDefinitionGroupVersionKind)),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: group,\n\t\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tPlural:     plural,\n\t\t\t\t\t\t\tSingular:   singular,\n\t\t\t\t\t\t\tKind:       kind,\n\t\t\t\t\t\t\tListKind:   listKind,\n\t\t\t\t\t\t\tCategories: []string{CategoryComposite},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tScope: extv1.ClusterScoped,\n\t\t\t\t\t\tVersions: []extv1.CustomResourceDefinitionVersion{{\n\t\t\t\t\t\t\tName:    version,\n\t\t\t\t\t\t\tServed:  true,\n\t\t\t\t\t\t\tStorage: true,\n\t\t\t\t\t\t\tSubresources: &extv1.CustomResourceSubresources{\n\t\t\t\t\t\t\t\tStatus: &extv1.CustomResourceSubresourceStatus{},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tAdditionalPrinterColumns: []extv1.CustomResourceColumnDefinition{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"SYNCED\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"READY\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.compositionRef.name\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITIONREVISION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.compositionRevisionRef.name\",\n\t\t\t\t\t\t\t\t\tPriority: 1,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"AGE\",\n\t\t\t\t\t\t\t\t\tType:     \"date\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tSchema: &extv1.CustomResourceValidation{\n\t\t\t\t\t\t\t\tOpenAPIV3Schema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\tDescription: \"What the resource is for.\",\n\t\t\t\t\t\t\t\t\tRequired:    []string{\"spec\"},\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"kind\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"metadata\": {\n\t\t\t\t\t\t\t\t\t\t\t// NOTE(muvaf): api-server takes care of validating\n\t\t\t\t\t\t\t\t\t\t\t// metadata.\n\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tMaxLength: ptr.To[int64](63),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\t\t\t\t\t\tType:                   \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired:               []string{\"storageGB\", \"engineVersion\"},\n\t\t\t\t\t\t\t\t\t\t\tDescription:            \"Specification of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tXPreserveUnknownFields: ptr.To(true),\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t// From CRDSpecTemplate.Validation\n\t\t\t\t\t\t\t\t\t\t\t\t\"storageGB\": {Type: \"integer\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"engineVersion\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.6\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.7\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someField\":      {Type: \"string\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someOtherField\": {Type: \"string\", Description: \"Pretend this is useful.\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceSpecProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionUpdatePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\", \"namespace\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"resourceRefs\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"atomic\"),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"writeConnectionSecretToRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\", \"namespace\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":      {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"namespace\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Cannot change engine version\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"self.engineVersion == oldSelf.engineVersion\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someField\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someOtherField\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"status\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Status of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"phase\":     {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"something\": {Type: \"string\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceStatusProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"conditions\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription:  \"Conditions of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:         \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType:    ptr.To(\"map\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListMapKeys: []string{\"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"lastTransitionTime\", \"reason\", \"status\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastTransitionTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"message\":            {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"reason\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"status\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\":               {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"observedGeneration\": {Type: \"integer\", Format: \"int64\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimConditionTypes\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"set\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"connectionDetails\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastPublishedTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Phase is required once set\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"!has(oldSelf.phase) || has(self.phase)\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"phase\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"something\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\"PreserveTopLevelXValidations\": {\n\t\t\treason: \"A CRD should be generated with top-level x-kubernetes-validations outside of spec.\",\n\t\t\targs: args{\n\t\t\t\txrd: &v1.CompositeResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:        name,\n\t\t\t\t\t\tLabels:      labels,\n\t\t\t\t\t\tAnnotations: annotations,\n\t\t\t\t\t\tUID:         types.UID(\"you-you-eye-dee\"),\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{\n\t\t\t\t\t\tScope: ptr.To(v1.CompositeResourceScopeNamespaced),\n\t\t\t\t\t\tGroup: group,\n\t\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tPlural:   plural,\n\t\t\t\t\t\t\tSingular: singular,\n\t\t\t\t\t\t\tKind:     kind,\n\t\t\t\t\t\t\tListKind: listKind,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVersions: []v1.CompositeResourceDefinitionVersion{{\n\t\t\t\t\t\t\tName:          version,\n\t\t\t\t\t\t\tReferenceable: true,\n\t\t\t\t\t\t\tServed:        true,\n\t\t\t\t\t\t}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tv: &v1.CompositeResourceValidation{\n\t\t\t\t\tOpenAPIV3Schema: runtime.RawExtension{Raw: []byte(strings.Replace(schema, `\"properties\":`, `\"x-kubernetes-validations\":[{\"rule\":\"self.metadata.name == ('database-' + self.spec.engineVersion)\",\"message\":\"metadata.name must be database-spec.engineVersion\"}],\"properties\":`, 1))},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tc: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:   name,\n\t\t\t\t\t\tLabels: labels,\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{\n\t\t\t\t\t\t\tmeta.AsController(meta.TypedReferenceTo(d, v1.CompositeResourceDefinitionGroupVersionKind)),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: group,\n\t\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tPlural:     plural,\n\t\t\t\t\t\t\tSingular:   singular,\n\t\t\t\t\t\t\tKind:       kind,\n\t\t\t\t\t\t\tListKind:   listKind,\n\t\t\t\t\t\t\tCategories: []string{CategoryComposite},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tScope: extv1.NamespaceScoped,\n\t\t\t\t\t\tVersions: []extv1.CustomResourceDefinitionVersion{{\n\t\t\t\t\t\t\tName:    version,\n\t\t\t\t\t\t\tServed:  true,\n\t\t\t\t\t\t\tStorage: true,\n\t\t\t\t\t\t\tSubresources: &extv1.CustomResourceSubresources{\n\t\t\t\t\t\t\t\tStatus: &extv1.CustomResourceSubresourceStatus{},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tAdditionalPrinterColumns: []extv1.CustomResourceColumnDefinition{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"SYNCED\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"READY\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.crossplane.compositionRef.name\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITIONREVISION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.crossplane.compositionRevisionRef.name\",\n\t\t\t\t\t\t\t\t\tPriority: 1,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"AGE\",\n\t\t\t\t\t\t\t\t\tType:     \"date\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tSchema: &extv1.CustomResourceValidation{\n\t\t\t\t\t\t\t\tOpenAPIV3Schema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\tDescription: \"What the resource is for.\",\n\t\t\t\t\t\t\t\t\tRequired:    []string{\"spec\"},\n\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tRule:    \"self.metadata.name == ('database-' + self.spec.engineVersion)\",\n\t\t\t\t\t\t\t\t\t\t\tMessage: \"metadata.name must be database-spec.engineVersion\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"kind\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"metadata\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tMaxLength: ptr.To[int64](63),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired:    []string{\"storageGB\", \"engineVersion\"},\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Specification of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"storageGB\": {Type: \"integer\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"engineVersion\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.6\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.7\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someField\":      {Type: \"string\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someOtherField\": {Type: \"string\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"crossplane\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription: \"Configures how Crossplane will reconcile this composite resource\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionUpdatePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"resourceRefs\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"atomic\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Cannot change engine version\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"self.engineVersion == oldSelf.engineVersion\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someField\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someOtherField\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"status\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Status of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"phase\":     {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"something\": {Type: \"string\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t\"conditions\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription:  \"Conditions of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:         \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType:    ptr.To(\"map\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListMapKeys: []string{\"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"lastTransitionTime\", \"reason\", \"status\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastTransitionTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"message\":            {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"reason\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"status\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\":               {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"observedGeneration\": {Type: \"integer\", Format: \"int64\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Phase is required once set\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"!has(oldSelf.phase) || has(self.phase)\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"phase\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"something\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\"SubresourcesScaleIsSet\": {\n\t\t\treason: \"A CRD should set Scale subresource based on the XRD subresources.\",\n\t\t\targs: args{\n\t\t\t\txrd: &v1.CompositeResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:        name,\n\t\t\t\t\t\tLabels:      labels,\n\t\t\t\t\t\tAnnotations: annotations,\n\t\t\t\t\t\tUID:         types.UID(\"you-you-eye-dee\"),\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{\n\t\t\t\t\t\tScope: ptr.To(v1.CompositeResourceScopeNamespaced),\n\t\t\t\t\t\tGroup: group,\n\t\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tPlural:   plural,\n\t\t\t\t\t\t\tSingular: singular,\n\t\t\t\t\t\t\tKind:     kind,\n\t\t\t\t\t\t\tListKind: listKind,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVersions: []v1.CompositeResourceDefinitionVersion{{\n\t\t\t\t\t\t\tName:          version,\n\t\t\t\t\t\t\tReferenceable: true,\n\t\t\t\t\t\t\tServed:        true,\n\t\t\t\t\t\t\tSubresources: &v1.CompositeResourceDefinitionVersionSubresources{\n\t\t\t\t\t\t\t\tScale: &extv1.CustomResourceSubresourceScale{\n\t\t\t\t\t\t\t\t\tSpecReplicasPath:   \"spec.replicas\",\n\t\t\t\t\t\t\t\t\tStatusReplicasPath: \"status.replicas\",\n\t\t\t\t\t\t\t\t\tLabelSelectorPath:  ptr.To(\"status.labelSelector\"),\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\tv: &v1.CompositeResourceValidation{\n\t\t\t\t\tOpenAPIV3Schema: runtime.RawExtension{Raw: []byte(schema)},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tc: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName:   name,\n\t\t\t\t\t\tLabels: labels,\n\t\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{\n\t\t\t\t\t\t\tmeta.AsController(meta.TypedReferenceTo(d, v1.CompositeResourceDefinitionGroupVersionKind)),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\t\t\t\tGroup: group,\n\t\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\t\tPlural:     plural,\n\t\t\t\t\t\t\tSingular:   singular,\n\t\t\t\t\t\t\tKind:       kind,\n\t\t\t\t\t\t\tListKind:   listKind,\n\t\t\t\t\t\t\tCategories: []string{CategoryComposite},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tScope: extv1.NamespaceScoped,\n\t\t\t\t\t\tVersions: []extv1.CustomResourceDefinitionVersion{{\n\t\t\t\t\t\t\tName:    version,\n\t\t\t\t\t\t\tServed:  true,\n\t\t\t\t\t\t\tStorage: true,\n\t\t\t\t\t\t\tSubresources: &extv1.CustomResourceSubresources{\n\t\t\t\t\t\t\t\tStatus: &extv1.CustomResourceSubresourceStatus{},\n\t\t\t\t\t\t\t\tScale: &extv1.CustomResourceSubresourceScale{\n\t\t\t\t\t\t\t\t\tSpecReplicasPath:   \"spec.replicas\",\n\t\t\t\t\t\t\t\t\tStatusReplicasPath: \"status.replicas\",\n\t\t\t\t\t\t\t\t\tLabelSelectorPath:  ptr.To(\"status.labelSelector\"),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tAdditionalPrinterColumns: []extv1.CustomResourceColumnDefinition{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"SYNCED\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"READY\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.crossplane.compositionRef.name\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"COMPOSITIONREVISION\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.crossplane.compositionRevisionRef.name\",\n\t\t\t\t\t\t\t\t\tPriority: 1,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"AGE\",\n\t\t\t\t\t\t\t\t\tType:     \"date\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tSchema: &extv1.CustomResourceValidation{\n\t\t\t\t\t\t\t\tOpenAPIV3Schema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\tDescription: \"What the resource is for.\",\n\t\t\t\t\t\t\t\t\tRequired:    []string{\"spec\"},\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"kind\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"metadata\": {\n\t\t\t\t\t\t\t\t\t\t\t// NOTE(muvaf): api-server takes care of validating\n\t\t\t\t\t\t\t\t\t\t\t// metadata.\n\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tMaxLength: ptr.To[int64](63),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired:    []string{\"storageGB\", \"engineVersion\"},\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Specification of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"storageGB\": {Type: \"integer\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"engineVersion\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.6\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.7\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someField\":      {Type: \"string\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"someOtherField\": {Type: \"string\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"crossplane\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription: \"Configures how Crossplane will reconcile this composite resource\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"compositionUpdatePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"resourceRefs\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"atomic\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Cannot change engine version\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"self.engineVersion == oldSelf.engineVersion\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someField\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"someOtherField\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"status\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Status of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"phase\":     {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"something\": {Type: \"string\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t\"conditions\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription:  \"Conditions of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:         \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType:    ptr.To(\"map\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListMapKeys: []string{\"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"lastTransitionTime\", \"reason\", \"status\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastTransitionTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"message\":            {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"reason\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"status\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\":               {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"observedGeneration\": {Type: \"integer\", Format: \"int64\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Phase is required once set\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"!has(oldSelf.phase) || has(self.phase)\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tOneOf: []extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"phase\"}},\n\t\t\t\t\t\t\t\t\t\t\t\t{Required: []string{\"something\"}},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\t// TODO(negz): This is surprising - refactor it. We should always\n\t\t\t// pass the xrd as an argument, not default it here. Same with the\n\t\t\t// version.\n\t\t\tvar xrd *v1.CompositeResourceDefinition\n\t\t\tif tc.args.xrd != nil {\n\t\t\t\txrd = tc.args.xrd\n\t\t\t} else {\n\t\t\t\txrd = d\n\t\t\t}\n\n\t\t\txrd.Spec.Versions[0].Schema = tc.args.v\n\n\t\t\tgot, err := ForCompositeResource(xrd)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nForCompositeResource(...): -want err, +got err:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.c, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"ForCompositeResource(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestValidateClaimNames(t *testing.T) {\n\tcases := map[string]struct {\n\t\td    *v1.CompositeResourceDefinition\n\t\twant error\n\t}{\n\t\t\"MissingClaimNames\": {\n\t\t\td:    &v1.CompositeResourceDefinition{},\n\t\t\twant: errors.New(errMissingClaimNames),\n\t\t},\n\t\t\"KindConflict\": {\n\t\t\td: &v1.CompositeResourceDefinition{\n\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{\n\t\t\t\t\tClaimNames: &extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tKind:     \"a\",\n\t\t\t\t\t\tListKind: \"a\",\n\t\t\t\t\t\tSingular: \"a\",\n\t\t\t\t\t\tPlural:   \"a\",\n\t\t\t\t\t},\n\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tKind:     \"a\",\n\t\t\t\t\t\tListKind: \"b\",\n\t\t\t\t\t\tSingular: \"b\",\n\t\t\t\t\t\tPlural:   \"b\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: errors.Errorf(errFmtConflictingClaimName, \"a\"),\n\t\t},\n\t\t\"ListKindConflict\": {\n\t\t\td: &v1.CompositeResourceDefinition{\n\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{\n\t\t\t\t\tClaimNames: &extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tKind:     \"a\",\n\t\t\t\t\t\tListKind: \"a\",\n\t\t\t\t\t\tSingular: \"a\",\n\t\t\t\t\t\tPlural:   \"a\",\n\t\t\t\t\t},\n\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tKind:     \"b\",\n\t\t\t\t\t\tListKind: \"a\",\n\t\t\t\t\t\tSingular: \"b\",\n\t\t\t\t\t\tPlural:   \"b\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: errors.Errorf(errFmtConflictingClaimName, \"a\"),\n\t\t},\n\t\t\"SingularConflict\": {\n\t\t\td: &v1.CompositeResourceDefinition{\n\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{\n\t\t\t\t\tClaimNames: &extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tKind:     \"a\",\n\t\t\t\t\t\tListKind: \"a\",\n\t\t\t\t\t\tSingular: \"a\",\n\t\t\t\t\t\tPlural:   \"a\",\n\t\t\t\t\t},\n\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tKind:     \"b\",\n\t\t\t\t\t\tListKind: \"b\",\n\t\t\t\t\t\tSingular: \"a\",\n\t\t\t\t\t\tPlural:   \"b\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: errors.Errorf(errFmtConflictingClaimName, \"a\"),\n\t\t},\n\t\t\"PluralConflict\": {\n\t\t\td: &v1.CompositeResourceDefinition{\n\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{\n\t\t\t\t\tClaimNames: &extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tKind:     \"a\",\n\t\t\t\t\t\tListKind: \"a\",\n\t\t\t\t\t\tSingular: \"a\",\n\t\t\t\t\t\tPlural:   \"a\",\n\t\t\t\t\t},\n\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tKind:       \"b\",\n\t\t\t\t\t\tListKind:   \"b\",\n\t\t\t\t\t\tSingular:   \"b\",\n\t\t\t\t\t\tPlural:     \"a\",\n\t\t\t\t\t\tCategories: []string{CategoryClaim},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: errors.Errorf(errFmtConflictingClaimName, \"a\"),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := validateClaimNames(tc.d)\n\t\t\tif diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"validateClaimNames(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestForCompositeResourceClaim(t *testing.T) {\n\tname := \"coolcomposites.example.org\"\n\tlabels := map[string]string{\"cool\": \"very\"}\n\tannotations := map[string]string{\"example.org/cool\": \"very\"}\n\n\tgroup := \"example.org\"\n\tversion := \"v1\"\n\n\tkind := \"CoolComposite\"\n\tlistKind := \"CoolCompositeList\"\n\tsingular := \"coolcomposite\"\n\tplural := \"coolcomposites\"\n\n\tclaimKind := \"CoolClaim\"\n\tclaimListKind := \"CoolClaimList\"\n\tclaimSingular := \"coolclaim\"\n\tclaimPlural := \"coolclaims\"\n\n\tdefaultPolicy := xpv2.CompositeDeletePolicy(\"Background\")\n\tschema := `\n{\n\t\"properties\": {\n\t\t\"spec\": {\n\t\t\t\"description\": \"Specification of the resource.\",\n\t\t\t\"required\": [\n\t\t\t\t\"storageGB\",\n\t\t\t\t\"engineVersion\"\n\t\t\t],\n\t\t\t\"properties\": {\n\t\t\t\t\"engineVersion\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"5.6\",\n\t\t\t\t\t\t\"5.7\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"storageGB\": {\n\t\t\t\t\t\"type\": \"integer\",\n\t\t\t\t\t\"description\": \"Pretend this is useful.\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"x-kubernetes-validations\": [\n\t\t\t\t{\n\t\t\t\t\t\"message\": \"Cannot change engine version\",\n\t\t\t\t\t\"rule\": \"self.engineVersion == oldSelf.engineVersion\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"status\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"phase\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"x-kubernetes-validations\": [\n\t\t\t\t{\n\t\t\t\t\t\"message\": \"Phase is required once set\",\n\t\t\t\t\t\"rule\": \"!has(oldSelf.phase) || has(self.phase)\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"type\": \"object\",\n\t\t\t\"description\": \"Status of the resource.\"\n\t\t}\n\t},\n\t\"type\": \"object\",\n\t\"description\": \"Description of the resource.\"\n}`\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tcrd    *v1.CompositeResourceDefinition\n\t\twant   *extv1.CustomResourceDefinition\n\t}{\n\t\t\"CompositeDeletionPolicyUnspecified\": {\n\t\t\treason: \"If default composite deletion unspecified on XRD, set no default value on claim's spec.compositeDeletionPolicy\",\n\t\t\tcrd: &v1.CompositeResourceDefinition{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:        name,\n\t\t\t\t\tLabels:      labels,\n\t\t\t\t\tAnnotations: annotations,\n\t\t\t\t\tUID:         types.UID(\"you-you-eye-dee\"),\n\t\t\t\t},\n\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{\n\t\t\t\t\tGroup: group,\n\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tPlural:   plural,\n\t\t\t\t\t\tSingular: singular,\n\t\t\t\t\t\tKind:     kind,\n\t\t\t\t\t\tListKind: listKind,\n\t\t\t\t\t},\n\t\t\t\t\tClaimNames: &extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tPlural:   claimPlural,\n\t\t\t\t\t\tSingular: claimSingular,\n\t\t\t\t\t\tKind:     claimKind,\n\t\t\t\t\t\tListKind: claimListKind,\n\t\t\t\t\t},\n\t\t\t\t\tVersions: []v1.CompositeResourceDefinitionVersion{{\n\t\t\t\t\t\tName:          version,\n\t\t\t\t\t\tReferenceable: true,\n\t\t\t\t\t\tServed:        true,\n\t\t\t\t\t\tSchema: &v1.CompositeResourceValidation{\n\t\t\t\t\t\t\tOpenAPIV3Schema: runtime.RawExtension{Raw: []byte(schema)},\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\twant: &extv1.CustomResourceDefinition{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:   claimPlural + \".\" + group,\n\t\t\t\t\tLabels: labels,\n\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{\n\t\t\t\t\t\tmeta.AsController(meta.TypedReferenceTo(d, v1.CompositeResourceDefinitionGroupVersionKind)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\t\t\tGroup: group,\n\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tPlural:     claimPlural,\n\t\t\t\t\t\tSingular:   claimSingular,\n\t\t\t\t\t\tKind:       claimKind,\n\t\t\t\t\t\tListKind:   claimListKind,\n\t\t\t\t\t\tCategories: []string{CategoryClaim},\n\t\t\t\t\t},\n\t\t\t\t\tScope: extv1.NamespaceScoped,\n\t\t\t\t\tVersions: []extv1.CustomResourceDefinitionVersion{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:    version,\n\t\t\t\t\t\t\tServed:  true,\n\t\t\t\t\t\t\tStorage: true,\n\t\t\t\t\t\t\tSubresources: &extv1.CustomResourceSubresources{\n\t\t\t\t\t\t\t\tStatus: &extv1.CustomResourceSubresourceStatus{},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tAdditionalPrinterColumns: []extv1.CustomResourceColumnDefinition{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"SYNCED\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"READY\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"CONNECTION-SECRET\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.writeConnectionSecretToRef.name\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"AGE\",\n\t\t\t\t\t\t\t\t\tType:     \"date\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tSchema: &extv1.CustomResourceValidation{\n\t\t\t\t\t\t\t\tOpenAPIV3Schema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\tRequired:    []string{\"spec\"},\n\t\t\t\t\t\t\t\t\tDescription: \"Description of the resource.\",\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"kind\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"metadata\": {\n\t\t\t\t\t\t\t\t\t\t\t// NOTE(muvaf): api-server takes care of validating\n\t\t\t\t\t\t\t\t\t\t\t// metadata.\n\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tMaxLength: ptr.To[int64](63),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired:    []string{\"storageGB\", \"engineVersion\"},\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Specification of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t// From CRDSpecTemplate.Validation\n\t\t\t\t\t\t\t\t\t\t\t\t\"storageGB\": {Type: \"integer\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"engineVersion\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.6\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.7\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositeDeletePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Background\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Foreground\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceClaimSpecProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionUpdatePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"resourceRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"writeConnectionSecretToRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Cannot change engine version\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"self.engineVersion == oldSelf.engineVersion\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"status\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Status of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"phase\": {Type: \"string\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceStatusProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"conditions\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription:  \"Conditions of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:         \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType:    ptr.To(\"map\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListMapKeys: []string{\"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"lastTransitionTime\", \"reason\", \"status\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastTransitionTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"message\":            {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"reason\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"status\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\":               {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"observedGeneration\": {Type: \"integer\", Format: \"int64\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimConditionTypes\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"set\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"connectionDetails\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastPublishedTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Phase is required once set\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"!has(oldSelf.phase) || has(self.phase)\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\"CompositeDeletionPolicySetToDefault\": {\n\t\t\treason: \"Propagate default composite deletion set on XRD as the default value on claim's spec.compositeDeletionPolicy\",\n\t\t\tcrd: &v1.CompositeResourceDefinition{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:        name,\n\t\t\t\t\tLabels:      labels,\n\t\t\t\t\tAnnotations: annotations,\n\t\t\t\t\tUID:         types.UID(\"you-you-eye-dee\"),\n\t\t\t\t},\n\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{\n\t\t\t\t\tGroup:                        group,\n\t\t\t\t\tDefaultCompositeDeletePolicy: &defaultPolicy,\n\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tPlural:   plural,\n\t\t\t\t\t\tSingular: singular,\n\t\t\t\t\t\tKind:     kind,\n\t\t\t\t\t\tListKind: listKind,\n\t\t\t\t\t},\n\t\t\t\t\tClaimNames: &extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tPlural:   claimPlural,\n\t\t\t\t\t\tSingular: claimSingular,\n\t\t\t\t\t\tKind:     claimKind,\n\t\t\t\t\t\tListKind: claimListKind,\n\t\t\t\t\t},\n\t\t\t\t\tVersions: []v1.CompositeResourceDefinitionVersion{{\n\t\t\t\t\t\tName:          version,\n\t\t\t\t\t\tReferenceable: true,\n\t\t\t\t\t\tServed:        true,\n\t\t\t\t\t\tSchema: &v1.CompositeResourceValidation{\n\t\t\t\t\t\t\tOpenAPIV3Schema: runtime.RawExtension{Raw: []byte(schema)},\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\twant: &extv1.CustomResourceDefinition{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:   claimPlural + \".\" + group,\n\t\t\t\t\tLabels: labels,\n\t\t\t\t\tOwnerReferences: []metav1.OwnerReference{\n\t\t\t\t\t\tmeta.AsController(meta.TypedReferenceTo(d, v1.CompositeResourceDefinitionGroupVersionKind)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\t\t\tGroup: group,\n\t\t\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\t\t\tPlural:     claimPlural,\n\t\t\t\t\t\tSingular:   claimSingular,\n\t\t\t\t\t\tKind:       claimKind,\n\t\t\t\t\t\tListKind:   claimListKind,\n\t\t\t\t\t\tCategories: []string{CategoryClaim},\n\t\t\t\t\t},\n\t\t\t\t\tScope: extv1.NamespaceScoped,\n\t\t\t\t\tVersions: []extv1.CustomResourceDefinitionVersion{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:    version,\n\t\t\t\t\t\t\tServed:  true,\n\t\t\t\t\t\t\tStorage: true,\n\t\t\t\t\t\t\tSubresources: &extv1.CustomResourceSubresources{\n\t\t\t\t\t\t\t\tStatus: &extv1.CustomResourceSubresourceStatus{},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tAdditionalPrinterColumns: []extv1.CustomResourceColumnDefinition{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"SYNCED\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"READY\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"CONNECTION-SECRET\",\n\t\t\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".spec.writeConnectionSecretToRef.name\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName:     \"AGE\",\n\t\t\t\t\t\t\t\t\tType:     \"date\",\n\t\t\t\t\t\t\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tSchema: &extv1.CustomResourceValidation{\n\t\t\t\t\t\t\t\tOpenAPIV3Schema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\tRequired:    []string{\"spec\"},\n\t\t\t\t\t\t\t\t\tDescription: \"Description of the resource.\",\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"kind\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"metadata\": {\n\t\t\t\t\t\t\t\t\t\t\t// NOTE(muvaf): api-server takes care of validating\n\t\t\t\t\t\t\t\t\t\t\t// metadata.\n\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tMaxLength: ptr.To[int64](63),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired:    []string{\"storageGB\", \"engineVersion\"},\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Specification of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t// From CRDSpecTemplate.Validation\n\t\t\t\t\t\t\t\t\t\t\t\t\"storageGB\": {Type: \"integer\", Description: \"Pretend this is useful.\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"engineVersion\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.6\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"5.7\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositeDeletePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:    \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tDefault: &extv1.JSON{Raw: fmt.Appendf(nil, \"\\\"%s\\\"\", defaultPolicy)},\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Background\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Foreground\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceClaimSpecProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionRevisionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"compositionUpdatePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"resourceRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"writeConnectionSecretToRef\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Cannot change engine version\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"self.engineVersion == oldSelf.engineVersion\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"status\": {\n\t\t\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\t\t\tDescription: \"Status of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"phase\": {Type: \"string\"},\n\n\t\t\t\t\t\t\t\t\t\t\t\t// From CompositeResourceStatusProps()\n\t\t\t\t\t\t\t\t\t\t\t\t\"conditions\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tDescription:  \"Conditions of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:         \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType:    ptr.To(\"map\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListMapKeys: []string{\"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"lastTransitionTime\", \"reason\", \"status\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastTransitionTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"message\":            {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"reason\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"status\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\":               {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"observedGeneration\": {Type: \"integer\", Format: \"int64\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"claimConditionTypes\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:      \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"set\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\"connectionDetails\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastPublishedTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tXValidations: extv1.ValidationRules{\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tMessage: \"Phase is required once set\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRule:    \"!has(oldSelf.phase) || has(self.phase)\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot, err := ForCompositeResourceClaim(tc.crd)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"ForCompositeResourceClaim(...): %s\", err)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"ForCompositeResourceClaim(...): -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestForCompositeResourceClaimEmptyXrd(t *testing.T) {\n\tname := \"coolcomposites.example.org\"\n\tlabels := map[string]string{\"cool\": \"very\"}\n\tannotations := map[string]string{\"example.org/cool\": \"very\"}\n\n\tgroup := \"example.org\"\n\tversion := \"v1\"\n\n\tkind := \"CoolComposite\"\n\tlistKind := \"CoolCompositeList\"\n\tsingular := \"coolcomposite\"\n\tplural := \"coolcomposites\"\n\n\tclaimKind := \"CoolClaim\"\n\tclaimListKind := \"CoolClaimList\"\n\tclaimSingular := \"coolclaim\"\n\tclaimPlural := \"coolclaims\"\n\n\tschema := \"{}\"\n\n\td := &v1.CompositeResourceDefinition{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:        name,\n\t\t\tLabels:      labels,\n\t\t\tAnnotations: annotations,\n\t\t\tUID:         types.UID(\"you-you-eye-dee\"),\n\t\t},\n\t\tSpec: v1.CompositeResourceDefinitionSpec{\n\t\t\tGroup: group,\n\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\tPlural:   plural,\n\t\t\t\tSingular: singular,\n\t\t\t\tKind:     kind,\n\t\t\t\tListKind: listKind,\n\t\t\t},\n\t\t\tClaimNames: &extv1.CustomResourceDefinitionNames{\n\t\t\t\tPlural:   claimPlural,\n\t\t\t\tSingular: claimSingular,\n\t\t\t\tKind:     claimKind,\n\t\t\t\tListKind: claimListKind,\n\t\t\t},\n\t\t\tVersions: []v1.CompositeResourceDefinitionVersion{{\n\t\t\t\tName:          version,\n\t\t\t\tReferenceable: true,\n\t\t\t\tServed:        true,\n\t\t\t\tSchema: &v1.CompositeResourceValidation{\n\t\t\t\t\tOpenAPIV3Schema: runtime.RawExtension{Raw: []byte(schema)},\n\t\t\t\t},\n\t\t\t}},\n\t\t},\n\t}\n\n\twant := &extv1.CustomResourceDefinition{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:   claimPlural + \".\" + group,\n\t\t\tLabels: labels,\n\t\t\tOwnerReferences: []metav1.OwnerReference{\n\t\t\t\tmeta.AsController(meta.TypedReferenceTo(d, v1.CompositeResourceDefinitionGroupVersionKind)),\n\t\t\t},\n\t\t},\n\t\tSpec: extv1.CustomResourceDefinitionSpec{\n\t\t\tGroup: group,\n\t\t\tNames: extv1.CustomResourceDefinitionNames{\n\t\t\t\tPlural:     claimPlural,\n\t\t\t\tSingular:   claimSingular,\n\t\t\t\tKind:       claimKind,\n\t\t\t\tListKind:   claimListKind,\n\t\t\t\tCategories: []string{CategoryClaim},\n\t\t\t},\n\t\t\tScope: extv1.NamespaceScoped,\n\t\t\tVersions: []extv1.CustomResourceDefinitionVersion{\n\t\t\t\t{\n\t\t\t\t\tName:    version,\n\t\t\t\t\tServed:  true,\n\t\t\t\t\tStorage: true,\n\t\t\t\t\tSubresources: &extv1.CustomResourceSubresources{\n\t\t\t\t\t\tStatus: &extv1.CustomResourceSubresourceStatus{},\n\t\t\t\t\t},\n\t\t\t\t\tAdditionalPrinterColumns: []extv1.CustomResourceColumnDefinition{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:     \"SYNCED\",\n\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:     \"READY\",\n\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:     \"CONNECTION-SECRET\",\n\t\t\t\t\t\t\tType:     \"string\",\n\t\t\t\t\t\t\tJSONPath: \".spec.writeConnectionSecretToRef.name\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:     \"AGE\",\n\t\t\t\t\t\t\tType:     \"date\",\n\t\t\t\t\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tSchema: &extv1.CustomResourceValidation{\n\t\t\t\t\t\tOpenAPIV3Schema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\tRequired:    []string{\"spec\"},\n\t\t\t\t\t\t\tDescription: \"\",\n\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\"apiVersion\": {\n\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"kind\": {\n\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"metadata\": {\n\t\t\t\t\t\t\t\t\t// NOTE(muvaf): api-server takes care of validating\n\t\t\t\t\t\t\t\t\t// metadata.\n\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\t\tType:      \"string\",\n\t\t\t\t\t\t\t\t\t\t\tMaxLength: ptr.To[int64](63),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"spec\": {\n\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\tDescription: \"\",\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\"compositeDeletePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Background\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Foreground\"`)},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t// From CompositeResourceClaimSpecProps()\n\t\t\t\t\t\t\t\t\t\t\"compositionRef\": {\n\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"compositionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"compositionRevisionRef\": {\n\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"compositionRevisionSelector\": {\n\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"matchLabels\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"compositionUpdatePolicy\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\tEnum: []extv1.JSON{\n\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t\t\t\t\t\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"resourceRef\": {\n\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"writeConnectionSecretToRef\": {\n\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"name\"},\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"status\": {\n\t\t\t\t\t\t\t\t\tType:        \"object\",\n\t\t\t\t\t\t\t\t\tDescription: \"\",\n\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t// From CompositeResourceStatusProps()\n\t\t\t\t\t\t\t\t\t\t\"conditions\": {\n\t\t\t\t\t\t\t\t\t\t\tDescription:  \"Conditions of the resource.\",\n\t\t\t\t\t\t\t\t\t\t\tType:         \"array\",\n\t\t\t\t\t\t\t\t\t\t\tXListType:    ptr.To(\"map\"),\n\t\t\t\t\t\t\t\t\t\t\tXListMapKeys: []string{\"type\"},\n\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\tType:     \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tRequired: []string{\"lastTransitionTime\", \"reason\", \"status\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"lastTransitionTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"message\":            {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"reason\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"status\":             {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\":               {Type: \"string\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"observedGeneration\": {Type: \"integer\", Format: \"int64\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"claimConditionTypes\": {\n\t\t\t\t\t\t\t\t\t\t\tType:      \"array\",\n\t\t\t\t\t\t\t\t\t\t\tXListType: ptr.To(\"set\"),\n\t\t\t\t\t\t\t\t\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\t\t\t\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\tType: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"connectionDetails\": {\n\t\t\t\t\t\t\t\t\t\t\tType: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\t\t\t\t\t\t\"lastPublishedTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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}\n\n\tgot, err := ForCompositeResourceClaim(d)\n\tif err != nil {\n\t\tt.Fatalf(\"ForCompositeResourceClaim(...): %s\", err)\n\t}\n\n\tif diff := cmp.Diff(want, got); diff != \"\" {\n\t\tt.Errorf(\"ForCompositeResourceClaim(...): -want, +got:\\n%s\", diff)\n\t}\n}\n\nfunc TestSetCrdMetadata(t *testing.T) {\n\ttype args struct {\n\t\tcrd *extv1.CustomResourceDefinition\n\t\txrd *v1.CompositeResourceDefinition\n\t}\n\n\ttests := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   *extv1.CustomResourceDefinition\n\t}{\n\t\t\"SetAnnotations\": {\n\t\t\treason: \"Should set CRD annotations only from XRD spec\",\n\t\t\targs: args{\n\t\t\t\tcrd: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"test\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\txrd: &v1.CompositeResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"test\",\n\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\"example.com/some-xrd-annotation\": \"not-propagated\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{Metadata: &v1.CompositeResourceDefinitionSpecMetadata{\n\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\"cert-manager.io/inject-ca-from\": \"example1-ns/webhook1-certificate\",\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\twant: &extv1.CustomResourceDefinition{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName: \"test\",\n\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\"cert-manager.io/inject-ca-from\": \"example1-ns/webhook1-certificate\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SetLabelsFromXRDSpec\": {\n\t\t\treason: \"Should set CRD labels from XRD spec\",\n\t\t\targs: args{\n\t\t\t\tcrd: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"test\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\txrd: &v1.CompositeResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"test\",\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{Metadata: &v1.CompositeResourceDefinitionSpecMetadata{\n\t\t\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\t\t\"example.com/some-crd-label\":            \"value1\",\n\t\t\t\t\t\t\t\"example.com/some-additional-crd-label\": \"value2\",\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\twant: &extv1.CustomResourceDefinition{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName: \"test\",\n\t\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\t\"example.com/some-crd-label\":            \"value1\",\n\t\t\t\t\t\t\"example.com/some-additional-crd-label\": \"value2\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"AppendLabelsFromXRDSpec\": {\n\t\t\treason: \"Should set CRD labels by appending labels from the XRD spec to the ones of the XRD itself\",\n\t\t\targs: args{\n\t\t\t\tcrd: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"test\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\txrd: &v1.CompositeResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"test\",\n\t\t\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\t\t\"example.com/some-xrd-label\":            \"value1\",\n\t\t\t\t\t\t\t\"example.com/some-additional-xrd-label\": \"value2\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{Metadata: &v1.CompositeResourceDefinitionSpecMetadata{\n\t\t\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\t\t\"example.com/some-crd-label\":            \"value3\",\n\t\t\t\t\t\t\t\"example.com/some-additional-crd-label\": \"value4\",\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\twant: &extv1.CustomResourceDefinition{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName: \"test\",\n\t\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\t\"example.com/some-xrd-label\":            \"value1\",\n\t\t\t\t\t\t\"example.com/some-additional-xrd-label\": \"value2\",\n\t\t\t\t\t\t\"example.com/some-crd-label\":            \"value3\",\n\t\t\t\t\t\t\"example.com/some-additional-crd-label\": \"value4\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SetLabelsAndAnnotations\": {\n\t\t\treason: \"Should set CRD labels and annotations from XRD spec and XRD itself\",\n\t\t\targs: args{\n\t\t\t\tcrd: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"test\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\txrd: &v1.CompositeResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"test\",\n\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\"example.com/some-xrd-annotation\":                  \"not-propagated\",\n\t\t\t\t\t\t\t\"example.com/some-additional-xrd-label-annotation\": \"not-propagated\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\t\t\"example.com/some-xrd-label\":            \"value1\",\n\t\t\t\t\t\t\t\"example.com/some-additional-xrd-label\": \"value2\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1.CompositeResourceDefinitionSpec{Metadata: &v1.CompositeResourceDefinitionSpecMetadata{\n\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\"example.com/some-crd-annotation\":                  \"value1\",\n\t\t\t\t\t\t\t\"example.com/some-additional-crd-label-annotation\": \"value2\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\t\t\"example.com/some-crd-label\":            \"value3\",\n\t\t\t\t\t\t\t\"example.com/some-additional-crd-label\": \"value4\",\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\twant: &extv1.CustomResourceDefinition{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName: \"test\",\n\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\"example.com/some-crd-annotation\":                  \"value1\",\n\t\t\t\t\t\t\"example.com/some-additional-crd-label-annotation\": \"value2\",\n\t\t\t\t\t},\n\t\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\t\"example.com/some-xrd-label\":            \"value1\",\n\t\t\t\t\t\t\"example.com/some-additional-xrd-label\": \"value2\",\n\t\t\t\t\t\t\"example.com/some-crd-label\":            \"value3\",\n\t\t\t\t\t\t\"example.com/some-additional-crd-label\": \"value4\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"NoLabelsAndAnnotations\": {\n\t\t\treason: \"Should do nothing if no annotations or labels are set in XRD spec or XRD itself\",\n\t\t\targs: args{\n\t\t\t\tcrd: &extv1.CustomResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"test\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\txrd: &v1.CompositeResourceDefinition{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"test\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: &extv1.CustomResourceDefinition{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName: \"test\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor name, tt := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := setCrdMetadata(tt.args.crd, tt.args.xrd)\n\t\t\tif diff := cmp.Diff(tt.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nsetCrdMetadata(...): -want, +got:\\n%s\", tt.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/xcrd/fuzz_test.go",
    "content": "/*\nCopyright 2023 The Crossplane 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 xcrd\n\nimport (\n\t\"testing\"\n\n\tfuzz \"github.com/AdaLogics/go-fuzz-headers\"\n\tv1 \"github.com/crossplane/crossplane/apis/v2/apiextensions/v1\"\n)\n\nfunc FuzzForCompositeResourceXcrd(f *testing.F) {\n\tf.Fuzz(func(_ *testing.T, data []byte) {\n\t\tff := fuzz.NewConsumer(data)\n\t\txrd := &v1.CompositeResourceDefinition{}\n\n\t\terr := ff.GenerateStruct(xrd)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t_, _ = ForCompositeResource(xrd)\n\t})\n}\n\nfunc FuzzForCompositeResourceClaim(f *testing.F) {\n\tf.Fuzz(func(_ *testing.T, data []byte) {\n\t\tff := fuzz.NewConsumer(data)\n\t\txrd := &v1.CompositeResourceDefinition{}\n\n\t\terr := ff.GenerateStruct(xrd)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t_, _ = ForCompositeResourceClaim(xrd)\n\t})\n}\n"
  },
  {
    "path": "pkg/xcrd/schemas.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 xcrd\n\nimport (\n\t\"fmt\"\n\n\tv1 \"github.com/crossplane/crossplane/apis/v2/apiextensions/v1\"\n\txpv2 \"github.com/crossplane/crossplane/apis/v2/core/v2\"\n\textv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\t\"k8s.io/utils/ptr\"\n)\n\n// Label keys.\nconst (\n\tLabelKeyNamePrefixForComposed = \"crossplane.io/composite\"\n\tLabelKeyClaimName             = \"crossplane.io/claim-name\"\n\tLabelKeyClaimNamespace        = \"crossplane.io/claim-namespace\"\n)\n\n// CompositionRevisionRef should be propagated dynamically.\nconst CompositionRevisionRef = \"compositionRevisionRef\"\n\n// PropagateSpecProps is the list of XRC spec properties to propagate\n// when translating an XRC into an XR.\nvar PropagateSpecProps = []string{\"compositionRef\", \"compositionSelector\", \"compositionUpdatePolicy\", \"compositionRevisionSelector\"} //nolint:gochecknoglobals // We treat this as a constant.\n\n// TODO(negz): Add descriptions to schema fields.\n\n// BaseProps is a partial OpenAPIV3Schema for the spec fields that Crossplane\n// expects to be present for all CRDs that it creates.\nfunc BaseProps() *extv1.JSONSchemaProps {\n\treturn &extv1.JSONSchemaProps{\n\t\tType:     \"object\",\n\t\tRequired: []string{\"spec\"},\n\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\"apiVersion\": {\n\t\t\t\tType: \"string\",\n\t\t\t},\n\t\t\t\"kind\": {\n\t\t\t\tType: \"string\",\n\t\t\t},\n\t\t\t\"metadata\": {\n\t\t\t\t// NOTE(muvaf): api-server takes care of validating\n\t\t\t\t// metadata.\n\t\t\t\tType: \"object\",\n\t\t\t},\n\t\t\t\"spec\": {\n\t\t\t\tType:       \"object\",\n\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{},\n\t\t\t},\n\t\t\t\"status\": {\n\t\t\t\tType:       \"object\",\n\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{},\n\t\t\t},\n\t\t},\n\t}\n}\n\n// CompositeResourceSpecProps is a partial OpenAPIV3Schema for the spec fields\n// that Crossplane expects to be present for all defined composite resources.\nfunc CompositeResourceSpecProps(s v1.CompositeResourceScope, defaultPol *xpv2.UpdatePolicy) map[string]extv1.JSONSchemaProps {\n\tprops := map[string]extv1.JSONSchemaProps{\n\t\t\"compositionRef\": {\n\t\t\tType:     \"object\",\n\t\t\tRequired: []string{\"name\"},\n\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t},\n\t\t},\n\t\t\"compositionSelector\": {\n\t\t\tType:     \"object\",\n\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\"matchLabels\": {\n\t\t\t\t\tType: \"object\",\n\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"compositionRevisionRef\": {\n\t\t\tType:     \"object\",\n\t\t\tRequired: []string{\"name\"},\n\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t},\n\t\t},\n\t\t\"compositionRevisionSelector\": {\n\t\t\tType:     \"object\",\n\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\"matchLabels\": {\n\t\t\t\t\tType: \"object\",\n\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"compositionUpdatePolicy\": {\n\t\t\tType: \"string\",\n\t\t\tEnum: []extv1.JSON{\n\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t},\n\t\t\tDefault: func() *extv1.JSON {\n\t\t\t\tif defaultPol == nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\treturn &extv1.JSON{Raw: fmt.Appendf(nil, \"\\\"%s\\\"\", *defaultPol)}\n\t\t\t}(),\n\t\t},\n\t\t\"resourceRefs\": {\n\t\t\tType: \"array\",\n\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\tType: \"object\",\n\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t},\n\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\t// Controllers should replace the entire resourceRefs array.\n\t\t\tXListType: ptr.To(\"atomic\"),\n\t\t},\n\t}\n\n\t// Namespaced XRs don't get to reference composed resources in other\n\t// namespaces.\n\tif s == v1.CompositeResourceScopeNamespaced {\n\t\tprops[\"resourceRefs\"] = extv1.JSONSchemaProps{\n\t\t\tType: \"array\",\n\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\tType: \"object\",\n\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\t},\n\t\t\t\t\tRequired: []string{\"apiVersion\", \"kind\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\t// Controllers should replace the entire resourceRefs array.\n\t\t\tXListType: ptr.To(\"atomic\"),\n\t\t}\n\t}\n\n\t// Legacy XRs have their Crossplane machinery fields directly under spec.\n\t// They also support referencing a claim, and writing a secret.\n\tif s == v1.CompositeResourceScopeLegacyCluster {\n\t\tprops[\"claimRef\"] = extv1.JSONSchemaProps{\n\t\t\tType:     \"object\",\n\t\t\tRequired: []string{\"apiVersion\", \"kind\", \"namespace\", \"name\"},\n\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\"namespace\":  {Type: \"string\"},\n\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t},\n\t\t}\n\n\t\tprops[\"writeConnectionSecretToRef\"] = extv1.JSONSchemaProps{\n\t\t\tType:     \"object\",\n\t\t\tRequired: []string{\"name\", \"namespace\"},\n\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\"name\":      {Type: \"string\"},\n\t\t\t\t\"namespace\": {Type: \"string\"},\n\t\t\t},\n\t\t}\n\n\t\treturn props\n\t}\n\n\t// Modern XRs nest their Crossplane machinery fields under spec.crossplane.\n\treturn map[string]extv1.JSONSchemaProps{\n\t\t\"crossplane\": {\n\t\t\tType:        \"object\",\n\t\t\tDescription: \"Configures how Crossplane will reconcile this composite resource\",\n\t\t\tProperties:  props,\n\t\t},\n\t}\n}\n\n// CompositeResourceClaimSpecProps is a partial OpenAPIV3Schema for the spec\n// fields that Crossplane expects to be present for all published infrastructure\n// resources.\nfunc CompositeResourceClaimSpecProps(defaultPol *xpv2.CompositeDeletePolicy) map[string]extv1.JSONSchemaProps {\n\treturn map[string]extv1.JSONSchemaProps{\n\t\t\"compositionRef\": {\n\t\t\tType:     \"object\",\n\t\t\tRequired: []string{\"name\"},\n\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t},\n\t\t},\n\t\t\"compositionSelector\": {\n\t\t\tType:     \"object\",\n\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\"matchLabels\": {\n\t\t\t\t\tType: \"object\",\n\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"compositionRevisionRef\": {\n\t\t\tType:     \"object\",\n\t\t\tRequired: []string{\"name\"},\n\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t},\n\t\t},\n\t\t\"compositionRevisionSelector\": {\n\t\t\tType:     \"object\",\n\t\t\tRequired: []string{\"matchLabels\"},\n\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\"matchLabels\": {\n\t\t\t\t\tType: \"object\",\n\t\t\t\t\tAdditionalProperties: &extv1.JSONSchemaPropsOrBool{\n\t\t\t\t\t\tAllows: true,\n\t\t\t\t\t\tSchema: &extv1.JSONSchemaProps{Type: \"string\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"compositionUpdatePolicy\": {\n\t\t\tType: \"string\",\n\t\t\tEnum: []extv1.JSON{\n\t\t\t\t{Raw: []byte(`\"Automatic\"`)},\n\t\t\t\t{Raw: []byte(`\"Manual\"`)},\n\t\t\t},\n\t\t},\n\t\t\"compositeDeletePolicy\": {\n\t\t\tType: \"string\",\n\t\t\tEnum: []extv1.JSON{\n\t\t\t\t{Raw: []byte(`\"Background\"`)},\n\t\t\t\t{Raw: []byte(`\"Foreground\"`)},\n\t\t\t},\n\t\t\tDefault: func() *extv1.JSON {\n\t\t\t\tif defaultPol == nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\treturn &extv1.JSON{Raw: fmt.Appendf(nil, \"\\\"%s\\\"\", *defaultPol)}\n\t\t\t}(),\n\t\t},\n\t\t\"resourceRef\": {\n\t\t\tType:     \"object\",\n\t\t\tRequired: []string{\"apiVersion\", \"kind\", \"name\"},\n\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\"apiVersion\": {Type: \"string\"},\n\t\t\t\t\"kind\":       {Type: \"string\"},\n\t\t\t\t\"name\":       {Type: \"string\"},\n\t\t\t},\n\t\t},\n\t\t\"writeConnectionSecretToRef\": {\n\t\t\tType:     \"object\",\n\t\t\tRequired: []string{\"name\"},\n\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\"name\": {Type: \"string\"},\n\t\t\t},\n\t\t},\n\t}\n}\n\n// CompositeResourceStatusProps is a partial OpenAPIV3Schema for the status\n// fields that Crossplane expects to be present for all composite resources.\nfunc CompositeResourceStatusProps(s v1.CompositeResourceScope) map[string]extv1.JSONSchemaProps {\n\tprops := map[string]extv1.JSONSchemaProps{\n\t\t\"conditions\": {\n\t\t\tDescription: \"Conditions of the resource.\",\n\t\t\tType:        \"array\",\n\t\t\tXListMapKeys: []string{\n\t\t\t\t\"type\",\n\t\t\t},\n\t\t\tXListType: ptr.To(\"map\"),\n\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\tType:     \"object\",\n\t\t\t\t\tRequired: []string{\"lastTransitionTime\", \"reason\", \"status\", \"type\"},\n\t\t\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\t\t\"lastTransitionTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t\t\t\t\"message\":            {Type: \"string\"},\n\t\t\t\t\t\t\"reason\":             {Type: \"string\"},\n\t\t\t\t\t\t\"status\":             {Type: \"string\"},\n\t\t\t\t\t\t\"type\":               {Type: \"string\"},\n\t\t\t\t\t\t\"observedGeneration\": {Type: \"integer\", Format: \"int64\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tswitch s {\n\tcase v1.CompositeResourceScopeNamespaced, v1.CompositeResourceScopeCluster:\n\t\t// Modern XRs don't have connection details or support claims, so\n\t\t// there's nothing else to put in the status for them\n\tcase v1.CompositeResourceScopeLegacyCluster:\n\t\t// Legacy XRs don't use status.crossplane, and support claims.\n\t\tprops[\"connectionDetails\"] = extv1.JSONSchemaProps{\n\t\t\tType: \"object\",\n\t\t\tProperties: map[string]extv1.JSONSchemaProps{\n\t\t\t\t\"lastPublishedTime\": {Type: \"string\", Format: \"date-time\"},\n\t\t\t},\n\t\t}\n\t\tprops[\"claimConditionTypes\"] = extv1.JSONSchemaProps{\n\t\t\tType:      \"array\",\n\t\t\tXListType: ptr.To(\"set\"),\n\t\t\tItems: &extv1.JSONSchemaPropsOrArray{\n\t\t\t\tSchema: &extv1.JSONSchemaProps{\n\t\t\t\t\tType: \"string\",\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t}\n\n\treturn props\n}\n\n// CompositeResourcePrinterColumns returns the set of default printer columns\n// that should exist in all generated composite resource CRDs.\nfunc CompositeResourcePrinterColumns(s v1.CompositeResourceScope) []extv1.CustomResourceColumnDefinition {\n\tcols := []extv1.CustomResourceColumnDefinition{\n\t\t{\n\t\t\tName:     \"SYNCED\",\n\t\t\tType:     \"string\",\n\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t},\n\t\t{\n\t\t\tName:     \"READY\",\n\t\t\tType:     \"string\",\n\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t},\n\t\t{\n\t\t\tName:     \"COMPOSITION\",\n\t\t\tType:     \"string\",\n\t\t\tJSONPath: \".spec.crossplane.compositionRef.name\",\n\t\t},\n\t\t{\n\t\t\tName:     \"COMPOSITIONREVISION\",\n\t\t\tType:     \"string\",\n\t\t\tJSONPath: \".spec.crossplane.compositionRevisionRef.name\",\n\t\t\tPriority: 1,\n\t\t},\n\t\t{\n\t\t\tName:     \"AGE\",\n\t\t\tType:     \"date\",\n\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t},\n\t}\n\n\tif s == v1.CompositeResourceScopeLegacyCluster {\n\t\tfor i := range cols {\n\t\t\tif cols[i].Name == \"COMPOSITION\" {\n\t\t\t\tcols[i].JSONPath = \".spec.compositionRef.name\"\n\t\t\t}\n\n\t\t\tif cols[i].Name == \"COMPOSITIONREVISION\" {\n\t\t\t\tcols[i].JSONPath = \".spec.compositionRevisionRef.name\"\n\t\t\t}\n\t\t}\n\t}\n\n\treturn cols\n}\n\n// CompositeResourceClaimPrinterColumns returns the set of default printer\n// columns that should exist in all generated composite resource claim CRDs.\nfunc CompositeResourceClaimPrinterColumns() []extv1.CustomResourceColumnDefinition {\n\treturn []extv1.CustomResourceColumnDefinition{\n\t\t{\n\t\t\tName:     \"SYNCED\",\n\t\t\tType:     \"string\",\n\t\t\tJSONPath: \".status.conditions[?(@.type=='Synced')].status\",\n\t\t},\n\t\t{\n\t\t\tName:     \"READY\",\n\t\t\tType:     \"string\",\n\t\t\tJSONPath: \".status.conditions[?(@.type=='Ready')].status\",\n\t\t},\n\t\t{\n\t\t\tName:     \"CONNECTION-SECRET\",\n\t\t\tType:     \"string\",\n\t\t\tJSONPath: \".spec.writeConnectionSecretToRef.name\",\n\t\t},\n\t\t{\n\t\t\tName:     \"AGE\",\n\t\t\tType:     \"date\",\n\t\t\tJSONPath: \".metadata.creationTimestamp\",\n\t\t},\n\t}\n}\n\n// GetPropFields returns the fields from a map of schema properties.\nfunc GetPropFields(props map[string]extv1.JSONSchemaProps) []string {\n\tpropFields := make([]string, len(props))\n\n\ti := 0\n\tfor k := range props {\n\t\tpropFields[i] = k\n\t\ti++\n\t}\n\n\treturn propFields\n}\n"
  },
  {
    "path": "pkg/xpkg/build.go",
    "content": "/*\nCopyright 2023 The Crossplane 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 xpkg\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\tpkgmetav1 \"github.com/crossplane/crossplane/apis/v2/pkg/meta/v1\"\n\t\"github.com/crossplane/crossplane/apis/v2/pkg/meta/v1beta1\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/empty\"\n\t\"github.com/google/go-containerregistry/pkg/v1/mutate\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/serializer/json\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg/parser\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg/parser/examples\"\n)\n\nconst (\n\terrParserPackage     = \"failed to parse package\"\n\terrParserExample     = \"failed to parse examples\"\n\terrLintPackage       = \"failed to lint package\"\n\terrInitBackend       = \"failed to initialize package parsing backend\"\n\terrTarFromStream     = \"failed to build tarball from stream\"\n\terrLayerFromTar      = \"failed to convert tarball to image layer\"\n\terrDigestInvalid     = \"failed to get digest from image layer\"\n\terrBuildImage        = \"failed to build image from layers\"\n\terrConfigFile        = \"failed to get config file from image\"\n\terrMutateConfig      = \"failed to mutate config for image\"\n\terrBuildObjectScheme = \"failed to build scheme for package encoder\"\n)\n\n// annotatedTeeReadCloser is a copy of io.TeeReader that implements\n// parser.AnnotatedReadCloser. It returns a Reader that writes to w what it\n// reads from r. All reads from r performed through it are matched with\n// corresponding writes to w. There is no internal buffering - the write must\n// complete before the read completes. Any error encountered while writing is\n// reported as a read error. If the underlying reader is a\n// parser.AnnotatedReadCloser the tee reader will invoke its Annotate function.\n// Otherwise it will return nil. Closing is always a no-op.\nfunc annotatedTeeReadCloser(r io.Reader, w io.Writer) *teeReader {\n\treturn &teeReader{r, w}\n}\n\ntype teeReader struct {\n\tr io.Reader\n\tw io.Writer\n}\n\nfunc (t *teeReader) Read(p []byte) (n int, err error) {\n\tn, err = t.r.Read(p)\n\tif n > 0 {\n\t\tif n, err := t.w.Write(p[:n]); err != nil {\n\t\t\treturn n, err\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (t *teeReader) Close() error {\n\treturn nil\n}\n\nfunc (t *teeReader) Annotate() any {\n\tanno, ok := t.r.(parser.AnnotatedReadCloser)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\treturn anno.Annotate()\n}\n\n// Builder defines an xpkg Builder.\ntype Builder struct {\n\tpackageSource parser.Backend\n\texampleSource parser.Backend\n\n\tpackageParser  parser.Parser\n\texamplesParser *examples.Parser\n}\n\n// New returns a new Builder.\nfunc New(packageSource, exampleSource parser.Backend, packageParser parser.Parser, examplesParser *examples.Parser) *Builder {\n\treturn &Builder{\n\t\tpackageSource:  packageSource,\n\t\texampleSource:  exampleSource,\n\t\tpackageParser:  packageParser,\n\t\texamplesParser: examplesParser,\n\t}\n}\n\ntype buildOpts struct {\n\tbase v1.Image\n}\n\n// A BuildOpt modifies how a package is built.\ntype BuildOpt func(*buildOpts)\n\n// WithBase sets the base image of the package.\nfunc WithBase(img v1.Image) BuildOpt {\n\treturn func(o *buildOpts) {\n\t\to.base = img\n\t}\n}\n\n// Build compiles a Crossplane package from an on-disk package.\nfunc (b *Builder) Build(ctx context.Context, opts ...BuildOpt) (v1.Image, runtime.Object, error) {\n\tbOpts := &buildOpts{\n\t\tbase: empty.Image,\n\t}\n\tfor _, o := range opts {\n\t\to(bOpts)\n\t}\n\n\t// assume examples exist\n\texamplesExist := true\n\t// Get package YAML stream.\n\tpkgReader, err := b.packageSource.Init(ctx)\n\tif err != nil {\n\t\treturn nil, nil, errors.Wrap(err, errInitBackend)\n\t}\n\n\tdefer func() { _ = pkgReader.Close() }()\n\n\t// Get examples YAML stream.\n\texReader, err := b.exampleSource.Init(ctx)\n\tif err != nil && !os.IsNotExist(err) {\n\t\treturn nil, nil, errors.Wrap(err, errInitBackend)\n\t}\n\n\tdefer func() { _ = exReader.Close() }()\n\t// examples/ doesn't exist\n\tif os.IsNotExist(err) {\n\t\texamplesExist = false\n\t}\n\n\tpkg, err := b.packageParser.Parse(ctx, pkgReader)\n\tif err != nil {\n\t\treturn nil, nil, errors.Wrap(err, errParserPackage)\n\t}\n\n\tmetas := pkg.GetMeta()\n\tif len(metas) != 1 {\n\t\treturn nil, nil, errors.New(errNotExactlyOneMeta)\n\t}\n\n\t// TODO(hasheddan): make linter selection logic configurable.\n\tmeta := metas[0]\n\n\tvar linter parser.Linter\n\n\tswitch meta.GetObjectKind().GroupVersionKind().Kind {\n\tcase pkgmetav1.ConfigurationKind:\n\t\tlinter = NewConfigurationLinter()\n\tcase v1beta1.FunctionKind:\n\t\tlinter = NewFunctionLinter()\n\tcase pkgmetav1.ProviderKind:\n\t\tlinter = NewProviderLinter()\n\t}\n\n\tif err := linter.Lint(pkg); err != nil {\n\t\treturn nil, nil, errors.Wrap(err, errLintPackage)\n\t}\n\n\tlayers := make([]v1.Layer, 0)\n\n\tcfgFile, err := bOpts.base.ConfigFile()\n\tif err != nil {\n\t\treturn nil, nil, errors.Wrap(err, errConfigFile)\n\t}\n\n\tcfg := cfgFile.Config\n\tif cfg.Labels == nil {\n\t\tcfg.Labels = make(map[string]string)\n\t}\n\n\tpkgBytes, err := encode(pkg)\n\tif err != nil {\n\t\treturn nil, nil, errors.Wrap(err, errConfigFile)\n\t}\n\n\tpkgLayer, err := Layer(pkgBytes, StreamFile, PackageAnnotation, int64(pkgBytes.Len()), StreamFileMode, &cfg)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tlayers = append(layers, pkgLayer)\n\n\t// examples exist, create the layer\n\tif examplesExist {\n\t\texBuf := new(bytes.Buffer)\n\t\tif _, err = b.examplesParser.Parse(ctx, annotatedTeeReadCloser(exReader, exBuf)); err != nil {\n\t\t\treturn nil, nil, errors.Wrap(err, errParserExample)\n\t\t}\n\n\t\texLayer, err := Layer(exBuf, XpkgExamplesFile, ExamplesAnnotation, int64(exBuf.Len()), StreamFileMode, &cfg)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\n\t\tlayers = append(layers, exLayer)\n\t}\n\n\tfor _, l := range layers {\n\t\tbOpts.base, err = mutate.AppendLayers(bOpts.base, l)\n\t\tif err != nil {\n\t\t\treturn nil, nil, errors.Wrap(err, errBuildImage)\n\t\t}\n\t}\n\n\tbOpts.base, err = mutate.Config(bOpts.base, cfg)\n\tif err != nil {\n\t\treturn nil, nil, errors.Wrap(err, errMutateConfig)\n\t}\n\n\treturn bOpts.base, meta, nil\n}\n\n// encode encodes a package as a YAML stream.  Does not check meta existence\n// or quantity i.e. it should be linted first to ensure that it is valid.\nfunc encode(pkg parser.Lintable) (*bytes.Buffer, error) {\n\tpkgBuf := new(bytes.Buffer)\n\n\tobjScheme, err := BuildObjectScheme()\n\tif err != nil {\n\t\treturn nil, errors.New(errBuildObjectScheme)\n\t}\n\n\tdo := json.NewSerializerWithOptions(json.DefaultMetaFactory, objScheme, objScheme, json.SerializerOptions{Yaml: true})\n\n\tpkgBuf.WriteString(\"---\\n\")\n\n\tif err = do.Encode(pkg.GetMeta()[0], pkgBuf); err != nil {\n\t\treturn nil, errors.Wrap(err, errBuildObjectScheme)\n\t}\n\n\tpkgBuf.WriteString(\"---\\n\")\n\n\tfor _, o := range pkg.GetObjects() {\n\t\tif err = do.Encode(o, pkgBuf); err != nil {\n\t\t\treturn nil, errors.Wrap(err, errBuildObjectScheme)\n\t\t}\n\n\t\tpkgBuf.WriteString(\"---\\n\")\n\t}\n\n\treturn pkgBuf, nil\n}\n\n// SkipContains supplies a FilterFn that skips paths that contain the give pattern.\nfunc SkipContains(pattern string) parser.FilterFn {\n\treturn func(path string, _ os.FileInfo) (bool, error) {\n\t\treturn strings.Contains(path, pattern), nil\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/build_test.go",
    "content": "/*\nCopyright 2023 The Crossplane 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 xpkg\n\nimport (\n\t\"archive/tar\"\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/mutate\"\n\t\"github.com/google/go-containerregistry/pkg/v1/partial\"\n\t\"github.com/spf13/afero\"\n\t\"github.com/spf13/afero/tarfs\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg/parser\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg/parser/examples\"\n)\n\nvar (\n\ttestCRD  []byte\n\ttestMeta []byte\n\ttestEx1  []byte\n\ttestEx2  []byte\n\ttestEx3  []byte\n\ttestEx4  []byte\n\n\t_ parser.Backend = &MockBackend{}\n)\n\nfunc init() {\n\ttestCRD, _ = afero.ReadFile(afero.NewOsFs(), \"testdata/providerconfigs.helm.crossplane.io.yaml\")\n\ttestMeta, _ = afero.ReadFile(afero.NewOsFs(), \"testdata/provider_meta.yaml\")\n\ttestEx1, _ = afero.ReadFile(afero.NewOsFs(), \"testdata/examples/ec2/instance.yaml\")\n\ttestEx2, _ = afero.ReadFile(afero.NewOsFs(), \"testdata/examples/ec2/internetgateway.yaml\")\n\ttestEx3, _ = afero.ReadFile(afero.NewOsFs(), \"testdata/examples/ecr/repository.yaml\")\n\ttestEx4, _ = afero.ReadFile(afero.NewOsFs(), \"testdata/examples/provider.yaml\")\n}\n\ntype MockBackend struct {\n\tMockInit func() (io.ReadCloser, error)\n}\n\nfunc NewMockInitFn(r io.ReadCloser, err error) func() (io.ReadCloser, error) {\n\treturn func() (io.ReadCloser, error) { return r, err }\n}\n\nfunc (m *MockBackend) Init(_ context.Context, _ ...parser.BackendOption) (io.ReadCloser, error) {\n\treturn m.MockInit()\n}\n\nvar _ parser.Parser = &MockParser{}\n\ntype MockParser struct {\n\tMockParse func() (*parser.Package, error)\n}\n\nfunc NewMockParseFn(pkg *parser.Package, err error) func() (*parser.Package, error) {\n\treturn func() (*parser.Package, error) { return pkg, err }\n}\n\nfunc (m *MockParser) Parse(context.Context, io.ReadCloser) (*parser.Package, error) {\n\treturn m.MockParse()\n}\n\nfunc TestBuild(t *testing.T) {\n\terrBoom := errors.New(\"boom\")\n\n\ttype args struct {\n\t\tbe parser.Backend\n\t\tex parser.Backend\n\t\tp  parser.Parser\n\t\te  *examples.Parser\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   error\n\t}{\n\t\t\"ErrInitBackend\": {\n\t\t\treason: \"Should return an error if we fail to initialize backend.\",\n\t\t\targs: args{\n\t\t\t\tbe: &MockBackend{\n\t\t\t\t\tMockInit: NewMockInitFn(nil, errBoom),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: errors.Wrap(errBoom, errInitBackend),\n\t\t},\n\t\t\"ErrParse\": {\n\t\t\treason: \"Should return an error if we fail to parse package.\",\n\t\t\targs: args{\n\t\t\t\tbe: parser.NewEchoBackend(\"\"),\n\t\t\t\tex: parser.NewEchoBackend(\"\"),\n\t\t\t\tp: &MockParser{\n\t\t\t\t\tMockParse: NewMockParseFn(nil, errBoom),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: errors.Wrap(errBoom, errParserPackage),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tbuilder := New(tc.args.be, tc.args.ex, tc.args.p, tc.args.e)\n\n\t\t\t_, _, err := builder.Build(context.TODO())\n\t\t\tif diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nBuild(...): -want err, +got err:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBuildExamples(t *testing.T) {\n\tpkgp, _ := yamlParser()\n\n\tdefaultFilters := []parser.FilterFn{\n\t\tparser.SkipDirs(),\n\t\tparser.SkipNotYAML(),\n\t\tparser.SkipEmpty(),\n\t}\n\n\ttype withFsFn func() afero.Fs\n\n\ttype args struct {\n\t\trootDir     string\n\t\texamplesDir string\n\t\tfs          withFsFn\n\t}\n\n\ttype want struct {\n\t\tpkgExists bool\n\t\texExists  bool\n\t\tlabels    []string\n\t\terr       error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"SuccessNoExamples\": {\n\t\t\targs: args{\n\t\t\t\trootDir:     \"/ws\",\n\t\t\t\texamplesDir: \"/ws/examples\",\n\t\t\t\tfs: func() afero.Fs {\n\t\t\t\t\tfs := afero.NewMemMapFs()\n\t\t\t\t\t_ = fs.Mkdir(\"/ws\", os.ModePerm)\n\t\t\t\t\t_ = fs.Mkdir(\"/ws/crds\", os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/ws/crossplane.yaml\", testMeta, os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/ws/crds/crd.yaml\", testCRD, os.ModePerm)\n\t\t\t\t\treturn fs\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpkgExists: true,\n\t\t\t\tlabels: []string{\n\t\t\t\t\tPackageAnnotation,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessExamplesAtRoot\": {\n\t\t\targs: args{\n\t\t\t\trootDir:     \"/ws\",\n\t\t\t\texamplesDir: \"/ws/examples\",\n\t\t\t\tfs: func() afero.Fs {\n\t\t\t\t\tfs := afero.NewMemMapFs()\n\t\t\t\t\t_ = fs.Mkdir(\"/ws\", os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/ws/crossplane.yaml\", testMeta, os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/ws/crds/crd.yaml\", testCRD, os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/ws/examples/ec2/instance.yaml\", testEx1, os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/ws/examples/ec2/internetgateway.yaml\", testEx2, os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/ws/examples/ecr/repository.yaml\", testEx3, os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/ws/examples/provider.yaml\", testEx4, os.ModePerm)\n\t\t\t\t\treturn fs\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpkgExists: true,\n\t\t\t\texExists:  true,\n\t\t\t\tlabels: []string{\n\t\t\t\t\tPackageAnnotation,\n\t\t\t\t\tExamplesAnnotation,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessExamplesAtCustomDir\": {\n\t\t\targs: args{\n\t\t\t\trootDir:     \"/ws\",\n\t\t\t\texamplesDir: \"/other_directory/examples\",\n\t\t\t\tfs: func() afero.Fs {\n\t\t\t\t\tfs := afero.NewMemMapFs()\n\t\t\t\t\t_ = fs.Mkdir(\"/ws\", os.ModePerm)\n\t\t\t\t\t_ = fs.Mkdir(\"/other_directory\", os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/ws/crossplane.yaml\", testMeta, os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/ws/crds/crd.yaml\", testCRD, os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/other_directory/examples/ec2/instance.yaml\", testEx1, os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/other_directory/examples/ec2/internetgateway.yaml\", testEx2, os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/other_directory/examples/ecr/repository.yaml\", testEx3, os.ModePerm)\n\t\t\t\t\t_ = afero.WriteFile(fs, \"/other_directory/examples/provider.yaml\", testEx4, os.ModePerm)\n\t\t\t\t\treturn fs\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpkgExists: true,\n\t\t\t\texExists:  true,\n\t\t\t\tlabels: []string{\n\t\t\t\t\tPackageAnnotation,\n\t\t\t\t\tExamplesAnnotation,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tpkgBe := parser.NewFsBackend(\n\t\t\t\ttc.args.fs(),\n\t\t\t\tparser.FsDir(tc.args.rootDir),\n\t\t\t\tparser.FsFilters([]parser.FilterFn{\n\t\t\t\t\tparser.SkipDirs(),\n\t\t\t\t\tparser.SkipNotYAML(),\n\t\t\t\t\tparser.SkipEmpty(),\n\t\t\t\t\tSkipContains(\"examples/\"), // don't try to parse the examples in the package\n\t\t\t\t}...),\n\t\t\t)\n\t\t\tpkgEx := parser.NewFsBackend(\n\t\t\t\ttc.args.fs(),\n\t\t\t\tparser.FsDir(tc.args.examplesDir),\n\t\t\t\tparser.FsFilters(defaultFilters...),\n\t\t\t)\n\n\t\t\tbuilder := New(pkgBe, pkgEx, pkgp, examples.New())\n\n\t\t\timg, _, err := builder.Build(context.TODO())\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nBuildExamples(...): -want err, +got err:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\t// validate the xpkg img has the correct annotations, etc\n\t\t\tcontents, err := readImg(img)\n\t\t\t// sort the contents slice for test comparison\n\t\t\tsort.Strings(contents.labels)\n\n\t\t\tif diff := cmp.Diff(tc.want.pkgExists, len(contents.pkgBytes) != 0); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nBuildExamples(...): -want err, +got err:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.exExists, len(contents.exBytes) != 0); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nBuildExamples(...): -want err, +got err:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.labels, contents.labels, cmpopts.SortSlices(func(i, j int) bool {\n\t\t\t\treturn contents.labels[i] < contents.labels[j]\n\t\t\t})); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nBuildExamples(...): -want err, +got err:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(nil, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nBuildExamples(...): -want err, +got err:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype xpkgContents struct {\n\tlabels   []string\n\tpkgBytes []byte\n\texBytes  []byte\n}\n\nfunc readImg(i v1.Image) (xpkgContents, error) {\n\tcontents := xpkgContents{\n\t\tlabels: make([]string, 0),\n\t}\n\n\treader := mutate.Extract(i)\n\tfs := tarfs.New(tar.NewReader(reader))\n\n\tpkgYaml, err := fs.Open(StreamFile)\n\tif err != nil {\n\t\treturn contents, err\n\t}\n\n\tpkgBytes, err := io.ReadAll(pkgYaml)\n\tif err != nil {\n\t\treturn contents, err\n\t}\n\n\tcontents.pkgBytes = pkgBytes\n\n\texYaml, err := fs.Open(XpkgExamplesFile)\n\tif err != nil && !os.IsNotExist(err) {\n\t\treturn contents, err\n\t}\n\n\tif exYaml != nil {\n\t\texBytes, err := io.ReadAll(exYaml)\n\t\tif err != nil {\n\t\t\treturn contents, err\n\t\t}\n\n\t\tcontents.exBytes = exBytes\n\t}\n\n\tlabels, err := allLabels(i)\n\tif err != nil {\n\t\treturn contents, err\n\t}\n\n\tcontents.labels = labels\n\n\treturn contents, nil\n}\n\nfunc allLabels(i partial.WithConfigFile) ([]string, error) {\n\tlabels := []string{}\n\n\tcfgFile, err := i.ConfigFile()\n\tif err != nil {\n\t\treturn labels, err\n\t}\n\n\tcfg := cfgFile.Config\n\n\tfor _, label := range cfg.Labels {\n\t\tlabels = append(labels, label)\n\t}\n\n\treturn labels, nil\n}\n\n// This is equivalent to yaml.New. Duplicated here to avoid an import cycle.\nfunc yamlParser() (*parser.PackageParser, error) {\n\tmetaScheme, err := BuildMetaScheme()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tobjScheme, err := BuildObjectScheme()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn parser.New(metaScheme, objScheme), nil\n}\n"
  },
  {
    "path": "pkg/xpkg/cache.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 xpkg\n\nimport (\n\t\"compress/gzip\"\n\t\"io\"\n\t\"os\"\n\t\"sync\"\n\n\t\"github.com/spf13/afero\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\nconst (\n\terrGetNopCache = \"cannot get content from a NopCache\"\n)\n\nconst cacheContentExt = \".gz\"\n\n// A PackageCache caches package content.\ntype PackageCache interface {\n\tHas(id string) bool\n\tGet(id string) (io.ReadCloser, error)\n\tStore(id string, content io.ReadCloser) error\n\tDelete(id string) error\n}\n\n// FsPackageCache stores and retrieves package content in a filesystem-backed\n// cache in a thread-safe manner.\ntype FsPackageCache struct {\n\tdir string\n\tfs  afero.Fs\n\tmu  sync.RWMutex\n}\n\n// NewFsPackageCache creates a new FsPackageCache.\nfunc NewFsPackageCache(dir string, fs afero.Fs) *FsPackageCache {\n\treturn &FsPackageCache{\n\t\tdir: dir,\n\t\tfs:  fs,\n\t}\n}\n\n// Has indicates whether an item with the given id is in the cache.\nfunc (c *FsPackageCache) Has(id string) bool {\n\tif fi, err := c.fs.Stat(BuildPath(c.dir, id, cacheContentExt)); err == nil && !fi.IsDir() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// Get retrieves package contents from the cache.\nfunc (c *FsPackageCache) Get(id string) (io.ReadCloser, error) {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\n\tf, err := c.fs.Open(BuildPath(c.dir, id, cacheContentExt))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn GzipReadCloser(f)\n}\n\n// Store saves the package contents to the cache.\nfunc (c *FsPackageCache) Store(id string, content io.ReadCloser) error {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tcf, err := c.fs.Create(BuildPath(c.dir, id, cacheContentExt))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cf.Close() //nolint:errcheck // Error is checked in the happy path.\n\n\tw, err := gzip.NewWriterLevel(cf, gzip.BestSpeed)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = io.Copy(w, content)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// NOTE(hasheddan): gzip writer must be closed to ensure all data is flushed\n\t// to file.\n\tif err := w.Close(); err != nil {\n\t\treturn err\n\t}\n\n\treturn cf.Close()\n}\n\n// Delete removes package contents from the cache.\nfunc (c *FsPackageCache) Delete(id string) error {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\terr := c.fs.Remove(BuildPath(c.dir, id, cacheContentExt))\n\tif os.IsNotExist(err) {\n\t\treturn nil\n\t}\n\n\treturn err\n}\n\n// NopCache is a cache implementation that does not store anything and always\n// returns an error on get.\ntype NopCache struct{}\n\n// NewNopCache creates a new NopCache.\nfunc NewNopCache() *NopCache {\n\treturn &NopCache{}\n}\n\n// Has indicates whether content is in the NopCache.\nfunc (c *NopCache) Has(string) bool {\n\treturn false\n}\n\n// Get retrieves content from the NopCache.\nfunc (c *NopCache) Get(string) (io.ReadCloser, error) {\n\treturn nil, errors.New(errGetNopCache)\n}\n\n// Store saves content to the NopCache.\nfunc (c *NopCache) Store(string, io.ReadCloser) error {\n\treturn nil\n}\n\n// Delete removes content from the NopCache.\nfunc (c *NopCache) Delete(string) error {\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/xpkg/cache_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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\thttp://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 xpkg\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"io\"\n\t\"os\"\n\t\"syscall\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/spf13/afero\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nvar _ PackageCache = &FsPackageCache{}\n\nfunc TestHas(t *testing.T) {\n\tfs := afero.NewMemMapFs()\n\tcf, _ := fs.Create(\"/cache/exists.gz\")\n\t_ = fs.Mkdir(\"/cache/some-dir.gz\", os.ModeDir)\n\n\tdefer cf.Close()\n\n\ttype args struct {\n\t\tcache PackageCache\n\t\tid    string\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   bool\n\t}{\n\t\t\"Success\": {\n\t\t\treason: \"Should not return an error if package exists at path.\",\n\t\t\targs: args{\n\t\t\t\tcache: NewFsPackageCache(\"/cache\", fs),\n\t\t\t\tid:    \"exists\",\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t\"ErrNotExist\": {\n\t\t\treason: \"Should return error if package does not exist at path.\",\n\t\t\targs: args{\n\t\t\t\tcache: NewFsPackageCache(\"/cache\", fs),\n\t\t\t\tid:    \"not-exist\",\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t\t\"ErrIsDir\": {\n\t\t\treason: \"Should return error if path is a directory.\",\n\t\t\targs: args{\n\t\t\t\tcache: NewFsPackageCache(\"/cache\", fs),\n\t\t\t\tid:    \"some-dir.gz\",\n\t\t\t},\n\t\t\twant: false,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\th := tc.args.cache.Has(tc.args.id)\n\n\t\t\tif diff := cmp.Diff(tc.want, h); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nHas(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGet(t *testing.T) {\n\tfs := afero.NewMemMapFs()\n\tcf, _ := fs.Create(\"/cache/exists.gz\")\n\t// NOTE(hasheddan): valid gzip header.\n\tcf.Write([]byte{31, 139, 8, 0, 0, 0, 0, 0, 0, 0})\n\tcf, _ = fs.Create(\"/cache/not-gzip.gz\")\n\n\tcf.WriteString(\"some content\")\n\tdefer cf.Close()\n\n\ttype args struct {\n\t\tcache PackageCache\n\t\tid    string\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   error\n\t}{\n\t\t\"Success\": {\n\t\t\treason: \"Should not return an error if package exists at path.\",\n\t\t\targs: args{\n\t\t\t\tcache: NewFsPackageCache(\"/cache\", fs),\n\t\t\t\tid:    \"exists\",\n\t\t\t},\n\t\t},\n\t\t\"ErrNotGzip\": {\n\t\t\treason: \"Should return error if package does not exist at path.\",\n\t\t\targs: args{\n\t\t\t\tcache: NewFsPackageCache(\"/cache\", fs),\n\t\t\t\tid:    \"not-gzip\",\n\t\t\t},\n\t\t\twant: gzip.ErrHeader,\n\t\t},\n\t\t\"ErrNotExist\": {\n\t\t\treason: \"Should return error if package does not exist at path.\",\n\t\t\targs: args{\n\t\t\t\tcache: NewFsPackageCache(\"/cache\", fs),\n\t\t\t\tid:    \"not-exist\",\n\t\t\t},\n\t\t\twant: &os.PathError{Op: \"open\", Path: \"/cache/not-exist.gz\", Err: afero.ErrFileNotFound},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\t_, err := tc.args.cache.Get(tc.args.id)\n\t\t\tif diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nGet(...): -want err, +got err:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestStore(t *testing.T) {\n\tfs := afero.NewMemMapFs()\n\n\ttype args struct {\n\t\tcache PackageCache\n\t\tid    string\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   error\n\t}{\n\t\t\"Success\": {\n\t\t\treason: \"Should not return an error if package is created at path.\",\n\t\t\targs: args{\n\t\t\t\tcache: NewFsPackageCache(\"/cache\", fs),\n\t\t\t\tid:    \"exists-1234567\",\n\t\t\t},\n\t\t},\n\t\t\"ErrFailedCreate\": {\n\t\t\treason: \"Should return an error if file creation fails.\",\n\t\t\targs: args{\n\t\t\t\tcache: NewFsPackageCache(\"/cache\", afero.NewReadOnlyFs(fs)),\n\t\t\t\tid:    \"exists-1234567\",\n\t\t\t},\n\t\t\twant: syscall.EPERM,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := tc.args.cache.Store(tc.args.id, io.NopCloser(new(bytes.Buffer)))\n\t\t\tif diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nStore(...): -want err, +got err:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDelete(t *testing.T) {\n\tfs := afero.NewMemMapFs()\n\t_, _ = fs.Create(\"/cache/exists.xpkg\")\n\n\ttype args struct {\n\t\tcache PackageCache\n\t\tid    string\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   error\n\t}{\n\t\t\"Success\": {\n\t\t\treason: \"Should not return an error if package is deleted at path.\",\n\t\t\targs: args{\n\t\t\t\tcache: NewFsPackageCache(\"/cache\", fs),\n\t\t\t\tid:    \"exists\",\n\t\t\t},\n\t\t},\n\t\t\"SuccessNotExist\": {\n\t\t\treason: \"Should not return an error if package does not exist.\",\n\t\t\targs: args{\n\t\t\t\tcache: NewFsPackageCache(\"/cache\", fs),\n\t\t\t\tid:    \"not-exist\",\n\t\t\t},\n\t\t},\n\t\t\"ErrFailedDelete\": {\n\t\t\treason: \"Should return an error if file deletion fails.\",\n\t\t\targs: args{\n\t\t\t\tcache: NewFsPackageCache(\"/cache\", afero.NewReadOnlyFs(fs)),\n\t\t\t\tid:    \"exists-1234567\",\n\t\t\t},\n\t\t\twant: syscall.EPERM,\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := tc.args.cache.Delete(tc.args.id)\n\t\t\tif diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nStore(...): -want err, +got err:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/client.go",
    "content": "/*\nCopyright 2025 The Crossplane 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 xpkg\n\nimport (\n\t\"archive/tar\"\n\t\"context\"\n\t\"io\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/Masterminds/semver/v3\"\n\tpkgmetav1 \"github.com/crossplane/crossplane/apis/v2/pkg/meta/v1\"\n\tociname \"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/mutate\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg/parser\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg/signature\"\n)\n\n// Client is a client for fetching and parsing Crossplane packages.\ntype Client interface {\n\t// Get fetches and parses a complete package from the given reference.\n\t// The ref parameter is a package reference (e.g.,\n\t// \"registry.io/org/package:v1.0.0\" or \"registry.io/org/package@sha256:...\").\n\t//\n\t// Caching and ImageConfig path rewriting are handled transparently.\n\tGet(ctx context.Context, ref string, opts ...GetOption) (*Package, error)\n\n\t// ListVersions returns available versions for a package source.\n\t// The source parameter is the package path without tag/digest\n\t// (e.g., \"registry.io/org/package\").\n\t//\n\t// Honors ImageConfig path rewriting when listing versions.\n\tListVersions(ctx context.Context, source string, opts ...GetOption) ([]string, error)\n}\n\n// ImageConfig represents an ImageConfig that was applied during package fetch.\ntype ImageConfig struct {\n\tName   string\n\tReason ImageConfigReason\n}\n\n// ImageConfigReason describes why an ImageConfig was applied.\ntype ImageConfigReason string\n\nconst (\n\t// ImageConfigReasonRewrite indicates the ImageConfig rewrote the image\n\t// path.\n\tImageConfigReasonRewrite ImageConfigReason = \"RewriteImage\"\n\n\t// ImageConfigReasonSetPullSecret indicates the ImageConfig provided a\n\t// pull secret.\n\tImageConfigReasonSetPullSecret ImageConfigReason = \"SetImagePullSecret\"\n\n\t// ImageConfigReasonVerify indicates the ImageConfig provided signature\n\t// verification config.\n\tImageConfigReasonVerify ImageConfigReason = \"VerifyImage\"\n)\n\n// SupportedImageConfigs returns the ImageConfigReasons that Client may return.\n// Callers tracking applied ImageConfigs should clear all of these before\n// setting the ones returned by Client.Get, to handle the case where a\n// previously-applied ImageConfig no longer matches.\nfunc SupportedImageConfigs() []ImageConfigReason {\n\treturn []ImageConfigReason{\n\t\tImageConfigReasonRewrite,\n\t\tImageConfigReasonSetPullSecret,\n\t\tImageConfigReasonVerify,\n\t}\n}\n\n// maxPackageSize is the maximum size of a package.yaml file that can be parsed.\n// This limit prevents denial of service attacks via maliciously large packages.\nconst maxPackageSize = 200 << 20 // 200 MB\n\n// Package represents a successfully fetched package with all its content.\ntype Package struct {\n\t*parser.Package\n\n\t// Digest is the immutable content identifier (sha256 from OCI image).\n\tDigest string\n\n\t// Version is the package version, either a semver tag (v1.0.0) or digest\n\t// (sha256:abc123). This is extracted from the original reference used to\n\t// fetch the package.\n\tVersion string\n\n\t// Source is the package source without tag/digest, normalized.\n\t// This is the ORIGINAL source before any ImageConfig rewriting.\n\tSource string\n\n\t// ResolvedVersion is the package version after ImageConfig rewriting.\n\t// May differ from Version if an ImageConfig rewrote the tag/digest.\n\tResolvedVersion string\n\n\t// ResolvedSource is the source after ImageConfig path rewriting.\n\t// May be the same as Source if no rewriting occurred.\n\tResolvedSource string\n\n\t// AppliedImageConfigs tracks which ImageConfigs were applied during fetch.\n\tAppliedImageConfigs []ImageConfig\n}\n\n// DigestHex returns the hex string of the digest without the algorithm prefix.\n// Returns empty string if the digest cannot be parsed.\nfunc (p *Package) DigestHex() string {\n\thash, err := v1.NewHash(p.Digest)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn hash.Hex\n}\n\n// GetMeta returns the package metadata object.\n// Returns nil if the package doesn't contain exactly one metadata object.\nfunc (p *Package) GetMeta() pkgmetav1.Pkg {\n\tmeta := p.Package.GetMeta()\n\tif len(meta) != 1 {\n\t\treturn nil\n\t}\n\n\tpkg, _ := TryConvertToPkg(meta[0], &pkgmetav1.Provider{}, &pkgmetav1.Configuration{}, &pkgmetav1.Function{})\n\treturn pkg\n}\n\n// GetDependencies returns the package dependencies from metadata.\n// Returns nil if metadata cannot be extracted.\nfunc (p *Package) GetDependencies() []pkgmetav1.Dependency {\n\tmeta := p.GetMeta()\n\tif meta == nil {\n\t\treturn nil\n\t}\n\treturn meta.GetDependencies()\n}\n\n// Ref returns the full original package reference (Source + Version).\nfunc (p *Package) Ref() string {\n\treturn BuildPackageRef(p.Source, p.Version)\n}\n\n// ResolvedRef returns the full resolved package reference after ImageConfig\n// rewriting (ResolvedSource + ResolvedVersion).\nfunc (p *Package) ResolvedRef() string {\n\treturn BuildPackageRef(p.ResolvedSource, p.ResolvedVersion)\n}\n\n// BuildPackageRef combines a source and version into a full package reference.\n// Uses \"@\" for digests (version contains \":\") and \":\" for tags.\nfunc BuildPackageRef(source, version string) string {\n\tif strings.Contains(version, \":\") {\n\t\treturn source + \"@\" + version\n\t}\n\treturn source + \":\" + version\n}\n\n// GetOption configures per-request package fetching behavior.\ntype GetOption func(*GetConfig)\n\n// WithPullSecrets specifies secrets for authenticating to private registries.\n// These are combined with any pull secrets from ImageConfig.\nfunc WithPullSecrets(secrets ...string) GetOption {\n\treturn func(c *GetConfig) {\n\t\tc.pullSecrets = secrets\n\t}\n}\n\n// WithPullPolicy specifies when to fetch from the registry vs use cache.\n// Default is IfNotPresent.\nfunc WithPullPolicy(policy corev1.PullPolicy) GetOption {\n\treturn func(c *GetConfig) {\n\t\tc.pullPolicy = policy\n\t}\n}\n\n// GetConfig configures the client's Get method.\ntype GetConfig struct {\n\tpullSecrets []string\n\tpullPolicy  corev1.PullPolicy\n}\n\n// CachedClient implements Client with caching support.\ntype CachedClient struct {\n\tfetcher   Fetcher\n\tparser    parser.Parser\n\tcache     PackageCache\n\tconfig    ConfigStore\n\tvalidator signature.Validator\n}\n\n// NewCachedClient creates a new package client.\nfunc NewCachedClient(f Fetcher, p parser.Parser, c PackageCache, s ConfigStore, v signature.Validator) *CachedClient {\n\treturn &CachedClient{\n\t\tfetcher:   f,\n\t\tparser:    p,\n\t\tcache:     c,\n\t\tconfig:    s,\n\t\tvalidator: v,\n\t}\n}\n\n// Get fetches and parses a complete package.\nfunc (c *CachedClient) Get(ctx context.Context, ref string, opts ...GetOption) (*Package, error) {\n\tcfg := &GetConfig{\n\t\tpullPolicy: corev1.PullIfNotPresent,\n\t}\n\tfor _, opt := range opts {\n\t\topt(cfg)\n\t}\n\n\toriginalRef := ref\n\tresolvedRef := ref\n\n\tvar applied []ImageConfig\n\n\tname, rewritten, err := c.config.RewritePath(ctx, ref)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"cannot get image rewrite config\")\n\t}\n\tif rewritten != \"\" {\n\t\tresolvedRef = rewritten\n\t\tapplied = append(applied, ImageConfig{Name: name, Reason: ImageConfigReasonRewrite})\n\t}\n\n\tparsedOriginalRef, err := ociname.ParseReference(originalRef)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"cannot parse package reference %s\", originalRef)\n\t}\n\n\tparsedResolvedRef, err := ociname.ParseReference(resolvedRef)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"cannot parse resolved package reference %s\", resolvedRef)\n\t}\n\n\tsecrets := cfg.pullSecrets\n\n\tname, secret, err := c.config.PullSecretFor(ctx, resolvedRef)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"cannot get image pull secret config\")\n\t}\n\tif secret != \"\" {\n\t\tsecrets = append(secrets, secret)\n\t\tapplied = append(applied, ImageConfig{Name: name, Reason: ImageConfigReasonSetPullSecret})\n\t}\n\n\tvar digest string\n\tif d, ok := parsedResolvedRef.(ociname.Digest); ok {\n\t\tdigest = d.Identifier()\n\t} else {\n\t\tdesc, err := c.fetcher.Head(ctx, parsedResolvedRef, secrets...)\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrapf(err, \"cannot resolve %s to digest\", parsedResolvedRef.String())\n\t\t}\n\t\tdigest = desc.Digest.String()\n\t}\n\n\tcacheKey := FriendlyID(ParsePackageSourceFromReference(parsedOriginalRef), digest)\n\n\tif cfg.pullPolicy != corev1.PullAlways {\n\t\trc, err := c.cache.Get(cacheKey)\n\t\tif err == nil {\n\t\t\tpkg, err := c.parser.Parse(ctx, struct {\n\t\t\t\tio.Reader\n\t\t\t\tio.Closer\n\t\t\t}{\n\t\t\t\tReader: io.LimitReader(rc, maxPackageSize),\n\t\t\t\tCloser: rc,\n\t\t\t})\n\t\t\trc.Close() //nolint:errcheck // Only open for reading.\n\t\t\tif err == nil {\n\t\t\t\treturn &Package{\n\t\t\t\t\tPackage:             pkg,\n\t\t\t\t\tDigest:              digest,\n\t\t\t\t\tVersion:             parsedOriginalRef.Identifier(),\n\t\t\t\t\tSource:              ParsePackageSourceFromReference(parsedOriginalRef),\n\t\t\t\t\tResolvedVersion:     parsedResolvedRef.Identifier(),\n\t\t\t\t\tResolvedSource:      ParsePackageSourceFromReference(parsedResolvedRef),\n\t\t\t\t\tAppliedImageConfigs: applied,\n\t\t\t\t}, nil\n\t\t\t}\n\t\t}\n\t}\n\n\tif cfg.pullPolicy == corev1.PullNever {\n\t\treturn nil, errors.New(\"package not in cache and pull policy is Never\")\n\t}\n\n\t// Verification only happens if we don't get a cache hit. This means we\n\t// won't verify packages if verification is enabled _after_ the package\n\t// was fetched and cached.\n\t//\n\t// We cache gzipped package.yaml files from the OCI image. We can't\n\t// cryptographically guarantee the cached files are the ones from the\n\t// verified package - e.g. if someone manually populated the cache. So\n\t// we're making a trade-off here. The only way to guarantee we're using\n\t// a verified OCI image on every reconcile would be to disable caching,\n\t// or cache the entire OCI image.\n\tname, vc, err := c.config.ImageVerificationConfigFor(ctx, resolvedRef)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"cannot get image verification config\")\n\t}\n\tif vc != nil {\n\t\tref := parsedResolvedRef.Context().Digest(digest)\n\t\tif err := c.validator.Validate(ctx, ref, vc, secrets...); err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"signature verification failed\")\n\t\t}\n\t\tapplied = append(applied, ImageConfig{Name: name, Reason: ImageConfigReasonVerify})\n\t}\n\n\timg, err := c.fetcher.Fetch(ctx, parsedResolvedRef, secrets...)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"cannot fetch package %s\", resolvedRef)\n\t}\n\n\trc, err := ExtractPackageYAML(img)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"cannot extract package content from %s\", resolvedRef)\n\t}\n\n\tpipeR, pipeW := io.Pipe()\n\tteeRC := TeeReadCloser(rc, pipeW)\n\tdefer teeRC.Close() //nolint:errcheck // Would only error if we called pipeW.CloseWithError()\n\n\tgo func() {\n\t\tdefer pipeR.Close() //nolint:errcheck // Only open for reading.\n\t\t_ = c.cache.Store(cacheKey, pipeR)\n\t}()\n\n\tpkg, err := c.parser.Parse(ctx, struct {\n\t\tio.Reader\n\t\tio.Closer\n\t}{\n\t\tReader: io.LimitReader(teeRC, maxPackageSize),\n\t\tCloser: teeRC,\n\t})\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"cannot parse package %s\", resolvedRef)\n\t}\n\n\treturn &Package{\n\t\tPackage:             pkg,\n\t\tDigest:              digest,\n\t\tVersion:             parsedOriginalRef.Identifier(),\n\t\tSource:              ParsePackageSourceFromReference(parsedOriginalRef),\n\t\tResolvedVersion:     parsedResolvedRef.Identifier(),\n\t\tResolvedSource:      ParsePackageSourceFromReference(parsedResolvedRef),\n\t\tAppliedImageConfigs: applied,\n\t}, nil\n}\n\n// ListVersions returns available versions for a package source.\nfunc (c *CachedClient) ListVersions(ctx context.Context, source string, opts ...GetOption) ([]string, error) {\n\tcfg := &GetConfig{\n\t\tpullPolicy: corev1.PullIfNotPresent,\n\t}\n\tfor _, opt := range opts {\n\t\topt(cfg)\n\t}\n\n\tresolvedSource := source\n\n\t_, rewritten, err := c.config.RewritePath(ctx, source)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"cannot get image rewrite config\")\n\t}\n\tif rewritten != \"\" {\n\t\tresolvedSource = rewritten\n\t}\n\n\tref, err := ociname.ParseReference(resolvedSource)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"cannot parse package source %s\", resolvedSource)\n\t}\n\n\tsecrets := cfg.pullSecrets\n\t_, secret, err := c.config.PullSecretFor(ctx, resolvedSource)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"cannot get image pull secret config\")\n\t}\n\tif secret != \"\" {\n\t\tsecrets = append(secrets, secret)\n\t}\n\n\ttags, err := c.fetcher.Tags(ctx, ref, secrets...)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"cannot list tags for %s\", resolvedSource)\n\t}\n\n\treturn FilterAndSortVersions(tags), nil\n}\n\n// ExtractPackageYAML extracts the package.yaml file from an OCI image.\n// It looks for the annotated package layer (io.crossplane.xpkg: base) and\n// falls back to the flattened filesystem from all layers if no annotation\n// is found, per the xpkg specification.\nfunc ExtractPackageYAML(img v1.Image) (io.ReadCloser, error) {\n\tmanifest, err := img.Manifest()\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"cannot get image manifest\")\n\t}\n\n\tvar tarc io.ReadCloser\n\tfor _, l := range manifest.Layers {\n\t\tif l.Annotations[AnnotationKey] != PackageAnnotation {\n\t\t\tcontinue\n\t\t}\n\n\t\tlayer, err := img.LayerByDigest(l.Digest)\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"cannot get annotated layer\")\n\t\t}\n\n\t\ttarc, err = layer.Uncompressed()\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"cannot uncompress layer\")\n\t\t}\n\t\tbreak // Only one annotated layer expected per xpkg spec.\n\t}\n\n\tif tarc == nil {\n\t\ttarc = mutate.Extract(img)\n\t}\n\n\tt := tar.NewReader(tarc)\n\n\tfor {\n\t\th, err := t.Next()\n\t\tif err == io.EOF {\n\t\t\treturn nil, errors.New(\"package.yaml not found in package\")\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"cannot read package contents\")\n\t\t}\n\n\t\tif filepath.Base(h.Name) == StreamFile {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn JoinedReadCloser(t, tarc), nil\n}\n\n// FilterAndSortVersions filters tags to valid semver versions and sorts them\n// in ascending order (oldest first).\nfunc FilterAndSortVersions(tags []string) []string {\n\tversions := make([]*semver.Version, 0, len(tags))\n\tfor _, tag := range tags {\n\t\tv, err := semver.NewVersion(tag)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tversions = append(versions, v)\n\t}\n\n\tsort.Slice(versions, func(i, j int) bool {\n\t\treturn versions[i].LessThan(versions[j])\n\t})\n\n\tresult := make([]string, len(versions))\n\tfor i, v := range versions {\n\t\tresult[i] = v.Original()\n\t}\n\n\treturn result\n}\n"
  },
  {
    "path": "pkg/xpkg/client_test.go",
    "content": "/*\nCopyright 2025 The Crossplane 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 xpkg\n\nimport (\n\t\"archive/tar\"\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/crossplane/crossplane/apis/v2/pkg/v1beta1\"\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg/parser\"\n)\n\nconst (\n\ttestDigest = \"sha256:abc123def456789012345678901234567890123456789012345678901234abcd\"\n\ttestSource = \"xpkg.crossplane.io/crossplane-contrib/provider-aws\"\n\ttestTag    = \"v1.0.0\"\n)\n\nvar _ Fetcher = &MockFetcher{}\n\ntype MockFetcher struct {\n\tMockFetch func(context.Context, name.Reference, ...string) (v1.Image, error)\n\tMockHead  func(context.Context, name.Reference, ...string) (*v1.Descriptor, error)\n\tMockTags  func(context.Context, name.Reference, ...string) ([]string, error)\n}\n\nfunc (m *MockFetcher) Fetch(ctx context.Context, ref name.Reference, secrets ...string) (v1.Image, error) {\n\treturn m.MockFetch(ctx, ref, secrets...)\n}\n\nfunc (m *MockFetcher) Head(ctx context.Context, ref name.Reference, secrets ...string) (*v1.Descriptor, error) {\n\treturn m.MockHead(ctx, ref, secrets...)\n}\n\nfunc (m *MockFetcher) Tags(ctx context.Context, ref name.Reference, secrets ...string) ([]string, error) {\n\treturn m.MockTags(ctx, ref, secrets...)\n}\n\nvar _ PackageCache = &MockCache{}\n\ntype MockCache struct {\n\tMockGet    func(string) (io.ReadCloser, error)\n\tMockStore  func(string, io.ReadCloser) error\n\tMockDelete func(string) error\n\tMockHas    func(string) bool\n}\n\nfunc (m *MockCache) Get(key string) (io.ReadCloser, error) {\n\treturn m.MockGet(key)\n}\n\nfunc (m *MockCache) Store(key string, rc io.ReadCloser) error {\n\treturn m.MockStore(key, rc)\n}\n\nfunc (m *MockCache) Delete(key string) error {\n\treturn m.MockDelete(key)\n}\n\nfunc (m *MockCache) Has(key string) bool {\n\treturn m.MockHas(key)\n}\n\nvar _ ConfigStore = &MockConfigStore{}\n\ntype MockConfigStore struct {\n\tMockRewritePath                func(context.Context, string) (string, string, error)\n\tMockPullSecretFor              func(context.Context, string) (string, string, error)\n\tMockImageVerificationConfigFor func(context.Context, string) (string, *v1beta1.ImageVerification, error)\n\tMockRuntimeConfigFor           func(context.Context, string) (string, *v1beta1.ImageRuntime, error)\n}\n\nfunc (m *MockConfigStore) RewritePath(ctx context.Context, ref string) (string, string, error) {\n\treturn m.MockRewritePath(ctx, ref)\n}\n\nfunc (m *MockConfigStore) PullSecretFor(ctx context.Context, ref string) (string, string, error) {\n\treturn m.MockPullSecretFor(ctx, ref)\n}\n\nfunc (m *MockConfigStore) ImageVerificationConfigFor(ctx context.Context, ref string) (string, *v1beta1.ImageVerification, error) {\n\treturn m.MockImageVerificationConfigFor(ctx, ref)\n}\n\nfunc (m *MockConfigStore) RuntimeConfigFor(ctx context.Context, ref string) (string, *v1beta1.ImageRuntime, error) {\n\treturn m.MockRuntimeConfigFor(ctx, ref)\n}\n\ntype MockValidator struct {\n\tMockValidate func(context.Context, name.Reference, *v1beta1.ImageVerification, ...string) error\n}\n\nfunc (m *MockValidator) Validate(ctx context.Context, ref name.Reference, config *v1beta1.ImageVerification, pullSecrets ...string) error {\n\treturn m.MockValidate(ctx, ref, config, pullSecrets...)\n}\n\ntype MockImage struct {\n\tv1.Image\n\tMockManifest      func() (*v1.Manifest, error)\n\tMockLayerByDigest func(v1.Hash) (v1.Layer, error)\n\tMockLayers        func() ([]v1.Layer, error)\n}\n\nfunc (m *MockImage) Manifest() (*v1.Manifest, error) {\n\treturn m.MockManifest()\n}\n\nfunc (m *MockImage) LayerByDigest(h v1.Hash) (v1.Layer, error) {\n\treturn m.MockLayerByDigest(h)\n}\n\nfunc (m *MockImage) Layers() ([]v1.Layer, error) {\n\tif m.MockLayers != nil {\n\t\treturn m.MockLayers()\n\t}\n\treturn nil, nil\n}\n\ntype MockLayer struct {\n\tv1.Layer\n\tcontent string\n}\n\nfunc NewMockLayer(content string) *MockLayer {\n\treturn &MockLayer{content: content}\n}\n\nfunc (m *MockLayer) Uncompressed() (io.ReadCloser, error) {\n\treturn io.NopCloser(strings.NewReader(m.content)), nil\n}\n\nfunc CreateTarWithPackageYAML(packageYAML string) string {\n\tvar buf bytes.Buffer\n\ttw := tar.NewWriter(&buf)\n\n\ttw.WriteHeader(&tar.Header{\n\t\tName: StreamFile,\n\t\tMode: 0o644,\n\t\tSize: int64(len(packageYAML)),\n\t})\n\ttw.Write([]byte(packageYAML))\n\ttw.Close()\n\n\treturn buf.String()\n}\n\nfunc NewTestParser(t *testing.T) parser.Parser {\n\tt.Helper()\n\tmeta, err := BuildMetaScheme()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to build meta scheme: %v\", err)\n\t}\n\tobj, err := BuildObjectScheme()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to build object scheme: %v\", err)\n\t}\n\treturn parser.New(meta, obj)\n}\n\nfunc NewTestPackage(t *testing.T, metaJSON string, objectsJSON ...string) *parser.Package {\n\tt.Helper()\n\n\tp := NewTestParser(t)\n\n\tvar allJSON strings.Builder\n\tallJSON.WriteString(\"---\\n\")\n\tallJSON.WriteString(metaJSON)\n\tfor _, objJSON := range objectsJSON {\n\t\tallJSON.WriteString(\"\\n---\\n\")\n\t\tallJSON.WriteString(objJSON)\n\t}\n\n\tpkg, err := p.Parse(context.Background(), io.NopCloser(strings.NewReader(allJSON.String())))\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse test package: %v\", err)\n\t}\n\n\treturn pkg\n}\n\nfunc PackageComparer() cmp.Option {\n\treturn cmp.Comparer(func(a, b *parser.Package) bool {\n\t\tif a == nil && b == nil {\n\t\t\treturn true\n\t\t}\n\t\tif a == nil || b == nil {\n\t\t\treturn false\n\t\t}\n\n\t\tif !cmp.Equal(a.GetMeta(), b.GetMeta()) {\n\t\t\treturn false\n\t\t}\n\n\t\treturn cmp.Equal(a.GetObjects(), b.GetObjects())\n\t})\n}\n\nfunc TestClientGet(t *testing.T) {\n\tproviderMeta := `{\"apiVersion\":\"meta.pkg.crossplane.io/v1\",\"kind\":\"Provider\",\"metadata\":{\"name\":\"provider-aws\"}}`\n\ttarContent := CreateTarWithPackageYAML(providerMeta)\n\n\ttype args struct {\n\t\tref  string\n\t\topts []GetOption\n\t}\n\ttype want struct {\n\t\tpkg *Package\n\t\terr error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tclient *CachedClient\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"SuccessWithTag\": {\n\t\t\treason: \"Should successfully fetch and parse a package with a tag reference\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn &v1.Descriptor{\n\t\t\t\t\t\t\tDigest: v1.Hash{\n\t\t\t\t\t\t\t\tAlgorithm: \"sha256\",\n\t\t\t\t\t\t\t\tHex:       \"abc123def456789012345678901234567890123456789012345678901234abcd\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t\tMockFetch: func(_ context.Context, _ name.Reference, _ ...string) (v1.Image, error) {\n\t\t\t\t\t\treturn &MockImage{\n\t\t\t\t\t\t\tMockManifest: func() (*v1.Manifest, error) {\n\t\t\t\t\t\t\t\treturn &v1.Manifest{\n\t\t\t\t\t\t\t\t\tLayers: []v1.Descriptor{\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\t\t\t\t\tAnnotationKey: PackageAnnotation,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tDigest: v1.Hash{Algorithm: \"sha256\", Hex: \"layer123\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tMockLayerByDigest: func(_ v1.Hash) (v1.Layer, error) {\n\t\t\t\t\t\t\t\treturn NewMockLayer(tarContent), nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tparser: NewTestParser(t),\n\t\t\t\tcache: &MockCache{\n\t\t\t\t\tMockGet: func(_ string) (io.ReadCloser, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"not in cache\")\n\t\t\t\t\t},\n\t\t\t\t\tMockStore: func(_ string, rc io.ReadCloser) error {\n\t\t\t\t\t\t_, _ = io.Copy(io.Discard, rc)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref: testSource + \":\" + testTag,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpkg: &Package{\n\t\t\t\t\tPackage:         NewTestPackage(t, providerMeta),\n\t\t\t\t\tDigest:          testDigest,\n\t\t\t\t\tVersion:         testTag,\n\t\t\t\t\tSource:          testSource,\n\t\t\t\t\tResolvedVersion: testTag,\n\t\t\t\t\tResolvedSource:  testSource,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessWithDigest\": {\n\t\t\treason: \"Should successfully fetch a package with a digest reference without calling Head\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"Head should not be called for digest refs\")\n\t\t\t\t\t},\n\t\t\t\t\tMockFetch: func(_ context.Context, _ name.Reference, _ ...string) (v1.Image, error) {\n\t\t\t\t\t\treturn &MockImage{\n\t\t\t\t\t\t\tMockManifest: func() (*v1.Manifest, error) {\n\t\t\t\t\t\t\t\treturn &v1.Manifest{\n\t\t\t\t\t\t\t\t\tLayers: []v1.Descriptor{\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\t\t\t\t\tAnnotationKey: PackageAnnotation,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tDigest: v1.Hash{Algorithm: \"sha256\", Hex: \"layer123\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tMockLayerByDigest: func(_ v1.Hash) (v1.Layer, error) {\n\t\t\t\t\t\t\t\treturn NewMockLayer(tarContent), nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tparser: NewTestParser(t),\n\t\t\t\tcache: &MockCache{\n\t\t\t\t\tMockGet: func(_ string) (io.ReadCloser, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"not in cache\")\n\t\t\t\t\t},\n\t\t\t\t\tMockStore: func(_ string, rc io.ReadCloser) error {\n\t\t\t\t\t\t_, _ = io.Copy(io.Discard, rc)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref: testSource + \"@\" + testDigest,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpkg: &Package{\n\t\t\t\t\tPackage:         NewTestPackage(t, providerMeta),\n\t\t\t\t\tDigest:          testDigest,\n\t\t\t\t\tVersion:         testDigest,\n\t\t\t\t\tSource:          testSource,\n\t\t\t\t\tResolvedVersion: testDigest,\n\t\t\t\t\tResolvedSource:  testSource,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessFromCache\": {\n\t\t\treason: \"Should return cached package without fetching from registry\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn &v1.Descriptor{\n\t\t\t\t\t\t\tDigest: v1.Hash{\n\t\t\t\t\t\t\t\tAlgorithm: \"sha256\",\n\t\t\t\t\t\t\t\tHex:       \"abc123def456789012345678901234567890123456789012345678901234abcd\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t\tMockFetch: func(_ context.Context, _ name.Reference, _ ...string) (v1.Image, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"Fetch should not be called when cached\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tparser: NewTestParser(t),\n\t\t\t\tcache: &MockCache{\n\t\t\t\t\tMockGet: func(_ string) (io.ReadCloser, error) {\n\t\t\t\t\t\treturn io.NopCloser(strings.NewReader(providerMeta)), nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref:  testSource + \":\" + testTag,\n\t\t\t\topts: []GetOption{WithPullPolicy(corev1.PullIfNotPresent)},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpkg: &Package{\n\t\t\t\t\tPackage:         NewTestPackage(t, providerMeta),\n\t\t\t\t\tDigest:          testDigest,\n\t\t\t\t\tVersion:         testTag,\n\t\t\t\t\tSource:          testSource,\n\t\t\t\t\tResolvedVersion: testTag,\n\t\t\t\t\tResolvedSource:  testSource,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessWithImageConfigRewrite\": {\n\t\t\treason: \"Should use rewritten path from ImageConfig and track which config was applied\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn &v1.Descriptor{\n\t\t\t\t\t\t\tDigest: v1.Hash{\n\t\t\t\t\t\t\t\tAlgorithm: \"sha256\",\n\t\t\t\t\t\t\t\tHex:       \"abc123def456789012345678901234567890123456789012345678901234abcd\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t\tMockFetch: func(_ context.Context, _ name.Reference, _ ...string) (v1.Image, error) {\n\t\t\t\t\t\treturn &MockImage{\n\t\t\t\t\t\t\tMockManifest: func() (*v1.Manifest, error) {\n\t\t\t\t\t\t\t\treturn &v1.Manifest{\n\t\t\t\t\t\t\t\t\tLayers: []v1.Descriptor{\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\t\t\t\t\tAnnotationKey: PackageAnnotation,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tDigest: v1.Hash{Algorithm: \"sha256\", Hex: \"layer123\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tMockLayerByDigest: func(_ v1.Hash) (v1.Layer, error) {\n\t\t\t\t\t\t\t\treturn NewMockLayer(tarContent), nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tparser: NewTestParser(t),\n\t\t\t\tcache: &MockCache{\n\t\t\t\t\tMockGet: func(_ string) (io.ReadCloser, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"not in cache\")\n\t\t\t\t\t},\n\t\t\t\t\tMockStore: func(_ string, rc io.ReadCloser) error {\n\t\t\t\t\t\t_, _ = io.Copy(io.Discard, rc)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"mirror-config\", \"private-registry.io/mirror/provider-aws:v1.0.0\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref: testSource + \":\" + testTag,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpkg: &Package{\n\t\t\t\t\tPackage:         NewTestPackage(t, providerMeta),\n\t\t\t\t\tDigest:          testDigest,\n\t\t\t\t\tVersion:         testTag,\n\t\t\t\t\tSource:          testSource,\n\t\t\t\t\tResolvedVersion: testTag,\n\t\t\t\t\tResolvedSource:  \"private-registry.io/mirror/provider-aws\",\n\t\t\t\t\tAppliedImageConfigs: []ImageConfig{\n\t\t\t\t\t\t{Name: \"mirror-config\", Reason: ImageConfigReasonRewrite},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessWithImageConfigRewriteAndPullSecret\": {\n\t\t\treason: \"Should track both rewrite and pull secret ImageConfigs when both are applied\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn &v1.Descriptor{\n\t\t\t\t\t\t\tDigest: v1.Hash{\n\t\t\t\t\t\t\t\tAlgorithm: \"sha256\",\n\t\t\t\t\t\t\t\tHex:       \"abc123def456789012345678901234567890123456789012345678901234abcd\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t\tMockFetch: func(_ context.Context, _ name.Reference, _ ...string) (v1.Image, error) {\n\t\t\t\t\t\treturn &MockImage{\n\t\t\t\t\t\t\tMockManifest: func() (*v1.Manifest, error) {\n\t\t\t\t\t\t\t\treturn &v1.Manifest{\n\t\t\t\t\t\t\t\t\tLayers: []v1.Descriptor{\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\t\t\t\t\tAnnotationKey: PackageAnnotation,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tDigest: v1.Hash{Algorithm: \"sha256\", Hex: \"layer123\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tMockLayerByDigest: func(_ v1.Hash) (v1.Layer, error) {\n\t\t\t\t\t\t\t\treturn NewMockLayer(tarContent), nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tparser: NewTestParser(t),\n\t\t\t\tcache: &MockCache{\n\t\t\t\t\tMockGet: func(_ string) (io.ReadCloser, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"not in cache\")\n\t\t\t\t\t},\n\t\t\t\t\tMockStore: func(_ string, rc io.ReadCloser) error {\n\t\t\t\t\t\t_, _ = io.Copy(io.Discard, rc)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"mirror-config\", \"private-registry.io/mirror/provider-aws:v1.0.0\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"secret-config\", \"registry-secret\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref: testSource + \":\" + testTag,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpkg: &Package{\n\t\t\t\t\tPackage:         NewTestPackage(t, providerMeta),\n\t\t\t\t\tDigest:          testDigest,\n\t\t\t\t\tVersion:         testTag,\n\t\t\t\t\tSource:          testSource,\n\t\t\t\t\tResolvedVersion: testTag,\n\t\t\t\t\tResolvedSource:  \"private-registry.io/mirror/provider-aws\",\n\t\t\t\t\tAppliedImageConfigs: []ImageConfig{\n\t\t\t\t\t\t{Name: \"mirror-config\", Reason: ImageConfigReasonRewrite},\n\t\t\t\t\t\t{Name: \"secret-config\", Reason: ImageConfigReasonSetPullSecret},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessWithPullAlways\": {\n\t\t\treason: \"Should bypass cache when PullAlways is specified\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn &v1.Descriptor{\n\t\t\t\t\t\t\tDigest: v1.Hash{\n\t\t\t\t\t\t\t\tAlgorithm: \"sha256\",\n\t\t\t\t\t\t\t\tHex:       \"abc123def456789012345678901234567890123456789012345678901234abcd\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t\tMockFetch: func(_ context.Context, _ name.Reference, _ ...string) (v1.Image, error) {\n\t\t\t\t\t\treturn &MockImage{\n\t\t\t\t\t\t\tMockManifest: func() (*v1.Manifest, error) {\n\t\t\t\t\t\t\t\treturn &v1.Manifest{\n\t\t\t\t\t\t\t\t\tLayers: []v1.Descriptor{\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\t\t\t\t\tAnnotationKey: PackageAnnotation,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tDigest: v1.Hash{Algorithm: \"sha256\", Hex: \"layer123\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tMockLayerByDigest: func(_ v1.Hash) (v1.Layer, error) {\n\t\t\t\t\t\t\t\treturn NewMockLayer(tarContent), nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tparser: NewTestParser(t),\n\t\t\t\tcache: &MockCache{\n\t\t\t\t\tMockGet: func(_ string) (io.ReadCloser, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"cache should not be checked with PullAlways\")\n\t\t\t\t\t},\n\t\t\t\t\tMockStore: func(_ string, rc io.ReadCloser) error {\n\t\t\t\t\t\t_, _ = io.Copy(io.Discard, rc)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref:  testSource + \":\" + testTag,\n\t\t\t\topts: []GetOption{WithPullPolicy(corev1.PullAlways)},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpkg: &Package{\n\t\t\t\t\tPackage:         NewTestPackage(t, providerMeta),\n\t\t\t\t\tDigest:          testDigest,\n\t\t\t\t\tVersion:         testTag,\n\t\t\t\t\tSource:          testSource,\n\t\t\t\t\tResolvedVersion: testTag,\n\t\t\t\t\tResolvedSource:  testSource,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"ErrorPullNeverNotInCache\": {\n\t\t\treason: \"Should return error when PullNever is specified and package not in cache\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn &v1.Descriptor{\n\t\t\t\t\t\t\tDigest: v1.Hash{\n\t\t\t\t\t\t\t\tAlgorithm: \"sha256\",\n\t\t\t\t\t\t\t\tHex:       \"abc123def456789012345678901234567890123456789012345678901234abcd\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t\tMockFetch: func(_ context.Context, _ name.Reference, _ ...string) (v1.Image, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"Fetch should not be called with PullNever\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tparser: NewTestParser(t),\n\t\t\t\tcache: &MockCache{\n\t\t\t\t\tMockGet: func(_ string) (io.ReadCloser, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"not in cache\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref:  testSource + \":\" + testTag,\n\t\t\t\topts: []GetOption{WithPullPolicy(corev1.PullNever)},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t\t\"ErrorInvalidReference\": {\n\t\t\treason: \"Should return error for invalid package reference\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref: \"invalid::reference\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t\t\"ErrorHeadFails\": {\n\t\t\treason: \"Should return error when Head request fails\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"network error\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tcache: &MockCache{\n\t\t\t\t\tMockGet: func(_ string) (io.ReadCloser, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"not in cache\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref: testSource + \":\" + testTag,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t\t\"ErrorFetchFails\": {\n\t\t\treason: \"Should return error when Fetch fails\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn &v1.Descriptor{\n\t\t\t\t\t\t\tDigest: v1.Hash{\n\t\t\t\t\t\t\t\tAlgorithm: \"sha256\",\n\t\t\t\t\t\t\t\tHex:       \"abc123def456789012345678901234567890123456789012345678901234abcd\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t\tMockFetch: func(_ context.Context, _ name.Reference, _ ...string) (v1.Image, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"fetch failed\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tcache: &MockCache{\n\t\t\t\t\tMockGet: func(_ string) (io.ReadCloser, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"not in cache\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref: testSource + \":\" + testTag,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t\t\"ErrorParseFails\": {\n\t\t\treason: \"Should return error when package parsing fails\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn &v1.Descriptor{\n\t\t\t\t\t\t\tDigest: v1.Hash{\n\t\t\t\t\t\t\t\tAlgorithm: \"sha256\",\n\t\t\t\t\t\t\t\tHex:       \"abc123def456789012345678901234567890123456789012345678901234abcd\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t\tMockFetch: func(_ context.Context, _ name.Reference, _ ...string) (v1.Image, error) {\n\t\t\t\t\t\tinvalidYAML := CreateTarWithPackageYAML(\"invalid yaml content {{{\")\n\t\t\t\t\t\treturn &MockImage{\n\t\t\t\t\t\t\tMockManifest: func() (*v1.Manifest, error) {\n\t\t\t\t\t\t\t\treturn &v1.Manifest{\n\t\t\t\t\t\t\t\t\tLayers: []v1.Descriptor{\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\t\t\t\t\tAnnotationKey: PackageAnnotation,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tDigest: v1.Hash{Algorithm: \"sha256\", Hex: \"layer123\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tMockLayerByDigest: func(_ v1.Hash) (v1.Layer, error) {\n\t\t\t\t\t\t\t\treturn NewMockLayer(invalidYAML), nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tparser: NewTestParser(t),\n\t\t\t\tcache: &MockCache{\n\t\t\t\t\tMockGet: func(_ string) (io.ReadCloser, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"not in cache\")\n\t\t\t\t\t},\n\t\t\t\t\tMockStore: func(_ string, rc io.ReadCloser) error {\n\t\t\t\t\t\t_, _ = io.Copy(io.Discard, rc)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref: testSource + \":\" + testTag,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t\t\"SuccessWithVerification\": {\n\t\t\treason: \"Should successfully verify and fetch a package when verification config exists\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn &v1.Descriptor{\n\t\t\t\t\t\t\tDigest: v1.Hash{\n\t\t\t\t\t\t\t\tAlgorithm: \"sha256\",\n\t\t\t\t\t\t\t\tHex:       \"abc123def456789012345678901234567890123456789012345678901234abcd\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t\tMockFetch: func(_ context.Context, _ name.Reference, _ ...string) (v1.Image, error) {\n\t\t\t\t\t\treturn &MockImage{\n\t\t\t\t\t\t\tMockManifest: func() (*v1.Manifest, error) {\n\t\t\t\t\t\t\t\treturn &v1.Manifest{\n\t\t\t\t\t\t\t\t\tLayers: []v1.Descriptor{\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\t\t\t\t\tAnnotationKey: PackageAnnotation,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tDigest: v1.Hash{Algorithm: \"sha256\", Hex: \"layer123\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}, nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tMockLayerByDigest: func(_ v1.Hash) (v1.Layer, error) {\n\t\t\t\t\t\t\t\treturn NewMockLayer(tarContent), nil\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tparser: NewTestParser(t),\n\t\t\t\tcache: &MockCache{\n\t\t\t\t\tMockGet: func(_ string) (io.ReadCloser, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"not in cache\")\n\t\t\t\t\t},\n\t\t\t\t\tMockStore: func(_ string, rc io.ReadCloser) error {\n\t\t\t\t\t\t_, _ = io.Copy(io.Discard, rc)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"test-verification-config\", &v1beta1.ImageVerification{\n\t\t\t\t\t\t\tProvider: v1beta1.ImageVerificationProviderCosign,\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tvalidator: &MockValidator{\n\t\t\t\t\tMockValidate: func(_ context.Context, _ name.Reference, _ *v1beta1.ImageVerification, _ ...string) error {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref: testSource + \":\" + testTag,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpkg: &Package{\n\t\t\t\t\tPackage:         NewTestPackage(t, providerMeta),\n\t\t\t\t\tDigest:          testDigest,\n\t\t\t\t\tVersion:         testTag,\n\t\t\t\t\tSource:          testSource,\n\t\t\t\t\tResolvedVersion: testTag,\n\t\t\t\t\tResolvedSource:  testSource,\n\t\t\t\t\tAppliedImageConfigs: []ImageConfig{\n\t\t\t\t\t\t{Name: \"test-verification-config\", Reason: ImageConfigReasonVerify},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"ErrorVerificationFails\": {\n\t\t\treason: \"Should return error when signature verification fails\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn &v1.Descriptor{\n\t\t\t\t\t\t\tDigest: v1.Hash{\n\t\t\t\t\t\t\t\tAlgorithm: \"sha256\",\n\t\t\t\t\t\t\t\tHex:       \"abc123def456789012345678901234567890123456789012345678901234abcd\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t\tMockFetch: func(_ context.Context, _ name.Reference, _ ...string) (v1.Image, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"fetch should not be called\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tparser: NewTestParser(t),\n\t\t\t\tcache: &MockCache{\n\t\t\t\t\tMockGet: func(_ string) (io.ReadCloser, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"not in cache\")\n\t\t\t\t\t},\n\t\t\t\t\tMockStore: func(_ string, _ io.ReadCloser) error {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"test-verification-config\", &v1beta1.ImageVerification{\n\t\t\t\t\t\t\tProvider: v1beta1.ImageVerificationProviderCosign,\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tvalidator: &MockValidator{\n\t\t\t\t\tMockValidate: func(_ context.Context, _ name.Reference, _ *v1beta1.ImageVerification, _ ...string) error {\n\t\t\t\t\t\treturn errors.New(\"signature verification failed\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref: testSource + \":\" + testTag,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t\t\"ErrorRewritePathFails\": {\n\t\t\treason: \"Should return error when RewritePath config lookup fails\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", errors.New(\"cannot list ImageConfigs\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref: testSource + \":\" + testTag,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t\t\"ErrorPullSecretForFails\": {\n\t\t\treason: \"Should return error when PullSecretFor config lookup fails\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn &v1.Descriptor{\n\t\t\t\t\t\t\tDigest: v1.Hash{\n\t\t\t\t\t\t\t\tAlgorithm: \"sha256\",\n\t\t\t\t\t\t\t\tHex:       \"abc123def456789012345678901234567890123456789012345678901234abcd\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", errors.New(\"cannot list ImageConfigs\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref: testSource + \":\" + testTag,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t\t\"ErrorImageVerificationConfigForFails\": {\n\t\t\treason: \"Should return error when ImageVerificationConfigFor lookup fails\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockHead: func(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\t\t\t\t\t\treturn &v1.Descriptor{\n\t\t\t\t\t\t\tDigest: v1.Hash{\n\t\t\t\t\t\t\t\tAlgorithm: \"sha256\",\n\t\t\t\t\t\t\t\tHex:       \"abc123def456789012345678901234567890123456789012345678901234abcd\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tcache: &MockCache{\n\t\t\t\t\tMockGet: func(_ string) (io.ReadCloser, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"not in cache\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, errors.New(\"cannot list ImageConfigs\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tref: testSource + \":\" + testTag,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot, err := tc.client.Get(context.Background(), tc.args.ref, tc.args.opts...)\n\n\t\t\tif diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nGet(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.pkg, got, PackageComparer()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nGet(...): -want Package, +got Package:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestClientListVersions(t *testing.T) {\n\ttype args struct {\n\t\tsource string\n\t\topts   []GetOption\n\t}\n\ttype want struct {\n\t\tversions []string\n\t\terr      error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tclient *CachedClient\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"Success\": {\n\t\t\treason: \"Should successfully list and filter versions\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockTags: func(_ context.Context, _ name.Reference, _ ...string) ([]string, error) {\n\t\t\t\t\t\treturn []string{\"v1.0.0\", \"v1.1.0\", \"v2.0.0\", \"latest\", \"main\"}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tsource: testSource,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tversions: []string{\"v1.0.0\", \"v1.1.0\", \"v2.0.0\"},\n\t\t\t},\n\t\t},\n\t\t\"SuccessWithImageConfigRewrite\": {\n\t\t\treason: \"Should use rewritten path from ImageConfig\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockTags: func(_ context.Context, _ name.Reference, _ ...string) ([]string, error) {\n\t\t\t\t\t\treturn []string{\"v1.0.0\"}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"private-registry.io/mirror/provider-aws\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tsource: testSource,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tversions: []string{\"v1.0.0\"},\n\t\t\t},\n\t\t},\n\t\t\"ErrorInvalidSource\": {\n\t\t\treason: \"Should return error for invalid source\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tsource: \"invalid::source\",\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t\t\"ErrorTagsFails\": {\n\t\t\treason: \"Should return error when Tags request fails\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockTags: func(_ context.Context, _ name.Reference, _ ...string) ([]string, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"network error\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockImageVerificationConfigFor: func(_ context.Context, _ string) (string, *v1beta1.ImageVerification, error) {\n\t\t\t\t\t\treturn \"\", nil, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tsource: testSource,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t\t\"ErrorRewritePathFails\": {\n\t\t\treason: \"Should return error when RewritePath config lookup fails\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", errors.New(\"cannot list ImageConfigs\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tsource: testSource,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t\t\"ErrorPullSecretForFails\": {\n\t\t\treason: \"Should return error when PullSecretFor config lookup fails\",\n\t\t\tclient: &CachedClient{\n\t\t\t\tfetcher: &MockFetcher{\n\t\t\t\t\tMockTags: func(_ context.Context, _ name.Reference, _ ...string) ([]string, error) {\n\t\t\t\t\t\treturn []string{\"v1.0.0\"}, nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tconfig: &MockConfigStore{\n\t\t\t\t\tMockRewritePath: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", nil\n\t\t\t\t\t},\n\t\t\t\t\tMockPullSecretFor: func(_ context.Context, _ string) (string, string, error) {\n\t\t\t\t\t\treturn \"\", \"\", errors.New(\"cannot list ImageConfigs\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tsource: testSource,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot, err := tc.client.ListVersions(context.Background(), tc.args.source, tc.args.opts...)\n\n\t\t\tif diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nListVersions(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.versions, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nListVersions(...): -want versions, +got versions:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExtractPackageYAML(t *testing.T) {\n\tproviderMeta := `{\"apiVersion\":\"meta.pkg.crossplane.io/v1\",\"kind\":\"Provider\",\"metadata\":{\"name\":\"provider-aws\"}}`\n\ttarContent := CreateTarWithPackageYAML(providerMeta)\n\n\ttype want struct {\n\t\tcontent string\n\t\terr     error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\timg    v1.Image\n\t\twant   want\n\t}{\n\t\t\"SuccessWithAnnotatedLayer\": {\n\t\t\treason: \"Should extract package.yaml from annotated layer\",\n\t\t\timg: &MockImage{\n\t\t\t\tMockManifest: func() (*v1.Manifest, error) {\n\t\t\t\t\treturn &v1.Manifest{\n\t\t\t\t\t\tLayers: []v1.Descriptor{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\t\tAnnotationKey: PackageAnnotation,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tDigest: v1.Hash{Algorithm: \"sha256\", Hex: \"layer123\"},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t}, nil\n\t\t\t\t},\n\t\t\t\tMockLayerByDigest: func(_ v1.Hash) (v1.Layer, error) {\n\t\t\t\t\treturn NewMockLayer(tarContent), nil\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcontent: providerMeta,\n\t\t\t},\n\t\t},\n\t\t\"SuccessWithoutAnnotatedLayer\": {\n\t\t\treason: \"Should fall back to flattened extraction when no annotated layer\",\n\t\t\timg: &MockImage{\n\t\t\t\tMockManifest: func() (*v1.Manifest, error) {\n\t\t\t\t\treturn &v1.Manifest{\n\t\t\t\t\t\tLayers: []v1.Descriptor{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tAnnotations: nil,\n\t\t\t\t\t\t\t\tDigest:      v1.Hash{Algorithm: \"sha256\", Hex: \"layer123\"},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t}, nil\n\t\t\t\t},\n\t\t\t\tMockLayers: func() ([]v1.Layer, error) {\n\t\t\t\t\treturn []v1.Layer{NewMockLayer(tarContent)}, nil\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tcontent: providerMeta,\n\t\t\t},\n\t\t},\n\t\t\"ErrorManifestFails\": {\n\t\t\treason: \"Should return error when manifest retrieval fails\",\n\t\t\timg: &MockImage{\n\t\t\t\tMockManifest: func() (*v1.Manifest, error) {\n\t\t\t\t\treturn nil, errors.New(\"manifest error\")\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t\t\"ErrorLayerByDigestFails\": {\n\t\t\treason: \"Should return error when layer retrieval fails\",\n\t\t\timg: &MockImage{\n\t\t\t\tMockManifest: func() (*v1.Manifest, error) {\n\t\t\t\t\treturn &v1.Manifest{\n\t\t\t\t\t\tLayers: []v1.Descriptor{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\t\tAnnotationKey: PackageAnnotation,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tDigest: v1.Hash{Algorithm: \"sha256\", Hex: \"layer123\"},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t}, nil\n\t\t\t\t},\n\t\t\t\tMockLayerByDigest: func(_ v1.Hash) (v1.Layer, error) {\n\t\t\t\t\treturn nil, errors.New(\"layer error\")\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t\t\"ErrorPackageYAMLNotFound\": {\n\t\t\treason: \"Should return error when package.yaml is not in tar\",\n\t\t\timg: &MockImage{\n\t\t\t\tMockManifest: func() (*v1.Manifest, error) {\n\t\t\t\t\treturn &v1.Manifest{\n\t\t\t\t\t\tLayers: []v1.Descriptor{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\t\t\t\t\tAnnotationKey: PackageAnnotation,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tDigest: v1.Hash{Algorithm: \"sha256\", Hex: \"layer123\"},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t}, nil\n\t\t\t\t},\n\t\t\t\tMockLayerByDigest: func(_ v1.Hash) (v1.Layer, error) {\n\t\t\t\t\tvar buf bytes.Buffer\n\t\t\t\t\ttw := tar.NewWriter(&buf)\n\t\t\t\t\ttw.WriteHeader(&tar.Header{\n\t\t\t\t\t\tName: \"other-file.txt\",\n\t\t\t\t\t\tMode: 0o644,\n\t\t\t\t\t\tSize: 5,\n\t\t\t\t\t})\n\t\t\t\t\ttw.Write([]byte(\"hello\"))\n\t\t\t\t\ttw.Close()\n\t\t\t\t\treturn NewMockLayer(buf.String()), nil\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: cmpopts.AnyError,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot, err := ExtractPackageYAML(tc.img)\n\n\t\t\tif diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nExtractPackageYAML(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tdefer got.Close()\n\t\t\tcontent, err := io.ReadAll(got)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"\\n%s\\nExtractPackageYAML(...): failed to read content: %v\", tc.reason, err)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.content, string(content)); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nExtractPackageYAML(...): -want content, +got content:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFilterAndSortVersions(t *testing.T) {\n\ttype args struct {\n\t\ttags []string\n\t}\n\ttype want struct {\n\t\tversions []string\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"Success\": {\n\t\t\treason: \"Should filter non-semver tags and sort ascending\",\n\t\t\targs: args{\n\t\t\t\ttags: []string{\"v2.0.0\", \"latest\", \"v1.0.0\", \"main\", \"v1.1.0\"},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tversions: []string{\"v1.0.0\", \"v1.1.0\", \"v2.0.0\"},\n\t\t\t},\n\t\t},\n\t\t\"EmptyInput\": {\n\t\t\treason: \"Should handle empty input\",\n\t\t\targs: args{\n\t\t\t\ttags: []string{},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tversions: []string{},\n\t\t\t},\n\t\t},\n\t\t\"NoValidVersions\": {\n\t\t\treason: \"Should return empty slice when no valid semver tags\",\n\t\t\targs: args{\n\t\t\t\ttags: []string{\"latest\", \"main\", \"dev\"},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tversions: []string{},\n\t\t\t},\n\t\t},\n\t\t\"PreReleaseVersions\": {\n\t\t\treason: \"Should include pre-release versions\",\n\t\t\targs: args{\n\t\t\t\ttags: []string{\"v1.0.0\", \"v1.1.0-alpha\", \"v1.1.0-beta\", \"v1.1.0\"},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tversions: []string{\"v1.0.0\", \"v1.1.0-alpha\", \"v1.1.0-beta\", \"v1.1.0\"},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := FilterAndSortVersions(tc.args.tags)\n\n\t\t\tif diff := cmp.Diff(tc.want.versions, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nFilterAndSortVersions(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestPackageDigestHex(t *testing.T) {\n\tconst testHex = \"abc123def456789012345678901234567890123456789012345678901234abcd\"\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tdigest string\n\t\twant   string\n\t}{\n\t\t\"ValidDigest\": {\n\t\t\treason: \"Should return hex part of valid SHA256 digest\",\n\t\t\tdigest: \"sha256:\" + testHex,\n\t\t\twant:   testHex,\n\t\t},\n\t\t\"InvalidDigest\": {\n\t\t\treason: \"Should return empty string for invalid digest\",\n\t\t\tdigest: \"invalid-digest\",\n\t\t\twant:   \"\",\n\t\t},\n\t\t\"EmptyDigest\": {\n\t\t\treason: \"Should return empty string for empty digest\",\n\t\t\tdigest: \"\",\n\t\t\twant:   \"\",\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tpkg := &Package{Digest: tc.digest}\n\t\t\tgot := pkg.DigestHex()\n\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nPackage.DigestHex(): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/config.go",
    "content": "package xpkg\n\nimport (\n\t\"context\"\n\t\"strings\"\n\n\t\"github.com/crossplane/crossplane/apis/v2/pkg/v1beta1\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\nconst (\n\terrListImageConfigs = \"cannot list ImageConfigs\"\n\terrFindBestMatch    = \"cannot find best matching ImageConfig\"\n)\n\n// ConfigStore is a store for image configuration.\ntype ConfigStore interface {\n\t// PullSecretFor returns the name of the selected image config and\n\t// name of the pull secret for a given image.\n\tPullSecretFor(ctx context.Context, image string) (imageConfig, pullSecret string, err error)\n\t// ImageVerificationConfigFor returns the ImageConfig for a given image.\n\tImageVerificationConfigFor(ctx context.Context, image string) (imageConfig string, iv *v1beta1.ImageVerification, err error)\n\t// RewritePath returns the name of the selected image config and the\n\t// rewritten path of the given image based on that config.\n\tRewritePath(ctx context.Context, image string) (imageConfig, newPath string, err error)\n\t// RuntimeConfigFor returns the name of the selected image config and the\n\t// runtime config for a given image.\n\tRuntimeConfigFor(ctx context.Context, image string) (imageConfig string, runtimeConfig *v1beta1.ImageRuntime, err error)\n}\n\n// isValidConfig is a function that determines if an ImageConfig is valid while\n// finding the best match for an image.\ntype isValidConfig func(c *v1beta1.ImageConfig) bool\n\n// ImageConfigStoreOption is an option for image configuration store.\ntype ImageConfigStoreOption func(*ImageConfigStore)\n\n// NewImageConfigStore creates a new image configuration store.\nfunc NewImageConfigStore(client client.Client, namespace string, opts ...ImageConfigStoreOption) ConfigStore {\n\ts := &ImageConfigStore{\n\t\tclient:    client,\n\t\tnamespace: namespace,\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(s)\n\t}\n\n\treturn s\n}\n\n// ImageConfigStore is a store for image configuration.\ntype ImageConfigStore struct {\n\tclient    client.Reader\n\tnamespace string\n}\n\n// PullSecretFor returns the pull secret name for a given image as\n// well as the name of the ImageConfig resource that contains the pull secret.\nfunc (s *ImageConfigStore) PullSecretFor(ctx context.Context, image string) (imageConfig, pullSecret string, err error) {\n\tconfig, err := s.bestMatch(ctx, image, func(c *v1beta1.ImageConfig) bool {\n\t\treturn c.Spec.Registry != nil && c.Spec.Registry.Authentication != nil && c.Spec.Registry.Authentication.PullSecretRef.Name != \"\"\n\t})\n\tif err != nil {\n\t\treturn \"\", \"\", errors.Wrap(err, errFindBestMatch)\n\t}\n\n\tif config == nil {\n\t\t// No ImageConfig with a pull secret found for this image, this is not\n\t\t// an error.\n\t\treturn \"\", \"\", nil\n\t}\n\n\treturn config.Name, config.Spec.Registry.Authentication.PullSecretRef.Name, nil\n}\n\n// ImageVerificationConfigFor returns the ImageConfig for a given image.\nfunc (s *ImageConfigStore) ImageVerificationConfigFor(ctx context.Context, image string) (imageConfig string, iv *v1beta1.ImageVerification, err error) {\n\tconfig, err := s.bestMatch(ctx, image, func(c *v1beta1.ImageConfig) bool {\n\t\treturn c.Spec.Verification != nil\n\t})\n\tif err != nil {\n\t\treturn \"\", nil, errors.Wrap(err, errFindBestMatch)\n\t}\n\n\tif config == nil {\n\t\t// No ImageConfig with a verification config found for this image, this\n\t\t// is not an error.\n\t\treturn \"\", nil, nil\n\t}\n\n\tif config.Spec.Verification.Cosign == nil {\n\t\t// Only cosign verification is supported for now.\n\t\treturn config.Name, nil, errors.New(\"cosign verification config is missing\")\n\t}\n\n\treturn config.Name, config.Spec.Verification, nil\n}\n\n// RewritePath returns the name of the selected image config and the rewritten\n// path of the given image based on that config.\nfunc (s *ImageConfigStore) RewritePath(ctx context.Context, image string) (imageConfig, newPath string, err error) {\n\tconfig, err := s.bestMatch(ctx, image, func(c *v1beta1.ImageConfig) bool {\n\t\treturn c.Spec.RewriteImage != nil\n\t})\n\tif err != nil {\n\t\treturn \"\", \"\", errors.Wrap(err, errFindBestMatch)\n\t}\n\n\tif config == nil {\n\t\t// No ImageConfig with a rewrite found for this image, this is not an\n\t\t// error.\n\t\treturn \"\", \"\", nil\n\t}\n\n\trewritePrefix := config.Spec.RewriteImage.Prefix\n\tif rewritePrefix == \"\" {\n\t\treturn config.Name, \"\", errors.New(\"rewrite prefix is missing\")\n\t}\n\n\t// Find the longest prefix match in the selected image config; this is what\n\t// we'll replace.\n\tmatchPrefix := \"\"\n\n\tfor _, m := range config.Spec.MatchImages {\n\t\tif !strings.HasPrefix(image, m.Prefix) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(m.Prefix) > len(matchPrefix) {\n\t\t\tmatchPrefix = m.Prefix\n\t\t}\n\t}\n\n\treturn config.Name, rewritePrefix + strings.TrimPrefix(image, matchPrefix), nil\n}\n\n// RuntimeConfigFor returns the name of the selected image config and the\n// runtime config for a given image.\nfunc (s *ImageConfigStore) RuntimeConfigFor(ctx context.Context, image string) (imageConfig string, runtimeConfig *v1beta1.ImageRuntime, err error) {\n\tconfig, err := s.bestMatch(ctx, image, func(c *v1beta1.ImageConfig) bool {\n\t\treturn c.Spec.Runtime != nil\n\t})\n\tif err != nil {\n\t\treturn \"\", nil, errors.Wrap(err, errFindBestMatch)\n\t}\n\n\tif config == nil {\n\t\t// No ImageConfig with a runtime config found for this image, this is\n\t\t// not an error.\n\t\treturn \"\", nil, nil\n\t}\n\n\treturn config.Name, config.Spec.Runtime, nil\n}\n\n// bestMatch finds the best matching ImageConfig for an image based on the\n// longest prefix match.\nfunc (s *ImageConfigStore) bestMatch(ctx context.Context, image string, valid isValidConfig) (*v1beta1.ImageConfig, error) {\n\tl := &v1beta1.ImageConfigList{}\n\n\tif err := s.client.List(ctx, l); err != nil {\n\t\treturn nil, errors.Wrap(err, errListImageConfigs)\n\t}\n\n\tvar (\n\t\tconfig  *v1beta1.ImageConfig\n\t\tlongest int\n\t)\n\n\tfor _, c := range l.Items {\n\t\tif !valid(&c) {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, m := range c.Spec.MatchImages {\n\t\t\tif strings.HasPrefix(image, m.Prefix) && len(m.Prefix) > longest {\n\t\t\t\tlongest = len(m.Prefix)\n\t\t\t\tconfig = &c\n\t\t\t}\n\t\t}\n\t}\n\n\treturn config, nil\n}\n"
  },
  {
    "path": "pkg/xpkg/config_test.go",
    "content": "package xpkg\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/crossplane/crossplane/apis/v2/pkg/v1beta1\"\n\t\"github.com/google/go-cmp/cmp\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nvar errBoom = errors.New(\"boom\")\n\nfunc TestImageConfigStoreBestMatch(t *testing.T) {\n\ttype args struct {\n\t\tclient  client.Client\n\t\timage   string\n\t\tisValid isValidConfig\n\t}\n\n\ttype want struct {\n\t\tconfig *v1beta1.ImageConfig\n\t\terr    error\n\t}\n\n\tcases := map[string]struct {\n\t\targs args\n\t\twant want\n\t}{\n\t\t\"ErrListConfig\": {\n\t\t\targs: args{\n\t\t\t\timage: \"registry1.com/acme-co/configuration-foo\",\n\t\t\t\tisValid: func(_ *v1beta1.ImageConfig) bool {\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\tclient: &test.MockClient{\n\t\t\t\t\tMockList: func(_ context.Context, _ client.ObjectList, _ ...client.ListOption) error {\n\t\t\t\t\t\treturn errBoom\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.Wrap(errBoom, errListImageConfigs),\n\t\t\t},\n\t\t},\n\t\t\"SingleConfig\": {\n\t\t\targs: args{\n\t\t\t\timage: \"registry1.com/acme-co/configuration-foo\",\n\t\t\t\tisValid: func(_ *v1beta1.ImageConfig) bool {\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\tclient: &test.MockClient{\n\t\t\t\t\tMockList: func(_ context.Context, list client.ObjectList, _ ...client.ListOption) error {\n\t\t\t\t\t\t*list.(*v1beta1.ImageConfigList) = v1beta1.ImageConfigList{\n\t\t\t\t\t\t\tItems: []v1beta1.ImageConfig{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\t\t\t\t\tName: \"registry1\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry1.com/acme-co\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tconfig: &v1beta1.ImageConfig{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"registry1\",\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t{Prefix: \"registry1.com/acme-co\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\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\"SingleConfigMultiPrefix\": {\n\t\t\targs: args{\n\t\t\t\timage: \"registry1.com/acme-co/configuration-foo\",\n\t\t\t\tisValid: func(_ *v1beta1.ImageConfig) bool {\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\tclient: &test.MockClient{\n\t\t\t\t\tMockList: func(_ context.Context, list client.ObjectList, _ ...client.ListOption) error {\n\t\t\t\t\t\t*list.(*v1beta1.ImageConfigList) = v1beta1.ImageConfigList{\n\t\t\t\t\t\t\tItems: []v1beta1.ImageConfig{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\t\t\t\t\tName: \"registry1\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry1.com/some-other\"},\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry1.com/acme-co\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tconfig: &v1beta1.ImageConfig{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"registry1\",\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t{Prefix: \"registry1.com/some-other\"},\n\t\t\t\t\t\t\t{Prefix: \"registry1.com/acme-co\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\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\"MultiConfig\": {\n\t\t\targs: args{\n\t\t\t\timage: \"registry1.com/acme-co/configuration-foo\",\n\t\t\t\tisValid: func(_ *v1beta1.ImageConfig) bool {\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\tclient: &test.MockClient{\n\t\t\t\t\tMockList: func(_ context.Context, list client.ObjectList, _ ...client.ListOption) error {\n\t\t\t\t\t\t*list.(*v1beta1.ImageConfigList) = v1beta1.ImageConfigList{\n\t\t\t\t\t\t\tItems: []v1beta1.ImageConfig{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\t\t\t\t\tName: \"registry2\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry2.com/acme-co\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\t\t\t\t\tName: \"registry1\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry1.com/acme-co\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tconfig: &v1beta1.ImageConfig{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"registry1\",\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t{Prefix: \"registry1.com/acme-co\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\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\"MultiConfigMultiPrefix\": {\n\t\t\targs: args{\n\t\t\t\timage: \"registry1.com/acme-co/configuration-foo\",\n\t\t\t\tisValid: func(_ *v1beta1.ImageConfig) bool {\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\tclient: &test.MockClient{\n\t\t\t\t\tMockList: func(_ context.Context, list client.ObjectList, _ ...client.ListOption) error {\n\t\t\t\t\t\t*list.(*v1beta1.ImageConfigList) = v1beta1.ImageConfigList{\n\t\t\t\t\t\t\tItems: []v1beta1.ImageConfig{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\t\t\t\t\tName: \"registry2\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry2.com/some-other\"},\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry2.com/acme-co\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\t\t\t\t\tName: \"registry1\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry1.com/some-other\"},\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry1.com/acme-co\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tconfig: &v1beta1.ImageConfig{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"registry1\",\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t{Prefix: \"registry1.com/some-other\"},\n\t\t\t\t\t\t\t{Prefix: \"registry1.com/acme-co\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\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\"MultiConfigMultiMatchFindsBest\": {\n\t\t\targs: args{\n\t\t\t\timage: \"registry1.com/acme-co/configuration-foo\",\n\t\t\t\tisValid: func(_ *v1beta1.ImageConfig) bool {\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\tclient: &test.MockClient{\n\t\t\t\t\tMockList: func(_ context.Context, list client.ObjectList, _ ...client.ListOption) error {\n\t\t\t\t\t\t*list.(*v1beta1.ImageConfigList) = v1beta1.ImageConfigList{\n\t\t\t\t\t\t\tItems: []v1beta1.ImageConfig{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\t\t\t\t\tName: \"registry1-base\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry1.com\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\t\t\t\t\tName: \"registry1-with-org\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry1.com/some-other\"},\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry1.com/acme-co\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\t\t\t\t\tName: \"registry1-full-image-ref\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry1.com/some-other\"},\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry1.com/acme-co/configuration-foo\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tconfig: &v1beta1.ImageConfig{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"registry1-full-image-ref\",\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t{Prefix: \"registry1.com/some-other\"},\n\t\t\t\t\t\t\t{Prefix: \"registry1.com/acme-co/configuration-foo\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\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\"SkipsInvalid\": {\n\t\t\targs: args{\n\t\t\t\timage: \"registry1.com/acme-co/configuration-foo\",\n\t\t\t\tisValid: func(c *v1beta1.ImageConfig) bool {\n\t\t\t\t\treturn c.Spec.Registry != nil\n\t\t\t\t},\n\t\t\t\tclient: &test.MockClient{\n\t\t\t\t\tMockList: func(_ context.Context, list client.ObjectList, _ ...client.ListOption) error {\n\t\t\t\t\t\t*list.(*v1beta1.ImageConfigList) = v1beta1.ImageConfigList{\n\t\t\t\t\t\t\tItems: []v1beta1.ImageConfig{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\t\t\t\t\tName: \"registry1-no-pull-secret\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t\t\t\t\t// Best match but invalid, no pull secret defined\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry1.com/acme-co\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\t\t\t\t\tName: \"registry1\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t\t\t\t\t{Prefix: \"registry1.com\"},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\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\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tconfig: &v1beta1.ImageConfig{\n\t\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\t\tName: \"registry1\",\n\t\t\t\t\t},\n\t\t\t\t\tSpec: v1beta1.ImageConfigSpec{\n\t\t\t\t\t\tMatchImages: []v1beta1.ImageMatch{\n\t\t\t\t\t\t\t{Prefix: \"registry1.com\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRegistry: &v1beta1.RegistryConfig{\n\t\t\t\t\t\t\tAuthentication: &v1beta1.RegistryAuthentication{\n\t\t\t\t\t\t\t\tPullSecretRef: corev1.LocalObjectReference{Name: \"test\"},\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}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\ts := &ImageConfigStore{\n\t\t\t\tclient: tc.args.client,\n\t\t\t}\n\n\t\t\tgot, err := s.bestMatch(context.Background(), tc.args.image, tc.args.isValid)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"bestMatch() error -want +got: %s\", diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.config, got, cmp.AllowUnexported(v1beta1.ImageConfig{})); diff != \"\" {\n\t\t\t\tt.Errorf(\"bestMatch() config -want +got: %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/doc.go",
    "content": "/*\nCopyright 2022 The Crossplane 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// Package xpkg contains functionality pertaining to Crossplane packages.\npackage xpkg\n"
  },
  {
    "path": "pkg/xpkg/fake/config.go",
    "content": "package fake\n\nimport (\n\t\"context\"\n\n\t\"github.com/crossplane/crossplane/apis/v2/pkg/v1beta1\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg\"\n)\n\nvar _ xpkg.ConfigStore = &MockConfigStore{}\n\n// MockConfigStore is a mock ConfigStore.\ntype MockConfigStore struct {\n\tMockPullSecretFor              func(ctx context.Context, image string) (imageConfig string, pullSecret string, err error)\n\tMockImageVerificationConfigFor func(ctx context.Context, image string) (imageConfig string, verificationConfig *v1beta1.ImageVerification, err error)\n\tMockRewritePath                func(ctx context.Context, image string) (imageConfig, newPath string, err error)\n\tMockRuntimeConfigFor           func(ctx context.Context, image string) (imageConfig string, runtimeConfig *v1beta1.ImageRuntime, err error)\n}\n\n// PullSecretFor calls the underlying MockPullSecretFor.\nfunc (s *MockConfigStore) PullSecretFor(ctx context.Context, image string) (imageConfig string, pullSecret string, err error) {\n\treturn s.MockPullSecretFor(ctx, image)\n}\n\n// ImageVerificationConfigFor calls the underlying MockImageVerificationConfigFor.\nfunc (s *MockConfigStore) ImageVerificationConfigFor(ctx context.Context, image string) (imageConfig string, verificationConfig *v1beta1.ImageVerification, err error) {\n\treturn s.MockImageVerificationConfigFor(ctx, image)\n}\n\n// RewritePath calls the underlying MockRewritePath.\nfunc (s *MockConfigStore) RewritePath(ctx context.Context, image string) (imageConfig, newPath string, err error) {\n\treturn s.MockRewritePath(ctx, image)\n}\n\n// RuntimeConfigFor calls the underlying MockRuntimeConfigFor.\nfunc (s *MockConfigStore) RuntimeConfigFor(ctx context.Context, image string) (imageConfig string, runtimeConfig *v1beta1.ImageRuntime, err error) {\n\treturn s.MockRuntimeConfigFor(ctx, image)\n}\n\n// NewMockConfigStorePullSecretForFn creates a new MockPullSecretFor function for MockConfigStore.\nfunc NewMockConfigStorePullSecretForFn(imageConfig, pullSecret string, err error) func(context.Context, string) (string, string, error) {\n\treturn func(context.Context, string) (string, string, error) {\n\t\treturn imageConfig, pullSecret, err\n\t}\n}\n\n// NewMockConfigStoreImageVerificationConfigForFn creates a new MockImageVerificationConfigFor function for MockConfigStore.\nfunc NewMockConfigStoreImageVerificationConfigForFn(imageConfig string, verificationConfig *v1beta1.ImageVerification, err error) func(context.Context, string) (string, *v1beta1.ImageVerification, error) {\n\treturn func(context.Context, string) (string, *v1beta1.ImageVerification, error) {\n\t\treturn imageConfig, verificationConfig, err\n\t}\n}\n\n// NewMockRewritePathFn creates a new MockRewritePath function for\n// MockConfigStore.\nfunc NewMockRewritePathFn(imageConfig, newPath string, err error) func(context.Context, string) (string, string, error) {\n\treturn func(_ context.Context, _ string) (string, string, error) {\n\t\treturn imageConfig, newPath, err\n\t}\n}\n\n// NewMockRuntimeConfigForFn creates a new MockRewritePath function for\n// MockConfigStore.\nfunc NewMockRuntimeConfigForFn(imageConfig string, runtimeConfig *v1beta1.ImageRuntime, err error) func(context.Context, string) (string, *v1beta1.ImageRuntime, error) {\n\treturn func(_ context.Context, _ string) (string, *v1beta1.ImageRuntime, error) {\n\t\treturn imageConfig, runtimeConfig, err\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/fake/mocks.go",
    "content": "/*\nCopyright 2020 The Crossplane 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// Package fake contains mock Crossplane package implementations.\npackage fake\n\nimport (\n\t\"context\"\n\t\"io\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg\"\n)\n\nvar _ xpkg.PackageCache = &MockCache{}\n\n// MockCache is a mock Cache.\ntype MockCache struct {\n\tMockHas    func() bool\n\tMockGet    func() (io.ReadCloser, error)\n\tMockStore  func(s string, rc io.ReadCloser) error\n\tMockDelete func() error\n}\n\n// NewMockCacheHasFn creates a new MockGet function for MockCache.\nfunc NewMockCacheHasFn(has bool) func() bool {\n\treturn func() bool { return has }\n}\n\n// NewMockCacheGetFn creates a new MockGet function for MockCache.\nfunc NewMockCacheGetFn(rc io.ReadCloser, err error) func() (io.ReadCloser, error) {\n\treturn func() (io.ReadCloser, error) { return rc, err }\n}\n\n// NewMockCacheStoreFn creates a new MockStore function for MockCache.\nfunc NewMockCacheStoreFn(err error) func(s string, rc io.ReadCloser) error {\n\treturn func(_ string, _ io.ReadCloser) error { return err }\n}\n\n// NewMockCacheDeleteFn creates a new MockDelete function for MockCache.\nfunc NewMockCacheDeleteFn(err error) func() error {\n\treturn func() error { return err }\n}\n\n// Has calls the underlying MockHas.\nfunc (c *MockCache) Has(string) bool {\n\treturn c.MockHas()\n}\n\n// Get calls the underlying MockGet.\nfunc (c *MockCache) Get(string) (io.ReadCloser, error) {\n\treturn c.MockGet()\n}\n\n// Store calls the underlying MockStore.\nfunc (c *MockCache) Store(s string, rc io.ReadCloser) error {\n\treturn c.MockStore(s, rc)\n}\n\n// Delete calls the underlying MockDelete.\nfunc (c *MockCache) Delete(string) error {\n\treturn c.MockDelete()\n}\n\nvar _ xpkg.Fetcher = &MockFetcher{}\n\n// MockFetcher is a mock fetcher.\ntype MockFetcher struct {\n\tMockFetch func() (v1.Image, error)\n\tMockHead  func(name.Reference) (*v1.Descriptor, error)\n\tMockTags  func(name.Reference) ([]string, error)\n}\n\n// NewMockFetchFn creates a new MockFetch function for MockFetcher.\nfunc NewMockFetchFn(img v1.Image, err error) func() (v1.Image, error) {\n\treturn func() (v1.Image, error) { return img, err }\n}\n\n// Fetch calls the underlying MockFetch.\nfunc (m *MockFetcher) Fetch(_ context.Context, _ name.Reference, _ ...string) (v1.Image, error) {\n\treturn m.MockFetch()\n}\n\n// NewMockHeadFn creates a new MockHead function for MockFetcher.\nfunc NewMockHeadFn(d *v1.Descriptor, err error) func(name.Reference) (*v1.Descriptor, error) {\n\treturn func(_ name.Reference) (*v1.Descriptor, error) { return d, err }\n}\n\n// Head calls the underlying MockHead.\nfunc (m *MockFetcher) Head(_ context.Context, ref name.Reference, _ ...string) (*v1.Descriptor, error) {\n\treturn m.MockHead(ref)\n}\n\n// NewMockTagsFn creates a new MockTags function for MockFetcher.\nfunc NewMockTagsFn(tags []string, err error) func(name.Reference) ([]string, error) {\n\treturn func(_ name.Reference) ([]string, error) { return tags, err }\n}\n\n// Tags calls the underlying MockTags.\nfunc (m *MockFetcher) Tags(_ context.Context, ref name.Reference, _ ...string) ([]string, error) {\n\treturn m.MockTags(ref)\n}\n\nvar _ xpkg.Client = &MockClient{}\n\n// MockClient is a mock xpkg.Client.\ntype MockClient struct {\n\tMockGet          func(ctx context.Context, ref string, opts ...xpkg.GetOption) (*xpkg.Package, error)\n\tMockListVersions func(ctx context.Context, source string, opts ...xpkg.GetOption) ([]string, error)\n}\n\n// Get calls the underlying MockGet.\nfunc (c *MockClient) Get(ctx context.Context, ref string, opts ...xpkg.GetOption) (*xpkg.Package, error) {\n\treturn c.MockGet(ctx, ref, opts...)\n}\n\n// ListVersions calls the underlying MockListVersions.\nfunc (c *MockClient) ListVersions(ctx context.Context, source string, opts ...xpkg.GetOption) ([]string, error) {\n\treturn c.MockListVersions(ctx, source, opts...)\n}\n\n// NewMockGetFn creates a new MockGet function for MockClient.\nfunc NewMockGetFn(pkg *xpkg.Package, err error) func(context.Context, string, ...xpkg.GetOption) (*xpkg.Package, error) {\n\treturn func(context.Context, string, ...xpkg.GetOption) (*xpkg.Package, error) { return pkg, err }\n}\n\n// NewMockListVersionsFn creates a new MockListVersions function for MockClient.\nfunc NewMockListVersionsFn(versions []string, err error) func(context.Context, string, ...xpkg.GetOption) ([]string, error) {\n\treturn func(context.Context, string, ...xpkg.GetOption) ([]string, error) { return versions, err }\n}\n"
  },
  {
    "path": "pkg/xpkg/fetch.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 xpkg\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/google/go-containerregistry/pkg/authn/k8schain\"\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/empty\"\n\t\"github.com/google/go-containerregistry/pkg/v1/remote\"\n\t\"github.com/sirupsen/logrus\"\n\t\"k8s.io/client-go/kubernetes\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\nfunc init() { //nolint:gochecknoinits // See comment below.\n\t// NOTE(hasheddan): we set the logrus package-level logger to discard output\n\t// due to the fact that the AWS ECR credential helper uses it to log errors\n\t// when parsing registry server URL, which happens any time a package is\n\t// pulled from a non-ECR registry.\n\t// https://github.com/awslabs/amazon-ecr-credential-helper/issues/308\n\tlogrus.SetOutput(io.Discard)\n}\n\n// Fetcher fetches package images.\ntype Fetcher interface {\n\tFetch(ctx context.Context, ref name.Reference, secrets ...string) (v1.Image, error)\n\tHead(ctx context.Context, ref name.Reference, secrets ...string) (*v1.Descriptor, error)\n\tTags(ctx context.Context, ref name.Reference, secrets ...string) ([]string, error)\n}\n\n// K8sFetcher uses kubernetes credentials to fetch package images.\ntype K8sFetcher struct {\n\tclient         kubernetes.Interface\n\tnamespace      string\n\tserviceAccount string\n\ttransport      http.RoundTripper\n\tuserAgent      string\n}\n\n// FetcherOpt can be used to add optional parameters to NewK8sFetcher.\ntype FetcherOpt func(k *K8sFetcher) error\n\n// WithCustomCA is a FetcherOpt that can be used to add a custom CA bundle to a K8sFetcher.\nfunc WithCustomCA(rootCAs *x509.CertPool) FetcherOpt {\n\treturn func(k *K8sFetcher) error {\n\t\tt, ok := k.transport.(*http.Transport)\n\t\tif !ok {\n\t\t\treturn errors.New(\"Fetcher transport is not an HTTP transport\")\n\t\t}\n\n\t\tt.TLSClientConfig = &tls.Config{RootCAs: rootCAs, MinVersion: tls.VersionTLS12}\n\n\t\treturn nil\n\t}\n}\n\n// WithUserAgent is a FetcherOpt that can be used to set the user agent on all HTTP requests.\nfunc WithUserAgent(userAgent string) FetcherOpt {\n\treturn func(k *K8sFetcher) error {\n\t\t// TODO(hasheddan): go-containerregistry currently does not allow for\n\t\t// removal of the go-containerregistry user-agent header, so the\n\t\t// provided one is appended rather than replacing. In the future, this\n\t\t// should be replaced with wrapping the transport with\n\t\t// transport.NewUserAgent.\n\t\tk.userAgent = userAgent\n\t\treturn nil\n\t}\n}\n\n// WithNamespace is a FetcherOpt that sets the Namespace for fetching package\n// pull secrets.\nfunc WithNamespace(ns string) FetcherOpt {\n\treturn func(k *K8sFetcher) error {\n\t\tk.namespace = ns\n\t\treturn nil\n\t}\n}\n\n// WithServiceAccount is a FetcherOpt that sets the ServiceAccount name for\n// fetching package pull secrets.\nfunc WithServiceAccount(sa string) FetcherOpt {\n\treturn func(k *K8sFetcher) error {\n\t\tk.serviceAccount = sa\n\t\treturn nil\n\t}\n}\n\n// NewK8sFetcher creates a new K8sFetcher.\nfunc NewK8sFetcher(client kubernetes.Interface, opts ...FetcherOpt) (*K8sFetcher, error) {\n\tdt, ok := remote.DefaultTransport.(*http.Transport)\n\tif !ok {\n\t\treturn nil, errors.Errorf(\"default transport was not a %T\", &http.Transport{})\n\t}\n\n\tk := &K8sFetcher{\n\t\tclient:    client,\n\t\ttransport: dt.Clone(),\n\t}\n\n\tfor _, o := range opts {\n\t\tif err := o(k); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn k, nil\n}\n\n// Fetch fetches a package image.\nfunc (i *K8sFetcher) Fetch(ctx context.Context, ref name.Reference, secrets ...string) (v1.Image, error) {\n\tauth, err := k8schain.New(ctx, i.client, k8schain.Options{\n\t\tNamespace:          i.namespace,\n\t\tServiceAccountName: i.serviceAccount,\n\t\tImagePullSecrets:   secrets,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn remote.Image(ref,\n\t\tremote.WithAuthFromKeychain(auth),\n\t\tremote.WithTransport(i.transport),\n\t\tremote.WithContext(ctx),\n\t\tremote.WithUserAgent(i.userAgent),\n\t)\n}\n\n// Head fetches a package descriptor.\nfunc (i *K8sFetcher) Head(ctx context.Context, ref name.Reference, secrets ...string) (*v1.Descriptor, error) {\n\tauth, err := k8schain.New(ctx, i.client, k8schain.Options{\n\t\tNamespace:          i.namespace,\n\t\tServiceAccountName: i.serviceAccount,\n\t\tImagePullSecrets:   secrets,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\td, err := remote.Head(ref,\n\t\tremote.WithAuthFromKeychain(auth),\n\t\tremote.WithTransport(i.transport),\n\t\tremote.WithContext(ctx),\n\t\tremote.WithUserAgent(i.userAgent),\n\t)\n\tif err != nil || d == nil {\n\t\trd, gErr := remote.Get(ref,\n\t\t\tremote.WithAuthFromKeychain(auth),\n\t\t\tremote.WithTransport(i.transport),\n\t\t\tremote.WithContext(ctx),\n\t\t\tremote.WithUserAgent(i.userAgent),\n\t\t)\n\t\tif gErr != nil {\n\t\t\treturn nil, errors.Wrapf(gErr, \"failed to fetch package descriptor with a GET request after a previous HEAD request failure: %v\", err)\n\t\t}\n\n\t\treturn &rd.Descriptor, nil\n\t}\n\n\treturn d, nil\n}\n\n// Tags fetches a package's tags.\nfunc (i *K8sFetcher) Tags(ctx context.Context, ref name.Reference, secrets ...string) ([]string, error) {\n\tauth, err := k8schain.New(ctx, i.client, k8schain.Options{\n\t\tNamespace:          i.namespace,\n\t\tServiceAccountName: i.serviceAccount,\n\t\tImagePullSecrets:   secrets,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn remote.List(ref.Context(),\n\t\tremote.WithAuthFromKeychain(auth),\n\t\tremote.WithTransport(i.transport),\n\t\tremote.WithContext(ctx),\n\t\tremote.WithUserAgent(i.userAgent),\n\t)\n}\n\n// NopFetcher always returns an empty image and never returns error.\ntype NopFetcher struct{}\n\n// NewNopFetcher creates a new NopFetcher.\nfunc NewNopFetcher() *NopFetcher {\n\treturn &NopFetcher{}\n}\n\n// Fetch fetches an empty image and does not return error.\nfunc (n *NopFetcher) Fetch(_ context.Context, _ name.Reference, _ ...string) (v1.Image, error) {\n\treturn empty.Image, nil\n}\n\n// Head returns a nil descriptor and does not return error.\nfunc (n *NopFetcher) Head(_ context.Context, _ name.Reference, _ ...string) (*v1.Descriptor, error) {\n\treturn nil, nil\n}\n\n// Tags returns a nil slice and does not return error.\nfunc (n *NopFetcher) Tags(_ context.Context, _ name.Reference, _ ...string) ([]string, error) {\n\treturn nil, nil\n}\n"
  },
  {
    "path": "pkg/xpkg/find.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 xpkg\n\nimport (\n\t\"path/filepath\"\n\n\t\"github.com/spf13/afero\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\nconst (\n\terrNoMatch    = \"directory does not contain a compiled crossplane package\"\n\terrMultiMatch = \"directory contains multiple compiled crossplane packages\"\n)\n\n// FindXpkgInDir finds compiled Crossplane packages in a directory.\nfunc FindXpkgInDir(fs afero.Fs, root string) (string, error) {\n\tf, err := fs.Open(root)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tdefer func() { _ = f.Close() }()\n\n\tfiles, err := f.Readdir(-1)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tpath := \"\"\n\n\tfor _, file := range files {\n\t\t// Match only returns an error if XpkgMatchPattern is malformed.\n\t\tmatch, _ := filepath.Match(XpkgMatchPattern, file.Name())\n\t\tif !match {\n\t\t\tcontinue\n\t\t}\n\n\t\tif path != \"\" && match {\n\t\t\treturn \"\", errors.New(errMultiMatch)\n\t\t}\n\n\t\tpath = file.Name()\n\t}\n\n\tif path == \"\" {\n\t\treturn \"\", errors.New(errNoMatch)\n\t}\n\n\treturn path, nil\n}\n"
  },
  {
    "path": "pkg/xpkg/find_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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\thttp://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 xpkg\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/spf13/afero\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nfunc TestFindXpkgInDir(t *testing.T) {\n\tmatch := afero.NewMemMapFs()\n\t_ = afero.WriteFile(match, \"one.xpkg\", []byte{}, StreamFileMode)\n\n\tmulti := afero.NewMemMapFs()\n\t_ = afero.WriteFile(multi, \"one.xpkg\", []byte{}, StreamFileMode)\n\t_ = afero.WriteFile(multi, \"two.xpkg\", []byte{}, StreamFileMode)\n\n\ttype args struct {\n\t\troot string\n\t\tfs   afero.Fs\n\t}\n\n\ttype want struct {\n\t\tpath string\n\t\terr  error\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"NoMatch\": {\n\t\t\treason: \"We should return an error if no matches.\",\n\t\t\targs: args{\n\t\t\t\troot: \".\",\n\t\t\t\tfs:   afero.NewMemMapFs(),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.New(errNoMatch),\n\t\t\t},\n\t\t},\n\t\t\"MultiMatch\": {\n\t\t\treason: \"We should return an error if multiple matches.\",\n\t\t\targs: args{\n\t\t\t\troot: \".\",\n\t\t\t\tfs:   multi,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: errors.New(errMultiMatch),\n\t\t\t},\n\t\t},\n\t\t\"NotExist\": {\n\t\t\treason: \"We should return an error root does not exist.\",\n\t\t\targs: args{\n\t\t\t\troot: \"/test\",\n\t\t\t\tfs:   afero.NewMemMapFs(),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: &os.PathError{Op: \"open\", Path: \"/test\", Err: os.ErrNotExist},\n\t\t\t},\n\t\t},\n\t\t\"NotDir\": {\n\t\t\treason: \"We should return an error if root is not a directory.\",\n\t\t\targs: args{\n\t\t\t\troot: \"one.xpkg\",\n\t\t\t\tfs:   multi,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\terr: &os.PathError{Op: \"readdir\", Path: \"one.xpkg\", Err: errors.New(\"not a dir\")},\n\t\t\t},\n\t\t},\n\t\t\"Successful\": {\n\t\t\treason: \"We should return file path if one package exists.\",\n\t\t\targs: args{\n\t\t\t\troot: \".\",\n\t\t\t\tfs:   match,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tpath: \"one.xpkg\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tpath, err := FindXpkgInDir(tc.args.fs, tc.args.root)\n\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nFindXpkgInDir(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.path, path); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nFindXpkgInDir(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/fuzz_test.go",
    "content": "/*\nCopyright 2023 The Crossplane 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 xpkg\n\nimport (\n\t\"testing\"\n\n\tfuzz \"github.com/AdaLogics/go-fuzz-headers\"\n\t\"github.com/spf13/afero\"\n)\n\nfunc FuzzFindXpkgInDir(f *testing.F) {\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tff := fuzz.NewConsumer(data)\n\n\t\tnoOfFiles, err := ff.GetInt()\n\t\tif err != nil {\n\t\t\tt.Skip()\n\t\t}\n\n\t\tfs := afero.NewMemMapFs()\n\t\tcreatedFiles := make([]string, 0)\n\n\t\tdefer func() {\n\t\t\tfor _, createdFile := range createdFiles {\n\t\t\t\tfs.Remove(createdFile)\n\t\t\t}\n\t\t}()\n\n\t\tfor range noOfFiles % 500 {\n\t\t\tfname, err := ff.GetString()\n\t\t\tif err != nil {\n\t\t\t\tt.Skip()\n\t\t\t}\n\n\t\t\tfcontents, err := ff.GetBytes()\n\t\t\tif err != nil {\n\t\t\t\tt.Skip()\n\t\t\t}\n\n\t\t\tif err = afero.WriteFile(fs, fname, fcontents, 0o777); err != nil {\n\t\t\t\tt.Skip()\n\t\t\t}\n\t\t}\n\n\t\t_, _ = FindXpkgInDir(fs, \"/\")\n\t\t_, _ = ParseNameFromMeta(fs, \"/\")\n\t})\n}\n"
  },
  {
    "path": "pkg/xpkg/layers.go",
    "content": "/*\nCopyright 2023 The Crossplane 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 xpkg\n\nimport (\n\t\"archive/tar\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/empty\"\n\t\"github.com/google/go-containerregistry/pkg/v1/mutate\"\n\t\"github.com/google/go-containerregistry/pkg/v1/tarball\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\n// Error strings.\nconst (\n\terrLayer  = \"cannot get image layers\"\n\terrDigest = \"cannot get image digest\"\n)\n\n// Layer creates a v1.Layer that represents the layer contents for the xpkg and\n// adds a corresponding label to the image Config for the layer.\nfunc Layer(r io.Reader, fileName, annotation string, fileSize int64, mode os.FileMode, cfg *v1.Config) (v1.Layer, error) {\n\ttarBuf := new(bytes.Buffer)\n\ttw := tar.NewWriter(tarBuf)\n\n\texHdr := &tar.Header{\n\t\tName: fileName,\n\t\tMode: int64(mode),\n\t\tSize: fileSize,\n\t}\n\n\tif err := writeLayer(tw, exHdr, r); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// TODO(hasheddan): we currently return a new reader every time here in\n\t// order to calculate digest, then subsequently write contents to disk. We\n\t// can greatly improve performance during package build by avoiding reading\n\t// every layer into memory.\n\tlayer, err := tarball.LayerFromOpener(func() (io.ReadCloser, error) {\n\t\treturn io.NopCloser(bytes.NewReader(tarBuf.Bytes())), nil\n\t})\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, errLayerFromTar)\n\t}\n\n\td, err := layer.Digest()\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, errDigestInvalid)\n\t}\n\n\t// Add annotation label to config if a non-empty label is specified. This is\n\t// an intermediary step. AnnotateLayers must be called on an image for it to\n\t// have valid layer annotations. It propagates these labels to annotations\n\t// on the layers.\n\tif annotation != \"\" {\n\t\tcfg.Labels[Label(d.String())] = annotation\n\t}\n\n\treturn layer, nil\n}\n\nfunc writeLayer(tw *tar.Writer, hdr *tar.Header, buf io.Reader) error {\n\tif err := tw.WriteHeader(hdr); err != nil {\n\t\treturn errors.Wrap(err, errTarFromStream)\n\t}\n\n\tif _, err := io.Copy(tw, buf); err != nil {\n\t\treturn errors.Wrap(err, errTarFromStream)\n\t}\n\n\tif err := tw.Close(); err != nil {\n\t\treturn errors.Wrap(err, errTarFromStream)\n\t}\n\n\treturn nil\n}\n\n// Label constructs a specially formated label using the annotationKey.\nfunc Label(annotation string) string {\n\treturn fmt.Sprintf(\"%s:%s\", AnnotationKey, annotation)\n}\n\n// NOTE(negz): AnnotateLayers originated in upbound/up. I was confused why we\n// store layer annotations as labels in the OCI config file when we build a\n// package, then propagate them to OCI layer annotations when we push one. I\n// believe this is because an xpkg file is really an OCI image tarball, and the\n// tarball format doesn't support layer annotations (or may just lose them in\n// some circumstances?), so we're using the config file to store them.\n// See https://github.com/upbound/up/pull/177#discussion_r866776584.\n\n// AnnotateLayers propagates labels from the supplied image's config file to\n// annotations on its layers.\nfunc AnnotateLayers(i v1.Image) (v1.Image, error) {\n\tcfgFile, err := i.ConfigFile()\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, errConfigFile)\n\t}\n\n\tlayers, err := i.Layers()\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, errLayer)\n\t}\n\n\taddendums := make([]mutate.Addendum, 0)\n\n\tfor _, l := range layers {\n\t\td, err := l.Digest()\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, errDigest)\n\t\t}\n\n\t\tif annotation, ok := cfgFile.Config.Labels[Label(d.String())]; ok {\n\t\t\taddendums = append(addendums, mutate.Addendum{\n\t\t\t\tLayer: l,\n\t\t\t\tAnnotations: map[string]string{\n\t\t\t\t\tAnnotationKey: annotation,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tcontinue\n\t\t}\n\n\t\taddendums = append(addendums, mutate.Addendum{\n\t\t\tLayer: l,\n\t\t})\n\t}\n\n\t// we didn't find any annotations, return original image\n\tif len(addendums) == 0 {\n\t\treturn i, nil\n\t}\n\n\timg := empty.Image\n\tfor _, a := range addendums {\n\t\timg, err = mutate.Append(img, a)\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, errBuildImage)\n\t\t}\n\t}\n\n\treturn mutate.ConfigFile(img, cfgFile)\n}\n"
  },
  {
    "path": "pkg/xpkg/lint.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 xpkg\n\nimport (\n\t\"github.com/Masterminds/semver/v3\"\n\tv1 \"github.com/crossplane/crossplane/apis/v2/apiextensions/v1\"\n\textv1alpha1 \"github.com/crossplane/crossplane/apis/v2/apiextensions/v1alpha1\"\n\tv2 \"github.com/crossplane/crossplane/apis/v2/apiextensions/v2\"\n\t\"github.com/crossplane/crossplane/apis/v2/ops/v1alpha1\"\n\tpkgmetav1 \"github.com/crossplane/crossplane/apis/v2/pkg/meta/v1\"\n\tadmv1 \"k8s.io/api/admissionregistration/v1\"\n\textv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\textv1beta1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/version\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg/parser\"\n)\n\nconst (\n\terrNotExactlyOneMeta                 = \"not exactly one package meta type\"\n\terrNotMeta                           = \"meta type is not a package\"\n\terrNotMetaProvider                   = \"package meta type is not Provider\"\n\terrNotMetaConfiguration              = \"package meta type is not Configuration\"\n\terrNotMetaFunction                   = \"package meta type is not Function\"\n\terrNotCRD                            = \"object is not a CRD\"\n\terrNotMRD                            = \"object is not an MRD\"\n\terrNotXRD                            = \"object is not an XRD\"\n\terrNotMutatingWebhookConfiguration   = \"object is not a MutatingWebhookConfiguration\"\n\terrNotValidatingWebhookConfiguration = \"object is not an ValidatingWebhookConfiguration\"\n\terrNotComposition                    = \"object is not a Composition\"\n\terrNotActivationPolicy               = \"object is not an ManagedResourceActivationPolicy\"\n\terrNotOperation                      = \"object is not an Operation\"\n\terrNotCronOperation                  = \"object is not a CronOperation\"\n\terrNotWatchOperation                 = \"object is not a WatchOperation\"\n\terrBadConstraints                    = \"package version constraints are poorly formatted\"\n\terrFmtCrossplaneIncompatible         = \"package is not compatible with Crossplane version (%s)\"\n)\n\n// NewProviderLinter is a convenience function for creating a package linter for\n// providers.\nfunc NewProviderLinter() parser.Linter {\n\treturn parser.NewPackageLinter(\n\t\tparser.PackageLinterFns(OneMeta),\n\t\tparser.ObjectLinterFns(IsProvider, PackageValidSemver),\n\t\tparser.ObjectLinterFns(parser.Or(\n\t\t\tIsCRD, IsMRD,\n\t\t\tIsValidatingWebhookConfiguration,\n\t\t\tIsMutatingWebhookConfiguration,\n\t\t)))\n}\n\n// NewConfigurationLinter is a convenience function for creating a package linter for\n// configurations.\nfunc NewConfigurationLinter() parser.Linter {\n\treturn parser.NewPackageLinter(\n\t\tparser.PackageLinterFns(OneMeta),\n\t\tparser.ObjectLinterFns(IsConfiguration, PackageValidSemver),\n\t\tparser.ObjectLinterFns(parser.Or(IsXRD, IsActivationPolicy, IsComposition, IsOperation, IsCronOperation, IsWatchOperation)))\n}\n\n// NewFunctionLinter is a convenience function for creating a package linter for\n// functions.\nfunc NewFunctionLinter() parser.Linter {\n\treturn parser.NewPackageLinter(\n\t\tparser.PackageLinterFns(OneMeta),\n\t\tparser.ObjectLinterFns(IsFunction, PackageValidSemver),\n\t\tparser.ObjectLinterFns(IsCRD))\n}\n\n// OneMeta checks that there is only one meta object in the package.\nfunc OneMeta(pkg parser.Lintable) error {\n\tif len(pkg.GetMeta()) != 1 {\n\t\treturn errors.New(errNotExactlyOneMeta)\n\t}\n\n\treturn nil\n}\n\n// IsProvider checks that an object is a Provider meta type.\nfunc IsProvider(o runtime.Object) error {\n\tpo, _ := TryConvert(o, &pkgmetav1.Provider{})\n\tif _, ok := po.(*pkgmetav1.Provider); !ok {\n\t\treturn errors.New(errNotMetaProvider)\n\t}\n\n\treturn nil\n}\n\n// IsConfiguration checks that an object is a Configuration meta type.\nfunc IsConfiguration(o runtime.Object) error {\n\tpo, _ := TryConvert(o, &pkgmetav1.Configuration{})\n\tif _, ok := po.(*pkgmetav1.Configuration); !ok {\n\t\treturn errors.New(errNotMetaConfiguration)\n\t}\n\n\treturn nil\n}\n\n// IsFunction checks that an object is a Function meta type.\nfunc IsFunction(o runtime.Object) error {\n\tpo, _ := TryConvert(o, &pkgmetav1.Function{})\n\tif _, ok := po.(*pkgmetav1.Function); !ok {\n\t\treturn errors.New(errNotMetaFunction)\n\t}\n\n\treturn nil\n}\n\n// PackageCrossplaneCompatible checks that the current Crossplane version is\n// compatible with the package constraints.\nfunc PackageCrossplaneCompatible(v version.Operations) parser.ObjectLinterFn {\n\treturn func(o runtime.Object) error {\n\t\tp, ok := TryConvertToPkg(o, &pkgmetav1.Provider{}, &pkgmetav1.Configuration{}, &pkgmetav1.Function{})\n\t\tif !ok {\n\t\t\treturn errors.New(errNotMeta)\n\t\t}\n\n\t\tif p.GetCrossplaneConstraints() == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\tin, err := v.InConstraints(p.GetCrossplaneConstraints().Version)\n\t\tif err != nil {\n\t\t\treturn errors.Wrapf(err, errFmtCrossplaneIncompatible, v.GetVersionString())\n\t\t}\n\n\t\tif !in {\n\t\t\treturn errors.Errorf(errFmtCrossplaneIncompatible, v.GetVersionString())\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\n// PackageValidSemver checks that the package uses valid semver ranges.\nfunc PackageValidSemver(o runtime.Object) error {\n\tp, ok := TryConvertToPkg(o, &pkgmetav1.Provider{}, &pkgmetav1.Configuration{}, &pkgmetav1.Function{})\n\tif !ok {\n\t\treturn errors.New(errNotMeta)\n\t}\n\n\tif p.GetCrossplaneConstraints() == nil {\n\t\treturn nil\n\t}\n\n\tif _, err := semver.NewConstraint(p.GetCrossplaneConstraints().Version); err != nil {\n\t\treturn errors.Wrap(err, errBadConstraints)\n\t}\n\n\treturn nil\n}\n\n// IsCRD checks that an object is a CustomResourceDefinition.\nfunc IsCRD(o runtime.Object) error {\n\tswitch o.(type) {\n\tcase *extv1beta1.CustomResourceDefinition, *extv1.CustomResourceDefinition:\n\t\treturn nil\n\tdefault:\n\t\treturn errors.New(errNotCRD)\n\t}\n}\n\n// IsMRD checks that an object is a ManagedResourceDefinition.\nfunc IsMRD(o runtime.Object) error {\n\tswitch o.(type) {\n\tcase *extv1alpha1.ManagedResourceDefinition:\n\t\treturn nil\n\tdefault:\n\t\treturn errors.New(errNotMRD)\n\t}\n}\n\n// IsMutatingWebhookConfiguration checks that an object is a MutatingWebhookConfiguration.\nfunc IsMutatingWebhookConfiguration(o runtime.Object) error {\n\tif _, ok := o.(*admv1.MutatingWebhookConfiguration); !ok {\n\t\treturn errors.New(errNotMutatingWebhookConfiguration)\n\t}\n\n\treturn nil\n}\n\n// IsValidatingWebhookConfiguration checks that an object is a ValidatingWebhookConfiguration.\nfunc IsValidatingWebhookConfiguration(o runtime.Object) error {\n\tif _, ok := o.(*admv1.ValidatingWebhookConfiguration); !ok {\n\t\treturn errors.New(errNotValidatingWebhookConfiguration)\n\t}\n\n\treturn nil\n}\n\n// IsXRD checks that an object is a CompositeResourceDefinition.\nfunc IsXRD(o runtime.Object) error {\n\tswitch o.(type) {\n\tcase *v1.CompositeResourceDefinition, *v2.CompositeResourceDefinition:\n\t\treturn nil\n\tdefault:\n\t\treturn errors.New(errNotXRD)\n\t}\n}\n\n// IsComposition checks that an object is a Composition.\nfunc IsComposition(o runtime.Object) error {\n\tif _, ok := o.(*v1.Composition); !ok {\n\t\treturn errors.New(errNotComposition)\n\t}\n\n\treturn nil\n}\n\n// IsActivationPolicy checks that an object is an ManagedResourceActivationPolicy.\nfunc IsActivationPolicy(o runtime.Object) error {\n\tif _, ok := o.(*extv1alpha1.ManagedResourceActivationPolicy); !ok {\n\t\treturn errors.New(errNotActivationPolicy)\n\t}\n\n\treturn nil\n}\n\n// IsOperation checks that an object is an Operation.\nfunc IsOperation(o runtime.Object) error {\n\tif _, ok := o.(*v1alpha1.Operation); !ok {\n\t\treturn errors.New(errNotOperation)\n\t}\n\n\treturn nil\n}\n\n// IsCronOperation checks that an object is a CronOperation.\nfunc IsCronOperation(o runtime.Object) error {\n\tif _, ok := o.(*v1alpha1.CronOperation); !ok {\n\t\treturn errors.New(errNotCronOperation)\n\t}\n\n\treturn nil\n}\n\n// IsWatchOperation checks that an object is a WatchOperation.\nfunc IsWatchOperation(o runtime.Object) error {\n\tif _, ok := o.(*v1alpha1.WatchOperation); !ok {\n\t\treturn errors.New(errNotWatchOperation)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/xpkg/lint_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 xpkg\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n\n\tv1 \"github.com/crossplane/crossplane/apis/v2/apiextensions/v1\"\n\textv1alpha1 \"github.com/crossplane/crossplane/apis/v2/apiextensions/v1alpha1\"\n\t\"github.com/crossplane/crossplane/apis/v2/ops/v1alpha1\"\n\tpkgmetav1 \"github.com/crossplane/crossplane/apis/v2/pkg/meta/v1\"\n\tpkgmetav1alpha1 \"github.com/crossplane/crossplane/apis/v2/pkg/meta/v1alpha1\"\n\tpkgmetav1beta1 \"github.com/crossplane/crossplane/apis/v2/pkg/meta/v1beta1\"\n\t\"github.com/google/go-cmp/cmp\"\n\tapiextensions \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"sigs.k8s.io/yaml\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/version\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/version/fake\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg/parser\"\n)\n\nvar (\n\tv1beta1CRDBytes = []byte(`apiVersion: apiextensions.k8s.io/v1beta1\nkind: CustomResourceDefinition\nmetadata:\n  name: test`)\n\n\tv1CRDBytes = []byte(`apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  name: test`)\n\n\tv1alpha1ProvBytes = []byte(`apiVersion: meta.pkg.crossplane.io/v1alpha1\nkind: Provider\nmetadata:\n  name: test`)\n\n\tv1alpha1ConfBytes = []byte(`apiVersion: meta.pkg.crossplane.io/v1alpha1\nkind: Configuration\nmetadata:\n  name: test`)\n\n\tv1beta1FuncBytes = []byte(`apiVersion: meta.pkg.crossplane.io/v1beta1\n  kind: Function\n  metadata:\n    name: test`)\n\n\tv1ProvBytes = []byte(`apiVersion: meta.pkg.crossplane.io/v1\nkind: Provider\nmetadata:\n  name: test`)\n\n\tv1ConfBytes = []byte(`apiVersion: meta.pkg.crossplane.io/v1\nkind: Configuration\nmetadata:\n  name: test`)\n\n\tv1FuncBytes = []byte(`apiVersion: meta.pkg.crossplane.io/v1\nkind: Function\nmetadata:\n  name: test`)\n\n\tv1XRDBytes = []byte(`apiVersion: apiextensions.crossplane.io/v1\nkind: CompositeResourceDefinition\nmetadata:\n  name: test`)\n\n\tv1CompBytes = []byte(`apiVersion: apiextensions.crossplane.io/v1\nkind: Composition\nmetadata:\n  name: test`)\n\n\tv1alpha1OpBytes = []byte(`apiVersion: ops.crossplane.io/v1alpha1\nkind: Operation\nmetadata:\n  name: test`)\n\n\tv1alpha1CronOpBytes = []byte(`apiVersion: ops.crossplane.io/v1alpha1\nkind: CronOperation\nmetadata:\n  name: test`)\n\n\tv1alpha1WatchOpBytes = []byte(`apiVersion: ops.crossplane.io/v1alpha1\nkind: WatchOperation\nmetadata:\n  name: test`)\n\n\tv1alpha1MRDBytes = []byte(`apiVersion: apiextensions.crossplane.io/v1alpha1\nkind: ManagedResourceDefinition\nmetadata:\n  name: test`)\n\n\tv1alpha1ActivationPolicyBytes = []byte(`apiVersion: apiextensions.crossplane.io/v1alpha1\nkind: ManagedResourceActivationPolicy\nmetadata:\n  name: test`)\n\n\tv1beta1crd               = &apiextensions.CustomResourceDefinition{}\n\t_                        = yaml.Unmarshal(v1beta1CRDBytes, v1beta1crd)\n\tv1crd                    = &apiextensions.CustomResourceDefinition{}\n\t_                        = yaml.Unmarshal(v1CRDBytes, v1crd)\n\tv1alpha1ProvMeta         = &pkgmetav1alpha1.Provider{}\n\t_                        = yaml.Unmarshal(v1alpha1ProvBytes, v1alpha1ProvMeta)\n\tv1alpha1ConfMeta         = &pkgmetav1alpha1.Configuration{}\n\t_                        = yaml.Unmarshal(v1alpha1ConfBytes, v1alpha1ConfMeta)\n\tv1beta1FuncMeta          = &pkgmetav1beta1.Function{}\n\t_                        = yaml.Unmarshal(v1beta1FuncBytes, v1beta1FuncMeta)\n\tv1ProvMeta               = &pkgmetav1.Provider{}\n\t_                        = yaml.Unmarshal(v1ProvBytes, v1ProvMeta)\n\tv1ConfMeta               = &pkgmetav1.Configuration{}\n\t_                        = yaml.Unmarshal(v1ConfBytes, v1ConfMeta)\n\tv1FuncMeta               = &pkgmetav1.Function{}\n\t_                        = yaml.Unmarshal(v1FuncBytes, v1FuncMeta)\n\tv1XRD                    = &v1.CompositeResourceDefinition{}\n\t_                        = yaml.Unmarshal(v1XRDBytes, v1XRD)\n\tv1Comp                   = &v1.Composition{}\n\t_                        = yaml.Unmarshal(v1CompBytes, v1Comp)\n\tv1alpha1Op               = &v1alpha1.Operation{}\n\t_                        = yaml.Unmarshal(v1alpha1OpBytes, v1alpha1Op)\n\tv1alpha1CronOp           = &v1alpha1.CronOperation{}\n\t_                        = yaml.Unmarshal(v1alpha1CronOpBytes, v1alpha1CronOp)\n\tv1alpha1WatchOp          = &v1alpha1.WatchOperation{}\n\t_                        = yaml.Unmarshal(v1alpha1WatchOpBytes, v1alpha1WatchOp)\n\tv1alpha1MRD              = &extv1alpha1.ManagedResourceDefinition{}\n\t_                        = yaml.Unmarshal(v1alpha1MRDBytes, v1alpha1MRD)\n\tv1alpha1ActivationPolicy = &extv1alpha1.ManagedResourceActivationPolicy{}\n\t_                        = yaml.Unmarshal(v1alpha1ActivationPolicyBytes, v1alpha1ActivationPolicy)\n\n\tmeta, _ = BuildMetaScheme()\n\tobj, _  = BuildObjectScheme()\n\tp       = parser.New(meta, obj)\n)\n\nfunc TestOneMeta(t *testing.T) {\n\toneR := bytes.NewReader(bytes.Join([][]byte{v1beta1CRDBytes, v1alpha1ProvBytes}, []byte(\"\\n---\\n\")))\n\toneMeta, _ := p.Parse(context.TODO(), io.NopCloser(oneR))\n\tnoneR := bytes.NewReader(v1beta1CRDBytes)\n\tnoneMeta, _ := p.Parse(context.TODO(), io.NopCloser(noneR))\n\tmultiR := bytes.NewReader(bytes.Join([][]byte{v1alpha1ProvBytes, v1alpha1ProvBytes}, []byte(\"\\n---\\n\")))\n\tmultiMeta, _ := p.Parse(context.TODO(), io.NopCloser(multiR))\n\n\tcases := map[string]struct {\n\t\treason string\n\t\tpkg    *parser.Package\n\t\terr    error\n\t}{\n\t\t\"Successful\": {\n\t\t\treason: \"Should not return error if only one meta object.\",\n\t\t\tpkg:    oneMeta,\n\t\t},\n\t\t\"ErrNoMeta\": {\n\t\t\treason: \"Should return error if no meta objects.\",\n\t\t\tpkg:    noneMeta,\n\t\t\terr:    errors.New(errNotExactlyOneMeta),\n\t\t},\n\t\t\"ErrMultiMeta\": {\n\t\t\treason: \"Should return error if multiple meta objects.\",\n\t\t\tpkg:    multiMeta,\n\t\t\terr:    errors.New(errNotExactlyOneMeta),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := OneMeta(tc.pkg)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nOneMeta(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsProvider(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\tobj    runtime.Object\n\t\terr    error\n\t}{\n\t\t\"v1alpha1\": {\n\t\t\treason: \"Should not return error if object is a v1alpha1 provider.\",\n\t\t\tobj:    v1alpha1ProvMeta,\n\t\t},\n\t\t\"v1\": {\n\t\t\treason: \"Should not return error if object is a v1 provider.\",\n\t\t\tobj:    v1ProvMeta,\n\t\t},\n\t\t\"ErrNotProvider\": {\n\t\t\treason: \"Should return error if object is not provider.\",\n\t\t\tobj:    v1beta1crd,\n\t\t\terr:    errors.New(errNotMetaProvider),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := IsProvider(tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIsProvider(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsConfiguration(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\tobj    runtime.Object\n\t\terr    error\n\t}{\n\t\t\"v1alpha1\": {\n\t\t\treason: \"Should not return error if object is a v1alpha1 configuration.\",\n\t\t\tobj:    v1alpha1ConfMeta,\n\t\t},\n\t\t\"v1\": {\n\t\t\treason: \"Should not return error if object is a v1 configuration.\",\n\t\t\tobj:    v1ConfMeta,\n\t\t},\n\t\t\"ErrNotConfiguration\": {\n\t\t\treason: \"Should return error if object is not configuration.\",\n\t\t\tobj:    v1beta1crd,\n\t\t\terr:    errors.New(errNotMetaConfiguration),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := IsConfiguration(tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIsConfiguration(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsFunction(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\tobj    runtime.Object\n\t\terr    error\n\t}{\n\t\t// Function packages were introduced at v1beta1. There was never a\n\t\t// v1alpha1 version of the package metadata.\n\t\t\"v1beta1\": {\n\t\t\treason: \"Should not return error if object is a v1beta1 function.\",\n\t\t\tobj:    v1beta1FuncMeta,\n\t\t},\n\t\t\"v1\": {\n\t\t\treason: \"Should not return error if object is a v1 function.\",\n\t\t\tobj:    v1FuncMeta,\n\t\t},\n\t\t\"ErrNotFunction\": {\n\t\t\treason: \"Should return error if object is not function.\",\n\t\t\tobj:    v1beta1crd,\n\t\t\terr:    errors.New(errNotMetaFunction),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := IsFunction(tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIsFunction(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestPackageCrossplaneCompatible(t *testing.T) {\n\tcrossplaneConstraint := \">v0.13.0\"\n\terrBoom := errors.New(\"boom\")\n\n\ttype args struct {\n\t\tobj runtime.Object\n\t\tver version.Operations\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\terr    error\n\t}{\n\t\t\"Successful\": {\n\t\t\treason: \"Should not return error if Crossplane version within constraints.\",\n\t\t\targs: args{\n\t\t\t\tobj: &pkgmetav1.Configuration{\n\t\t\t\t\tSpec: pkgmetav1.ConfigurationSpec{\n\t\t\t\t\t\tMetaSpec: pkgmetav1.MetaSpec{\n\t\t\t\t\t\t\tCrossplane: &pkgmetav1.CrossplaneConstraints{\n\t\t\t\t\t\t\t\tVersion: crossplaneConstraint,\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\tver: &fake.MockVersioner{\n\t\t\t\t\tMockInConstraints: fake.NewMockInConstraintsFn(true, nil),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulNoConstraints\": {\n\t\t\treason: \"Should not return error if no constraints provided.\",\n\t\t\targs: args{\n\t\t\t\tobj: v1ProvMeta,\n\t\t\t},\n\t\t},\n\t\t\"ErrInvalidConstraints\": {\n\t\t\treason: \"Should return error if constraints are invalid.\",\n\t\t\targs: args{\n\t\t\t\tobj: &pkgmetav1.Configuration{\n\t\t\t\t\tSpec: pkgmetav1.ConfigurationSpec{\n\t\t\t\t\t\tMetaSpec: pkgmetav1.MetaSpec{\n\t\t\t\t\t\t\tCrossplane: &pkgmetav1.CrossplaneConstraints{\n\t\t\t\t\t\t\t\tVersion: crossplaneConstraint,\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\tver: &fake.MockVersioner{\n\t\t\t\t\tMockInConstraints:    fake.NewMockInConstraintsFn(false, errBoom),\n\t\t\t\t\tMockGetVersionString: fake.NewMockGetVersionStringFn(\"v0.12.0\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\terr: errors.Wrapf(errBoom, errFmtCrossplaneIncompatible, \"v0.12.0\"),\n\t\t},\n\t\t\"ErrOutsideConstraints\": {\n\t\t\treason: \"Should return error if Crossplane version outside constraints.\",\n\t\t\targs: args{\n\t\t\t\tobj: &pkgmetav1.Configuration{\n\t\t\t\t\tSpec: pkgmetav1.ConfigurationSpec{\n\t\t\t\t\t\tMetaSpec: pkgmetav1.MetaSpec{\n\t\t\t\t\t\t\tCrossplane: &pkgmetav1.CrossplaneConstraints{\n\t\t\t\t\t\t\t\tVersion: crossplaneConstraint,\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\tver: &fake.MockVersioner{\n\t\t\t\t\tMockInConstraints:    fake.NewMockInConstraintsFn(false, nil),\n\t\t\t\t\tMockGetVersionString: fake.NewMockGetVersionStringFn(\"v0.12.0\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\terr: errors.Errorf(errFmtCrossplaneIncompatible, \"v0.12.0\"),\n\t\t},\n\t\t\"ErrNotMeta\": {\n\t\t\treason: \"Should return error if object is not a meta package type.\",\n\t\t\targs: args{\n\t\t\t\tobj: v1crd,\n\t\t\t},\n\t\t\terr: errors.New(errNotMeta),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := PackageCrossplaneCompatible(tc.args.ver)(tc.args.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nPackageCrossplaneCompatible(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestPackageValidSemver(t *testing.T) {\n\tvalidConstraint := \">v0.13.0\"\n\tinvalidConstraint := \">a0.13.0\"\n\n\ttype args struct {\n\t\tobj runtime.Object\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\terr    error\n\t}{\n\t\t\"Valid\": {\n\t\t\treason: \"Should not return error if constraints are valid.\",\n\t\t\targs: args{\n\t\t\t\tobj: &pkgmetav1.Configuration{\n\t\t\t\t\tSpec: pkgmetav1.ConfigurationSpec{\n\t\t\t\t\t\tMetaSpec: pkgmetav1.MetaSpec{\n\t\t\t\t\t\t\tCrossplane: &pkgmetav1.CrossplaneConstraints{\n\t\t\t\t\t\t\t\tVersion: validConstraint,\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\"ErrInvalidConstraints\": {\n\t\t\treason: \"Should return error if constraints are invalid.\",\n\t\t\targs: args{\n\t\t\t\tobj: &pkgmetav1.Configuration{\n\t\t\t\t\tSpec: pkgmetav1.ConfigurationSpec{\n\t\t\t\t\t\tMetaSpec: pkgmetav1.MetaSpec{\n\t\t\t\t\t\t\tCrossplane: &pkgmetav1.CrossplaneConstraints{\n\t\t\t\t\t\t\t\tVersion: invalidConstraint,\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\terr: errors.Wrap(fmt.Errorf(\"improper constraint: %s\", invalidConstraint), errBadConstraints),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := PackageValidSemver(tc.args.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nPackageValidSemver(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsCRD(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\tobj    runtime.Object\n\t\terr    error\n\t}{\n\t\t\"v1beta1\": {\n\t\t\treason: \"Should not return error if object is a v1beta1 CRD.\",\n\t\t\tobj:    v1beta1crd,\n\t\t},\n\t\t\"v1\": {\n\t\t\treason: \"Should not return error if object is a v1 CRD.\",\n\t\t\tobj:    v1crd,\n\t\t},\n\t\t\"ErrNotCRD\": {\n\t\t\treason: \"Should return error if object is not CRD.\",\n\t\t\tobj:    v1alpha1ConfMeta,\n\t\t\terr:    errors.New(errNotCRD),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := IsCRD(tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIsCRD(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsMRD(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\tobj    runtime.Object\n\t\terr    error\n\t}{\n\t\t\"v1alpha1\": {\n\t\t\treason: \"Should not return error if object is MRD.\",\n\t\t\tobj:    v1alpha1MRD,\n\t\t},\n\t\t\"ErrNotMRD\": {\n\t\t\treason: \"Should return error if object is not MRD.\",\n\t\t\tobj:    v1beta1crd,\n\t\t\terr:    errors.New(errNotMRD),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := IsMRD(tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIsMRD(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsXRD(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\tobj    runtime.Object\n\t\terr    error\n\t}{\n\t\t\"v1\": {\n\t\t\treason: \"Should not return error if object is XRD.\",\n\t\t\tobj:    v1XRD,\n\t\t},\n\t\t\"ErrNotConfiguration\": {\n\t\t\treason: \"Should return error if object is not XRD.\",\n\t\t\tobj:    v1beta1crd,\n\t\t\terr:    errors.New(errNotXRD),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := IsXRD(tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIsXRD(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsComposition(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\tobj    runtime.Object\n\t\terr    error\n\t}{\n\t\t\"v1\": {\n\t\t\treason: \"Should not return error if object is composition.\",\n\t\t\tobj:    v1Comp,\n\t\t},\n\t\t\"ErrNotComposition\": {\n\t\t\treason: \"Should return error if object is not composition.\",\n\t\t\tobj:    v1beta1crd,\n\t\t\terr:    errors.New(errNotComposition),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := IsComposition(tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIsComposition(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsActivationPolicy(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\tobj    runtime.Object\n\t\terr    error\n\t}{\n\t\t\"v1alpha1\": {\n\t\t\treason: \"Should not return error if object is an activation policy.\",\n\t\t\tobj:    v1alpha1ActivationPolicy,\n\t\t},\n\t\t\"ErrNotActivationPolicy\": {\n\t\t\treason: \"Should return error if object is not an activation policy.\",\n\t\t\tobj:    v1beta1crd,\n\t\t\terr:    errors.New(errNotActivationPolicy),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := IsActivationPolicy(tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIsActivationPolicy(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsOperation(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\tobj    runtime.Object\n\t\terr    error\n\t}{\n\t\t\"v1\": {\n\t\t\treason: \"Should not return error if object is an operation.\",\n\t\t\tobj:    v1alpha1Op,\n\t\t},\n\t\t\"ErrNotOperation\": {\n\t\t\treason: \"Should return error if object is not an operation.\",\n\t\t\tobj:    v1beta1crd,\n\t\t\terr:    errors.New(errNotOperation),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := IsOperation(tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIsOperation(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsCronOperation(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\tobj    runtime.Object\n\t\terr    error\n\t}{\n\t\t\"v1\": {\n\t\t\treason: \"Should not return error if object is a cron operation.\",\n\t\t\tobj:    v1alpha1CronOp,\n\t\t},\n\t\t\"ErrNotCronOperation\": {\n\t\t\treason: \"Should return error if object is not a cron operation.\",\n\t\t\tobj:    v1beta1crd,\n\t\t\terr:    errors.New(errNotCronOperation),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := IsCronOperation(tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIsCronOperation(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsWatchOperation(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\tobj    runtime.Object\n\t\terr    error\n\t}{\n\t\t\"v1\": {\n\t\t\treason: \"Should not return error if object is a watch operation.\",\n\t\t\tobj:    v1alpha1WatchOp,\n\t\t},\n\t\t\"ErrNotWatchOperation\": {\n\t\t\treason: \"Should return error if object is not a watch operation.\",\n\t\t\tobj:    v1beta1crd,\n\t\t\terr:    errors.New(errNotWatchOperation),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := IsWatchOperation(tc.obj)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nIsWatchOperation(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/name.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 xpkg\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/spf13/afero\"\n\t\"sigs.k8s.io/yaml\"\n)\n\nconst (\n\t// MetaFile is the name of a Crossplane package metadata file.\n\tMetaFile string = \"crossplane.yaml\"\n\n\t// StreamFile is the name of the file in a Crossplane package image that\n\t// contains its YAML stream.\n\tStreamFile string = \"package.yaml\"\n\n\t// StreamFileMode determines the permissions on the stream file.\n\tStreamFileMode os.FileMode = 0o644\n\n\t// XpkgExtension is the extension for compiled Crossplane packages.\n\tXpkgExtension string = \".xpkg\"\n\n\t// XpkgMatchPattern is the match pattern for identifying compiled Crossplane packages.\n\tXpkgMatchPattern string = \"*\" + XpkgExtension\n\n\t// XpkgExamplesFile is the name of the file in a Crossplane package image\n\t// that contains the examples YAML stream.\n\tXpkgExamplesFile string = \".up/examples.yaml\"\n\n\t// AnnotationKey is the key value for xpkg annotations.\n\tAnnotationKey string = \"io.crossplane.xpkg\"\n\n\t// PackageAnnotation is the annotation value used for the package.yaml\n\t// layer.\n\tPackageAnnotation string = \"base\"\n\n\t// ExamplesAnnotation is the annotation value used for the examples.yaml\n\t// layer.\n\t// TODO(lsviben) Consider changing this to \"examples\". This has been preserved\n\t// to not break existing packages.\n\tExamplesAnnotation string = \"upbound\"\n)\n\nconst (\n\t// identifierDelimeters is the set of valid OCI image identifier delimeter\n\t// characters.\n\tidentifierDelimeters string = \":@\"\n)\n\nfunc truncate(str string, num int) string {\n\tt := str\n\tif len(str) > num {\n\t\tt = str[0:num]\n\t}\n\n\treturn t\n}\n\n// FriendlyID builds a valid DNS label string made up of the name of a package\n// and its image digest.\nfunc FriendlyID(name, hash string) string {\n\treturn ToDNSLabel(strings.Join([]string{truncate(name, 50), truncate(hash, 12)}, \"-\"))\n}\n\n// ToDNSLabel converts the string to a valid DNS label.\nfunc ToDNSLabel(s string) string {\n\tvar cut strings.Builder\n\n\tfor i := range s {\n\t\tb := s[i]\n\t\tif ('a' <= b && b <= 'z') || ('0' <= b && b <= '9') {\n\t\t\tcut.WriteByte(b)\n\t\t}\n\n\t\tif (b == '.' || b == '/' || b == ':' || b == '-') && (i != 0 && i != 62 && i != len(s)-1) {\n\t\t\tcut.WriteByte('-')\n\t\t}\n\n\t\tif i == 62 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn strings.Trim(cut.String(), \"-\")\n}\n\n// BuildPath builds a path with the provided extension.\nfunc BuildPath(path, name, ext string) string {\n\tfull := filepath.Join(path, name)\n\texistExt := filepath.Ext(full)\n\n\treturn full[0:len(full)-len(existExt)] + ext\n}\n\n// ParseNameFromMeta extracts the package name from its meta file.\nfunc ParseNameFromMeta(fs afero.Fs, path string) (string, error) {\n\tbs, err := afero.ReadFile(fs, filepath.Clean(path))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tpkgName, err := parseNameFromPackage(bs)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn pkgName, nil\n}\n\n// ParsePackageSourceFromReference parses a package source from an OCI image\n// reference. A source is defined as an OCI image reference with the identifier\n// (tag or digest) stripped and no other changes to the original reference\n// source. This is necessary because go-containerregistry will convert docker.io\n// to index.docker.io for backwards compatibility before pulling an image. We do\n// not want to do that in cases where we are not pulling an image because it\n// breaks comparison with dependencies defined in a Configuration manifest.\nfunc ParsePackageSourceFromReference(ref name.Reference) string {\n\treturn strings.TrimRight(strings.TrimSuffix(ref.String(), ref.Identifier()), identifierDelimeters)\n}\n\ntype metaPkg struct {\n\tMetadata struct {\n\t\tName string `json:\"name\"`\n\t}\n}\n\nfunc parseNameFromPackage(bs []byte) (string, error) {\n\tp := &metaPkg{}\n\terr := yaml.Unmarshal(bs, p)\n\n\treturn p.Metadata.Name, err\n}\n\n// ReplaceExt replaces the file extension of the given path.\nfunc ReplaceExt(path, ext string) string {\n\told := filepath.Ext(path)\n\treturn path[0:len(path)-len(old)] + ext\n}\n"
  },
  {
    "path": "pkg/xpkg/name_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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\thttp://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 xpkg\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-containerregistry/pkg/name\"\n)\n\nfunc TestFriendlyID(t *testing.T) {\n\ttype args struct {\n\t\tpkg  string\n\t\thash string\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   string\n\t}{\n\t\t\"BothUnderLimit\": {\n\t\t\treason: \"If both package and hash are under limit neither should be truncated.\",\n\t\t\targs: args{\n\t\t\t\tpkg:  \"provider-aws\",\n\t\t\t\thash: \"1234567\",\n\t\t\t},\n\t\t\twant: \"provider-aws-1234567\",\n\t\t},\n\t\t\"PackageOverLimit\": {\n\t\t\treason: \"If package is over limit it should be truncated.\",\n\t\t\targs: args{\n\t\t\t\tpkg:  \"provider-aws-plusabunchofothernonsensethatisgoingtogetslicedoff\",\n\t\t\t\thash: \"1234567\",\n\t\t\t},\n\t\t\twant: \"provider-aws-plusabunchofothernonsensethatisgoingt-1234567\",\n\t\t},\n\t\t\"HashOverLimit\": {\n\t\t\treason: \"If hash is over limit it should be truncated.\",\n\t\t\targs: args{\n\t\t\t\tpkg:  \"provider-aws\",\n\t\t\t\thash: \"1234567891234567\",\n\t\t\t},\n\t\t\twant: \"provider-aws-123456789123\",\n\t\t},\n\t\t\"BothOverLimit\": {\n\t\t\treason: \"If both package and hash are over limit both should be truncated.\",\n\t\t\targs: args{\n\t\t\t\tpkg:  \"provider-aws-plusabunchofothernonsensethatisgoingtogetslicedoff\",\n\t\t\t\thash: \"1234567891234567\",\n\t\t\t},\n\t\t\twant: \"provider-aws-plusabunchofothernonsensethatisgoingt-123456789123\",\n\t\t},\n\t\t\"ReplacePeriod\": {\n\t\t\treason: \"All period characters should be replaced with a dash.\",\n\t\t\targs: args{\n\t\t\t\tpkg:  \"provider.aws-plusabunchofothernonsensethatisgoingtogetslicedoff\",\n\t\t\t\thash: \"1234.567891234567\",\n\t\t\t},\n\t\t\twant: \"provider-aws-plusabunchofothernonsensethatisgoingt-1234-5678912\",\n\t\t},\n\t\t\"DigestIsName\": {\n\t\t\treason: \"A valid DNS label should be returned when package digest is a name.\",\n\t\t\targs: args{\n\t\t\t\tpkg:  \"provider-in-cluster\",\n\t\t\t\thash: \"provider-in-cluster\",\n\t\t\t},\n\t\t\twant: \"provider-in-cluster-provider-in\",\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\twant := FriendlyID(tc.args.pkg, tc.args.hash)\n\n\t\t\tif diff := cmp.Diff(tc.want, want); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nFriendlyID(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestToDNSLabel(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\targ    string\n\t\twant   string\n\t}{\n\t\t\"ReplaceAll\": {\n\t\t\treason: \"All valid symbols should be replaced with dash.\",\n\t\t\targ:    \"-hi/my.name/is-\",\n\t\t\twant:   \"hi-my-name-is\",\n\t\t},\n\t\t\"TrimTo63\": {\n\t\t\treason: \"A string longer than 63 valid or replaceable characters should be truncated.\",\n\t\t\targ:    \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n\t\t\twant:   \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n\t\t},\n\t\t\"TrimTo63MinusDashes\": {\n\t\t\treason: \"A string longer than 63 valid or replaceable characters should be truncated with trailing symbol removed.\",\n\t\t\targ:    \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa----\",\n\t\t\twant:   \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\twant := ToDNSLabel(tc.arg)\n\n\t\t\tif diff := cmp.Diff(tc.want, want); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nToDNSLabel(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSourceFromReference(t *testing.T) {\n\tcases := map[string]struct {\n\t\treason string\n\t\targ    name.Reference\n\t\twant   string\n\t}{\n\t\t\"SuccessfulTagWithDocker\": {\n\t\t\treason: \"If registry is docker.io it should be reflected in parsed source.\",\n\t\t\targ: func() name.Reference {\n\t\t\t\tref, _ := name.ParseReference(\"docker.io/hasheddan/xpkg-test:v0.1.0\")\n\t\t\t\treturn ref\n\t\t\t}(),\n\t\t\twant: \"docker.io/hasheddan/xpkg-test\",\n\t\t},\n\t\t\"SuccessfulTagWithDockerIndex\": {\n\t\t\treason: \"If registry is index.docker.io it should be reflected in parsed source.\",\n\t\t\targ: func() name.Reference {\n\t\t\t\tref, _ := name.ParseReference(\"index.docker.io/hasheddan/xpkg-test:v0.1.0\")\n\t\t\t\treturn ref\n\t\t\t}(),\n\t\t\twant: \"index.docker.io/hasheddan/xpkg-test\",\n\t\t},\n\t\t\"SuccessfulTagWithRegistryDefaulting\": {\n\t\t\treason: \"If no registry is supplied, but defaulting is enabled, default registry should not be reflected in parsed source.\",\n\t\t\targ: func() name.Reference {\n\t\t\t\tref, _ := name.ParseReference(\"hasheddan/xpkg-test:v0.1.0\", name.WithDefaultRegistry(\"registry.crossplane.io\"))\n\t\t\t\treturn ref\n\t\t\t}(),\n\t\t\twant: \"hasheddan/xpkg-test\",\n\t\t},\n\t\t\"SuccessfulDigestWithRegistryDefaulting\": {\n\t\t\treason: \"If no registry is supplied, but defaulting is enabled, default registry should not be reflected in parsed source.\",\n\t\t\targ: func() name.Reference {\n\t\t\t\tref, _ := name.ParseReference(\"hasheddan/xpkg-test@sha256:c88b938d6e7b2ed43d40b71e5a55df9c60fa653bea0c0961f3294fac46d5b56e\", name.WithDefaultRegistry(\"registry.crossplane.io\"))\n\t\t\t\treturn ref\n\t\t\t}(),\n\t\t\twant: \"hasheddan/xpkg-test\",\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\twant := ParsePackageSourceFromReference(tc.arg)\n\n\t\t\tif diff := cmp.Diff(tc.want, want); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nParseSourceFromReference(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBuildPath(t *testing.T) {\n\ttype args struct {\n\t\tpath string\n\t\tname string\n\t\text  string\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   string\n\t}{\n\t\t\"NoExtension\": {\n\t\t\treason: \"We should append extension if it does not exist.\",\n\t\t\targs: args{\n\t\t\t\tpath: \"path/to/somewhere\",\n\t\t\t\tname: \"test\",\n\t\t\t\text:  XpkgExtension,\n\t\t\t},\n\t\t\twant: \"path/to/somewhere/test.xpkg\",\n\t\t},\n\t\t\"ReplaceExtensionName\": {\n\t\t\treason: \"We should replace an extension if one exists in name.\",\n\t\t\targs: args{\n\t\t\t\tpath: \"path/to/somewhere\",\n\t\t\t\tname: \"test.tar\",\n\t\t\t\text:  XpkgExtension,\n\t\t\t},\n\t\t\twant: \"path/to/somewhere/test.xpkg\",\n\t\t},\n\t\t\"ReplaceExtensionPath\": {\n\t\t\treason: \"We should replace an extension if one exists in path.\",\n\t\t\targs: args{\n\t\t\t\tpath: \"path/to/somewhere.tar\",\n\t\t\t\tname: \"\",\n\t\t\t\text:  XpkgExtension,\n\t\t\t},\n\t\t\twant: \"path/to/somewhere.xpkg\",\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tfull := BuildPath(tc.args.path, tc.args.name, tc.args.ext)\n\n\t\t\tif diff := cmp.Diff(tc.want, full); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nBuildPath(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReplaceExt(t *testing.T) {\n\ttype args struct {\n\t\tpath string\n\t\text  string\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   string\n\t}{\n\t\t\"ReplaceWithTxt\": {\n\t\t\treason: \"Should replace the existing extension with .txt\",\n\t\t\targs: args{\n\t\t\t\tpath: \"file.doc\",\n\t\t\t\text:  \".txt\",\n\t\t\t},\n\t\t\twant: \"file.txt\",\n\t\t},\n\t\t\"ReplaceWithEmpty\": {\n\t\t\treason: \"Should remove the extension if an empty string is given\",\n\t\t\targs: args{\n\t\t\t\tpath: \"file.doc\",\n\t\t\t\text:  \"\",\n\t\t\t},\n\t\t\twant: \"file\",\n\t\t},\n\t\t\"NoExtensionToAdd\": {\n\t\t\treason: \"Should add an extension if there was none before\",\n\t\t\targs: args{\n\t\t\t\tpath: \"file\",\n\t\t\t\text:  \".txt\",\n\t\t\t},\n\t\t\twant: \"file.txt\",\n\t\t},\n\t\t\"MultipleDots\": {\n\t\t\treason: \"Should correctly replace only the last extension\",\n\t\t\targs: args{\n\t\t\t\tpath: \"archive.tar.gz\",\n\t\t\t\text:  \".zip\",\n\t\t\t},\n\t\t\twant: \"archive.tar.zip\",\n\t\t},\n\t\t\"HiddenFile\": {\n\t\t\treason: \"Should correctly replace extension of hidden files\",\n\t\t\targs: args{\n\t\t\t\tpath: \".hiddenfile.conf\",\n\t\t\t\text:  \".bak\",\n\t\t\t},\n\t\t\twant: \".hiddenfile.bak\",\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := ReplaceExt(tc.args.path, tc.args.ext)\n\n\t\t\tif diff := cmp.Diff(tc.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nReplaceExt(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/parser/examples/parser.go",
    "content": "/*\nCopyright 2023 The Crossplane 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// Package examples contains utilities for parsing examples.\npackage examples\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"io\"\n\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/util/yaml\"\n\tk8syaml \"sigs.k8s.io/yaml\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg/parser\"\n)\n\n// Examples is the set of metadata and objects in a package.\ntype Examples struct {\n\tobjects []unstructured.Unstructured\n}\n\n// Parser is a Parser implementation for parsing examples.\ntype Parser struct{}\n\n// NewExamples creates a new Examples object.\nfunc NewExamples() *Examples {\n\treturn &Examples{}\n}\n\n// New creates a new Package.\nfunc New() *Parser {\n\treturn &Parser{}\n}\n\n// Parse is the underlying logic for parsing examples.\nfunc (p *Parser) Parse(_ context.Context, reader io.ReadCloser) (*Examples, error) {\n\tex := NewExamples()\n\tif reader == nil {\n\t\treturn ex, nil\n\t}\n\n\tdefer func() { _ = reader.Close() }()\n\n\tyr := yaml.NewYAMLReader(bufio.NewReader(reader))\n\tfor {\n\t\tbytes, err := yr.Read()\n\t\tif err != nil && !errors.Is(err, io.EOF) {\n\t\t\treturn ex, annotateErr(err, reader)\n\t\t}\n\n\t\tif errors.Is(err, io.EOF) {\n\t\t\tbreak\n\t\t}\n\n\t\tif len(bytes) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar obj unstructured.Unstructured\n\t\tif err := k8syaml.Unmarshal(bytes, &obj); err != nil {\n\t\t\treturn ex, annotateErr(err, reader)\n\t\t}\n\n\t\tex.objects = append(ex.objects, obj)\n\t}\n\n\treturn ex, nil\n}\n\n// annotateErr annotates an error with context if the reader implements\n// parser.AnnotatedReadCloser. Returns nil if err is nil.\nfunc annotateErr(err error, reader io.ReadCloser) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif anno, ok := reader.(parser.AnnotatedReadCloser); ok {\n\t\treturn errors.Wrapf(err, \"%+v\", anno.Annotate())\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "pkg/xpkg/parser/examples/parser_test.go",
    "content": "/*\nCopyright 2026 The Crossplane 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 examples\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\n// mockAnnotatedReadCloser implements parser.AnnotatedReadCloser for testing.\ntype mockAnnotatedReadCloser struct {\n\tio.ReadCloser\n\tannotation any\n}\n\nfunc (m *mockAnnotatedReadCloser) Annotate() any {\n\treturn m.annotation\n}\n\nfunc TestParse(t *testing.T) {\n\tinvalidYAML := `apiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: test\n  invalid: [broken`\n\tvalidYAML := `apiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: test\n`\n\n\ttype annotation struct {\n\t\tpath     string\n\t\tposition int\n\t}\n\n\ttype args struct {\n\t\treader     io.ReadCloser\n\t\tannotation any\n\t}\n\n\ttype want struct {\n\t\tobjCount       int\n\t\terr            error\n\t\terrContains    string\n\t\texpectAnyError bool\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"NilReader\": {\n\t\t\treason: \"Should return empty examples when reader is nil.\",\n\t\t\targs: args{\n\t\t\t\treader: nil,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobjCount: 0,\n\t\t\t\terr:      nil,\n\t\t\t},\n\t\t},\n\t\t\"ValidYAML\": {\n\t\t\treason: \"Should successfully parse valid YAML.\",\n\t\t\targs: args{\n\t\t\t\treader: io.NopCloser(strings.NewReader(validYAML)),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobjCount: 1,\n\t\t\t\terr:      nil,\n\t\t\t},\n\t\t},\n\t\t\"InvalidYAMLNoAnnotation\": {\n\t\t\treason: \"Should return error without annotation when reader is not AnnotatedReadCloser.\",\n\t\t\targs: args{\n\t\t\t\treader: io.NopCloser(strings.NewReader(invalidYAML)),\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobjCount:       0,\n\t\t\t\texpectAnyError: true,\n\t\t\t},\n\t\t},\n\t\t\"InvalidYAMLWithAnnotation\": {\n\t\t\treason: \"Should include annotation in error message when reader is AnnotatedReadCloser.\",\n\t\t\targs: args{\n\t\t\t\treader:     io.NopCloser(strings.NewReader(invalidYAML)),\n\t\t\t\tannotation: annotation{path: \"/examples/test.yaml\", position: 42},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tobjCount:       0,\n\t\t\t\texpectAnyError: true,\n\t\t\t\terrContains:    \"/examples/test.yaml\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tp := New()\n\t\t\treader := tc.args.reader\n\t\t\tif tc.args.annotation != nil {\n\t\t\t\treader = &mockAnnotatedReadCloser{\n\t\t\t\t\tReadCloser: tc.args.reader,\n\t\t\t\t\tannotation: tc.args.annotation,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tex, err := p.Parse(context.Background(), reader)\n\n\t\t\tif tc.want.expectAnyError {\n\t\t\t\tif err == nil {\n\t\t\t\t\tt.Errorf(\"\\n%s\\nParse(...): expected error, got nil\", tc.reason)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif tc.want.errContains != \"\" && !strings.Contains(err.Error(), tc.want.errContains) {\n\t\t\t\t\tt.Errorf(\"\\n%s\\nParse(...): expected error to contain %q, got %q\", tc.reason, tc.want.errContains, err.Error())\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !tc.want.expectAnyError {\n\t\t\t\tif diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"\\n%s\\nParse(...): -want err, +got err:\\n%s\", tc.reason, diff)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.objCount, len(ex.objects)); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nParse(...): -want objCount, +got objCount:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/parser/fsreader.go",
    "content": "/*\nCopyright 2020 The Crossplane 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\thttp://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 parser\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/spf13/afero\"\n)\n\nvar _ AnnotatedReadCloser = &FsReadCloser{}\n\n// FsReadCloserAnnotation annotates data for an FsReadCloser.\ntype FsReadCloserAnnotation struct {\n\tpath     string\n\tposition int\n}\n\n// FsReadCloser implements io.ReadCloser for an Afero filesystem.\ntype FsReadCloser struct {\n\tfs         afero.Fs\n\tdir        string\n\tpaths      []string\n\tindex      int\n\tposition   int\n\twriteBreak bool\n\twroteBreak bool\n}\n\n// A FilterFn filters files when the FsReadCloser walks the filesystem.\n// Returning true indicates the file should be skipped. Returning an error will\n// cause the FsReadCloser to stop walking the filesystem and return.\ntype FilterFn func(path string, info os.FileInfo) (bool, error)\n\n// SkipPath skips files at a certain path.\nfunc SkipPath(pattern string) FilterFn {\n\treturn func(path string, _ os.FileInfo) (bool, error) {\n\t\treturn filepath.Match(pattern, path)\n\t}\n}\n\n// SkipDirs skips directories.\nfunc SkipDirs() FilterFn {\n\treturn func(_ string, info os.FileInfo) (bool, error) {\n\t\tif info.IsDir() {\n\t\t\treturn true, nil\n\t\t}\n\n\t\treturn false, nil\n\t}\n}\n\n// SkipEmpty skips empty files.\nfunc SkipEmpty() FilterFn {\n\treturn func(_ string, info os.FileInfo) (bool, error) {\n\t\treturn info.Size() == 0, nil\n\t}\n}\n\n// SkipNotYAML skips files that do not have YAML extension.\nfunc SkipNotYAML() FilterFn {\n\treturn func(path string, _ os.FileInfo) (bool, error) {\n\t\tif filepath.Ext(path) != \".yaml\" && filepath.Ext(path) != \".yml\" {\n\t\t\treturn true, nil\n\t\t}\n\n\t\treturn false, nil\n\t}\n}\n\n// NewFsReadCloser returns an FsReadCloser that implements io.ReadCloser. It\n// walks the filesystem ahead of time, then reads file contents when Read is\n// invoked. It does not follow symbolic links.\nfunc NewFsReadCloser(fs afero.Fs, dir string, fns ...FilterFn) (*FsReadCloser, error) {\n\tpaths := []string{}\n\terr := afero.Walk(fs, dir, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, fn := range fns {\n\t\t\tfilter, err := fn(path, info)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif filter {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\tpaths = append(paths, path)\n\n\t\treturn nil\n\t})\n\n\treturn &FsReadCloser{\n\t\tfs:         fs,\n\t\tdir:        dir,\n\t\tpaths:      paths,\n\t\tindex:      0,\n\t\tposition:   0,\n\t\twriteBreak: false,\n\t\twroteBreak: false,\n\t}, err\n}\n\nfunc (r *FsReadCloser) Read(p []byte) (n int, err error) {\n\tif r.wroteBreak {\n\t\tr.index++\n\t\tr.position = 0\n\t\tr.wroteBreak = false\n\t\tn = copy(p, \"\\n---\\n\")\n\n\t\treturn n, nil\n\t}\n\n\tif r.index == len(r.paths) {\n\t\treturn 0, io.EOF\n\t}\n\n\tif r.writeBreak {\n\t\tn = copy(p, \"\\n...\\n\")\n\t\tr.writeBreak = false\n\t\tr.wroteBreak = true\n\n\t\treturn n, nil\n\t}\n\n\tb, err := afero.ReadFile(r.fs, r.paths[r.index])\n\tn = copy(p, b[r.position:])\n\n\tr.position += n\n\tif errors.Is(err, io.EOF) || n == 0 {\n\t\tr.writeBreak = true\n\t\terr = nil\n\t}\n\n\treturn n, err\n}\n\n// Close is a no op for an FsReadCloser.\nfunc (r *FsReadCloser) Close() error {\n\treturn nil\n}\n\n// Annotate returns additional about the data currently being read.\nfunc (r *FsReadCloser) Annotate() any {\n\t// Index will be out of bounds if we error after the final file has been\n\t// read.\n\tindex := r.index\n\tif index == len(r.paths) {\n\t\tindex--\n\t}\n\n\treturn FsReadCloserAnnotation{\n\t\tpath:     r.paths[index],\n\t\tposition: r.position,\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/parser/fuzz_test.go",
    "content": "package parser\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"testing\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n)\n\nfunc FuzzParse(f *testing.F) {\n\tf.Fuzz(func(_ *testing.T, data []byte) {\n\t\tobjScheme := runtime.NewScheme()\n\t\tmetaScheme := runtime.NewScheme()\n\t\tp := New(metaScheme, objScheme)\n\t\tr := io.NopCloser(bytes.NewReader(data))\n\t\t_, _ = p.Parse(context.Background(), r)\n\t})\n}\n"
  },
  {
    "path": "pkg/xpkg/parser/linter.go",
    "content": "/*\nCopyright 2020 The Crossplane 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\thttp://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 parser\n\nimport (\n\t\"strings\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\nconst (\n\terrNilLinterFn = \"linter function is nil\"\n\n\terrOrFmt = \"object did not pass any of the linters with following errors: %s\"\n)\n\n// A Linter lints packages.\ntype Linter interface {\n\tLint(l Lintable) error\n}\n\n// PackageLinterFn lints an entire package. If function applies a check for\n// multiple objects, consider using an ObjectLinterFn.\ntype PackageLinterFn func(Lintable) error\n\n// PackageLinterFns is a convenience function to pass multiple PackageLinterFn\n// to a function that cannot accept variadic arguments.\nfunc PackageLinterFns(fns ...PackageLinterFn) []PackageLinterFn {\n\treturn fns\n}\n\n// ObjectLinterFn lints an object in a package.\ntype ObjectLinterFn func(runtime.Object) error\n\n// ObjectLinterFns is a convenience function to pass multiple ObjectLinterFn to\n// a function that cannot accept variadic arguments.\nfunc ObjectLinterFns(fns ...ObjectLinterFn) []ObjectLinterFn {\n\treturn fns\n}\n\n// PackageLinter lints packages by applying package and object linter functions\n// to it.\ntype PackageLinter struct {\n\tpre       []PackageLinterFn\n\tperMeta   []ObjectLinterFn\n\tperObject []ObjectLinterFn\n}\n\n// NewPackageLinter creates a new PackageLinter.\nfunc NewPackageLinter(pre []PackageLinterFn, perMeta, perObject []ObjectLinterFn) *PackageLinter {\n\treturn &PackageLinter{\n\t\tpre:       pre,\n\t\tperMeta:   perMeta,\n\t\tperObject: perObject,\n\t}\n}\n\n// Lint executes all linter functions against a package.\nfunc (l *PackageLinter) Lint(pkg Lintable) error {\n\tfor _, fn := range l.pre {\n\t\tif err := fn(pkg); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfor _, o := range pkg.GetMeta() {\n\t\tfor _, fn := range l.perMeta {\n\t\t\tif err := fn(o); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, o := range pkg.GetObjects() {\n\t\tfor _, fn := range l.perObject {\n\t\t\tif err := fn(o); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Or checks that at least one of the passed linter functions does not return an\n// error.\nfunc Or(linters ...ObjectLinterFn) ObjectLinterFn {\n\treturn func(o runtime.Object) error {\n\t\tvar errs []string\n\n\t\tfor _, l := range linters {\n\t\t\tif l == nil {\n\t\t\t\treturn errors.New(errNilLinterFn)\n\t\t\t}\n\n\t\t\terr := l(o)\n\t\t\tif err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\terrs = append(errs, err.Error())\n\t\t}\n\n\t\treturn errors.Errorf(errOrFmt, strings.Join(errs, \", \"))\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/parser/linter_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 parser\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/test\"\n)\n\nvar _ Linter = &PackageLinter{}\n\nvar (\n\terrBoom = errors.New(\"boom\")\n\n\tpkgPass = func(_ Lintable) error {\n\t\treturn nil\n\t}\n\tpkgFail = func(_ Lintable) error {\n\t\treturn errBoom\n\t}\n\tobjPass = func(_ runtime.Object) error {\n\t\treturn nil\n\t}\n\tobjFail = func(_ runtime.Object) error {\n\t\treturn errBoom\n\t}\n)\n\nfunc TestLinter(t *testing.T) {\n\ttype args struct {\n\t\tlinter Linter\n\t\tpkg    *Package\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\terr    error\n\t}{\n\t\t\"SuccessfulNoOp\": {\n\t\t\treason: \"Passing no checks should always be successful.\",\n\t\t\targs: args{\n\t\t\t\tlinter: NewPackageLinter(nil, nil, nil),\n\t\t\t\tpkg:    NewPackage(),\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulNoObjects\": {\n\t\t\treason: \"Passing object linters on empty package should always be successful.\",\n\t\t\targs: args{\n\t\t\t\tlinter: NewPackageLinter(nil, ObjectLinterFns(objFail), ObjectLinterFns(objFail)),\n\t\t\t\t// Object linters do not run if a package has no objects.\n\t\t\t\tpkg: NewPackage(),\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulWithChecks\": {\n\t\t\treason: \"Passing checks for a valid package should always be successful.\",\n\t\t\targs: args{\n\t\t\t\tlinter: NewPackageLinter(PackageLinterFns(pkgPass), ObjectLinterFns(objPass), ObjectLinterFns(Or(objPass, objFail))),\n\t\t\t\tpkg: &Package{\n\t\t\t\t\tmeta:    []runtime.Object{deploy},\n\t\t\t\t\tobjects: []runtime.Object{crd},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"ErrorPackageLint\": {\n\t\t\treason: \"Passing package linters for an invalid package should always fail.\",\n\t\t\targs: args{\n\t\t\t\tlinter: NewPackageLinter(PackageLinterFns(pkgFail), ObjectLinterFns(objPass), ObjectLinterFns(objPass)),\n\t\t\t\tpkg: &Package{\n\t\t\t\t\tmeta:    []runtime.Object{deploy},\n\t\t\t\t\tobjects: []runtime.Object{crd},\n\t\t\t\t},\n\t\t\t},\n\t\t\terr: errBoom,\n\t\t},\n\t\t\"ErrorMetaLint\": {\n\t\t\treason: \"Passing meta linters for a package with invalid meta should always fail.\",\n\t\t\targs: args{\n\t\t\t\tlinter: NewPackageLinter(PackageLinterFns(pkgPass), ObjectLinterFns(objFail), ObjectLinterFns(objPass)),\n\t\t\t\tpkg: &Package{\n\t\t\t\t\tmeta:    []runtime.Object{deploy},\n\t\t\t\t\tobjects: []runtime.Object{crd},\n\t\t\t\t},\n\t\t\t},\n\t\t\terr: errBoom,\n\t\t},\n\t\t\"ErrorObjectLint\": {\n\t\t\treason: \"Passing object linters for a package with invalid objects should always fail.\",\n\t\t\targs: args{\n\t\t\t\tlinter: NewPackageLinter(PackageLinterFns(pkgPass), ObjectLinterFns(objPass), ObjectLinterFns(Or(objFail, objFail))),\n\t\t\t\tpkg: &Package{\n\t\t\t\t\tmeta:    []runtime.Object{deploy},\n\t\t\t\t\tobjects: []runtime.Object{crd},\n\t\t\t\t},\n\t\t\t},\n\t\t\terr: errors.Errorf(errOrFmt, errBoom.Error()+\", \"+errBoom.Error()),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := tc.args.linter.Lint(tc.args.pkg)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nl.Lint(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar _ ObjectLinterFn = Or(nil, nil)\n\nfunc TestOr(t *testing.T) {\n\ttype args struct {\n\t\tone ObjectLinterFn\n\t\ttwo ObjectLinterFn\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\terr    error\n\t}{\n\t\t\"SuccessfulBothPass\": {\n\t\t\treason: \"Passing two successful linters should never return error.\",\n\t\t\targs: args{\n\t\t\t\tone: objPass,\n\t\t\t\ttwo: objPass,\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulOnePass\": {\n\t\t\treason: \"Passing one successful linters should never return error.\",\n\t\t\targs: args{\n\t\t\t\tone: objPass,\n\t\t\t\ttwo: objFail,\n\t\t\t},\n\t\t},\n\t\t\"ErrNeitherPass\": {\n\t\t\treason: \"Passing two unsuccessful linters should always return error.\",\n\t\t\targs: args{\n\t\t\t\tone: objFail,\n\t\t\t\ttwo: objFail,\n\t\t\t},\n\t\t\terr: errors.Errorf(errOrFmt, errBoom.Error()+\", \"+errBoom.Error()),\n\t\t},\n\t\t\"ErrNilLinter\": {\n\t\t\treason: \"Passing a nil linter will should always return error.\",\n\t\t\targs: args{\n\t\t\t\tone: nil,\n\t\t\t\ttwo: objPass,\n\t\t\t},\n\t\t\terr: errors.New(errNilLinterFn),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\terr := Or(tc.args.one, tc.args.two)(crd)\n\t\t\tif diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nOr(...): -want error, +got error:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/parser/parser.go",
    "content": "/*\nCopyright 2020 The Crossplane 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\thttp://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// Package parser implements a parser for Crossplane packages.\npackage parser\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"io\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/spf13/afero\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/serializer/json\"\n\t\"k8s.io/apimachinery/pkg/util/yaml\"\n\t\"k8s.io/client-go/kubernetes\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\n// Lintable defines the common API for lintable packages.\ntype Lintable interface {\n\t// GetMeta returns metadata objects of the lintable package, such as\n\t// Provider, Configuration or Function.\n\tGetMeta() []runtime.Object\n\t// GetObjects returns objects of the lintable package.\n\tGetObjects() []runtime.Object\n}\n\n// AnnotatedReadCloser is a wrapper around io.ReadCloser that allows\n// implementations to supply additional information about data that is read.\ntype AnnotatedReadCloser interface {\n\tio.ReadCloser\n\tAnnotate() any\n}\n\n// ObjectCreaterTyper know how to create and determine the type of objects.\ntype ObjectCreaterTyper interface {\n\truntime.ObjectCreater\n\truntime.ObjectTyper\n}\n\n// Package is the set of metadata and objects in a package.\ntype Package struct {\n\tmeta    []runtime.Object\n\tobjects []runtime.Object\n}\n\n// NewPackage creates a new Package.\nfunc NewPackage() *Package {\n\treturn &Package{}\n}\n\n// GetMeta gets metadata from the package.\nfunc (p *Package) GetMeta() []runtime.Object {\n\treturn p.meta\n}\n\n// GetObjects gets objects from the package.\nfunc (p *Package) GetObjects() []runtime.Object {\n\treturn p.objects\n}\n\n// Parser is a package parser.\ntype Parser interface {\n\tParse(ctx context.Context, rc io.ReadCloser) (*Package, error)\n}\n\n// PackageParser is a Parser implementation for parsing packages.\ntype PackageParser struct {\n\tmetaScheme ObjectCreaterTyper\n\tobjScheme  ObjectCreaterTyper\n}\n\n// New returns a new PackageParser.\nfunc New(meta, obj ObjectCreaterTyper) *PackageParser {\n\treturn &PackageParser{\n\t\tmetaScheme: meta,\n\t\tobjScheme:  obj,\n\t}\n}\n\n// Parse is the underlying logic for parsing packages. It first attempts to\n// decode objects recognized by the meta scheme, then attempts to decode objects\n// recognized by the object scheme. Objects not recognized by either scheme\n// return an error rather than being skipped.\nfunc (p *PackageParser) Parse(_ context.Context, reader io.ReadCloser) (*Package, error) {\n\tpkg := NewPackage()\n\tif reader == nil {\n\t\treturn pkg, nil\n\t}\n\n\tdefer func() { _ = reader.Close() }()\n\n\tyr := yaml.NewYAMLReader(bufio.NewReader(reader))\n\tdm := json.NewSerializerWithOptions(json.DefaultMetaFactory, p.metaScheme, p.metaScheme, json.SerializerOptions{Yaml: true})\n\tdo := json.NewSerializerWithOptions(json.DefaultMetaFactory, p.objScheme, p.objScheme, json.SerializerOptions{Yaml: true})\n\n\tfor {\n\t\tcontent, err := yr.Read()\n\t\tif err != nil && !errors.Is(err, io.EOF) {\n\t\t\treturn pkg, err\n\t\t}\n\n\t\tif errors.Is(err, io.EOF) {\n\t\t\tbreak\n\t\t}\n\n\t\tif isEmptyYAML(content) {\n\t\t\tcontinue\n\t\t}\n\n\t\tm, _, err := dm.Decode(content, nil, nil)\n\t\tif err != nil {\n\t\t\t// NOTE(hasheddan): we only try to decode with object scheme if the\n\t\t\t// error is due the object not being registered in the meta scheme.\n\t\t\tif !runtime.IsNotRegisteredError(err) {\n\t\t\t\treturn pkg, annotateErr(err, reader)\n\t\t\t}\n\n\t\t\to, _, err := do.Decode(content, nil, nil)\n\t\t\tif err != nil {\n\t\t\t\treturn pkg, annotateErr(err, reader)\n\t\t\t}\n\n\t\t\tpkg.objects = append(pkg.objects, o)\n\n\t\t\tcontinue\n\t\t}\n\n\t\tpkg.meta = append(pkg.meta, m)\n\t}\n\n\treturn pkg, nil\n}\n\n// isEmptyYAML checks whether the provided YAML can be considered empty. This\n// is useful for filtering out empty YAML documents that would otherwise\n// cause issues when decoded.\nfunc isEmptyYAML(y []byte) bool {\n\tfor line := range strings.SplitSeq(string(y), \"\\n\") {\n\t\ttrimmed := strings.TrimLeftFunc(line, unicode.IsSpace)\n\t\t// We don't want to return an empty document with only separators that\n\t\t// have nothing in-between.\n\t\tif trimmed != \"\" && trimmed != \"---\" && trimmed != \"...\" && !strings.HasPrefix(trimmed, \"#\") {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\n// annotateErr annotates an error if the reader is an AnnotatedReadCloser.\nfunc annotateErr(err error, reader io.ReadCloser) error {\n\tif anno, ok := reader.(AnnotatedReadCloser); ok {\n\t\treturn errors.Wrapf(err, \"%+v\", anno.Annotate())\n\t}\n\n\treturn err\n}\n\n// BackendOption modifies the parser backend. Backends may accept options at\n// creation time, but must accept them at initialization.\ntype BackendOption func(Backend)\n\n// Backend provides a source for a parser.\ntype Backend interface {\n\tInit(ctx context.Context, o ...BackendOption) (io.ReadCloser, error)\n}\n\n// PodLogBackend is a parser backend that uses Kubernetes pod logs as source.\ntype PodLogBackend struct {\n\tclient    kubernetes.Interface\n\tname      string\n\tnamespace string\n}\n\n// NewPodLogBackend returns a new PodLogBackend.\nfunc NewPodLogBackend(bo ...BackendOption) *PodLogBackend {\n\tp := &PodLogBackend{}\n\tfor _, o := range bo {\n\t\to(p)\n\t}\n\n\treturn p\n}\n\n// Init initializes a PodLogBackend.\nfunc (p *PodLogBackend) Init(ctx context.Context, bo ...BackendOption) (io.ReadCloser, error) {\n\tfor _, o := range bo {\n\t\to(p)\n\t}\n\n\tlogs := p.client.CoreV1().Pods(p.namespace).GetLogs(p.name, &corev1.PodLogOptions{})\n\n\treader, err := logs.Stream(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn reader, nil\n}\n\n// PodName sets the pod name of a PodLogBackend.\nfunc PodName(name string) BackendOption {\n\treturn func(p Backend) {\n\t\tpl, ok := p.(*PodLogBackend)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tpl.name = name\n\t}\n}\n\n// PodNamespace sets the pod namespace of a PodLogBackend.\nfunc PodNamespace(namespace string) BackendOption {\n\treturn func(p Backend) {\n\t\tpl, ok := p.(*PodLogBackend)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tpl.namespace = namespace\n\t}\n}\n\n// PodClient sets the pod client of a PodLogBackend.\nfunc PodClient(client kubernetes.Interface) BackendOption {\n\treturn func(p Backend) {\n\t\tpl, ok := p.(*PodLogBackend)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tpl.client = client\n\t}\n}\n\n// NopBackend is a parser backend with empty source.\ntype NopBackend struct{}\n\n// NewNopBackend returns a new NopBackend.\nfunc NewNopBackend(...BackendOption) *NopBackend {\n\treturn &NopBackend{}\n}\n\n// Init initializes a NopBackend.\nfunc (p *NopBackend) Init(_ context.Context, _ ...BackendOption) (io.ReadCloser, error) {\n\treturn nil, nil\n}\n\n// FsBackend is a parser backend that uses a filestystem as source.\ntype FsBackend struct {\n\tfs    afero.Fs\n\tdir   string\n\tskips []FilterFn\n}\n\n// NewFsBackend returns an FsBackend.\nfunc NewFsBackend(fs afero.Fs, bo ...BackendOption) *FsBackend {\n\tf := &FsBackend{\n\t\tfs: fs,\n\t}\n\tfor _, o := range bo {\n\t\to(f)\n\t}\n\n\treturn f\n}\n\n// Init initializes an FsBackend.\nfunc (p *FsBackend) Init(_ context.Context, bo ...BackendOption) (io.ReadCloser, error) {\n\tfor _, o := range bo {\n\t\to(p)\n\t}\n\n\treturn NewFsReadCloser(p.fs, p.dir, p.skips...)\n}\n\n// FsDir sets the directory of an FsBackend.\nfunc FsDir(dir string) BackendOption {\n\treturn func(p Backend) {\n\t\tf, ok := p.(*FsBackend)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tf.dir = dir\n\t}\n}\n\n// FsFilters adds FilterFns to an FsBackend.\nfunc FsFilters(skips ...FilterFn) BackendOption {\n\treturn func(p Backend) {\n\t\tf, ok := p.(*FsBackend)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tf.skips = skips\n\t}\n}\n\n// EchoBackend is a parser backend that uses string input as source.\ntype EchoBackend struct {\n\techo string\n}\n\n// NewEchoBackend returns a new EchoBackend.\nfunc NewEchoBackend(echo string) Backend {\n\treturn &EchoBackend{\n\t\techo: echo,\n\t}\n}\n\n// Init initializes an EchoBackend.\nfunc (p *EchoBackend) Init(_ context.Context, bo ...BackendOption) (io.ReadCloser, error) {\n\tfor _, o := range bo {\n\t\to(p)\n\t}\n\n\treturn io.NopCloser(strings.NewReader(p.echo)), nil\n}\n"
  },
  {
    "path": "pkg/xpkg/parser/parser_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 parser\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\t\"github.com/spf13/afero\"\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tapiextensions \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"sigs.k8s.io/yaml\"\n)\n\nvar _ Parser = &PackageParser{}\n\nvar (\n\tcrdBytes = []byte(`apiVersion: apiextensions.k8s.io/v1beta1\nkind: CustomResourceDefinition\nmetadata:\n  name: test`)\n\n\twhitespaceBytes = []byte(`---\napiVersion: apiextensions.k8s.io/v1beta1\nkind: CustomResourceDefinition\nmetadata:\n  name: test\n---\n\n---\n\n---\napiVersion: apiextensions.k8s.io/v1beta1\nkind: CustomResourceDefinition\nmetadata:\n  name: test`)\n\n\tdeployBytes = []byte(`apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: test\n  annotations:\n    crossplane.io/managed: |\n      #!/bin/bash some script\n      some script\n`)\n\n\tcommentedOutBytes = []byte(`# apiVersion: apps/v1\n# kind: Deployment\n# metadata:\n#   name: test`)\n\tmanifestWithComments = []byte(`\napiVersion: apiextensions.k8s.io/v1beta1\n# Some Comment\nkind: CustomResourceDefinition\nmetadata:\n  name: test`)\n\n\tcrd    = &apiextensions.CustomResourceDefinition{}\n\t_      = yaml.Unmarshal(crdBytes, crd)\n\tdeploy = &appsv1.Deployment{}\n\t_      = yaml.Unmarshal(deployBytes, deploy)\n)\n\nfunc TestParser(t *testing.T) {\n\tallBytes := bytes.Join([][]byte{crdBytes, deployBytes}, []byte(\"\\n---\\n\"))\n\tfs := afero.NewMemMapFs()\n\t_ = afero.WriteFile(fs, \"crd.yaml\", crdBytes, 0o644)\n\t_ = afero.WriteFile(fs, \"whitespace.yaml\", whitespaceBytes, 0o644)\n\t_ = afero.WriteFile(fs, \"deployment.yaml\", deployBytes, 0o644)\n\t_ = afero.WriteFile(fs, \"some/nested/dir/crd.yaml\", crdBytes, 0o644)\n\t_ = afero.WriteFile(fs, \".crossplane/bad.yaml\", crdBytes, 0o644)\n\tallFs := afero.NewMemMapFs()\n\t_ = afero.WriteFile(allFs, \"all.yaml\", allBytes, 0o644)\n\terrFs := afero.NewMemMapFs()\n\t_ = afero.WriteFile(errFs, \"bad.yaml\", []byte(\"definitely not yaml\"), 0o644)\n\temptyFs := afero.NewMemMapFs()\n\t_ = afero.WriteFile(emptyFs, \"empty.yaml\", []byte(\"\"), 0o644)\n\t_ = afero.WriteFile(emptyFs, \"bad.yam\", []byte(\"definitely not yaml\"), 0o644)\n\tcommentedFs := afero.NewMemMapFs()\n\t_ = afero.WriteFile(commentedFs, \"commented.yaml\", commentedOutBytes, 0o644)\n\t_ = afero.WriteFile(commentedFs, \".crossplane/realmanifest.yaml\", manifestWithComments, 0o644)\n\tobjScheme := runtime.NewScheme()\n\t_ = apiextensions.AddToScheme(objScheme)\n\tmetaScheme := runtime.NewScheme()\n\t_ = appsv1.AddToScheme(metaScheme)\n\n\tcases := map[string]struct {\n\t\treason  string\n\t\tparser  Parser\n\t\tbackend Backend\n\t\tpkg     *Package\n\t\twantErr bool\n\t}{\n\t\t\"EchoBackendEmpty\": {\n\t\t\treason:  \"should have empty output with empty input\",\n\t\t\tparser:  New(metaScheme, objScheme),\n\t\t\tbackend: NewEchoBackend(\"\"),\n\t\t\tpkg:     NewPackage(),\n\t\t},\n\t\t\"EchoBackendError\": {\n\t\t\treason:  \"should have error with invalid yaml\",\n\t\t\tparser:  New(metaScheme, objScheme),\n\t\t\tbackend: NewEchoBackend(\"definitely not yaml\"),\n\t\t\tpkg:     NewPackage(),\n\t\t\twantErr: true,\n\t\t},\n\t\t\"EchoBackend\": {\n\t\t\treason:  \"should parse input stream successfully\",\n\t\t\tparser:  New(metaScheme, objScheme),\n\t\t\tbackend: NewEchoBackend(string(allBytes)),\n\t\t\tpkg: &Package{\n\t\t\t\tmeta:    []runtime.Object{deploy},\n\t\t\t\tobjects: []runtime.Object{crd},\n\t\t\t},\n\t\t},\n\t\t\"NopBackend\": {\n\t\t\treason:  \"should never parse any objects and never return an error\",\n\t\t\tparser:  New(metaScheme, objScheme),\n\t\t\tbackend: NewNopBackend(),\n\t\t\tpkg:     NewPackage(),\n\t\t},\n\t\t\"FsBackend\": {\n\t\t\treason:  \"should parse filesystem successfully\",\n\t\t\tparser:  New(metaScheme, objScheme),\n\t\t\tbackend: NewFsBackend(fs, FsDir(\".\"), FsFilters(SkipDirs(), SkipNotYAML(), SkipPath(\".crossplane/*\"))),\n\t\t\tpkg: &Package{\n\t\t\t\tmeta:    []runtime.Object{deploy},\n\t\t\t\tobjects: []runtime.Object{crd, crd, crd, crd},\n\t\t\t},\n\t\t},\n\t\t\"FsBackendCommentedOut\": {\n\t\t\treason:  \"should parse filesystem successfully even if all the files are commented out\",\n\t\t\tparser:  New(metaScheme, objScheme),\n\t\t\tbackend: NewFsBackend(commentedFs, FsDir(\".\"), FsFilters(SkipDirs(), SkipNotYAML(), SkipPath(\".crossplane/*\"))),\n\t\t\tpkg: &Package{\n\t\t\t\tmeta:    nil,\n\t\t\t\tobjects: nil,\n\t\t\t},\n\t\t},\n\t\t\"FsBackendWithComments\": {\n\t\t\treason:  \"should parse filesystem successfully when some of the manifests contain comments\",\n\t\t\tparser:  New(metaScheme, objScheme),\n\t\t\tbackend: NewFsBackend(commentedFs, FsDir(\".\"), FsFilters(SkipDirs(), SkipNotYAML())),\n\t\t\tpkg: &Package{\n\t\t\t\tmeta:    nil,\n\t\t\t\tobjects: []runtime.Object{crd},\n\t\t\t},\n\t\t},\n\t\t\"FsBackendAll\": {\n\t\t\treason:  \"should parse filesystem successfully with multiple objects in single file\",\n\t\t\tparser:  New(metaScheme, objScheme),\n\t\t\tbackend: NewFsBackend(allFs, FsDir(\".\"), FsFilters(SkipDirs(), SkipNotYAML(), SkipPath(\".crossplane/*\"))),\n\t\t\tpkg: &Package{\n\t\t\t\tmeta:    []runtime.Object{deploy},\n\t\t\t\tobjects: []runtime.Object{crd},\n\t\t\t},\n\t\t},\n\t\t\"FsBackendError\": {\n\t\t\treason:  \"should error if yaml file with invalid yaml\",\n\t\t\tparser:  New(metaScheme, objScheme),\n\t\t\tbackend: NewFsBackend(fs, FsDir(\".\")),\n\t\t\tpkg:     NewPackage(),\n\t\t\twantErr: true,\n\t\t},\n\t\t\"FsBackendSkip\": {\n\t\t\treason:  \"should skip empty files and files without yaml extension\",\n\t\t\tparser:  New(metaScheme, objScheme),\n\t\t\tbackend: NewFsBackend(emptyFs, FsDir(\".\"), FsFilters(SkipDirs(), SkipEmpty(), SkipNotYAML())),\n\t\t\tpkg:     NewPackage(),\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tr, err := tc.backend.Init(context.TODO())\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"backend.Init(...): unexpected error: %s\", err)\n\t\t\t}\n\n\t\t\tpkg, err := tc.parser.Parse(context.TODO(), r)\n\t\t\tif err != nil && !tc.wantErr {\n\t\t\t\tt.Errorf(\"parser.Parse(...): unexpected error: %s\", err)\n\t\t\t}\n\n\t\t\tif tc.wantErr {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.pkg.GetObjects(), pkg.GetObjects(), cmpopts.SortSlices(func(i, j runtime.Object) bool {\n\t\t\t\treturn i.GetObjectKind().GroupVersionKind().String() > j.GetObjectKind().GroupVersionKind().String()\n\t\t\t})); diff != \"\" {\n\t\t\t\tt.Errorf(\"Objects: -want, +got:\\n%s\", diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.pkg.GetMeta(), pkg.GetMeta(), cmpopts.SortSlices(func(i, j runtime.Object) bool {\n\t\t\t\treturn i.GetObjectKind().GroupVersionKind().String() > j.GetObjectKind().GroupVersionKind().String()\n\t\t\t})); diff != \"\" {\n\t\t\t\tt.Errorf(\"Meta: -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCleanYAML(t *testing.T) {\n\ttype args struct {\n\t\tin []byte\n\t}\n\n\ttype want struct {\n\t\tout bool\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"Empty\": {\n\t\t\treason: \"Should return true on empty input\",\n\t\t\targs:   args{in: []byte(\"\")},\n\t\t\twant:   want{out: true},\n\t\t},\n\t\t\"EmptyLine\": {\n\t\t\treason: \"Should return true on an input with an empty line\",\n\t\t\targs:   args{in: []byte(\"\\n\")},\n\t\t\twant:   want{out: true},\n\t\t},\n\t\t\"WhitespaceOnly\": {\n\t\t\treason: \"Should return true on an input with only whitespaces\",\n\t\t\targs:   args{in: []byte(\"    \\n\\t \")},\n\t\t\twant:   want{out: true},\n\t\t},\n\t\t\"OnlyYAMLSeparators\": {\n\t\t\treason: \"Should return true on an input with only YAML separators\",\n\t\t\targs:   args{in: []byte(\"---\\n...\")},\n\t\t\twant:   want{out: true},\n\t\t},\n\t\t\"YAMLWithWhitespaceLineAndNonEmptyLine\": {\n\t\t\treason: \"Should return false on having whitespace and non empty line in the input\",\n\t\t\targs:   args{in: []byte(\" \\nkey: value\")},\n\t\t\twant:   want{out: false},\n\t\t},\n\t\t\"CommentedOut\": {\n\t\t\treason: \"Should return true on a fully commented out input\",\n\t\t\targs: args{in: []byte(`# apiVersion: apps/v1\n# kind: Deployment\n# metadata:\n#   name: test`)},\n\t\t\twant: want{out: true},\n\t\t},\n\t\t\"CommentedOutExceptSeparator\": {\n\t\t\treason: \"Should return true on a fully commented out input with a separator not commented\",\n\t\t\targs: args{in: []byte(`---\n# apiVersion: apps/v1\n# kind: Deployment\n# metadata:\n#   name: test`)},\n\t\t\twant: want{out: true},\n\t\t},\n\t\t\"NotFullyCommentedOut\": {\n\t\t\treason: \"Should return false on a partially commented out input\",\n\t\t\targs: args{in: []byte(`---\n# some comment\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: test`)},\n\t\t\twant: want{out: false},\n\t\t},\n\t\t\"ShebangAnnotation\": {\n\t\t\treason: \"Should return false with just a shebang annotation\",\n\t\t\targs: args{in: []byte(`---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: test\n  annotations:\n    someScriptWithAShebang: |\n      #!/bin/bash\n      some script`)},\n\t\t\twant: want{out: false},\n\t\t},\n\t}\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := isEmptyYAML(tc.args.in)\n\t\t\tif diff := cmp.Diff(tc.want.out, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"isEmptyYAML: -want, +got:\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/parser/yaml/parser.go",
    "content": "/*\nCopyright 2023 The Crossplane 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// Package yaml contains utilities for reading yaml packages.\npackage yaml\n\nimport (\n\t\"errors\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg\"\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg/parser\"\n)\n\nconst (\n\terrBuildMetaScheme   = \"failed to build meta scheme for package parser\"\n\terrBuildObjectScheme = \"failed to build object scheme for package parser\"\n)\n\n// New returns a new PackageParser that targets yaml files.\nfunc New() (*parser.PackageParser, error) {\n\tmetaScheme, err := xpkg.BuildMetaScheme()\n\tif err != nil {\n\t\treturn nil, errors.New(errBuildMetaScheme)\n\t}\n\n\tobjScheme, err := xpkg.BuildObjectScheme()\n\tif err != nil {\n\t\treturn nil, errors.New(errBuildObjectScheme)\n\t}\n\n\treturn parser.New(metaScheme, objScheme), nil\n}\n"
  },
  {
    "path": "pkg/xpkg/reader.go",
    "content": "/*\nCopyright 2022 The Crossplane 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 xpkg\n\nimport (\n\t\"compress/gzip\"\n\t\"io\"\n)\n\nvar _ io.ReadCloser = &gzipReadCloser{}\n\n// gzipReadCloser reads compressed contents from a file.\ntype gzipReadCloser struct {\n\trc   io.ReadCloser\n\tgzip *gzip.Reader\n}\n\n// GzipReadCloser constructs a new gzipReadCloser from the passed file.\nfunc GzipReadCloser(rc io.ReadCloser) (io.ReadCloser, error) {\n\tr, err := gzip.NewReader(rc)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &gzipReadCloser{\n\t\trc:   rc,\n\t\tgzip: r,\n\t}, nil\n}\n\n// Read calls the underlying gzip reader's Read method.\nfunc (g *gzipReadCloser) Read(p []byte) (n int, err error) {\n\treturn g.gzip.Read(p)\n}\n\n// Close first closes the gzip reader, then closes the underlying closer.\nfunc (g *gzipReadCloser) Close() error {\n\tif err := g.gzip.Close(); err != nil {\n\t\t_ = g.rc.Close()\n\t\treturn err\n\t}\n\n\treturn g.rc.Close()\n}\n\nvar _ io.ReadCloser = &teeReadCloser{}\n\n// teeReadCloser is a TeeReader that also closes the underlying writer.\ntype teeReadCloser struct {\n\tw io.WriteCloser\n\tr io.ReadCloser\n\tt io.Reader\n}\n\n// TeeReadCloser constructs a teeReadCloser from the passed reader and writer.\nfunc TeeReadCloser(r io.ReadCloser, w io.WriteCloser) io.ReadCloser {\n\treturn &teeReadCloser{\n\t\tw: w,\n\t\tr: r,\n\t\tt: io.TeeReader(r, w),\n\t}\n}\n\n// Read calls the underlying TeeReader Read method.\nfunc (t *teeReadCloser) Read(b []byte) (int, error) {\n\treturn t.t.Read(b)\n}\n\n// Close closes the underlying ReadCloser, then the Writer for the TeeReader.\nfunc (t *teeReadCloser) Close() error {\n\tif err := t.r.Close(); err != nil {\n\t\t_ = t.w.Close()\n\t\treturn err\n\t}\n\n\treturn t.w.Close()\n}\n\nvar _ io.ReadCloser = &joinedReadCloser{}\n\n// joinedReadCloser joins a reader and a closer. It is typically used in the\n// context of a ReadCloser being wrapped by a Reader.\ntype joinedReadCloser struct {\n\tr io.Reader\n\tc io.Closer\n}\n\n// JoinedReadCloser constructs a new joinedReadCloser from the passed reader and\n// closer.\nfunc JoinedReadCloser(r io.Reader, c io.Closer) io.ReadCloser {\n\treturn &joinedReadCloser{\n\t\tr: r,\n\t\tc: c,\n\t}\n}\n\n// Read calls the underlying reader Read method.\nfunc (r *joinedReadCloser) Read(b []byte) (int, error) {\n\treturn r.r.Read(b)\n}\n\n// Close closes the closer for the JoinedReadCloser.\nfunc (r *joinedReadCloser) Close() error {\n\treturn r.c.Close()\n}\n"
  },
  {
    "path": "pkg/xpkg/scheme.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 xpkg\n\nimport (\n\tv1 \"github.com/crossplane/crossplane/apis/v2/apiextensions/v1\"\n\t\"github.com/crossplane/crossplane/apis/v2/apiextensions/v1alpha1\"\n\tv2 \"github.com/crossplane/crossplane/apis/v2/apiextensions/v2\"\n\topsv1alpha1 \"github.com/crossplane/crossplane/apis/v2/ops/v1alpha1\"\n\tpkgmetav1 \"github.com/crossplane/crossplane/apis/v2/pkg/meta/v1\"\n\tpkgmetav1alpha1 \"github.com/crossplane/crossplane/apis/v2/pkg/meta/v1alpha1\"\n\tpkgmetav1beta1 \"github.com/crossplane/crossplane/apis/v2/pkg/meta/v1beta1\"\n\tadmv1 \"k8s.io/api/admissionregistration/v1\"\n\textv1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\"\n\textv1beta1 \"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/conversion\"\n)\n\n// BuildMetaScheme builds the default scheme used for identifying metadata in a\n// Crossplane package.\nfunc BuildMetaScheme() (*runtime.Scheme, error) {\n\tmetaScheme := runtime.NewScheme()\n\tif err := pkgmetav1alpha1.SchemeBuilder.AddToScheme(metaScheme); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := pkgmetav1beta1.SchemeBuilder.AddToScheme(metaScheme); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := pkgmetav1.SchemeBuilder.AddToScheme(metaScheme); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn metaScheme, nil\n}\n\n// BuildObjectScheme builds the default scheme used for identifying objects in a\n// Crossplane package.\nfunc BuildObjectScheme() (*runtime.Scheme, error) {\n\tobjScheme := runtime.NewScheme()\n\tif err := v1.AddToScheme(objScheme); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := v1alpha1.AddToScheme(objScheme); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := opsv1alpha1.AddToScheme(objScheme); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := v2.AddToScheme(objScheme); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := extv1beta1.AddToScheme(objScheme); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := extv1.AddToScheme(objScheme); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := admv1.AddToScheme(objScheme); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn objScheme, nil\n}\n\n// TryConvert converts the supplied object to the first supplied candidate that\n// does not return an error. Returns the converted object and true when\n// conversion succeeds, or the original object and false if it does not.\nfunc TryConvert(obj runtime.Object, candidates ...conversion.Hub) (runtime.Object, bool) {\n\t// Note that if we already converted the supplied object to one of the\n\t// supplied Hubs in a previous call this will ensure we skip conversion if\n\t// and when it's called again.\n\tcvt, ok := obj.(conversion.Convertible)\n\tif !ok {\n\t\treturn obj, false\n\t}\n\n\tfor _, c := range candidates {\n\t\tif err := cvt.ConvertTo(c); err == nil {\n\t\t\treturn c, true\n\t\t}\n\t}\n\n\treturn obj, false\n}\n\n// TryConvertToPkg converts the supplied object to a pkgmeta.Pkg, if possible.\nfunc TryConvertToPkg(obj runtime.Object, candidates ...conversion.Hub) (pkgmetav1.Pkg, bool) {\n\tpo, _ := TryConvert(obj, candidates...)\n\tm, ok := po.(pkgmetav1.Pkg)\n\n\treturn m, ok\n}\n"
  },
  {
    "path": "pkg/xpkg/scheme_test.go",
    "content": "/*\nCopyright 2020 The Crossplane 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 xpkg\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/conversion\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\ntype mockHub struct{ runtime.Object }\n\nfunc (h mockHub) Hub() {}\n\ntype mockConvertible struct {\n\tconversion.Convertible\n\tFail bool\n}\n\nfunc (c *mockConvertible) ConvertTo(_ conversion.Hub) error {\n\tif c.Fail {\n\t\treturn errors.New(\"nope\")\n\t}\n\n\treturn nil\n}\n\nfunc TestTryConvert(t *testing.T) {\n\ttype args struct {\n\t\tmeta       runtime.Object\n\t\tcandidates []conversion.Hub\n\t}\n\n\ttype want struct {\n\t\tmeta runtime.Object\n\t\tok   bool\n\t}\n\n\tcases := map[string]struct {\n\t\treason string\n\t\targs   args\n\t\twant   want\n\t}{\n\t\t\"NotConvertible\": {\n\t\t\treason: \"We should return the object unchanged if we try to convert an object that is not convertible.\",\n\t\t\targs: args{\n\t\t\t\tmeta: nil,\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tmeta: nil,\n\t\t\t\tok:   false,\n\t\t\t},\n\t\t},\n\t\t\"ErrNoConversion\": {\n\t\t\treason: \"We should return false if none of the supplied candidates convert successfully.\",\n\t\t\targs: args{\n\t\t\t\tmeta:       &mockConvertible{Fail: true},\n\t\t\t\tcandidates: []conversion.Hub{&mockHub{}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tmeta: &mockConvertible{Fail: true},\n\t\t\t\tok:   false,\n\t\t\t},\n\t\t},\n\t\t\"SuccessfulConversion\": {\n\t\t\treason: \"We should not return true if one of the supplied candidates converted successfully.\",\n\t\t\targs: args{\n\t\t\t\tmeta:       &mockConvertible{},\n\t\t\t\tcandidates: []conversion.Hub{&mockHub{}},\n\t\t\t},\n\t\t\twant: want{\n\t\t\t\tmeta: &mockHub{},\n\t\t\t\tok:   true,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor name, tc := range cases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot, ok := TryConvert(tc.args.meta, tc.args.candidates...)\n\t\t\tif diff := cmp.Diff(tc.want.ok, ok); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nTryConvert(...): -want ok, +got ok:\\n%s\", tc.reason, diff)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.want.meta, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"\\n%s\\nTryConvert(...): -want, +got:\\n%s\", tc.reason, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/signature/attestation.go",
    "content": "//\n// Copyright 2022 The Sigstore Authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Note(turkenh): This file is copied from https://github.com/sigstore/cosign/blob/ad478088320a3c04a96b3c183bbde2205fff7bbb/pkg/policy/attestation.go#L59\n// with little modification to remove the dependency on \"github.com/sigstore/cosign/v2/cmd/cosign/cli/options\"\n// which brings in a lot of dependencies. Keeping the original license above.\n\npackage signature\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\n\tattestationv1 \"github.com/in-toto/attestation/go/v1\"\n\t\"github.com/in-toto/in-toto-golang/in_toto\"\n\tslsa02 \"github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2\"\n\tslsa1 \"github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1\"\n\t\"github.com/sigstore/cosign/v3/pkg/cosign/attestation\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci\"\n\t\"google.golang.org/protobuf/encoding/protojson\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\nconst (\n\tpredicateCustom    = \"custom\"\n\tpredicateSLSA      = \"slsaprovenance\"\n\tpredicateSLSA02    = \"slsaprovenance02\"\n\tpredicateSLSA1     = \"slsaprovenance1\"\n\tpredicateSPDX      = \"spdx\"\n\tpredicateSPDXJSON  = \"spdxjson\"\n\tpredicateCycloneDX = \"cyclonedx\"\n\tpredicateLink      = \"link\"\n\tpredicateVuln      = \"vuln\"\n\tpredicateOpenVEX   = \"openvex\"\n)\n\n// AttestationToPayloadJSON takes in a verified Attestation (oci.Signature) and\n// marshals it into a JSON depending on the payload that's then consumable\n// by policy engine like cue, rego, etc.\n//\n// Anything fed here must have been validated with either\n// `VerifyLocalImageAttestations` or `VerifyImageAttestations`\n//\n// If there's no error, and payload is empty means the predicateType did not\n// match the attestation.\n// Returns the attestation type (PredicateType) if the payload was decoded\n// before the error happened, or in the case the predicateType that was\n// requested does not match. This is useful for callers to be able to provide\n// better error messages. For example, if there's a typo in the predicateType,\n// or the predicateType is not the one they are looking for. Without returning\n// this, it's hard for users to know which attestations/predicateTypes were\n// inspected.\nfunc attestationToPayloadJSON(_ context.Context, predicateType string, verifiedAttestation oci.Signature) ([]byte, string, error) { //nolint:gocognit // Copied from cosign, see the above note.\n\t// PredicateTypeMap is the mapping between the predicate `type` option to predicate URI.\n\tPredicateTypeMap := map[string]string{\n\t\tpredicateCustom:    attestation.CosignCustomProvenanceV01,\n\t\tpredicateSLSA:      slsa02.PredicateSLSAProvenance,\n\t\tpredicateSLSA02:    slsa02.PredicateSLSAProvenance,\n\t\tpredicateSLSA1:     slsa1.PredicateSLSAProvenance,\n\t\tpredicateSPDX:      in_toto.PredicateSPDX,\n\t\tpredicateSPDXJSON:  in_toto.PredicateSPDX,\n\t\tpredicateCycloneDX: in_toto.PredicateCycloneDX,\n\t\tpredicateLink:      in_toto.PredicateLinkV1,\n\t\tpredicateVuln:      attestation.CosignVulnProvenanceV01,\n\t\tpredicateOpenVEX:   attestation.OpenVexNamespace,\n\t}\n\n\tif predicateType == \"\" {\n\t\treturn nil, \"\", errors.New(\"missing predicate type\")\n\t}\n\n\tpredicateURI, ok := PredicateTypeMap[predicateType]\n\tif !ok {\n\t\t// Not a custom one, use it as is.\n\t\tpredicateURI = predicateType\n\t}\n\n\tvar payloadData map[string]any\n\n\tp, err := verifiedAttestation.Payload()\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"getting payload: %w\", err)\n\t}\n\n\terr = json.Unmarshal(p, &payloadData)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"unmarshaling payload data\")\n\t}\n\n\tvar decodedPayload []byte\n\tif val, ok := payloadData[\"payload\"]; ok {\n\t\tdecodedPayload, err = base64.StdEncoding.DecodeString(val.(string)) //nolint:forcetypeassert // TODO(negz): Will this always be a string?\n\t\tif err != nil {\n\t\t\treturn nil, \"\", fmt.Errorf(\"decoding payload: %w\", err)\n\t\t}\n\t} else {\n\t\treturn nil, \"\", fmt.Errorf(\"could not find payload in payload data\")\n\t}\n\n\t// Only apply the policy against the requested predicate type\n\tvar statement attestationv1.Statement\n\tif err := protojson.Unmarshal(decodedPayload, &statement); err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"unmarshal in-toto statement: %w\", err)\n\t}\n\n\tif statement.GetPredicateType() != predicateURI {\n\t\t// This is not the predicate we're looking for, so skip it.\n\t\treturn nil, statement.GetPredicateType(), nil\n\t}\n\n\t// NB: In many (all?) of these cases, we could just return the\n\t// 'json.Marshal', but we check for errors here to decorate them\n\t// with more meaningful error message.\n\tvar payload []byte\n\n\tswitch predicateType {\n\tcase predicateCustom:\n\t\tpayload, err = protojson.Marshal(&statement)\n\t\tif err != nil {\n\t\t\treturn nil, statement.GetPredicateType(), fmt.Errorf(\"generating CosignStatement: %w\", err)\n\t\t}\n\tcase predicateLink:\n\t\tvar linkStatement in_toto.LinkStatement\n\t\tif err := json.Unmarshal(decodedPayload, &linkStatement); err != nil {\n\t\t\treturn nil, statement.GetPredicateType(), fmt.Errorf(\"unmarshaling LinkStatement: %w\", err)\n\t\t}\n\n\t\tpayload, err = json.Marshal(linkStatement)\n\t\tif err != nil {\n\t\t\treturn nil, statement.GetPredicateType(), fmt.Errorf(\"marshaling LinkStatement: %w\", err)\n\t\t}\n\tcase predicateSLSA:\n\t\tvar slsaProvenanceStatement in_toto.ProvenanceStatementSLSA02\n\t\tif err := json.Unmarshal(decodedPayload, &slsaProvenanceStatement); err != nil {\n\t\t\treturn nil, statement.GetPredicateType(), fmt.Errorf(\"unmarshaling ProvenanceStatementSLSA02): %w\", err)\n\t\t}\n\n\t\tpayload, err = json.Marshal(slsaProvenanceStatement)\n\t\tif err != nil {\n\t\t\treturn nil, statement.GetPredicateType(), fmt.Errorf(\"marshaling ProvenanceStatementSLSA02: %w\", err)\n\t\t}\n\tcase predicateSPDX, predicateSPDXJSON:\n\t\tvar spdxStatement in_toto.SPDXStatement\n\t\tif err := json.Unmarshal(decodedPayload, &spdxStatement); err != nil {\n\t\t\treturn nil, statement.GetPredicateType(), fmt.Errorf(\"unmarshaling SPDXStatement: %w\", err)\n\t\t}\n\n\t\tpayload, err = json.Marshal(spdxStatement)\n\t\tif err != nil {\n\t\t\treturn nil, statement.GetPredicateType(), fmt.Errorf(\"marshaling SPDXStatement: %w\", err)\n\t\t}\n\tcase predicateCycloneDX:\n\t\tvar cyclonedxStatement in_toto.CycloneDXStatement\n\t\tif err := json.Unmarshal(decodedPayload, &cyclonedxStatement); err != nil {\n\t\t\treturn nil, statement.GetPredicateType(), fmt.Errorf(\"unmarshaling CycloneDXStatement: %w\", err)\n\t\t}\n\n\t\tpayload, err = json.Marshal(cyclonedxStatement)\n\t\tif err != nil {\n\t\t\treturn nil, statement.GetPredicateType(), fmt.Errorf(\"marshaling CycloneDXStatement: %w\", err)\n\t\t}\n\tcase predicateVuln:\n\t\tvar vulnStatement attestation.CosignVulnStatement\n\t\tif err := json.Unmarshal(decodedPayload, &vulnStatement); err != nil {\n\t\t\treturn nil, statement.GetPredicateType(), fmt.Errorf(\"unmarshaling CosignVulnStatement: %w\", err)\n\t\t}\n\n\t\tpayload, err = json.Marshal(vulnStatement)\n\t\tif err != nil {\n\t\t\treturn nil, statement.GetPredicateType(), fmt.Errorf(\"marshaling CosignVulnStatement: %w\", err)\n\t\t}\n\tdefault:\n\t\t// Valid URI type reaches here.\n\t\tpayload, err = protojson.Marshal(&statement)\n\t\tif err != nil {\n\t\t\treturn nil, statement.GetPredicateType(), fmt.Errorf(\"generating Statement: %w\", err)\n\t\t}\n\t}\n\n\treturn payload, statement.GetPredicateType(), nil\n}\n"
  },
  {
    "path": "pkg/xpkg/signature/doc.go",
    "content": "/*\nCopyright 2025 The Crossplane 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// Package signature implements image signature verification for Crossplane packages.\npackage signature\n"
  },
  {
    "path": "pkg/xpkg/signature/validate.go",
    "content": "package signature\n\nimport (\n\t\"context\"\n\t\"crypto\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/crossplane/crossplane/apis/v2/pkg/v1beta1\"\n\t\"github.com/google/go-containerregistry/pkg/authn/k8schain\"\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/go-containerregistry/pkg/v1/remote\"\n\t\"github.com/sigstore/cosign/v3/pkg/cosign\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci\"\n\tociremote \"github.com/sigstore/cosign/v3/pkg/oci/remote\"\n\t\"github.com/sigstore/sigstore/pkg/cryptoutils\"\n\t\"github.com/sigstore/sigstore/pkg/fulcioroots\"\n\t\"github.com/sigstore/sigstore/pkg/signature\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"k8s.io/client-go/kubernetes\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\n\t\"github.com/crossplane/crossplane-runtime/v2/pkg/errors\"\n)\n\nconst fetchCertTimeout = 30 * time.Second\n\n// Validator validates image signatures.\ntype Validator interface {\n\tValidate(ctx context.Context, ref name.Reference, config *v1beta1.ImageVerification, pullSecrets ...string) error\n}\n\n// NopValidator is a Validator that always succeeds.\ntype NopValidator struct{}\n\n// Validate always returns nil, skipping signature verification.\nfunc (NopValidator) Validate(context.Context, name.Reference, *v1beta1.ImageVerification, ...string) error {\n\treturn nil\n}\n\n// NewCosignValidator returns a new CosignValidator.\nfunc NewCosignValidator(c client.Reader, k kubernetes.Interface, namespace, serviceAccount string) (*CosignValidator, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), fetchCertTimeout)\n\tdefer cancel()\n\n\tvar err error\n\n\topts := cosign.CheckOpts{}\n\n\topts.RootCerts, err = fulcioroots.Get()\n\tif err != nil {\n\t\treturn nil, errors.Errorf(\"cannot fetch Fulcio roots: %w\", err)\n\t}\n\n\topts.IntermediateCerts, err = fulcioroots.GetIntermediates()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot fetch Fulcio intermediates: %w\", err)\n\t}\n\n\topts.CTLogPubKeys, err = cosign.GetCTLogPubs(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot fetch CTLog public keys: %w\", err)\n\t}\n\n\topts.RekorPubKeys, err = cosign.GetRekorPubs(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot fetch Rekor public keys: %w\", err)\n\t}\n\n\treturn &CosignValidator{\n\t\tclient:         c,\n\t\tclientset:      k,\n\t\tnamespace:      namespace,\n\t\tserviceAccount: serviceAccount,\n\n\t\tbaseCheckOpts: opts,\n\t}, nil\n}\n\n// CosignValidator validates image signatures using cosign.\ntype CosignValidator struct {\n\tclient         client.Reader\n\tclientset      kubernetes.Interface\n\tnamespace      string\n\tserviceAccount string\n\n\tbaseCheckOpts cosign.CheckOpts\n}\n\n// Validate validates the image signature.\nfunc (c *CosignValidator) Validate(ctx context.Context, ref name.Reference, config *v1beta1.ImageVerification, pullSecrets ...string) error {\n\tif config.Provider != v1beta1.ImageVerificationProviderCosign {\n\t\treturn errors.New(\"unsupported image verification provider\")\n\t}\n\n\tauth, err := k8schain.New(ctx, c.clientset, k8schain.Options{\n\t\tNamespace:          c.namespace,\n\t\tServiceAccountName: c.serviceAccount,\n\t\tImagePullSecrets:   pullSecrets,\n\t})\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"cannot create k8s auth chain\")\n\t}\n\n\tvar errs []error\n\n\tfor _, a := range config.Cosign.Authorities {\n\t\tco, err := c.buildCosignCheckOpts(ctx, a, ociremote.WithRemoteOptions(remote.WithAuthFromKeychain(auth)))\n\t\tif err != nil {\n\t\t\terrs = append(errs, errors.Errorf(\"authority %q: cannot build cosign check options %v\", a.Name, err))\n\t\t\tcontinue\n\t\t}\n\n\t\tvar res []oci.Signature\n\t\tvar ok bool\n\n\t\tco.ClaimVerifier = cosign.SimpleClaimVerifier\n\t\tif len(a.Attestations) > 0 {\n\t\t\tco.ClaimVerifier = cosign.IntotoSubjectClaimVerifier\n\t\t\tres, ok, err = cosign.VerifyImageAttestations(ctx, ref, co)\n\t\t} else {\n\t\t\tres, ok, err = cosign.VerifyImageSignatures(ctx, ref, co)\n\t\t}\n\n\t\tif err != nil {\n\t\t\terrs = append(errs, errors.Errorf(\"authority %q: signature verification failed with %v\", a.Name, err))\n\t\t\tcontinue\n\t\t}\n\n\t\tif !ok {\n\t\t\terrs = append(errs, errors.Errorf(\"authority %q: signature verification failed\", a.Name))\n\t\t\tcontinue\n\t\t}\n\n\t\t// If there are no attestations, return success given that the signature\n\t\t// verification was successful for this authority.\n\t\tif len(a.Attestations) == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\t// If there are attestations to be verified, check if the attestation\n\t\t// is valid for at least one of the resulting/checked\n\t\t// signatures/attestations.\n\t\tfor _, att := range a.Attestations {\n\t\t\tfor _, s := range res {\n\t\t\t\tb, _, err := attestationToPayloadJSON(ctx, att.PredicateType, s)\n\t\t\t\tif err != nil {\n\t\t\t\t\terrs = append(errs, errors.Errorf(\"authority %q: cannot convert attestation %q to payload JSON: %v\", a.Name, att.Name, err))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif len(b) == 0 {\n\t\t\t\t\terrs = append(errs, errors.Errorf(\"authority %q: no attestation of type %q found for %q\", a.Name, att.PredicateType, att.Name))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// If the attestation is valid for at least one of the resulting\n\t\t\t\t// payloads, return nil. Otherwise, continue with the next\n\t\t\t\t// signature.\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\n\t// If we reach this point, none of the authorities were able to verify the\n\t// image signature or attestations. So, return an error with all the errors\n\t// encountered.\n\treturn errors.Join(errs...)\n}\n\nfunc (c *CosignValidator) buildCosignCheckOpts(ctx context.Context, a v1beta1.CosignAuthority, remoteOpts ...ociremote.Option) (*cosign.CheckOpts, error) {\n\topts := c.baseCheckOpts\n\n\topts.RegistryClientOpts = remoteOpts\n\n\tif kl := a.Keyless; kl != nil {\n\t\tfor _, id := range kl.Identities {\n\t\t\topts.Identities = append(opts.Identities, cosign.Identity{\n\t\t\t\tIssuer:        id.Issuer,\n\t\t\t\tSubject:       id.Subject,\n\t\t\t\tIssuerRegExp:  id.IssuerRegExp,\n\t\t\t\tSubjectRegExp: id.SubjectRegExp,\n\t\t\t})\n\t\t}\n\n\t\tif kl.InsecureIgnoreSCT != nil {\n\t\t\topts.IgnoreSCT = *kl.InsecureIgnoreSCT\n\t\t}\n\t}\n\n\tif kr := a.Key; kr != nil {\n\t\ts := &corev1.Secret{}\n\t\tif err := c.client.Get(ctx, types.NamespacedName{Name: kr.SecretRef.Name, Namespace: c.namespace}, s); err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"cannot get secret\")\n\t\t}\n\n\t\tv := s.Data[kr.SecretRef.Key]\n\t\tif len(v) == 0 {\n\t\t\treturn nil, errors.Errorf(\"no data found for key %q in secret %q\", kr.SecretRef.Key, kr.SecretRef.Name)\n\t\t}\n\n\t\tpublicKey, err := cryptoutils.UnmarshalPEMToPublicKey(v)\n\t\tif err != nil || publicKey == nil {\n\t\t\treturn nil, errors.Errorf(\"secret %q contains an invalid public key: %w\", kr.SecretRef.Key, err)\n\t\t}\n\n\t\tha, err := hashAlgorithm(a.Key.HashAlgorithm)\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"invalid hash algorithm\")\n\t\t}\n\n\t\topts.SigVerifier, err = signature.LoadVerifier(publicKey, ha)\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"cannot load signature verifier\")\n\t\t}\n\t}\n\n\treturn &opts, nil\n}\n\nfunc hashAlgorithm(algorithm string) (crypto.Hash, error) {\n\tswitch strings.ToLower(strings.TrimSpace(algorithm)) {\n\tcase \"sha224\":\n\t\treturn crypto.SHA224, nil\n\tcase \"sha256\":\n\t\treturn crypto.SHA256, nil\n\tcase \"sha384\":\n\t\treturn crypto.SHA384, nil\n\tcase \"sha512\":\n\t\treturn crypto.SHA512, nil\n\tdefault:\n\t\treturn 0, errors.Errorf(\"unsupported algorithm %q\", algorithm)\n\t}\n}\n"
  },
  {
    "path": "pkg/xpkg/testdata/examples/ec2/instance.yaml",
    "content": "# Copyright 2023 the Crossplane authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\napiVersion: ec2.aws.crossplane.io/v1alpha2\nkind: Instance\nmetadata:\n  name: sample-instance\nspec:\n  forProvider:\n    region: us-west-1\n    ami: ami-07b068f843ec78e72\n    instanceType: t2.micro\n    networkInterface:\n    - deviceIndex: 0\n      networkInterfaceIdRef:\n        name: sample-ni\n    creditSpecification:\n    - cpuCredits: unlimited"
  },
  {
    "path": "pkg/xpkg/testdata/examples/ec2/internetgateway.yaml",
    "content": "# Copyright 2023 the Crossplane authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\napiVersion: ec2.aws.crossplane.io/v1alpha2\nkind: InternetGateway\nmetadata:\n  name: example\nspec:\n  forProvider:\n    region: us-west-1\n    tags:\n      Name: main\n    vpcIdRef:\n      name: example\n  providerConfigRef:\n    name: example"
  },
  {
    "path": "pkg/xpkg/testdata/examples/ecr/repository.yaml",
    "content": "# Copyright 2023 the Crossplane authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\napiVersion: ecr.aws.crossplane.io/v1alpha2\nkind: Repository\nmetadata:\n  name: sample-repository\nspec:\n  forProvider:\n    region: us-east-1\n    imageScanningConfiguration:\n      - scanOnPush: true\n    imageTagMutability: \"IMMUTABLE\"\n    tags:\n      key1: value1"
  },
  {
    "path": "pkg/xpkg/testdata/examples/provider.yaml",
    "content": "# Copyright 2023 the Crossplane authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n---\napiVersion: pkg.crossplane.io/v1\nkind: Provider\nmetadata:\n  name: provider-aws\nspec:\n  package: crossplane/provider-aws:main"
  },
  {
    "path": "pkg/xpkg/testdata/provider_meta.yaml",
    "content": "apiVersion: meta.pkg.crossplane.io/v1alpha1\nkind: Provider\nmetadata:\n  annotations:\n    company: Crossplane\n    description: |\n      The Amazon Web Services (AWS) Crossplane provider adds support for\n      managing AWS resources in Kubernetes.\n    descriptionShort: |\n      The AWS Crossplane provider enables infrastructure management for Amazon\n      Web Services.\n    friendly-group-name.meta.crossplane.io/acm.aws.crossplane.io: Certificate Manager\n    friendly-group-name.meta.crossplane.io/acmpca.aws.crossplane.io: Private CA\n    friendly-group-name.meta.crossplane.io/apigatewayv2.aws.crossplane.io: API Gateway\n    friendly-group-name.meta.crossplane.io/cache.aws.crossplane.io: ElastiCache\n    friendly-group-name.meta.crossplane.io/database.aws.crossplane.io: Databases\n    friendly-group-name.meta.crossplane.io/dynamodb.aws.crossplane.io: DynamoDB\n    friendly-group-name.meta.crossplane.io/ec2.aws.crossplane.io: Elastic Compute\n    friendly-group-name.meta.crossplane.io/ecr.aws.crossplane.io: Elastic Container\n      Registry\n    friendly-group-name.meta.crossplane.io/efs.aws.crossplane.io: Elastic Filesystem\n    friendly-group-name.meta.crossplane.io/eks.aws.crossplane.io: Elastic Kubernetes\n    friendly-group-name.meta.crossplane.io/elasticloadbalancing.aws.crossplane.io: Elastic\n      Load Balancing\n    friendly-group-name.meta.crossplane.io/identity.aws.crossplane.io: IAM\n    friendly-group-name.meta.crossplane.io/kms.aws.crossplane.io: Key Managment Service\n    friendly-group-name.meta.crossplane.io/notification.aws.crossplane.io: SNS\n    friendly-group-name.meta.crossplane.io/rds.aws.crossplane.io: RDS\n    friendly-group-name.meta.crossplane.io/redshift.aws.crossplane.io: Redshift\n    friendly-group-name.meta.crossplane.io/route53.aws.crossplane.io: Route 53\n    friendly-group-name.meta.crossplane.io/s3.aws.crossplane.io: S3\n    friendly-group-name.meta.crossplane.io/secretsmanager.aws.crossplane.io: Secrets\n      Manager\n    friendly-group-name.meta.crossplane.io/sfn.aws.crossplane.io: Step Functions\n    friendly-group-name.meta.crossplane.io/sqs.aws.crossplane.io: SQS\n    friendly-kind-name.meta.crossplane.io/activity.sfn.aws.crossplane.io: Activity\n    friendly-kind-name.meta.crossplane.io/address.ec2.aws.crossplane.io: Address\n    friendly-kind-name.meta.crossplane.io/api.apigatewayv2.aws.crossplane.io: API\n    friendly-kind-name.meta.crossplane.io/apimapping.apigatewayv2.aws.crossplane.io: API\n      Mapping\n    friendly-kind-name.meta.crossplane.io/authorizer.apigatewayv2.aws.crossplane.io: Authorizer\n    friendly-kind-name.meta.crossplane.io/backup.dynamodb.aws.crossplane.io: Backup\n    friendly-kind-name.meta.crossplane.io/bucket.s3.aws.crossplane.io: Bucket\n    friendly-kind-name.meta.crossplane.io/bucketpolicy.s3.aws.crossplane.io: Bucket\n      Policy\n    friendly-kind-name.meta.crossplane.io/cachecluster.cache.aws.crossplane.io: Cache\n      Cluster\n    friendly-kind-name.meta.crossplane.io/cachesubnetgroup.cache.aws.crossplane.io: Cache\n      Subnet Group\n    friendly-kind-name.meta.crossplane.io/certificate.acm.aws.crossplane.io: Certificate\n    friendly-kind-name.meta.crossplane.io/certificateauthority.acmpca.aws.crossplane.io: CA\n    friendly-kind-name.meta.crossplane.io/certificateauthoritypermission.acmpca.aws.crossplane.io: CA\n      Permission\n    friendly-kind-name.meta.crossplane.io/cluster.eks.aws.crossplane.io: EKS Cluster\n    friendly-kind-name.meta.crossplane.io/cluster.redshift.aws.crossplane.io: Redshift\n      Cluster\n    friendly-kind-name.meta.crossplane.io/dbcluster.rds.aws.crossplane.io: Database\n      Cluster\n    friendly-kind-name.meta.crossplane.io/dbparametergroup.rds.aws.crossplane.io: Database\n      Parameter Group\n    friendly-kind-name.meta.crossplane.io/dbsubnetgroup.database.aws.crossplane.io: Database\n      Subnet Group\n    friendly-kind-name.meta.crossplane.io/deployment.apigatewayv2.aws.crossplane.io: Deployment\n    friendly-kind-name.meta.crossplane.io/domainname.apigatewayv2.aws.crossplane.io: Domain\n      Name\n    friendly-kind-name.meta.crossplane.io/elb.elasticloadbalancing.aws.crossplane.io: Elastic\n      Load Balancer\n    friendly-kind-name.meta.crossplane.io/elbattachment.elasticloadbalancing.aws.crossplane.io: ELB\n      Attachment\n    friendly-kind-name.meta.crossplane.io/fargateprofile.eks.aws.crossplane.io: Fargate\n      Profile\n    friendly-kind-name.meta.crossplane.io/filesystem.efs.aws.crossplane.io: Filesystem\n    friendly-kind-name.meta.crossplane.io/globaltable.dynamodb.aws.crossplane.io: Global\n      Table\n    friendly-kind-name.meta.crossplane.io/hostedzone.route53.aws.crossplane.io: Hosted\n      Zone\n    friendly-kind-name.meta.crossplane.io/iamaccesskey.identity.aws.crossplane.io: IAM\n      Access Key\n    friendly-kind-name.meta.crossplane.io/iamgroup.identity.aws.crossplane.io: IAM\n      Group\n    friendly-kind-name.meta.crossplane.io/iamgrouppolicyattachment.identity.aws.crossplane.io: IAM\n      Group Policy Attachment\n    friendly-kind-name.meta.crossplane.io/iamgroupusermembership.identity.aws.crossplane.io: IAM\n      Group User Membership\n    friendly-kind-name.meta.crossplane.io/iampolicy.identity.aws.crossplane.io: IAM\n      Policy\n    friendly-kind-name.meta.crossplane.io/iamrole.identity.aws.crossplane.io: IAM\n      Role\n    friendly-kind-name.meta.crossplane.io/iamrolepolicyattachment.identity.aws.crossplane.io: IAM\n      Role Policy Attachment\n    friendly-kind-name.meta.crossplane.io/iamuser.identity.aws.crossplane.io: IAM\n      User\n    friendly-kind-name.meta.crossplane.io/iamuserpolicyattachment.identity.aws.crossplane.io: IAM\n      User Policy Attachment\n    friendly-kind-name.meta.crossplane.io/integration.apigatewayv2.aws.crossplane.io: Integration\n    friendly-kind-name.meta.crossplane.io/integrationresponse.apigatewayv2.aws.crossplane.io: Integration\n      Response\n    friendly-kind-name.meta.crossplane.io/internetgateway.ec2.aws.crossplane.io: Internet\n      Gateway\n    friendly-kind-name.meta.crossplane.io/key.kms.aws.crossplane.io: Key\n    friendly-kind-name.meta.crossplane.io/model.apigatewayv2.aws.crossplane.io: Model\n    friendly-kind-name.meta.crossplane.io/natgateway.ec2.aws.crossplane.io: NAT Gateway\n    friendly-kind-name.meta.crossplane.io/nodegroup.eks.aws.crossplane.io: EKS Node\n      Group\n    friendly-kind-name.meta.crossplane.io/queue.sqs.aws.crossplane.io: SQS Queue\n    friendly-kind-name.meta.crossplane.io/rdsinstance.database.aws.crossplane.io: RDS\n      Instance\n    friendly-kind-name.meta.crossplane.io/replicationgroup.cache.aws.crossplane.io: Replication\n      Group\n    friendly-kind-name.meta.crossplane.io/repository.ecr.aws.crossplane.io: Repository\n    friendly-kind-name.meta.crossplane.io/repositorypolicy.ecr.aws.crossplane.io: Repository\n      Policy\n    friendly-kind-name.meta.crossplane.io/resourcerecordset.route53.aws.crossplane.io: Resource\n      Record Set\n    friendly-kind-name.meta.crossplane.io/route.apigatewayv2.aws.crossplane.io: Route\n    friendly-kind-name.meta.crossplane.io/routeresponse.apigatewayv2.aws.crossplane.io: Route\n      Response\n    friendly-kind-name.meta.crossplane.io/routetable.ec2.aws.crossplane.io: Route\n      Table\n    friendly-kind-name.meta.crossplane.io/secret.secretsmanager.aws.crossplane.io: Secret\n    friendly-kind-name.meta.crossplane.io/securitygroup.ec2.aws.crossplane.io: Security\n      Group\n    friendly-kind-name.meta.crossplane.io/snssubscription.notification.aws.crossplane.io: Subscription\n    friendly-kind-name.meta.crossplane.io/snstopic.notification.aws.crossplane.io: Topic\n    friendly-kind-name.meta.crossplane.io/stage.apigatewayv2.aws.crossplane.io: Stage\n    friendly-kind-name.meta.crossplane.io/statemachine.sfn.aws.crossplane.io: State\n      Machine\n    friendly-kind-name.meta.crossplane.io/subnet.ec2.aws.crossplane.io: Subnet\n    friendly-kind-name.meta.crossplane.io/table.dynamodb.aws.crossplane.io: Table\n    friendly-kind-name.meta.crossplane.io/vpc.ec2.aws.crossplane.io: VPC\n    friendly-kind-name.meta.crossplane.io/vpccidrblock.ec2.aws.crossplane.io: VPC\n      CIDR Block\n    friendly-kind-name.meta.crossplane.io/vpclink.apigatewayv2.aws.crossplane.io: VPC\n      Link\n    friendly-name.meta.crossplane.io: Provider AWS\n    iconData: CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iNjUiIGhlaWdodD0iNjUiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PHJlY3Qgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiB4PSIuNSIgeT0iLjUiIGZpbGw9IiNGQUZBRkEiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSIjRDhEOERBIiByeD0iMTYiLz48cGF0aCBmaWxsPSIjMjUyRjNFIiBkPSJNMjMuMjQ2MzY1NiAyOS45Njg5OTdjMCAuNTY0MTg0NC4wNTY0MjQ2IDEuMDIxNjMxMi4xNTUxNjc2IDEuMzU3MDkyMi4xMTI4NDkyLjMzNTQ2MS4yNTM5MTA3LjcwMTQxODQuNDUxMzk2OCAxLjA5Nzg3MjMuMDcwNTMwOC4xMjE5ODU4LjA5ODc0MzEuMjQzOTcxNi4wOTg3NDMxLjM1MDcwOTIgMCAuMTUyNDgyMy0uMDg0NjM2OS4zMDQ5NjQ2LS4yNjgwMTY5LjQ1NzQ0NjhsLS44ODg2ODc1LjY0MDQyNTZjLS4xMjY5NTUzLjA5MTQ4OTMtLjI1NDA1MTcuMTM3MjM0LS4zNjY3NTk5LjEzNzIzNC0uMTQxMDYxNSAwLS4yODIxMjMtLjA3NjI0MTEtLjQyMzE4NDUtLjIxMzQ3NTItLjE5NzQ4NjEtLjIyODcyMzQtLjM2Njc1OTktLjQ3MjY5NS0uNTA3ODIxNC0uNzE2NjY2Ni0uMTQxMDYxNS0uMjU5MjE5OS0uMjgyMTIzLS41NDg5MzYyLS40MzcyOTA2LS44OTk2NDU0LTEuMTAwNDIwNzggMS40MDI4MzY4LTIuNDgyNjgyNDIgMi4xMDQyNTUzLTQuMTQ3MjA4MTMgMi4xMDQyNTUzLTEuMTg0OTE2NjEgMC0yLjEzMDAyODY2LS4zNjU5NTc1LTIuODIxMjMwMDItMS4wOTc4NzI0LS42OTEyMDEzNS0uNzMxOTE0OC0xLjA0Mzg1NTEtMS43MDc4MDE0LTEuMDQzODU1MS0yLjkyNzY1OTUgMC0xLjI5NjA5OTMyLjQyMzA0MzQ0LTIuMzQ4MjI2OTggMS4yODM2NTk2NS0zLjE0MTEzNDc4Ljg2MDQ3NTE2LS43OTI5MDc4IDIuMDAzMDczMzEtMS4xODkzNjE3IDMuNDU2MDA2NzctMS4xODkzNjE3LjQ3OTQ2ODA0IDAgLjk3MzMyNDM2LjA0NTc0NDY4IDEuNDk1MjUxOTEuMTIxOTg1ODEuNTIxOTI3NTUuMDc2MjQxMTQgMS4wNTc5NjEyNi4xOTgyMjY5NSAxLjYyMjIwNzI2LjMzNTQ2MXYtMS4xMTMxMjA1N2MwLTEuMTU4ODY1MjUtLjIyNTY5ODQtMS45NjcwMjEyOC0uNjYyOTg5MDUtMi40Mzk3MTYzMS0uNDUxMzk2ODEtLjQ3MjY5NTA0LTEuMjEzMTI4OTEtLjcwMTQxODQ0LTIuMjk5MzAyNDctLjcwMTQxODQ0LS40OTM3MTUyNSAwLTEuMDAxNTM2NjUuMDYwOTkyOTEtMS41MjM0NjQyLjE5ODIyNjk1LS41MjE5Mjc1Ni4xMzcyMzQwNC0xLjAyOTc0ODk2LjMwNDk2NDU0LTEuNTIzNDY0MjEuNTE4NDM5NzEtLjIyNTY5ODQuMTA2NzM3NTktLjM5NDk3MjIuMTY3NzMwNS0uNDkzNzE1MjYuMTk4MjI2OTYtLjA5ODc0MzA1LjAzMDQ5NjQ1LS4xNjkyNzM4LjA0NTc0NDY4LS4yMjU4Mzk0Ni4wNDU3NDQ2OC0uMTk3MzQ1MDQgMC0uMjk2MDg4MDktLjE1MjQ4MjI3LS4yOTYwODgwOS0uNDcyNjk1MDR2LS43NDcxNjMxMmMwLS4yNDM5NzE2My4wMjgyMTIzLS40MjY5NTAzNS4wOTg3NDMwNS0uNTMzNjg3OTRzLjE5NzM0NTA0LS4yMTM0NzUxOC4zOTQ5NzIyLS4zMjAyMTI3N2MuNDkzNTc0Mi0uMjc0NDY4MDggMS4wODYxNzM1Ni0uNTAzMTkxNDkgMS43NzczNzQ5MS0uNjg2MTcwMjEuNjkxMjAxMzYtLjE5ODIyNjk1IDEuNDI0NzIxMTYtLjI4OTcxNjMxIDIuMjAwNTU5NDItLjI4OTcxNjMxIDEuNjc4NjMxODUgMCAyLjkwNTg2NjkyLjQxMTcwMjEyIDMuNjk1ODExMzIgMS4yMzUxMDYzOC43NzU4MzgyLjgyMzQwNDI1IDEuMTcwODEwNSAyLjA3Mzc1ODg2IDEuMTcwODEwNSAzLjc1MTA2MzgzVjI5Ljk2ODk5N2guMDI4MjEyM3ptLTUuNzI3MDk2OTggMi4zMTc3MzA1Yy40NjU1MDI5NSAwIC45NDQ5NzA5OS0uMDkxNDg5NCAxLjQ1MjkzMzQ2LS4yNzQ0NjgxLjUwNzgyMTQtLjE4Mjk3ODguOTU5MjE4Mi0uNTE4NDM5NyAxLjM0MDA4NDI2LS45NzU4ODY2LjIyNTY5ODQtLjI4OTcxNjMuMzk0ODMxMTQtLjYwOTkyOS40Nzk2MDkxLS45NzU4ODY1LjA4NDYzNjktLjM2NTk1NzQuMTQxMDYxNS0uODA4MTU2MDEuMTQxMDYxNS0xLjMyNjU5NTcydi0uNjQwNDI1NTNjLS40MDkwNzgzNS0uMTA2NzM3NTktLjg0NjM2OTAxLS4xOTgyMjY5NS0xLjI5Nzc2NTgxLS4yNTkyMTk4Ni0uNDUxMzk2OC0uMDYwOTkyOTEtLjg4ODY4NzQ1LS4wOTE0ODkzNi0xLjMyNTk3ODEtLjA5MTQ4OTM2LS45NDUxMTIwNiAwLTEuNjM2MzEzNDEuMTk4MjI2OTUtMi4xMDE4MTYzNy42MDk5MjkwNy0uNDY1NTAyOTUuNDExNzAyMTMtLjY5MTIwMTM1Ljk5MTEzNDc2LS42OTEyMDEzNSAxLjc1MzU0NjEgMCAuNzE2NjY2Ny4xNjkyNzM4IDEuMjUwMzU0Ni41MjE5Mjc1NSAxLjYxNjMxMjEuMzM4NTQ3NjEuMzgxMjA1Ni44MzIyNjI4Ni41NjQxODQ0IDEuNDgxMTQ1NzYuNTY0MTg0NHpNMjguODQ2NTA3MSAzMy45MzM1MzZjLS4yNTM5MTA3IDAtLjQyMzE4NDUtLjA0NTc0NDctLjUzNjAzMzctLjE1MjQ4MjMtLjExMjg0OTItLjA5MTQ4OTQtLjIxMTU5MjItLjMwNDk2NDUtLjI5NjIyOTEtLjU5NDY4MDlMMjQuNjk5Mjk5IDIxLjM5OTQ5MzQxYy0uMDg0Nzc4LS4zMDQ5NjQ1My0uMTI2OTU1My0uNTAzMTkxNDgtLjEyNjk1NTMtLjYwOTkyOTA3IDAtLjI0Mzk3MTYzLjExMjcwODEtLjM4MTIwNTY4LjMzODU0NzYtLjM4MTIwNTY4aDEuMzgyNDAyN2MuMjY3ODc1OCAwIC40NTEzOTY4LjA0NTc0NDY4LjU1MDEzOTguMTUyNDgyMjcuMTEyODQ5Mi4wOTE0ODkzNi4xOTc0ODYxLjMwNDk2NDU0LjI4MjEyMy41OTQ2ODA4NUwyOS40OTUzOSAzMS4yNDk4NDhsMi4yMDA0MTg0LTEwLjA5NDMyNjIyYy4wNzA2NzE4LS4zMDQ5NjQ1NC4xNTUxNjc2LS41MDMxOTE0OS4yNjgxNTc5LS41OTQ2ODA4NS4xMTI4NDkyLS4wOTE0ODkzNi4zMTAxOTQyLS4xNTI0ODIyNy41NjQyNDYtLjE1MjQ4MjI3aDEuMTI4NDkyYy4yNjc4NzU4IDAgLjQ1MTM5NjguMDQ1NzQ0NjguNTY0MjQ2LjE1MjQ4MjI3LjExMjg0OTIuMDkxNDg5MzYuMjExNTkyMy4zMDQ5NjQ1NC4yNjgwMTY5LjU5NDY4MDg1bDIuMjI4NjMwNiAxMC4yMTYzMTIwMiAyLjQ0MDUwNS0xMC4yMTYzMTIwMmMuMDg0NjM2OS0uMzA0OTY0NTQuMTgzMzgtLjUwMzE5MTQ5LjI4MjEyMy0uNTk0NjgwODUuMTEyODQ5Mi0uMDkxNDg5MzYuMjk2MjI5Mi0uMTUyNDgyMjcuNTUwMTM5OS0uMTUyNDgyMjdoMS4zMTE4NzE5Yy4yMjU2OTg0IDAgLjM1MjY1MzguMTIxOTg1ODIuMzUyNjUzOC4zODEyMDU2OCAwIC4wNzYyNDExMy0uMDE0MTA2Mi4xNTI0ODIyNy0uMDI4MjEyMy4yNDM5NzE2My0uMDE0MjQ3Mi4wOTE0ODkzNi0uMDQyMzE4NS4yMTM0NzUxNy0uMDk4NzQzMS4zODEyMDU2N0wzOC4xMjgzNTM5IDMzLjIwMTYyMTFjLS4wODQ3NzguMzA0OTY0NS0uMTgzNTIxLjUwMzE5MTUtLjI5NjM3MDIuNTk0NjgwOC0uMTEyNzA4Mi4wOTE0ODk0LS4yOTYwODgxLjE1MjQ4MjMtLjUzNTg5MjcuMTUyNDgyM2gtMS4yMTMyNjk5Yy0uMjY3ODc1OCAwLS40NTEyNTU4LS4wNDU3NDQ3LS41NjQyNDYtLjE1MjQ4MjMtLjExMjg0OTItLjEwNjczNzYtLjIxMTU5MjMtLjMwNDk2NDUtLjI2Nzg3NTgtLjYwOTkyOTFsLTIuMTg2NDUzMy05LjgzNTEwNjM0LTIuMTcyMzQ3MSA5LjgxOTg1ODE0Yy0uMDcwNjcxOC4zMDQ5NjQ2LS4xNTUxNjc3LjUwMzE5MTUtLjI2ODAxNjkuNjA5OTI5MS0uMTEyODQ5Mi4xMDY3Mzc2LS4zMTAzMzUzLjE1MjQ4MjMtLjU2NDI0Ni4xNTI0ODIzaC0xLjIxMzEyODl6bTE4LjEyNjQwMjkuNDExNzAyMWMtLjczMzUxOTggMC0xLjQ2NzAzOTYtLjA5MTQ4OTQtMi4xNzIzNDcxLS4yNzQ0Njgxcy0xLjI1NTQ0NzQtLjM4MTIwNTctMS42MjIyMDczLS42MDk5MjkxYy0uMjI1Njk4NC0uMTM3MjM0LS4zODEwMDcxLS4yODk3MTYzLS40MzcyOTA2LS40MjY5NTAzLS4wNTY0MjQ2LS4xMzcyMzQxLS4wODQ2MzY5LS4yODk3MTYzLS4wODQ2MzY5LS40MjY5NTA0di0uNzc3NjU5NmMwLS4zMjAyMTI3LjExMjg0OTItLjQ3MjY5NS4zMjQ0NDE0LS40NzI2OTUuMDg0NjM2OSAwIC4xNjkxMzI3LjAxNTI0ODIuMjUzNzY5Ni4wNDU3NDQ3LjA4NDc3OC4wMzA0OTY0LjIxMTU5MjMuMDkxNDg5NC4zNTI3OTQ5LjE1MjQ4MjMuNDc5NDY4LjIyODcyMzQgMS4wMDE1MzY2LjQxMTcwMjEgMS41NTE1MzU0LjUzMzY4NzkuNTY0Mzg3MS4xMjE5ODU4IDEuMTE0Mzg1OS4xODI5Nzg3IDEuNjc4NzcyOS4xODI5Nzg3Ljg4ODY4NzUgMCAxLjU3OTg4ODgtLjE2NzczMDUgMi4wNTkzNTY5LS41MDMxOTE1LjQ3OTc1MDEtLjMzNTQ2MS43MzM2NjA4LS44MjM0MDQyLjczMzY2MDgtMS40NDg1ODE1IDAtLjQyNjk1MDQtLjEyNjk1NTMtLjc3NzY1OTYtLjM4MTAwNzEtMS4wNjczNzU5MS0uMjUzNzY5Ni0uMjg5NzE2MzEtLjczMzM3ODctLjU0ODkzNjE3LTEuNDI0NzIxMS0uNzkyOTA3OGwtMi4wNDUyNTA3LS42ODYxNzAyMWMtMS4wMjk3NDktLjM1MDcwOTIyLTEuNzkxNDgxMS0uODY5MTQ4OTQtMi4yNTY5ODQtMS41NTUzMTkxNS0uNDY1NTAzLS42NzA5MjE5OS0uNzA1NDQ4Ni0xLjQxODA4NTExLS43MDU0NDg2LTIuMjEwOTkyOTEgMC0uNjQwNDI1NTMuMTI3MDk2NC0xLjIwNDYwOTkzLjM4MTAwNzEtMS42OTI1NTMxOS4yNTM3Njk2LS40ODc5NDMyNi41OTI0NTgzLS45MTQ4OTM2MiAxLjAxNTY0MjgtMS4yNTAzNTQ2MS40MjMxODQ1LS4zNTA3MDkyMi45MDI3OTM2LS42MDk5MjkwOCAxLjQ2NzAzOTYtLjc5MjkwNzguNTY0MjQ2LS4xODI5Nzg3MiAxLjE1NjcwNDMtLjI1OTIxOTg2IDEuNzc3Mzc0OS0uMjU5MjE5ODYuMzEwMTk0MyAwIC42MzQ3NzY4LjAxNTI0ODIzLjk0NDk3MS4wNjA5OTI5MS4zMjQ1ODI1LjA0NTc0NDY4LjYyMDY3MDYuMTA2NzM3NTkuOTE2ODk5OC4xNjc3MzA1LjI4MjEyMy4wNzYyNDExMy41NTAxMzk4LjE1MjQ4MjI3LjgwNDE5MTYuMjQzOTcxNjMuMjUzNzY5Ni4wOTE0ODkzNi40NTEzOTY4LjE4Mjk3ODcyLjU5MjQ1ODMuMjc0NDY4MDguMTk3NDg2MS4xMjE5ODU4Mi4zMzg1NDc2LjI0Mzk3MTYzLjQyMzE4NDUuMzgxMjA1NjguMDg0NjM2OS4xMjE5ODU4MS4xMjY5NTUzLjI4OTcxNjMxLjEyNjk1NTMuNTAzMTkxNDl2LjcxNjY2NjY2YzAgLjMyMDIxMjc3LS4xMTI4NDkyLjQ4Nzk0MzI2LS4zMjQ0NDE0LjQ4Nzk0MzI2LS4xMTI4NDkyIDAtLjI5NjIyOTItLjA2MDk5MjktLjUzNjAzMzctLjE4Mjk3ODcyLS44MDQwNTA2LS4zOTY0NTM5LTEuNzA2ODQ0Mi0uNTk0NjgwODUtMi43MDgzODA4LS41OTQ2ODA4NS0uODA0MDUwNiAwLTEuNDM4ODI3My4xMzcyMzQwNC0xLjg3NjExOC40MjY5NTAzNS0uNDM3MjkwNi4yODk3MTYzMi0uNjYyOTg5LjczMTkxNDktLjY2Mjk4OSAxLjM1NzA5MjIgMCAuNDI2OTUwMzYuMTQxMDYxNS43OTI5MDc4LjQyMzE4NDUgMS4wODI2MjQxMi4yODIxMjMuMjg5NzE2MzEuODA0MDUwNS41Nzk0MzI2MiAxLjU1MTY3NjUuODM4NjUyNDhsMi4wMDMwNzMzLjY4NjE3MDIxYzEuMDE1NjQyOC4zNTA3MDkyMiAxLjc0OTE2MjYuODM4NjUyNDggMi4xODY0NTMyIDEuNDYzODI5NzkuNDM3MjkwNy42MjUxNzczLjY0ODg4MyAxLjM0MTg0Mzk3LjY0ODg4MyAyLjEzNDc1MTc1IDAgLjY1NTY3MzgtLjEyNjk1NTQgMS4yNTAzNTQ2LS4zNjY3NTk5IDEuNzY4Nzk0My0uMjUzOTEwOC41MTg0Mzk4LS41OTI0NTg0Ljk3NTg4NjYtMS4wMjk3NDkgMS4zNDE4NDQtLjQzNzI5MDcuMzgxMjA1Ny0uOTU5MjE4Mi42NTU2NzM4LTEuNTY1NzgyNy44NTM5MDA3LS42MzQ3NzY3LjIxMzQ3NTItMS4yOTc3NjU4LjMyMDIxMjgtMi4wMTcxNzk0LjMyMDIxMjh6Ii8+PHBhdGggZmlsbD0iI0ZGOTkwMCIgZD0iTTQ5Ljg3NTUwNyA0MS4wMDU5MzcyYy00LjcwNTEzNjcgMy42NzQ0MDUxLTExLjU0MTE3MTIgNS42MjUwMTUyLTE3LjQxODg3MzggNS42MjUwMTUyLTguMjM3NTY0NiAwLTE1LjY1OTk1MzYxLTMuMjIwNzc0OC0yMS4yNjYwNzM5OC04LjU3MzYxMTktLjQ0MzQ4NDE2LS40MjMzODgyLS4wNDI5MDM5OC0uOTk3OTg2NS40ODYyNDUxNC0uNjY1MzI0NCA2LjA2Mzc2Mjg1IDMuNzE5NzY4MiAxMy41NDMyMTQxNCA1Ljk3Mjc5ODQgMjEuMjgwMzc1MjQgNS45NzI3OTg0IDUuMjE5OTg0NiAwIDEwLjk1NDgxNjktMS4xNDkxOTY2IDE2LjIzMjAwNjctMy41MDgwNzQuNzg2NTczLS4zNzgwMjUyIDEuNDU4NzM1NC41NDQzNTYzLjY4NjMyMDcgMS4xNDkxOTY3em0xLjk1OTQyNDktMi4zNTg4NzczYy0uNjAwNzk4OC0uODE2NTM0NS0zLjk3NTc2OS0uMzkzMTQ2My01LjUwNjAxMTEtLjE5NjU3MzItLjQ1Nzc4NTUuMDYwNDg0MS0uNTI5MjkyMS0uMzYyOTA0Mi0uMTE0NDEwNi0uNjgwNDQ1MyAyLjY4ODY0OTYtMS45OTU5NzMyIDcuMTA3NjE2Ny0xLjQyMTM3NDggNy42MjI2MDc2LS43NTYwNTA1LjUxNDg0NzcuNjgwNDQ1NC0uMTQzMDEzMyA1LjM1MjgzNzEtMi42NjAxOSA3LjU5MDc0NjMtLjM4NTk5MjguMzQ3NzgzMi0uNzU3ODI3My4xNjYzMzExLS41ODYzNTQ0LS4yODcyOTkxLjU3MjA1MzEtMS40OTY5Nzk5IDEuODQ1MDE0My00Ljg2ODk2NDggMS4yNDQzNTg1LTUuNjcwMzc4MnoiLz48L2c+PC9zdmc+\n    license: Apache-2.0\n    maintainer: Crossplane Maintainers <crossplane-info@lists.cncf.io>\n    meta.crossplane.io/description: |\n      The Amazon Web Services (AWS) Crossplane provider adds support for\n      managing AWS resources in Kubernetes.\n    meta.crossplane.io/iconURI: data:image/svg+xml;base64,CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iNjUiIGhlaWdodD0iNjUiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PHJlY3Qgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiB4PSIuNSIgeT0iLjUiIGZpbGw9IiNGQUZBRkEiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSIjRDhEOERBIiByeD0iMTYiLz48cGF0aCBmaWxsPSIjMjUyRjNFIiBkPSJNMjMuMjQ2MzY1NiAyOS45Njg5OTdjMCAuNTY0MTg0NC4wNTY0MjQ2IDEuMDIxNjMxMi4xNTUxNjc2IDEuMzU3MDkyMi4xMTI4NDkyLjMzNTQ2MS4yNTM5MTA3LjcwMTQxODQuNDUxMzk2OCAxLjA5Nzg3MjMuMDcwNTMwOC4xMjE5ODU4LjA5ODc0MzEuMjQzOTcxNi4wOTg3NDMxLjM1MDcwOTIgMCAuMTUyNDgyMy0uMDg0NjM2OS4zMDQ5NjQ2LS4yNjgwMTY5LjQ1NzQ0NjhsLS44ODg2ODc1LjY0MDQyNTZjLS4xMjY5NTUzLjA5MTQ4OTMtLjI1NDA1MTcuMTM3MjM0LS4zNjY3NTk5LjEzNzIzNC0uMTQxMDYxNSAwLS4yODIxMjMtLjA3NjI0MTEtLjQyMzE4NDUtLjIxMzQ3NTItLjE5NzQ4NjEtLjIyODcyMzQtLjM2Njc1OTktLjQ3MjY5NS0uNTA3ODIxNC0uNzE2NjY2Ni0uMTQxMDYxNS0uMjU5MjE5OS0uMjgyMTIzLS41NDg5MzYyLS40MzcyOTA2LS44OTk2NDU0LTEuMTAwNDIwNzggMS40MDI4MzY4LTIuNDgyNjgyNDIgMi4xMDQyNTUzLTQuMTQ3MjA4MTMgMi4xMDQyNTUzLTEuMTg0OTE2NjEgMC0yLjEzMDAyODY2LS4zNjU5NTc1LTIuODIxMjMwMDItMS4wOTc4NzI0LS42OTEyMDEzNS0uNzMxOTE0OC0xLjA0Mzg1NTEtMS43MDc4MDE0LTEuMDQzODU1MS0yLjkyNzY1OTUgMC0xLjI5NjA5OTMyLjQyMzA0MzQ0LTIuMzQ4MjI2OTggMS4yODM2NTk2NS0zLjE0MTEzNDc4Ljg2MDQ3NTE2LS43OTI5MDc4IDIuMDAzMDczMzEtMS4xODkzNjE3IDMuNDU2MDA2NzctMS4xODkzNjE3LjQ3OTQ2ODA0IDAgLjk3MzMyNDM2LjA0NTc0NDY4IDEuNDk1MjUxOTEuMTIxOTg1ODEuNTIxOTI3NTUuMDc2MjQxMTQgMS4wNTc5NjEyNi4xOTgyMjY5NSAxLjYyMjIwNzI2LjMzNTQ2MXYtMS4xMTMxMjA1N2MwLTEuMTU4ODY1MjUtLjIyNTY5ODQtMS45NjcwMjEyOC0uNjYyOTg5MDUtMi40Mzk3MTYzMS0uNDUxMzk2ODEtLjQ3MjY5NTA0LTEuMjEzMTI4OTEtLjcwMTQxODQ0LTIuMjk5MzAyNDctLjcwMTQxODQ0LS40OTM3MTUyNSAwLTEuMDAxNTM2NjUuMDYwOTkyOTEtMS41MjM0NjQyLjE5ODIyNjk1LS41MjE5Mjc1Ni4xMzcyMzQwNC0xLjAyOTc0ODk2LjMwNDk2NDU0LTEuNTIzNDY0MjEuNTE4NDM5NzEtLjIyNTY5ODQuMTA2NzM3NTktLjM5NDk3MjIuMTY3NzMwNS0uNDkzNzE1MjYuMTk4MjI2OTYtLjA5ODc0MzA1LjAzMDQ5NjQ1LS4xNjkyNzM4LjA0NTc0NDY4LS4yMjU4Mzk0Ni4wNDU3NDQ2OC0uMTk3MzQ1MDQgMC0uMjk2MDg4MDktLjE1MjQ4MjI3LS4yOTYwODgwOS0uNDcyNjk1MDR2LS43NDcxNjMxMmMwLS4yNDM5NzE2My4wMjgyMTIzLS40MjY5NTAzNS4wOTg3NDMwNS0uNTMzNjg3OTRzLjE5NzM0NTA0LS4yMTM0NzUxOC4zOTQ5NzIyLS4zMjAyMTI3N2MuNDkzNTc0Mi0uMjc0NDY4MDggMS4wODYxNzM1Ni0uNTAzMTkxNDkgMS43NzczNzQ5MS0uNjg2MTcwMjEuNjkxMjAxMzYtLjE5ODIyNjk1IDEuNDI0NzIxMTYtLjI4OTcxNjMxIDIuMjAwNTU5NDItLjI4OTcxNjMxIDEuNjc4NjMxODUgMCAyLjkwNTg2NjkyLjQxMTcwMjEyIDMuNjk1ODExMzIgMS4yMzUxMDYzOC43NzU4MzgyLjgyMzQwNDI1IDEuMTcwODEwNSAyLjA3Mzc1ODg2IDEuMTcwODEwNSAzLjc1MTA2MzgzVjI5Ljk2ODk5N2guMDI4MjEyM3ptLTUuNzI3MDk2OTggMi4zMTc3MzA1Yy40NjU1MDI5NSAwIC45NDQ5NzA5OS0uMDkxNDg5NCAxLjQ1MjkzMzQ2LS4yNzQ0NjgxLjUwNzgyMTQtLjE4Mjk3ODguOTU5MjE4Mi0uNTE4NDM5NyAxLjM0MDA4NDI2LS45NzU4ODY2LjIyNTY5ODQtLjI4OTcxNjMuMzk0ODMxMTQtLjYwOTkyOS40Nzk2MDkxLS45NzU4ODY1LjA4NDYzNjktLjM2NTk1NzQuMTQxMDYxNS0uODA4MTU2MDEuMTQxMDYxNS0xLjMyNjU5NTcydi0uNjQwNDI1NTNjLS40MDkwNzgzNS0uMTA2NzM3NTktLjg0NjM2OTAxLS4xOTgyMjY5NS0xLjI5Nzc2NTgxLS4yNTkyMTk4Ni0uNDUxMzk2OC0uMDYwOTkyOTEtLjg4ODY4NzQ1LS4wOTE0ODkzNi0xLjMyNTk3ODEtLjA5MTQ4OTM2LS45NDUxMTIwNiAwLTEuNjM2MzEzNDEuMTk4MjI2OTUtMi4xMDE4MTYzNy42MDk5MjkwNy0uNDY1NTAyOTUuNDExNzAyMTMtLjY5MTIwMTM1Ljk5MTEzNDc2LS42OTEyMDEzNSAxLjc1MzU0NjEgMCAuNzE2NjY2Ny4xNjkyNzM4IDEuMjUwMzU0Ni41MjE5Mjc1NSAxLjYxNjMxMjEuMzM4NTQ3NjEuMzgxMjA1Ni44MzIyNjI4Ni41NjQxODQ0IDEuNDgxMTQ1NzYuNTY0MTg0NHpNMjguODQ2NTA3MSAzMy45MzM1MzZjLS4yNTM5MTA3IDAtLjQyMzE4NDUtLjA0NTc0NDctLjUzNjAzMzctLjE1MjQ4MjMtLjExMjg0OTItLjA5MTQ4OTQtLjIxMTU5MjItLjMwNDk2NDUtLjI5NjIyOTEtLjU5NDY4MDlMMjQuNjk5Mjk5IDIxLjM5OTQ5MzQxYy0uMDg0Nzc4LS4zMDQ5NjQ1My0uMTI2OTU1My0uNTAzMTkxNDgtLjEyNjk1NTMtLjYwOTkyOTA3IDAtLjI0Mzk3MTYzLjExMjcwODEtLjM4MTIwNTY4LjMzODU0NzYtLjM4MTIwNTY4aDEuMzgyNDAyN2MuMjY3ODc1OCAwIC40NTEzOTY4LjA0NTc0NDY4LjU1MDEzOTguMTUyNDgyMjcuMTEyODQ5Mi4wOTE0ODkzNi4xOTc0ODYxLjMwNDk2NDU0LjI4MjEyMy41OTQ2ODA4NUwyOS40OTUzOSAzMS4yNDk4NDhsMi4yMDA0MTg0LTEwLjA5NDMyNjIyYy4wNzA2NzE4LS4zMDQ5NjQ1NC4xNTUxNjc2LS41MDMxOTE0OS4yNjgxNTc5LS41OTQ2ODA4NS4xMTI4NDkyLS4wOTE0ODkzNi4zMTAxOTQyLS4xNTI0ODIyNy41NjQyNDYtLjE1MjQ4MjI3aDEuMTI4NDkyYy4yNjc4NzU4IDAgLjQ1MTM5NjguMDQ1NzQ0NjguNTY0MjQ2LjE1MjQ4MjI3LjExMjg0OTIuMDkxNDg5MzYuMjExNTkyMy4zMDQ5NjQ1NC4yNjgwMTY5LjU5NDY4MDg1bDIuMjI4NjMwNiAxMC4yMTYzMTIwMiAyLjQ0MDUwNS0xMC4yMTYzMTIwMmMuMDg0NjM2OS0uMzA0OTY0NTQuMTgzMzgtLjUwMzE5MTQ5LjI4MjEyMy0uNTk0NjgwODUuMTEyODQ5Mi0uMDkxNDg5MzYuMjk2MjI5Mi0uMTUyNDgyMjcuNTUwMTM5OS0uMTUyNDgyMjdoMS4zMTE4NzE5Yy4yMjU2OTg0IDAgLjM1MjY1MzguMTIxOTg1ODIuMzUyNjUzOC4zODEyMDU2OCAwIC4wNzYyNDExMy0uMDE0MTA2Mi4xNTI0ODIyNy0uMDI4MjEyMy4yNDM5NzE2My0uMDE0MjQ3Mi4wOTE0ODkzNi0uMDQyMzE4NS4yMTM0NzUxNy0uMDk4NzQzMS4zODEyMDU2N0wzOC4xMjgzNTM5IDMzLjIwMTYyMTFjLS4wODQ3NzguMzA0OTY0NS0uMTgzNTIxLjUwMzE5MTUtLjI5NjM3MDIuNTk0NjgwOC0uMTEyNzA4Mi4wOTE0ODk0LS4yOTYwODgxLjE1MjQ4MjMtLjUzNTg5MjcuMTUyNDgyM2gtMS4yMTMyNjk5Yy0uMjY3ODc1OCAwLS40NTEyNTU4LS4wNDU3NDQ3LS41NjQyNDYtLjE1MjQ4MjMtLjExMjg0OTItLjEwNjczNzYtLjIxMTU5MjMtLjMwNDk2NDUtLjI2Nzg3NTgtLjYwOTkyOTFsLTIuMTg2NDUzMy05LjgzNTEwNjM0LTIuMTcyMzQ3MSA5LjgxOTg1ODE0Yy0uMDcwNjcxOC4zMDQ5NjQ2LS4xNTUxNjc3LjUwMzE5MTUtLjI2ODAxNjkuNjA5OTI5MS0uMTEyODQ5Mi4xMDY3Mzc2LS4zMTAzMzUzLjE1MjQ4MjMtLjU2NDI0Ni4xNTI0ODIzaC0xLjIxMzEyODl6bTE4LjEyNjQwMjkuNDExNzAyMWMtLjczMzUxOTggMC0xLjQ2NzAzOTYtLjA5MTQ4OTQtMi4xNzIzNDcxLS4yNzQ0Njgxcy0xLjI1NTQ0NzQtLjM4MTIwNTctMS42MjIyMDczLS42MDk5MjkxYy0uMjI1Njk4NC0uMTM3MjM0LS4zODEwMDcxLS4yODk3MTYzLS40MzcyOTA2LS40MjY5NTAzLS4wNTY0MjQ2LS4xMzcyMzQxLS4wODQ2MzY5LS4yODk3MTYzLS4wODQ2MzY5LS40MjY5NTA0di0uNzc3NjU5NmMwLS4zMjAyMTI3LjExMjg0OTItLjQ3MjY5NS4zMjQ0NDE0LS40NzI2OTUuMDg0NjM2OSAwIC4xNjkxMzI3LjAxNTI0ODIuMjUzNzY5Ni4wNDU3NDQ3LjA4NDc3OC4wMzA0OTY0LjIxMTU5MjMuMDkxNDg5NC4zNTI3OTQ5LjE1MjQ4MjMuNDc5NDY4LjIyODcyMzQgMS4wMDE1MzY2LjQxMTcwMjEgMS41NTE1MzU0LjUzMzY4NzkuNTY0Mzg3MS4xMjE5ODU4IDEuMTE0Mzg1OS4xODI5Nzg3IDEuNjc4NzcyOS4xODI5Nzg3Ljg4ODY4NzUgMCAxLjU3OTg4ODgtLjE2NzczMDUgMi4wNTkzNTY5LS41MDMxOTE1LjQ3OTc1MDEtLjMzNTQ2MS43MzM2NjA4LS44MjM0MDQyLjczMzY2MDgtMS40NDg1ODE1IDAtLjQyNjk1MDQtLjEyNjk1NTMtLjc3NzY1OTYtLjM4MTAwNzEtMS4wNjczNzU5MS0uMjUzNzY5Ni0uMjg5NzE2MzEtLjczMzM3ODctLjU0ODkzNjE3LTEuNDI0NzIxMS0uNzkyOTA3OGwtMi4wNDUyNTA3LS42ODYxNzAyMWMtMS4wMjk3NDktLjM1MDcwOTIyLTEuNzkxNDgxMS0uODY5MTQ4OTQtMi4yNTY5ODQtMS41NTUzMTkxNS0uNDY1NTAzLS42NzA5MjE5OS0uNzA1NDQ4Ni0xLjQxODA4NTExLS43MDU0NDg2LTIuMjEwOTkyOTEgMC0uNjQwNDI1NTMuMTI3MDk2NC0xLjIwNDYwOTkzLjM4MTAwNzEtMS42OTI1NTMxOS4yNTM3Njk2LS40ODc5NDMyNi41OTI0NTgzLS45MTQ4OTM2MiAxLjAxNTY0MjgtMS4yNTAzNTQ2MS40MjMxODQ1LS4zNTA3MDkyMi45MDI3OTM2LS42MDk5MjkwOCAxLjQ2NzAzOTYtLjc5MjkwNzguNTY0MjQ2LS4xODI5Nzg3MiAxLjE1NjcwNDMtLjI1OTIxOTg2IDEuNzc3Mzc0OS0uMjU5MjE5ODYuMzEwMTk0MyAwIC42MzQ3NzY4LjAxNTI0ODIzLjk0NDk3MS4wNjA5OTI5MS4zMjQ1ODI1LjA0NTc0NDY4LjYyMDY3MDYuMTA2NzM3NTkuOTE2ODk5OC4xNjc3MzA1LjI4MjEyMy4wNzYyNDExMy41NTAxMzk4LjE1MjQ4MjI3LjgwNDE5MTYuMjQzOTcxNjMuMjUzNzY5Ni4wOTE0ODkzNi40NTEzOTY4LjE4Mjk3ODcyLjU5MjQ1ODMuMjc0NDY4MDguMTk3NDg2MS4xMjE5ODU4Mi4zMzg1NDc2LjI0Mzk3MTYzLjQyMzE4NDUuMzgxMjA1NjguMDg0NjM2OS4xMjE5ODU4MS4xMjY5NTUzLjI4OTcxNjMxLjEyNjk1NTMuNTAzMTkxNDl2LjcxNjY2NjY2YzAgLjMyMDIxMjc3LS4xMTI4NDkyLjQ4Nzk0MzI2LS4zMjQ0NDE0LjQ4Nzk0MzI2LS4xMTI4NDkyIDAtLjI5NjIyOTItLjA2MDk5MjktLjUzNjAzMzctLjE4Mjk3ODcyLS44MDQwNTA2LS4zOTY0NTM5LTEuNzA2ODQ0Mi0uNTk0NjgwODUtMi43MDgzODA4LS41OTQ2ODA4NS0uODA0MDUwNiAwLTEuNDM4ODI3My4xMzcyMzQwNC0xLjg3NjExOC40MjY5NTAzNS0uNDM3MjkwNi4yODk3MTYzMi0uNjYyOTg5LjczMTkxNDktLjY2Mjk4OSAxLjM1NzA5MjIgMCAuNDI2OTUwMzYuMTQxMDYxNS43OTI5MDc4LjQyMzE4NDUgMS4wODI2MjQxMi4yODIxMjMuMjg5NzE2MzEuODA0MDUwNS41Nzk0MzI2MiAxLjU1MTY3NjUuODM4NjUyNDhsMi4wMDMwNzMzLjY4NjE3MDIxYzEuMDE1NjQyOC4zNTA3MDkyMiAxLjc0OTE2MjYuODM4NjUyNDggMi4xODY0NTMyIDEuNDYzODI5NzkuNDM3MjkwNy42MjUxNzczLjY0ODg4MyAxLjM0MTg0Mzk3LjY0ODg4MyAyLjEzNDc1MTc1IDAgLjY1NTY3MzgtLjEyNjk1NTQgMS4yNTAzNTQ2LS4zNjY3NTk5IDEuNzY4Nzk0My0uMjUzOTEwOC41MTg0Mzk4LS41OTI0NTg0Ljk3NTg4NjYtMS4wMjk3NDkgMS4zNDE4NDQtLjQzNzI5MDcuMzgxMjA1Ny0uOTU5MjE4Mi42NTU2NzM4LTEuNTY1NzgyNy44NTM5MDA3LS42MzQ3NzY3LjIxMzQ3NTItMS4yOTc3NjU4LjMyMDIxMjgtMi4wMTcxNzk0LjMyMDIxMjh6Ii8+PHBhdGggZmlsbD0iI0ZGOTkwMCIgZD0iTTQ5Ljg3NTUwNyA0MS4wMDU5MzcyYy00LjcwNTEzNjcgMy42NzQ0MDUxLTExLjU0MTE3MTIgNS42MjUwMTUyLTE3LjQxODg3MzggNS42MjUwMTUyLTguMjM3NTY0NiAwLTE1LjY1OTk1MzYxLTMuMjIwNzc0OC0yMS4yNjYwNzM5OC04LjU3MzYxMTktLjQ0MzQ4NDE2LS40MjMzODgyLS4wNDI5MDM5OC0uOTk3OTg2NS40ODYyNDUxNC0uNjY1MzI0NCA2LjA2Mzc2Mjg1IDMuNzE5NzY4MiAxMy41NDMyMTQxNCA1Ljk3Mjc5ODQgMjEuMjgwMzc1MjQgNS45NzI3OTg0IDUuMjE5OTg0NiAwIDEwLjk1NDgxNjktMS4xNDkxOTY2IDE2LjIzMjAwNjctMy41MDgwNzQuNzg2NTczLS4zNzgwMjUyIDEuNDU4NzM1NC41NDQzNTYzLjY4NjMyMDcgMS4xNDkxOTY3em0xLjk1OTQyNDktMi4zNTg4NzczYy0uNjAwNzk4OC0uODE2NTM0NS0zLjk3NTc2OS0uMzkzMTQ2My01LjUwNjAxMTEtLjE5NjU3MzItLjQ1Nzc4NTUuMDYwNDg0MS0uNTI5MjkyMS0uMzYyOTA0Mi0uMTE0NDEwNi0uNjgwNDQ1MyAyLjY4ODY0OTYtMS45OTU5NzMyIDcuMTA3NjE2Ny0xLjQyMTM3NDggNy42MjI2MDc2LS43NTYwNTA1LjUxNDg0NzcuNjgwNDQ1NC0uMTQzMDEzMyA1LjM1MjgzNzEtMi42NjAxOSA3LjU5MDc0NjMtLjM4NTk5MjguMzQ3NzgzMi0uNzU3ODI3My4xNjYzMzExLS41ODYzNTQ0LS4yODcyOTkxLjU3MjA1MzEtMS40OTY5Nzk5IDEuODQ1MDE0My00Ljg2ODk2NDggMS4yNDQzNTg1LTUuNjcwMzc4MnoiLz48L2c+PC9zdmc+\n    meta.crossplane.io/license: Apache-2.0\n    meta.crossplane.io/maintainer: Crossplane Maintainers <crossplane-info@lists.cncf.io>\n    meta.crossplane.io/readme: |\n      `provider-aws` is the Crossplane infrastructure provider for [Amazon Web\n      Services (AWS)](https://aws.amazon.com/).\n\n      Available resources and their fields can be found in the [CRD\n      Docs](https://doc.crds.dev/github.com/crossplane/provider-aws).\n\n      If you encounter an issue please reach out on\n      [slack.crossplane.io](https://slack.crossplane.io) and create an issue in\n      the [crossplane/provider-aws](https://github.com/crossplane/provider-aws)\n      repo.\n    meta.crossplane.io/source: github.com/crossplane/provider-aws\n    readme: |\n      `provider-aws` is the Crossplane infrastructure provider for [Amazon Web\n      Services (AWS)](https://aws.amazon.com/).\n\n      Available resources and their fields can be found in the [CRD\n      Docs](https://doc.crds.dev/github.com/crossplane/provider-aws).\n\n      If you encounter an issue please reach out on\n      [slack.crossplane.io](https://slack.crossplane.io) and create an issue in\n      the [crossplane/provider-aws](https://github.com/crossplane/provider-aws)\n      repo.\n    source: github.com/crossplane/provider-aws\n  creationTimestamp: null\n  name: provider-aws\nspec:\n  controller:\n    image: crossplane/provider-aws-controller:v0.20.0\n"
  },
  {
    "path": "pkg/xpkg/testdata/providerconfigs.helm.crossplane.io.yaml",
    "content": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    controller-gen.kubebuilder.io/version: v0.6.1\n  name: providerconfigs.helm.crossplane.io\nspec:\n  group: helm.crossplane.io\n  names:\n    categories:\n    - crossplane\n    - provider\n    - helm\n    kind: ProviderConfig\n    listKind: ProviderConfigList\n    plural: providerconfigs\n    singular: providerconfig\n  scope: Cluster\n  versions:\n  - additionalPrinterColumns:\n    - jsonPath: .metadata.creationTimestamp\n      name: AGE\n      type: date\n    - jsonPath: .spec.credentialsSecretRef.name\n      name: SECRET-NAME\n      priority: 1\n      type: string\n    name: v1alpha1\n    schema:\n      openAPIV3Schema:\n        description: A ProviderConfig configures a Helm 'provider', i.e. a connection\n          to a particular\n        properties:\n          apiVersion:\n            description: 'APIVersion defines the versioned schema of this representation\n              of an object. Servers should convert recognized schemas to the latest\n              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'\n            type: string\n          kind:\n            description: 'Kind is a string value representing the REST resource this\n              object represents. Servers may infer this from the endpoint the client\n              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'\n            type: string\n          metadata:\n            type: object\n          spec:\n            description: A ProviderConfigSpec defines the desired state of a Provider.\n            properties:\n              credentials:\n                description: Credentials used to connect to the Kubernetes API. Typically\n                  a kubeconfig file. Use InjectedIdentity for in-cluster config.\n                properties:\n                  env:\n                    description: Env is a reference to an environment variable that\n                      contains credentials that must be used to connect to the provider.\n                    properties:\n                      name:\n                        description: Name is the name of an environment variable.\n                        type: string\n                    required:\n                    - name\n                    type: object\n                  fs:\n                    description: Fs is a reference to a filesystem location that contains\n                      credentials that must be used to connect to the provider.\n                    properties:\n                      path:\n                        description: Path is a filesystem path.\n                        type: string\n                    required:\n                    - path\n                    type: object\n                  secretRef:\n                    description: A SecretRef is a reference to a secret key that contains\n                      the credentials that must be used to connect to the provider.\n                    properties:\n                      key:\n                        description: The key to select.\n                        type: string\n                      name:\n                        description: Name of the secret.\n                        type: string\n                      namespace:\n                        description: Namespace of the secret.\n                        type: string\n                    required:\n                    - key\n                    - name\n                    - namespace\n                    type: object\n                  source:\n                    description: Source of the provider credentials.\n                    enum:\n                    - None\n                    - Secret\n                    - InjectedIdentity\n                    - Environment\n                    - Filesystem\n                    type: string\n                required:\n                - source\n                type: object\n              identity:\n                description: Identity used to authenticate to the Kubernetes API.\n                  The identity credentials can be used to supplement kubeconfig 'credentials',\n                  for example by configuring a bearer token source such as OAuth.\n                properties:\n                  env:\n                    description: Env is a reference to an environment variable that\n                      contains credentials that must be used to connect to the provider.\n                    properties:\n                      name:\n                        description: Name is the name of an environment variable.\n                        type: string\n                    required:\n                    - name\n                    type: object\n                  fs:\n                    description: Fs is a reference to a filesystem location that contains\n                      credentials that must be used to connect to the provider.\n                    properties:\n                      path:\n                        description: Path is a filesystem path.\n                        type: string\n                    required:\n                    - path\n                    type: object\n                  secretRef:\n                    description: A SecretRef is a reference to a secret key that contains\n                      the credentials that must be used to connect to the provider.\n                    properties:\n                      key:\n                        description: The key to select.\n                        type: string\n                      name:\n                        description: Name of the secret.\n                        type: string\n                      namespace:\n                        description: Namespace of the secret.\n                        type: string\n                    required:\n                    - key\n                    - name\n                    - namespace\n                    type: object\n                  source:\n                    description: Source of the provider credentials.\n                    enum:\n                    - None\n                    - Secret\n                    - InjectedIdentity\n                    - Environment\n                    - Filesystem\n                    type: string\n                  type:\n                    description: Type of identity.\n                    enum:\n                    - GoogleApplicationCredentials\n                    type: string\n                required:\n                - source\n                - type\n                type: object\n            required:\n            - credentials\n            type: object\n          status:\n            description: A ProviderConfigStatus defines the status of a Provider.\n            properties:\n              conditions:\n                description: Conditions of the resource.\n                items:\n                  description: A Condition that may apply to a resource.\n                  properties:\n                    lastTransitionTime:\n                      description: LastTransitionTime is the last time this condition\n                        transitioned from one status to another.\n                      format: date-time\n                      type: string\n                    message:\n                      description: A Message containing details about this condition's\n                        last transition from one status to another, if any.\n                      type: string\n                    reason:\n                      description: A Reason for this condition's last transition from\n                        one status to another.\n                      type: string\n                    status:\n                      description: Status of this condition; is it currently True,\n                        False, or Unknown?\n                      type: string\n                    type:\n                      description: Type of this condition. At most one of each condition\n                        type may apply to a resource at any point in time.\n                      type: string\n                  required:\n                  - lastTransitionTime\n                  - reason\n                  - status\n                  - type\n                  type: object\n                type: array\n              users:\n                description: Users of this provider configuration.\n                format: int64\n                type: integer\n            type: object\n        required:\n        - spec\n        type: object\n    served: true\n    storage: false\n    subresources:\n      status: {}\n  - additionalPrinterColumns:\n    - jsonPath: .metadata.creationTimestamp\n      name: AGE\n      type: date\n    - jsonPath: .spec.credentialsSecretRef.name\n      name: SECRET-NAME\n      priority: 1\n      type: string\n    name: v1beta1\n    schema:\n      openAPIV3Schema:\n        description: A ProviderConfig configures a Helm 'provider', i.e. a connection\n          to a particular\n        properties:\n          apiVersion:\n            description: 'APIVersion defines the versioned schema of this representation\n              of an object. Servers should convert recognized schemas to the latest\n              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'\n            type: string\n          kind:\n            description: 'Kind is a string value representing the REST resource this\n              object represents. Servers may infer this from the endpoint the client\n              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'\n            type: string\n          metadata:\n            type: object\n          spec:\n            description: A ProviderConfigSpec defines the desired state of a Provider.\n            properties:\n              credentials:\n                description: Credentials used to connect to the Kubernetes API. Typically\n                  a kubeconfig file. Use InjectedIdentity for in-cluster config.\n                properties:\n                  env:\n                    description: Env is a reference to an environment variable that\n                      contains credentials that must be used to connect to the provider.\n                    properties:\n                      name:\n                        description: Name is the name of an environment variable.\n                        type: string\n                    required:\n                    - name\n                    type: object\n                  fs:\n                    description: Fs is a reference to a filesystem location that contains\n                      credentials that must be used to connect to the provider.\n                    properties:\n                      path:\n                        description: Path is a filesystem path.\n                        type: string\n                    required:\n                    - path\n                    type: object\n                  secretRef:\n                    description: A SecretRef is a reference to a secret key that contains\n                      the credentials that must be used to connect to the provider.\n                    properties:\n                      key:\n                        description: The key to select.\n                        type: string\n                      name:\n                        description: Name of the secret.\n                        type: string\n                      namespace:\n                        description: Namespace of the secret.\n                        type: string\n                    required:\n                    - key\n                    - name\n                    - namespace\n                    type: object\n                  source:\n                    description: Source of the provider credentials.\n                    enum:\n                    - None\n                    - Secret\n                    - InjectedIdentity\n                    - Environment\n                    - Filesystem\n                    type: string\n                required:\n                - source\n                type: object\n              identity:\n                description: Identity used to authenticate to the Kubernetes API.\n                  The identity credentials can be used to supplement kubeconfig 'credentials',\n                  for example by configuring a bearer token source such as OAuth.\n                properties:\n                  env:\n                    description: Env is a reference to an environment variable that\n                      contains credentials that must be used to connect to the provider.\n                    properties:\n                      name:\n                        description: Name is the name of an environment variable.\n                        type: string\n                    required:\n                    - name\n                    type: object\n                  fs:\n                    description: Fs is a reference to a filesystem location that contains\n                      credentials that must be used to connect to the provider.\n                    properties:\n                      path:\n                        description: Path is a filesystem path.\n                        type: string\n                    required:\n                    - path\n                    type: object\n                  secretRef:\n                    description: A SecretRef is a reference to a secret key that contains\n                      the credentials that must be used to connect to the provider.\n                    properties:\n                      key:\n                        description: The key to select.\n                        type: string\n                      name:\n                        description: Name of the secret.\n                        type: string\n                      namespace:\n                        description: Namespace of the secret.\n                        type: string\n                    required:\n                    - key\n                    - name\n                    - namespace\n                    type: object\n                  source:\n                    description: Source of the provider credentials.\n                    enum:\n                    - None\n                    - Secret\n                    - InjectedIdentity\n                    - Environment\n                    - Filesystem\n                    type: string\n                  type:\n                    description: Type of identity.\n                    enum:\n                    - GoogleApplicationCredentials\n                    type: string\n                required:\n                - source\n                - type\n                type: object\n            required:\n            - credentials\n            type: object\n          status:\n            description: A ProviderConfigStatus defines the status of a Provider.\n            properties:\n              conditions:\n                description: Conditions of the resource.\n                items:\n                  description: A Condition that may apply to a resource.\n                  properties:\n                    lastTransitionTime:\n                      description: LastTransitionTime is the last time this condition\n                        transitioned from one status to another.\n                      format: date-time\n                      type: string\n                    message:\n                      description: A Message containing details about this condition's\n                        last transition from one status to another, if any.\n                      type: string\n                    reason:\n                      description: A Reason for this condition's last transition from\n                        one status to another.\n                      type: string\n                    status:\n                      description: Status of this condition; is it currently True,\n                        False, or Unknown?\n                      type: string\n                    type:\n                      description: Type of this condition. At most one of each condition\n                        type may apply to a resource at any point in time.\n                      type: string\n                  required:\n                  - lastTransitionTime\n                  - reason\n                  - status\n                  - type\n                  type: object\n                type: array\n              users:\n                description: Users of this provider configuration.\n                format: int64\n                type: integer\n            type: object\n        required:\n        - spec\n        type: object\n    served: true\n    storage: true\n    subresources:\n      status: {}\nstatus:\n  acceptedNames:\n    kind: \"\"\n    plural: \"\"\n  conditions: []\n  storedVersions: []\n"
  },
  {
    "path": "pkg/xpkg/validate.go",
    "content": "/*\nCopyright 2025 The Crossplane 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 xpkg\n\nimport \"github.com/crossplane/crossplane-runtime/v2/pkg/xpkg/parser\"\n\n// Validator validates packages before installation is attempted.\ntype Validator parser.Linter\n\n// NewProviderValidator is a convenience function for creating a package\n// validator for providers.\nfunc NewProviderValidator() Validator {\n\treturn parser.NewPackageLinter(\n\t\tparser.PackageLinterFns(OneMeta),\n\t\tparser.ObjectLinterFns(IsProvider, PackageValidSemver),\n\t\tparser.ObjectLinterFns())\n}\n\n// NewConfigurationValidator is a convenience function for creating a package\n// validator for configurations.\nfunc NewConfigurationValidator() Validator {\n\treturn parser.NewPackageLinter(\n\t\tparser.PackageLinterFns(OneMeta),\n\t\tparser.ObjectLinterFns(IsConfiguration, PackageValidSemver),\n\t\tparser.ObjectLinterFns())\n}\n\n// NewFunctionValidator is a convenience function for creating a package\n// validator for functions.\nfunc NewFunctionValidator() Validator {\n\treturn parser.NewPackageLinter(\n\t\tparser.PackageLinterFns(OneMeta),\n\t\tparser.ObjectLinterFns(IsFunction, PackageValidSemver),\n\t\tparser.ObjectLinterFns())\n}\n"
  },
  {
    "path": "test/fuzz/oss_fuzz_build.sh",
    "content": "#!/bin/bash -eu\n#\n# IMPORTANT: Fuzz* test cases should be in a dedicated file, conventionally\n# called `fuzz_test.go`, but that's not a requirement. Otherwise once the file\n# name is changed to have the _test_fuzz.go termination instead of _test.go as\n# required by oss-fuzz, the code won't compile as other Test* test cases might\n# not find some requirements given that they are not in a _test.go file.\n#\n# DO NOT DELETE: this script is used from oss-fuzz. You can find more details\n# in the official documentation:\n# https://google.github.io/oss-fuzz/getting-started/new-project-guide/go-lang/\n#\n# To run this locally you can go through the following steps: - $ git clone\n# https://github.com/google/oss-fuzz --depth=1 - $ cd\n# oss-fuzz/projects/crossplane\n# - modify Dockerfile to point to your branch with all the fuzzers being merged.\n# - modify build.sh to call the build script in Crossplanes repository\n# - $ python3 ../../infra/helper.py build_image crossplane\n# - $ python3 ../../infra/helper.py build_fuzzers crossplane\n\nset -o nounset\nset -o pipefail\nset -o errexit\nset -x\n\nprintf \"package main\\nimport ( \\n _ \\\"github.com/AdamKorcz/go-118-fuzz-build/testing\\\"\\n )\\n\" > register.go\n\n# Moving all the fuzz_test.go to fuzz_test_fuzz.go, as oss-fuzz uses go build to build fuzzers\n# shellcheck disable=SC2016\ngrep --line-buffered --include '*_test.go' -Pr 'func Fuzz.*\\(.* \\*testing\\.F' | cut -d: -f1 | sort -u | xargs -I{} sh -c '\n  file=\"{}\"\n  file_no_ext=\"$(basename \"$file\" | cut -d\".\" -f1)\"\n  folder=\"$(dirname $file)\"\n  mv \"$file\" \"$folder/${file_no_ext}_fuzz.go\"\n'\n\n# Now we can tidy and download all our dependencies\ngo mod tidy\ngo mod vendor\n\n# Find all native fuzzers and compile them\n# shellcheck disable=SC2016\ngrep --line-buffered --include '*_test_fuzz.go' -Pr 'func Fuzz.*\\(.* \\*testing\\.F' | sed -E 's/(func Fuzz(.*)\\(.*)/\\2/' | xargs -I{} sh -c '\n  file=\"$(echo \"{}\" | cut -d: -f1)\"\n  folder=\"$(dirname $file)\"\n  func=\"Fuzz$(echo \"{}\" | cut -d: -f2)\"\n  compile_native_go_fuzzer github.com/crossplane/crossplane-runtime/v2/$folder $func $func\n'\n"
  }
]