[
  {
    "path": ".github/workflows/auto-label.yml",
    "content": "---\nname: Label issues\non:\n  issues:\n    types:\n      - opened\n      - reopened\njobs:\n  label_issues:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n    steps:\n      - run: gh issue edit \"$NUMBER\" --add-label \"$LABELS\"\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          GH_REPO: ${{ github.repository }}\n          NUMBER: ${{ github.event.issue.number }}\n          LABELS: sig/multicluster\n"
  },
  {
    "path": ".gitignore",
    "content": "*~\n.\\#*\n._*\n\\#*\\#\n/_artifacts/\n/bazel-*\nbin\n.classpath\n/cluster\n/.config/gcloud*/\n*.dll\n/doc_tmp/\n!\\.drone\\.sec\n.DS_Store\n*.dylib\n.envrc\n*.exe\n*.exe~\n/_gopath/\n/.gsutil/\n/hack/.test-cmd-auth\n**/.hg*\n.idea/\n*.iml\n/junit*.xml\n/.make/\n.netrwhist\nnetwork_closure.sh\n*.out\n/_output*/\n/output*/\n.project\n*.pyc\n[._]*.s[a-w][a-z]\n[._]s[a-w][a-z]\nSession.vim\n.settings/**\n*.so\n*.swo\n*.swp\n.tags*\n*.test\n/third_party/pkg\n.*.timestamp\n/_tmp/\n*.un~\n.vagrant\n!vendor/**/zz_generated.*\n.vscode\n/www/test_out\nreport.html\nreport.yaml\n.*.timestamp\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing Guidelines\n\nWelcome to Kubernetes. We are excited about the prospect of you joining our [community](https://git.k8s.io/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt:\n\n_As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._\n\n## Getting Started\n\nWe have full documentation on how to get started contributing here:\n\n<!---\nIf your repo has certain guidelines for contribution, put them here ahead of the general k8s resources\n-->\n\n- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests\n- [Kubernetes Contributor Guide](https://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](https://git.k8s.io/community/contributors/guide#contributing)\n- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet) - Common resources for existing developers\n\n## Mentorship\n\n- [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers!\n\n## Contact Information\n\n- [Slack](https://kubernetes.slack.com/messages/sig-service-catalog)\n- [Mailing List](https://groups.google.com/forum/#!forum/kubernetes-sig-service-catalog)\n"
  },
  {
    "path": "Dockerfile",
    "content": "# Build the manager binary\nFROM golang:1.23 as builder\n\nWORKDIR /workspace\n# Copy the Go Modules manifests\nCOPY go.mod go.mod\nCOPY go.sum go.sum\nRUN go mod download\n\n# Copy the go source\nCOPY controllers/ controllers/\nRUN go -C controllers mod download\nCOPY pkg/ pkg/\n\n# Build\nRUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go -C controllers build -a -o /workspace/controller cmd/servicecontroller/servicecontroller.go\n\n# Use distroless as minimal base image to package the manager binary\n# Refer to https://github.com/GoogleContainerTools/distroless for more details\nFROM gcr.io/distroless/static:nonroot\nWORKDIR /\nCOPY --from=builder /workspace/controller .\nUSER nonroot:nonroot\n\nENTRYPOINT [\"/controller\"]\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 {yyyy} {name of copyright owner}\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": "Makefile",
    "content": "# Copyright 2019 The Kubernetes 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\nDOCKER ?= docker\n# TOP is the current directory where this Makefile lives.\nTOP := $(dir $(firstword $(MAKEFILE_LIST)))\n# ROOT is the root of the mkdocs tree.\nROOT := $(abspath $(TOP))\n# Image URL to use all building/pushing image targets\nIMG ?= mcs-api-controller:latest\n# Need v1 to support defaults in CRDs, unfortunately limiting us to k8s 1.16+\nCRD_OPTIONS ?= \"crd:crdVersions=v1\"\n\nCONTROLLER_GEN=go -C tools run sigs.k8s.io/controller-tools/cmd/controller-gen\n# enable Go modules\nexport GO111MODULE=on\n\n.PHONY: all\nall: generate manifests controller verify\n\n.PHONY: e2e-test\ne2e-test: export MCS_CONTROLLER_IMAGE := $(IMG)\ne2e-test: docker-build\n\t./scripts/e2e-test.sh\n\n.PHONY: demo\ndemo: export MCS_CONTROLLER_IMAGE := $(IMG)\ndemo: docker-build\n\t./scripts/up.sh\n\t./demo/demo.sh\n\t./scripts/down.sh\n\n# Build controller binary\n.PHONY: controller\ncontroller: generate fmt vet\n\tgo -C controllers build -o $(ROOT)/bin/manager cmd/servicecontroller/servicecontroller.go\n\n# Run go fmt against code\n.PHONY: fmt\nfmt:\n\tfor m in . conformance controllers e2e; do go -C $$m fmt ./...; done\n\n# Run go vet against code\n.PHONY: vet\nvet:\n\tfor m in . conformance controllers e2e; do go -C $$m vet ./...; done\n\n# Run generators for Deepcopy funcs and CRDs\n.PHONY: generate\ngenerate:\n\t./hack/update-codegen.sh\n\t$(CONTROLLER_GEN) object:headerFile=$(ROOT)/hack/boilerplate.go.txt paths=\"$(ROOT)/...\"\n\n# Generate manifests e.g. CRD, RBAC etc.\n.PHONY: manifests\nmanifests:\n\t$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=mcs-derived-service-manager output:rbac:dir=\"$(ROOT)/config/rbac\" webhook schemapatch:manifests=\"$(ROOT)/config/crd-base\" paths=\"$(ROOT)/...\" output:crd:none output:schemapatch:dir=\"$(ROOT)/config/crd\"\n\n# Run tests\n.PHONY: test\ntest: generate fmt vet manifests\n\tfor m in . controllers; do go -C $$m test ./... -coverprofile cover.out; done\n\n# Install CRD's and example resources to a pre-existing cluster.\n.PHONY: install\ninstall: manifests crd\n\n# Remove installed CRD's and CR's.\n.PHONY: uninstall\nuninstall:\n\t./hack/delete-crds.sh\n\n# Run static analysis.\n.PHONY: verify\nverify:\n\t./hack/verify-all.sh -v\n\n# Build docker containers\n.PHONY: docker-build\ndocker-build: generate fmt vet manifests\n\tdocker build . -t ${IMG}\n\n# Push the docker image\n.PHONY: docker-push\ndocker-push: docker-build\n\tdocker push ${IMG}\n\n# Run against the configured Kubernetes cluster in ~/.kube/config\nrun: generate fmt vet manifests\n\tgo run ./cmd/servicecontroller/servicecontroller.go\n"
  },
  {
    "path": "OWNERS",
    "content": "# See the OWNERS docs at https://go.k8s.io/owners\n\nreviewers:\n  - jeremyot\n  - lauralorenz\n  - skitt\n  - MrFreezeex\n  - munnerz\n  - RainbowMango\n  - ryanzhang-oss\n# Pending org membership\n# - jnpacker\n\napprovers:\n  - JeremyOT\n  - lauralorenz\n  - skitt\n\nemeritus_approvers:\n  - pmorie\n\nlabels:\n  - sig/multicluster\n"
  },
  {
    "path": "README.md",
    "content": "# Multi-cluster Service APIs\n\nThis repository hosts the Multi-Cluster Service APIs. Providers can import packages in this repo to ensure their multi-cluster service controller implementations will be compatible with MCS data planes.\n\nThis repo contains the initial implementation according to [KEP-1645][kep].\n\n[kep]: https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api\n\n## Try it out\n\nTo see the API in action, run `make demo` to build and run a local demo against\na pair of kind clusters. Alternatively, you can take a self guided tour. Use:\n\n- `./scripts/up.sh` to create a pair of clusters with mutually connected networks\n  and install the `mcs-api-controller`.\n\n  _This will use a pre-existing controller image if available, it's recommended\n  to run `make docker-build` first._\n- `./demo/demo.sh` to run the same demo as above against your newly created\n  clusters (must run `./scripts/up.sh` first).\n- `./scripts/down.sh` to tear down your clusters.\n\n## Community, discussion, contribution, and support\n\nLearn how to engage with the Kubernetes community on the [community page](http://kubernetes.io/community/).\n\nYou can reach the maintainers of this project at:\n\n- [Slack](https://kubernetes.slack.com/messages/sig-multicluster)\n- [Mailing List](https://groups.google.com/forum/#!forum/kubernetes-sig-multicluster)\n\n[Our meeting schedule is here]( https://github.com/kubernetes/community/tree/master/sig-multicluster#meetings)\n\n\n## Technical Leads\n\n- @pmorie\n- @jeremyot\n\n### Code of conduct\n\nParticipation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).\n"
  },
  {
    "path": "RELEASE.md",
    "content": "# Release Process\n\nThe Kubernetes Template Project is released on an as-needed basis. The process is as follows:\n\n1. An issue is proposing a new release with a changelog since the last release\n1. All [OWNERS](OWNERS) must LGTM this release\n1. An OWNER runs `git tag -s $VERSION` and inserts the changelog and pushes the tag with `git push $VERSION`\n1. The release issue is closed\n1. An announcement email is sent to `kubernetes-dev@googlegroups.com` with the subject `[ANNOUNCE] kubernetes-template-project $VERSION is released`\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Security Announcements\n\nJoin the [kubernetes-security-announce] group for security and vulnerability announcements.\n\nYou can also subscribe to an RSS feed of the above using [this link][kubernetes-security-announce-rss].\n\n## Reporting a Vulnerability\n\nInstructions for reporting a vulnerability can be found on the\n[Kubernetes Security and Disclosure Information] page.\n\n## Supported Versions\n\nInformation about supported Kubernetes versions can be found on the\n[Kubernetes version and version skew support policy] page on the Kubernetes website.\n\n[kubernetes-security-announce]: https://groups.google.com/forum/#!forum/kubernetes-security-announce\n[kubernetes-security-announce-rss]: https://groups.google.com/forum/feed/kubernetes-security-announce/msgs/rss_v2_0.xml?num=50\n[Kubernetes version and version skew support policy]: https://kubernetes.io/docs/setup/release/version-skew-policy/#supported-versions\n[Kubernetes Security and Disclosure Information]: https://kubernetes.io/docs/reference/issues-security/security/#report-a-vulnerability\n"
  },
  {
    "path": "SECURITY_CONTACTS",
    "content": "# Defined below are the security contacts for this repo.\n#\n# They are the contact point for the Product Security Committee to reach out\n# to for triaging and handling of incoming issues.\n#\n# The below names agree to abide by the\n# [Embargo Policy](https://git.k8s.io/security/private-distributors-list.md#embargo-policy)\n# and will be removed and replaced if they violate that agreement.\n#\n# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE\n# INSTRUCTIONS AT https://kubernetes.io/security/\n\npmorie\nJeremyOT\n"
  },
  {
    "path": "code-of-conduct.md",
    "content": "# Kubernetes Community Code of Conduct\n\nPlease refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)\n"
  },
  {
    "path": "config/crd/embed.go",
    "content": "/*\nCopyright 2025 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage crd\n\nimport _ \"embed\" // import embed to be able to use go:embed\n\nvar (\n\t// ServiceExportCRD is the embedded YAML for the ServiceExport CRD\n\t//go:embed multicluster.x-k8s.io_serviceexports.yaml\n\tServiceExportCRD []byte\n\t// ServiceImportCRD is the embedded YAML for the ServiceImport CRD\n\t//go:embed multicluster.x-k8s.io_serviceimports.yaml\n\tServiceImportCRD []byte\n)\n\nconst (\n\t// ReleaseVersionLabel is the label which indicate the release version\n\tReleaseVersionLabel = \"multicluster.x-k8s.io/release-version\"\n\t// CustomResourceDefinitionSchemaRevisionLabel is the label which holds the CRD schema revision\n\tCustomResourceDefinitionSchemaRevisionLabel = \"multicluster.x-k8s.io/crd-schema-revision\"\n)\n"
  },
  {
    "path": "config/crd/multicluster.x-k8s.io_serviceexports.yaml",
    "content": "# Copyright 2020 The Kubernetes 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.\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  name: serviceexports.multicluster.x-k8s.io\n  labels:\n    multicluster.x-k8s.io/release-version: \"v0.5.0\"\n    # The revision is updated on each CRD change and reset back to 0 on every new version.\n    # It can be used together with the version label when installing those CRDs\n    # and prevent any downgrades.\n    multicluster.x-k8s.io/crd-schema-revision: \"0\"\nspec:\n  group: multicluster.x-k8s.io\n  scope: Namespaced\n  names:\n    plural: serviceexports\n    singular: serviceexport\n    kind: ServiceExport\n    shortNames:\n      - svcex\n      - svcexport\n  versions:\n    - name: v1alpha1\n      served: true\n      storage: false\n      subresources:\n        status: {}\n      additionalPrinterColumns:\n        - name: Age\n          type: date\n          jsonPath: .metadata.creationTimestamp\n      \"schema\":\n        \"openAPIV3Schema\":\n          description: |-\n            ServiceExport declares that the Service with the same name and namespace\n            as this export should be consumable from other clusters.\n          type: object\n          properties:\n            apiVersion:\n              description: |-\n                APIVersion defines the versioned schema of this representation of an object.\n                Servers should convert recognized schemas to the latest internal value, and\n                may reject unrecognized values.\n                More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\n              type: string\n            kind:\n              description: |-\n                Kind is a string value representing the REST resource this object represents.\n                Servers may infer this from the endpoint the client submits requests to.\n                Cannot be updated.\n                In CamelCase.\n                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: spec defines the behavior of a ServiceExport.\n              type: object\n              properties:\n                exportedAnnotations:\n                  description: exportedAnnotations describes the annotations exported. It is optional for implementation.\n                  type: object\n                  additionalProperties:\n                    type: string\n                exportedLabels:\n                  description: exportedLabels describes the labels exported. It is optional for implementation.\n                  type: object\n                  additionalProperties:\n                    type: string\n            status:\n              description: |-\n                status describes the current state of an exported service.\n                Service configuration comes from the Service that had the same\n                name and namespace as this ServiceExport.\n                Populated by the multi-cluster service implementation's controller.\n              type: object\n              properties:\n                conditions:\n                  type: array\n                  items:\n                    description: Condition contains details for one aspect of the current state of this API Resource.\n                    type: object\n                    required:\n                      - lastTransitionTime\n                      - message\n                      - reason\n                      - status\n                      - type\n                    properties:\n                      lastTransitionTime:\n                        description: |-\n                          lastTransitionTime is the last time the condition transitioned from one status to another.\n                          This should be when the underlying condition changed.  If that is not known, then using the time when the API field changed is acceptable.\n                        type: string\n                        format: date-time\n                      message:\n                        description: |-\n                          message is a human readable message indicating details about the transition.\n                          This may be an empty string.\n                        type: string\n                        maxLength: 32768\n                      observedGeneration:\n                        description: |-\n                          observedGeneration represents the .metadata.generation that the condition was set based upon.\n                          For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date\n                          with respect to the current state of the instance.\n                        type: integer\n                        format: int64\n                        minimum: 0\n                      reason:\n                        description: |-\n                          reason contains a programmatic identifier indicating the reason for the condition's last transition.\n                          Producers of specific condition types may define expected values and meanings for this field,\n                          and whether the values are considered a guaranteed API.\n                          The value should be a CamelCase string.\n                          This field may not be empty.\n                        type: string\n                        maxLength: 1024\n                        minLength: 1\n                        pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$\n                      status:\n                        description: status of the condition, one of True, False, Unknown.\n                        type: string\n                        enum:\n                          - \"True\"\n                          - \"False\"\n                          - Unknown\n                      type:\n                        description: type of condition in CamelCase or in foo.example.com/CamelCase.\n                        type: string\n                        maxLength: 316\n                        pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$\n                  x-kubernetes-list-map-keys:\n                    - type\n                  x-kubernetes-list-type: map\n    - name: v1beta1\n      served: true\n      storage: true\n      subresources:\n        status: {}\n      additionalPrinterColumns:\n        - name: Age\n          type: date\n          jsonPath: .metadata.creationTimestamp\n      \"schema\":\n        \"openAPIV3Schema\":\n          description: |-\n            ServiceExport declares that the Service with the same name and namespace\n            as this export should be consumable from other clusters.\n          type: object\n          properties:\n            apiVersion:\n              description: |-\n                APIVersion defines the versioned schema of this representation of an object.\n                Servers should convert recognized schemas to the latest internal value, and\n                may reject unrecognized values.\n                More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\n              type: string\n            kind:\n              description: |-\n                Kind is a string value representing the REST resource this object represents.\n                Servers may infer this from the endpoint the client submits requests to.\n                Cannot be updated.\n                In CamelCase.\n                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: spec defines the behavior of a ServiceExport.\n              type: object\n              properties:\n                exportedAnnotations:\n                  description: exportedAnnotations describes the annotations exported. It is optional for implementation.\n                  type: object\n                  additionalProperties:\n                    type: string\n                exportedLabels:\n                  description: exportedLabels describes the labels exported. It is optional for implementation.\n                  type: object\n                  additionalProperties:\n                    type: string\n            status:\n              description: |-\n                status describes the current state of an exported service.\n                Service configuration comes from the Service that had the same\n                name and namespace as this ServiceExport.\n                Populated by the multi-cluster service implementation's controller.\n              type: object\n              properties:\n                conditions:\n                  type: array\n                  items:\n                    description: Condition contains details for one aspect of the current state of this API Resource.\n                    type: object\n                    required:\n                      - lastTransitionTime\n                      - message\n                      - reason\n                      - status\n                      - type\n                    properties:\n                      lastTransitionTime:\n                        description: |-\n                          lastTransitionTime is the last time the condition transitioned from one status to another.\n                          This should be when the underlying condition changed.  If that is not known, then using the time when the API field changed is acceptable.\n                        type: string\n                        format: date-time\n                      message:\n                        description: |-\n                          message is a human readable message indicating details about the transition.\n                          This may be an empty string.\n                        type: string\n                        maxLength: 32768\n                      observedGeneration:\n                        description: |-\n                          observedGeneration represents the .metadata.generation that the condition was set based upon.\n                          For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date\n                          with respect to the current state of the instance.\n                        type: integer\n                        format: int64\n                        minimum: 0\n                      reason:\n                        description: |-\n                          reason contains a programmatic identifier indicating the reason for the condition's last transition.\n                          Producers of specific condition types may define expected values and meanings for this field,\n                          and whether the values are considered a guaranteed API.\n                          The value should be a CamelCase string.\n                          This field may not be empty.\n                        type: string\n                        maxLength: 1024\n                        minLength: 1\n                        pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$\n                      status:\n                        description: status of the condition, one of True, False, Unknown.\n                        type: string\n                        enum:\n                          - \"True\"\n                          - \"False\"\n                          - Unknown\n                      type:\n                        description: type of condition in CamelCase or in foo.example.com/CamelCase.\n                        type: string\n                        maxLength: 316\n                        pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$\n                  x-kubernetes-list-map-keys:\n                    - type\n                  x-kubernetes-list-type: map\n"
  },
  {
    "path": "config/crd/multicluster.x-k8s.io_serviceimports.yaml",
    "content": "# Copyright 2020 The Kubernetes 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.\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  name: serviceimports.multicluster.x-k8s.io\n  labels:\n    multicluster.x-k8s.io/release-version: \"v0.5.0\"\n    # The revision is updated on each CRD change and reset back to 0 on every new version.\n    # It can be used together with the version label when installing those CRDs\n    # and prevent any downgrades.\n    multicluster.x-k8s.io/crd-schema-revision: \"0\"\nspec:\n  group: multicluster.x-k8s.io\n  scope: Namespaced\n  names:\n    plural: serviceimports\n    singular: serviceimport\n    kind: ServiceImport\n    shortNames:\n      - svcim\n      - svcimport\n  versions:\n    - name: v1alpha1\n      served: true\n      storage: false\n      subresources:\n        status: {}\n      additionalPrinterColumns:\n        - name: Type\n          type: string\n          description: The type of this ServiceImport\n          jsonPath: .spec.type\n        - name: IP\n          type: string\n          description: The VIP for this ServiceImport\n          jsonPath: .spec.ips\n        - name: Age\n          type: date\n          jsonPath: .metadata.creationTimestamp\n      \"schema\":\n        \"openAPIV3Schema\":\n          description: ServiceImport describes a service imported from clusters in a ClusterSet.\n          type: object\n          properties:\n            apiVersion:\n              description: |-\n                APIVersion defines the versioned schema of this representation of an object.\n                Servers should convert recognized schemas to the latest internal value, and\n                may reject unrecognized values.\n                More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\n              type: string\n            kind:\n              description: |-\n                Kind is a string value representing the REST resource this object represents.\n                Servers may infer this from the endpoint the client submits requests to.\n                Cannot be updated.\n                In CamelCase.\n                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: spec defines the behavior of a ServiceImport.\n              type: object\n              required:\n                - ports\n                - type\n              properties:\n                internalTrafficPolicy:\n                  description: |-\n                    InternalTrafficPolicy describes how nodes distribute service traffic they\n                    receive on the ClusterIP. If set to \"Local\", the proxy will assume that pods\n                    only want to talk to endpoints of the service on the same node as the pod,\n                    dropping the traffic if there are no local endpoints. The default value,\n                    \"Cluster\", uses the standard behavior of routing to all endpoints evenly\n                    (possibly modified by topology and other features).\n                  type: string\n                ipFamilies:\n                  description: IPFamilies identifies all the IPFamilies assigned for this ServiceImport.\n                  type: array\n                  maxItems: 2\n                  items:\n                    description: |-\n                      IPFamily represents the IP Family (IPv4 or IPv6). This type is used\n                      to express the family of an IP expressed by a type (e.g. service.spec.ipFamilies).\n                    type: string\n                ips:\n                  description: ip will be used as the VIP for this service when type is ClusterSetIP.\n                  type: array\n                  maxItems: 2\n                  items:\n                    type: string\n                ports:\n                  type: array\n                  items:\n                    description: ServicePort represents the port on which the service is exposed\n                    type: object\n                    required:\n                      - port\n                    properties:\n                      appProtocol:\n                        description: |-\n                          The application protocol for this port.\n                          This is used as a hint for implementations to offer richer behavior for protocols that they understand.\n                          This field follows standard Kubernetes label syntax.\n                          Valid values are either:\n\n                          * Un-prefixed protocol names - reserved for IANA standard service names (as per\n                          RFC-6335 and https://www.iana.org/assignments/service-names).\n\n                          * Kubernetes-defined prefixed names:\n                            * 'kubernetes.io/h2c' - HTTP/2 over cleartext as described in https://www.rfc-editor.org/rfc/rfc7540\n\n                          * Other protocols should use implementation-defined prefixed names such as\n                          mycompany.com/my-custom-protocol.\n                          Field can be enabled with ServiceAppProtocol feature gate.\n                        type: string\n                      name:\n                        description: |-\n                          The name of this port within the service. This must be a DNS_LABEL.\n                          All ports within a ServiceSpec must have unique names. When considering\n                          the endpoints for a Service, this must match the 'name' field in the\n                          EndpointPort.\n                          Optional if only one ServicePort is defined on this service.\n                        type: string\n                      port:\n                        description: The port that will be exposed by this service.\n                        type: integer\n                        format: int32\n                      protocol:\n                        description: |-\n                          The IP protocol for this port. Supports \"TCP\", \"UDP\", and \"SCTP\".\n                          Default is TCP.\n                        type: string\n                  x-kubernetes-list-type: atomic\n                sessionAffinity:\n                  description: |-\n                    Supports \"ClientIP\" and \"None\". Used to maintain session affinity.\n                    Enable client IP based session affinity.\n                    Must be ClientIP or None.\n                    Defaults to None.\n                    Ignored when type is Headless\n                    More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\n                  type: string\n                sessionAffinityConfig:\n                  description: sessionAffinityConfig contains session affinity configuration.\n                  type: object\n                  properties:\n                    clientIP:\n                      description: clientIP contains the configurations of Client IP based session affinity.\n                      type: object\n                      properties:\n                        timeoutSeconds:\n                          description: |-\n                            timeoutSeconds specifies the seconds of ClientIP type session sticky time.\n                            The value must be >0 && <=86400(for 1 day) if ServiceAffinity == \"ClientIP\".\n                            Default value is 10800(for 3 hours).\n                          type: integer\n                          format: int32\n                trafficDistribution:\n                  description: |-\n                    TrafficDistribution offers a way to express preferences for how traffic\n                    is distributed to Service endpoints. Implementations can use this field\n                    as a hint, but are not required to guarantee strict adherence. If the\n                    field is not set, the implementation will apply its default routing\n                    strategy. If set to \"PreferClose\", implementations should prioritize\n                    endpoints that are in the same zone.\n                  type: string\n                type:\n                  description: |-\n                    type defines the type of this service.\n                    Must be ClusterSetIP or Headless.\n                  type: string\n                  enum:\n                    - ClusterSetIP\n                    - Headless\n            status:\n              description: |-\n                status contains information about the exported services that form\n                the multi-cluster service referenced by this ServiceImport.\n              type: object\n              properties:\n                clusters:\n                  description: |-\n                    clusters is the list of exporting clusters from which this service\n                    was derived.\n                  type: array\n                  items:\n                    description: ClusterStatus contains service configuration mapped to a specific source cluster\n                    type: object\n                    required:\n                      - cluster\n                    properties:\n                      cluster:\n                        description: |-\n                          cluster is the name of the exporting cluster. Must be a valid RFC-1123 DNS\n                          label.\n                        type: string\n                  x-kubernetes-list-map-keys:\n                    - cluster\n                  x-kubernetes-list-type: map\n                conditions:\n                  type: array\n                  items:\n                    description: Condition contains details for one aspect of the current state of this API Resource.\n                    type: object\n                    required:\n                      - lastTransitionTime\n                      - message\n                      - reason\n                      - status\n                      - type\n                    properties:\n                      lastTransitionTime:\n                        description: |-\n                          lastTransitionTime is the last time the condition transitioned from one status to another.\n                          This should be when the underlying condition changed.  If that is not known, then using the time when the API field changed is acceptable.\n                        type: string\n                        format: date-time\n                      message:\n                        description: |-\n                          message is a human readable message indicating details about the transition.\n                          This may be an empty string.\n                        type: string\n                        maxLength: 32768\n                      observedGeneration:\n                        description: |-\n                          observedGeneration represents the .metadata.generation that the condition was set based upon.\n                          For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date\n                          with respect to the current state of the instance.\n                        type: integer\n                        format: int64\n                        minimum: 0\n                      reason:\n                        description: |-\n                          reason contains a programmatic identifier indicating the reason for the condition's last transition.\n                          Producers of specific condition types may define expected values and meanings for this field,\n                          and whether the values are considered a guaranteed API.\n                          The value should be a CamelCase string.\n                          This field may not be empty.\n                        type: string\n                        maxLength: 1024\n                        minLength: 1\n                        pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$\n                      status:\n                        description: status of the condition, one of True, False, Unknown.\n                        type: string\n                        enum:\n                          - \"True\"\n                          - \"False\"\n                          - Unknown\n                      type:\n                        description: type of condition in CamelCase or in foo.example.com/CamelCase.\n                        type: string\n                        maxLength: 316\n                        pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$\n                  x-kubernetes-list-map-keys:\n                    - type\n                  x-kubernetes-list-type: map\n    - name: v1beta1\n      served: true\n      storage: true\n      subresources:\n        status: {}\n      additionalPrinterColumns:\n        - name: Type\n          type: string\n          description: The type of this ServiceImport\n          jsonPath: .spec.type\n        - name: IP\n          type: string\n          description: The VIP for this ServiceImport\n          jsonPath: .spec.ips\n        - name: Age\n          type: date\n          jsonPath: .metadata.creationTimestamp\n      \"schema\":\n        \"openAPIV3Schema\":\n          description: ServiceImport describes a service imported from clusters in a ClusterSet.\n          type: object\n          properties:\n            apiVersion:\n              description: |-\n                APIVersion defines the versioned schema of this representation of an object.\n                Servers should convert recognized schemas to the latest internal value, and\n                may reject unrecognized values.\n                More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources\n              type: string\n            kind:\n              description: |-\n                Kind is a string value representing the REST resource this object represents.\n                Servers may infer this from the endpoint the client submits requests to.\n                Cannot be updated.\n                In CamelCase.\n                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: spec defines the behavior of a ServiceImport.\n              type: object\n              required:\n                - ports\n                - type\n              properties:\n                internalTrafficPolicy:\n                  description: |-\n                    InternalTrafficPolicy describes how nodes distribute service traffic they\n                    receive on the ClusterIP. If set to \"Local\", the proxy will assume that pods\n                    only want to talk to endpoints of the service on the same node as the pod,\n                    dropping the traffic if there are no local endpoints. The default value,\n                    \"Cluster\", uses the standard behavior of routing to all endpoints evenly\n                    (possibly modified by topology and other features).\n                  type: string\n                ipFamilies:\n                  description: IPFamilies identifies all the IPFamilies assigned for this ServiceImport.\n                  type: array\n                  maxItems: 2\n                  items:\n                    description: |-\n                      IPFamily represents the IP Family (IPv4 or IPv6). This type is used\n                      to express the family of an IP expressed by a type (e.g. service.spec.ipFamilies).\n                    type: string\n                ips:\n                  description: ip will be used as the VIP for this service when type is ClusterSetIP.\n                  type: array\n                  maxItems: 2\n                  items:\n                    type: string\n                ports:\n                  type: array\n                  items:\n                    description: ServicePort represents the port on which the service is exposed\n                    type: object\n                    required:\n                      - port\n                    properties:\n                      appProtocol:\n                        description: |-\n                          The application protocol for this port.\n                          This is used as a hint for implementations to offer richer behavior for protocols that they understand.\n                          This field follows standard Kubernetes label syntax.\n                          Valid values are either:\n\n                          * Un-prefixed protocol names - reserved for IANA standard service names (as per\n                          RFC-6335 and https://www.iana.org/assignments/service-names).\n\n                          * Kubernetes-defined prefixed names:\n                            * 'kubernetes.io/h2c' - HTTP/2 over cleartext as described in https://www.rfc-editor.org/rfc/rfc7540\n\n                          * Other protocols should use implementation-defined prefixed names such as\n                          mycompany.com/my-custom-protocol.\n                          Field can be enabled with ServiceAppProtocol feature gate.\n                        type: string\n                      name:\n                        description: |-\n                          The name of this port within the service. This must be a DNS_LABEL.\n                          All ports within a ServiceSpec must have unique names. When considering\n                          the endpoints for a Service, this must match the 'name' field in the\n                          EndpointPort.\n                          Optional if only one ServicePort is defined on this service.\n                        type: string\n                      port:\n                        description: The port that will be exposed by this service.\n                        type: integer\n                        format: int32\n                      protocol:\n                        description: |-\n                          The IP protocol for this port. Supports \"TCP\", \"UDP\", and \"SCTP\".\n                          Default is TCP.\n                        type: string\n                  x-kubernetes-list-type: atomic\n                sessionAffinity:\n                  description: |-\n                    Supports \"ClientIP\" and \"None\". Used to maintain session affinity.\n                    Enable client IP based session affinity.\n                    Must be ClientIP or None.\n                    Defaults to None.\n                    Ignored when type is Headless\n                    More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\n                  type: string\n                sessionAffinityConfig:\n                  description: sessionAffinityConfig contains session affinity configuration.\n                  type: object\n                  properties:\n                    clientIP:\n                      description: clientIP contains the configurations of Client IP based session affinity.\n                      type: object\n                      properties:\n                        timeoutSeconds:\n                          description: |-\n                            timeoutSeconds specifies the seconds of ClientIP type session sticky time.\n                            The value must be >0 && <=86400(for 1 day) if ServiceAffinity == \"ClientIP\".\n                            Default value is 10800(for 3 hours).\n                          type: integer\n                          format: int32\n                trafficDistribution:\n                  description: |-\n                    TrafficDistribution offers a way to express preferences for how traffic\n                    is distributed to Service endpoints. Implementations can use this field\n                    as a hint, but are not required to guarantee strict adherence. If the\n                    field is not set, the implementation will apply its default routing\n                    strategy. If set to \"PreferClose\", implementations should prioritize\n                    endpoints that are in the same zone.\n                  type: string\n                type:\n                  description: |-\n                    type defines the type of this service.\n                    Must be ClusterSetIP or Headless.\n                  type: string\n                  enum:\n                    - ClusterSetIP\n                    - Headless\n            status:\n              description: |-\n                status contains information about the exported services that form\n                the multi-cluster service referenced by this ServiceImport.\n              type: object\n              properties:\n                clusters:\n                  description: |-\n                    clusters is the list of exporting clusters from which this service\n                    was derived.\n                  type: array\n                  items:\n                    description: ClusterStatus contains service configuration mapped to a specific source cluster\n                    type: object\n                    required:\n                      - cluster\n                    properties:\n                      cluster:\n                        description: |-\n                          cluster is the name of the exporting cluster. Must be a valid RFC-1123 DNS\n                          label.\n                        type: string\n                  x-kubernetes-list-map-keys:\n                    - cluster\n                  x-kubernetes-list-type: map\n                conditions:\n                  type: array\n                  items:\n                    description: Condition contains details for one aspect of the current state of this API Resource.\n                    type: object\n                    required:\n                      - lastTransitionTime\n                      - message\n                      - reason\n                      - status\n                      - type\n                    properties:\n                      lastTransitionTime:\n                        description: |-\n                          lastTransitionTime is the last time the condition transitioned from one status to another.\n                          This should be when the underlying condition changed.  If that is not known, then using the time when the API field changed is acceptable.\n                        type: string\n                        format: date-time\n                      message:\n                        description: |-\n                          message is a human readable message indicating details about the transition.\n                          This may be an empty string.\n                        type: string\n                        maxLength: 32768\n                      observedGeneration:\n                        description: |-\n                          observedGeneration represents the .metadata.generation that the condition was set based upon.\n                          For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date\n                          with respect to the current state of the instance.\n                        type: integer\n                        format: int64\n                        minimum: 0\n                      reason:\n                        description: |-\n                          reason contains a programmatic identifier indicating the reason for the condition's last transition.\n                          Producers of specific condition types may define expected values and meanings for this field,\n                          and whether the values are considered a guaranteed API.\n                          The value should be a CamelCase string.\n                          This field may not be empty.\n                        type: string\n                        maxLength: 1024\n                        minLength: 1\n                        pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$\n                      status:\n                        description: status of the condition, one of True, False, Unknown.\n                        type: string\n                        enum:\n                          - \"True\"\n                          - \"False\"\n                          - Unknown\n                      type:\n                        description: type of condition in CamelCase or in foo.example.com/CamelCase.\n                        type: string\n                        maxLength: 316\n                        pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$\n                  x-kubernetes-list-map-keys:\n                    - type\n                  x-kubernetes-list-type: map\n"
  },
  {
    "path": "config/crd-base/multicluster.x-k8s.io_serviceexports.yaml",
    "content": "# Copyright 2020 The Kubernetes 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.\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  name: serviceexports.multicluster.x-k8s.io\n  labels:\n    multicluster.x-k8s.io/release-version: \"v0.5.0\"\n    # The revision is updated on each CRD change and reset back to 0 on every new version.\n    # It can be used together with the version label when installing those CRDs\n    # and prevent any downgrades.\n    multicluster.x-k8s.io/crd-schema-revision: \"0\"\nspec:\n  group: multicluster.x-k8s.io\n  scope: Namespaced\n  names:\n    plural: serviceexports\n    singular: serviceexport\n    kind: ServiceExport\n    shortNames:\n    - svcex\n    - svcexport\n  versions:\n  - name: v1alpha1\n    served: true\n    storage: false\n    subresources:\n      status: {}\n    additionalPrinterColumns:\n    - name: Age\n      type: date\n      jsonPath: .metadata.creationTimestamp\n  - name: v1beta1\n    served: true\n    storage: true\n    subresources:\n      status: {}\n    additionalPrinterColumns:\n    - name: Age\n      type: date\n      jsonPath: .metadata.creationTimestamp\n"
  },
  {
    "path": "config/crd-base/multicluster.x-k8s.io_serviceimports.yaml",
    "content": "# Copyright 2020 The Kubernetes 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.\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  name: serviceimports.multicluster.x-k8s.io\n  labels:\n    multicluster.x-k8s.io/release-version: \"v0.5.0\"\n    # The revision is updated on each CRD change and reset back to 0 on every new version.\n    # It can be used together with the version label when installing those CRDs\n    # and prevent any downgrades.\n    multicluster.x-k8s.io/crd-schema-revision: \"0\"\nspec:\n  group: multicluster.x-k8s.io\n  scope: Namespaced\n  names:\n    plural: serviceimports\n    singular: serviceimport\n    kind: ServiceImport\n    shortNames:\n    - svcim\n    - svcimport\n  versions:\n  - name: v1alpha1\n    served: true\n    storage: false\n    subresources:\n      status: {}\n    additionalPrinterColumns:\n    - name: Type\n      type: string\n      description: The type of this ServiceImport\n      jsonPath: .spec.type\n    - name: IP\n      type: string\n      description: The VIP for this ServiceImport\n      jsonPath: .spec.ips\n    - name: Age\n      type: date\n      jsonPath: .metadata.creationTimestamp\n  - name: v1beta1\n    served: true\n    storage: true\n    subresources:\n      status: {}\n    additionalPrinterColumns:\n    - name: Type\n      type: string\n      description: The type of this ServiceImport\n      jsonPath: .spec.type\n    - name: IP\n      type: string\n      description: The VIP for this ServiceImport\n      jsonPath: .spec.ips\n    - name: Age\n      type: date\n      jsonPath: .metadata.creationTimestamp\n"
  },
  {
    "path": "config/rbac/role.yaml",
    "content": "---\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n  name: mcs-derived-service-manager\nrules:\n- apiGroups:\n  - \"\"\n  resources:\n  - services\n  - services/status\n  verbs:\n  - create\n  - get\n  - list\n  - patch\n  - update\n  - watch\n- apiGroups:\n  - discovery.k8s.io\n  resources:\n  - endpointslices\n  verbs:\n  - get\n  - list\n  - patch\n  - update\n  - watch\n- apiGroups:\n  - multicluster.x-k8s.io\n  resources:\n  - serviceimports\n  verbs:\n  - get\n  - list\n  - patch\n  - update\n  - watch\n"
  },
  {
    "path": "conformance/clusterip_service_dns.go",
    "content": "/*\nCopyright 2023 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage conformance\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\nvar _ = Describe(\"\", Label(OptionalLabel, DNSLabel, ClusterIPLabel), func() {\n\tt := newTestDriver()\n\n\tSpecify(\"A DNS lookup of the <service>.<ns>.svc.\"+dnsDomain+\" domain for a ClusterIP service should resolve to the \"+\n\t\t\"clusterset IP\", func(ctx context.Context) {\n\t\tAddReportEntry(SpecRefReportEntry, \"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#dns\")\n\n\t\tfor _, client := range clients {\n\t\t\tserviceImport := t.awaitServiceImport(ctx, &client, t.helloService.Name, false,\n\t\t\t\tfunc(g Gomega, serviceImport *v1beta1.ServiceImport) {\n\t\t\t\t\tg.Expect(serviceImport.Spec.IPs).ToNot(BeEmpty(), \"ServiceImport on cluster %q does not contain an IP\", client.name)\n\t\t\t\t})\n\n\t\t\tBy(fmt.Sprintf(\"Found ServiceImport on cluster %q with clusterset IPs %v\",\n\t\t\t\tclient.name, strings.Join(serviceImport.Spec.IPs, \",\")))\n\n\t\t\tfor _, clusterSetIP := range serviceImport.Spec.IPs {\n\t\t\t\tcommand := []string{\"sh\", \"-c\", fmt.Sprintf(\"nslookup -type=%s %s.%s.svc.%s.\",\n\t\t\t\t\tdnsRecordTypeOf(ipFamilyOf(clusterSetIP)), t.helloService.Name, t.namespace, dnsDomain)}\n\n\t\t\t\tBy(fmt.Sprintf(\"Executing %s command %q on cluster %q\", ipFamilyOf(clusterSetIP),\n\t\t\t\t\tstrings.Join(command, \" \"), client.name))\n\n\t\t\t\tt.awaitCmdOutputMatches(&client, command, clusterSetIP, 1, reportNonConformant(\"\"))\n\t\t\t}\n\t\t}\n\t})\n\n\tSpecify(\"A DNS SRV query of the <service>.<ns>.svc.\"+dnsDomain+\" domain for a ClusterIP service should return valid SRV \"+\n\t\t\"records\", func() {\n\t\tAddReportEntry(SpecRefReportEntry, \"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#dns\")\n\n\t\tdomainName := fmt.Sprintf(\"%s.%s.svc.%s\", t.helloService.Name, t.namespace, dnsDomain)\n\n\t\tfor _, client := range clients {\n\t\t\texpSRVRecs := []srvRecord{{\n\t\t\t\tport:       t.helloService.Spec.Ports[0].Port,\n\t\t\t\tdomainName: domainName,\n\t\t\t}}\n\n\t\t\tsrvRecs := t.expectSRVRecords(&client, domainName, len(expSRVRecs))\n\n\t\t\tExpect(srvRecs).To(Equal(expSRVRecs), reportNonConformant(\n\t\t\t\tfmt.Sprintf(\"Received SRV records %v do not match the expected records %v\", srvRecs, expSRVRecs)))\n\t\t}\n\t})\n\n\tSpecify(\"DNS lookups of the <service>.<ns>.svc.cluster.local domain for a ClusterIP service should only resolve \"+\n\t\t\"local services\", func(ctx context.Context) {\n\t\tAddReportEntry(SpecRefReportEntry, \"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#dns\")\n\n\t\tBy(fmt.Sprintf(\"Retrieving local Service on cluster %q\", clients[0].name))\n\n\t\tvar resolvedIP string\n\n\t\tEventually(func(ctx context.Context) string {\n\t\t\tsvc, err := clients[0].k8s.CoreV1().Services(t.namespace).Get(ctx, t.helloService.Name, metav1.GetOptions{})\n\t\t\tExpect(err).ToNot(HaveOccurred(), \"Error retrieving the local Service\")\n\n\t\t\tresolvedIP = svc.Spec.ClusterIP\n\n\t\t\treturn resolvedIP\n\t\t}).WithContext(ctx).Within(20*time.Second).ProbeEvery(1*time.Second).ShouldNot(BeEmpty(), \"The service was not assigned a cluster IP\")\n\n\t\tBy(fmt.Sprintf(\"Found local Service cluster IP %q\", resolvedIP))\n\n\t\t// Add trailing dot to prevent search domain from being appended\n\t\tcommand := []string{\"sh\", \"-c\", fmt.Sprintf(\"nslookup -type=%s %s.%s.svc.cluster.local.\",\n\t\t\tdnsRecordTypeOf(ipFamilyOf(resolvedIP)), t.helloService.Name, t.namespace)}\n\n\t\tBy(fmt.Sprintf(\"Executing command %q on cluster %q\", strings.Join(command, \" \"), clients[0].name))\n\n\t\tt.awaitCmdOutputMatches(&clients[0], command, resolvedIP, 1, reportNonConformant(\"\"))\n\t})\n})\n\nfunc (t *testDriver) expectSRVRecords(c *clusterClients, domainName string, expectedCount int) []srvRecord {\n\t// Add trailing dot to prevent search domain from being appended\n\tcommand := []string{\"sh\", \"-c\", \"nslookup -type=SRV \" + domainName + \".\"}\n\n\tBy(fmt.Sprintf(\"Executing command %q on cluster %q\", strings.Join(command, \" \"), c.name))\n\n\tvar srvRecs []srvRecord\n\n\tEventually(func(g Gomega) {\n\t\tsrvRecs = parseSRVRecords(t.execCmdOnRequestPod(c, command))\n\t\tg.Expect(srvRecs).To(HaveLen(expectedCount),\n\t\t\tfmt.Sprintf(\"Expected %d SRV records but got %d: %v\", expectedCount, len(srvRecs), srvRecs))\n\t}, 20, 1).Should(Succeed(), reportNonConformant(\"\"))\n\n\treturn srvRecs\n}\n\n// Match SRV records from nslookup of the form:\n//\n//\thello.mcs-conformance-1686874467.svc.clusterset.local\tservice = 0 50 42 hello.mcs-conformance-1686874467.svc.clusterset.local\n//\n// to extract the port and target domain name (the last two tokens)\nvar srvRecordRegEx = regexp.MustCompile(`.*=\\s*\\d*\\s*\\d*\\s*(\\d*)\\s*([a-zA-Z0-9-.]*)`)\n\ntype srvRecord struct {\n\tport       int32\n\tdomainName string\n}\n\nfunc (s srvRecord) String() string {\n\treturn fmt.Sprintf(\"port:%d, domainName:%q\", s.port, s.domainName)\n}\n\nfunc parseSRVRecords(str string) []srvRecord {\n\tvar recs []srvRecord\n\n\tmatches := srvRecordRegEx.FindAllStringSubmatch(str, -1)\n\tfor i := range matches {\n\t\t// First match at index 0 is the full text that was matched; index 1 is the port and index 2 is the domain name.\n\t\tport, _ := strconv.ParseInt(matches[i][1], 10, 32)\n\t\tdomainName := matches[i][2]\n\t\t// Strip trailing period from FQDN (some nslookup versions include it)\n\t\tdomainName = strings.TrimSuffix(domainName, \".\")\n\t\trecs = append(recs, srvRecord{\n\t\t\tport:       int32(port),\n\t\t\tdomainName: domainName,\n\t\t})\n\t}\n\n\treturn recs\n}\n"
  },
  {
    "path": "conformance/conformance_suite.go",
    "content": "/*\nCopyright 2023 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage conformance\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"slices\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n\t\"github.com/onsi/gomega/types\"\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tdiscoveryv1 \"k8s.io/api/discovery/v1\"\n\tapierrors \"k8s.io/apimachinery/pkg/api/errors\"\n\t\"k8s.io/apimachinery/pkg/api/meta\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/client-go/kubernetes\"\n\trest \"k8s.io/client-go/rest\"\n\t\"k8s.io/client-go/tools/clientcmd\"\n\tk8snet \"k8s.io/utils/net\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n\tmcsclient \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned\"\n)\n\ntype clusterClients struct {\n\tname string\n\tk8s  kubernetes.Interface\n\tmcs  mcsclient.Interface\n\trest *rest.Config\n}\n\nvar (\n\tcontexts                         string\n\tclients                          []clusterClients\n\tloadingRules                     *clientcmd.ClientConfigLoadingRules\n\tskipVerifyEndpointSliceManagedBy bool\n\tdnsDomain                        string\n\torganization                     string\n\tproject                          string\n\tversion                          string\n\turl                              string\n)\n\n// TestConformance runs the conformance test.\nfunc TestConformance(t *testing.T) {\n\tRegisterFailHandler(Fail)\n\tRunSpecs(t, \"Conformance Suite\")\n}\n\nfunc init() {\n\tloadingRules = clientcmd.NewDefaultClientConfigLoadingRules()\n\tloadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig\n\tflag.StringVar(&loadingRules.ExplicitPath, \"kubeconfig\", \"\", \"absolute path(s) to the kubeconfig file(s)\")\n\tflag.StringVar(&contexts, \"contexts\", \"\", \"comma-separated list of contexts to use\")\n\tflag.BoolVar(&skipVerifyEndpointSliceManagedBy, \"skip-verify-eps-managed-by\", false,\n\t\tfmt.Sprintf(\"The MSC spec states that any EndpointSlice created by an mcs-controller must be marked as managed by \"+\n\t\t\t\"the mcs-controller. By default, the conformance test verifies that the %q label on MCS EndpointSlices is not equal to %q. \"+\n\t\t\t\"However with some implementations, MCS EndpointSlices may be created and managed by K8s. If this flag is set to true, \"+\n\t\t\t\"the test only verifies the presence of the label.\",\n\t\t\tdiscoveryv1.LabelManagedBy, K8sEndpointSliceManagedByName))\n\tflag.StringVar(&dnsDomain, \"dns-domain\", \"clusterset.local\", \"The DNS domain suffix used for multi-cluster services. \"+\n\t\t\"The default is \\\"clusterset.local\\\" as specified by the MCS spec, but some implementations may use a custom domain.\")\n\tflag.StringVar(&organization, \"organization\", \"\", \"Name of the organization responsible for the MCS implementation\")\n\tflag.StringVar(&project, \"project\", \"\", \"Name of the MCS implementation project being tested\")\n\tflag.StringVar(&version, \"version\", \"\", \"Version of the MCS implementation being tested\")\n\tflag.StringVar(&url, \"url\", \"\", \"A URL pointing to the MCS implementation project or documentation\")\n}\n\nvar _ = BeforeSuite(func(ctx context.Context) {\n\tExpect(setupClients(ctx)).To(Succeed(), \"Test suite set up failed\")\n})\n\nfunc setupClients(ctx context.Context) error {\n\tsplitContexts := strings.Split(contexts, \",\")\n\tclients = make([]clusterClients, len(splitContexts))\n\taccumulatedErrors := []error{}\n\n\tfor i, kubeContext := range splitContexts {\n\t\terr := func() error {\n\t\t\toverrides := clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults}\n\t\t\toverrides.CurrentContext = kubeContext\n\n\t\t\tclientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &overrides)\n\n\t\t\trawConfig, err := clientConfig.RawConfig()\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error setting up a Kubernetes API client on context %s: %w\", kubeContext, err)\n\t\t\t}\n\n\t\t\tname := kubeContext\n\t\t\tif name == \"\" {\n\t\t\t\tname = rawConfig.CurrentContext\n\t\t\t}\n\n\t\t\tconfigContext, ok := rawConfig.Contexts[name]\n\t\t\tif ok {\n\t\t\t\tname = configContext.Cluster\n\t\t\t}\n\n\t\t\trestConfig, err := clientConfig.ClientConfig()\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error setting up a Kubernetes API client on context %s: %w\", name, err)\n\t\t\t}\n\n\t\t\tk8sClient, err := kubernetes.NewForConfig(restConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error setting up a Kubernetes API client on context %s: %w\", name, err)\n\t\t\t}\n\n\t\t\tmcsClient, err := mcsclient.NewForConfig(restConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error setting up an MCS API client on context %s: %w\", name, err)\n\t\t\t}\n\n\t\t\tif _, err := mcsClient.MulticlusterV1beta1().ServiceExports(\"\").List(ctx, metav1.ListOptions{}); err != nil {\n\t\t\t\treturn fmt.Errorf(\"error listing ServiceExports on context %s: %w. Is the MCS API installed?\", name, err)\n\t\t\t}\n\n\t\t\tif _, err := mcsClient.MulticlusterV1beta1().ServiceImports(\"\").List(ctx, metav1.ListOptions{}); err != nil {\n\t\t\t\treturn fmt.Errorf(\"error listing ServiceImports on context %s: %w. Is the MCS API installed?\", name, err)\n\t\t\t}\n\n\t\t\tclients[i] = clusterClients{name: name, k8s: k8sClient, mcs: mcsClient, rest: restConfig}\n\n\t\t\treturn nil\n\t\t}()\n\n\t\taccumulatedErrors = append(accumulatedErrors, err)\n\t}\n\n\treturn errors.Join(accumulatedErrors...)\n}\n\ntype testDriver struct {\n\tnamespace          string\n\thelloService       *corev1.Service\n\thelloServiceExport *v1beta1.ServiceExport\n\thelloDeployment    *appsv1.Deployment\n\trequestPod         *corev1.Pod\n\tautoExportService  bool\n}\n\nfunc newTestDriver() *testDriver {\n\tt := &testDriver{}\n\n\tBeforeEach(func() {\n\t\tt.namespace = fmt.Sprintf(\"mcs-conformance-%v\", rand.Uint32())\n\t\tt.helloService = newHelloService()\n\t\tt.helloServiceExport = newHelloServiceExport()\n\t\tt.helloDeployment = newHelloDeployment()\n\t\tt.requestPod = newRequestPod()\n\t\tt.autoExportService = true\n\t})\n\n\tJustBeforeEach(func(ctx context.Context) {\n\t\tExpect(clients).ToNot(BeEmpty())\n\n\t\t// Set up the shared namespace\n\t\tfor _, client := range clients {\n\t\t\t_, err := client.k8s.CoreV1().Namespaces().Create(ctx, &corev1.Namespace{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{Name: t.namespace},\n\t\t\t}, metav1.CreateOptions{})\n\t\t\tExpect(err).ToNot(HaveOccurred())\n\t\t}\n\n\t\t// Set up the remote service (the first cluster is considered to be the remote)\n\t\tt.helloService = t.deployHelloService(ctx, &clients[0], t.helloService)\n\n\t\t// Start the request pod on all clusters\n\t\tfor _, client := range clients {\n\t\t\tt.startRequestPod(ctx, client)\n\t\t}\n\n\t\tif t.autoExportService {\n\t\t\tt.createServiceExport(ctx, &clients[0], t.helloServiceExport)\n\t\t}\n\t})\n\n\tAfterEach(func(ctx context.Context) {\n\t\t// Clean up the shared namespace\n\t\tfor _, client := range clients {\n\t\t\terr := client.k8s.CoreV1().Namespaces().Delete(ctx, t.namespace, metav1.DeleteOptions{})\n\t\t\tif !apierrors.IsNotFound(err) {\n\t\t\t\tExpect(err).ToNot(HaveOccurred())\n\t\t\t}\n\t\t}\n\t})\n\n\treturn t\n}\n\nfunc (t *testDriver) createServiceExport(ctx context.Context, c *clusterClients, serviceExport *v1beta1.ServiceExport) {\n\t_, err := c.mcs.MulticlusterV1beta1().ServiceExports(t.namespace).Create(\n\t\tctx, serviceExport, metav1.CreateOptions{})\n\tExpect(err).ToNot(HaveOccurred())\n\n\tBy(fmt.Sprintf(\"Service \\\"%s/%s\\\" exported on cluster %q\", t.namespace, helloServiceName, c.name))\n}\n\nfunc (t *testDriver) deleteServiceExport(ctx context.Context, c *clusterClients) {\n\tExpect(c.mcs.MulticlusterV1beta1().ServiceExports(t.namespace).Delete(ctx, helloServiceName,\n\t\tmetav1.DeleteOptions{})).ToNot(HaveOccurred())\n\n\tBy(fmt.Sprintf(\"Service \\\"%s/%s\\\" unexported on cluster %q\", t.namespace, helloServiceName, c.name))\n}\n\nfunc (t *testDriver) deployHelloService(ctx context.Context, c *clusterClients, service *corev1.Service) *corev1.Service {\n\tif t.helloDeployment != nil {\n\t\t_, err := c.k8s.AppsV1().Deployments(t.namespace).Create(ctx, t.helloDeployment, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t}\n\n\tdeployed, err := c.k8s.CoreV1().Services(t.namespace).Create(ctx, service, metav1.CreateOptions{})\n\tExpect(err).ToNot(HaveOccurred())\n\n\tBy(fmt.Sprintf(\"Service \\\"%s/%s\\\" with IP families %v deployed on cluster %q\", deployed.Namespace, deployed.Name,\n\t\tdeployed.Spec.IPFamilies, c.name))\n\n\treturn deployed\n}\n\nfunc (t *testDriver) getServiceImport(ctx context.Context, c *clusterClients, name string) *v1beta1.ServiceImport {\n\tsi, err := c.mcs.MulticlusterV1beta1().ServiceImports(t.namespace).Get(ctx, name, metav1.GetOptions{})\n\tif apierrors.IsNotFound(err) || errors.Is(err, context.DeadlineExceeded) ||\n\t\t(err != nil && strings.Contains(err.Error(), \"rate limiter\")) {\n\t\treturn nil\n\t}\n\n\tExpect(err).ToNot(HaveOccurred(), \"Error retrieving ServiceImport\")\n\n\treturn si\n}\n\nfunc (t *testDriver) awaitServiceImport(ctx context.Context, c *clusterClients, name string, reportNonConformanceOnMissing bool,\n\tverify func(Gomega, *v1beta1.ServiceImport)) *v1beta1.ServiceImport {\n\tvar serviceImport *v1beta1.ServiceImport\n\n\tBy(fmt.Sprintf(\"Retrieving ServiceImport for %q on cluster %q\", name, c.name))\n\n\tEventually(func(g Gomega, ctx context.Context) {\n\t\tsi := t.getServiceImport(ctx, c, name)\n\n\t\tmissingMsg := fmt.Sprintf(\"ServiceImport was not found on cluster %q\", c.name)\n\n\t\tvar missing any = missingMsg\n\t\tif reportNonConformanceOnMissing {\n\t\t\tmissing = reportNonConformant(missingMsg)\n\t\t}\n\n\t\tg.Expect(si).NotTo(BeNil(), missing)\n\n\t\tserviceImport = si\n\n\t\tif verify != nil {\n\t\t\tverify(g, serviceImport)\n\t\t}\n\n\t\t// The final run succeeded so cancel any prior non-conformance reported.\n\t\tcancelNonConformanceReport()\n\t}).WithContext(ctx).Within(20 * time.Second).WithPolling(100 * time.Millisecond).Should(Succeed())\n\n\treturn serviceImport\n}\n\nfunc (t *testDriver) awaitServiceImportIPFamilies(ctx context.Context, c *clusterClients) []corev1.IPFamily {\n\tserviceImport := t.awaitServiceImport(ctx, c, t.helloService.Name, false,\n\t\tfunc(g Gomega, serviceImport *v1beta1.ServiceImport) {\n\t\t\tg.Expect(serviceImport.Spec.IPFamilies).NotTo(BeEmpty(),\n\t\t\t\t\"ServiceImport on cluster %q does not contain an IP family\", c.name)\n\t\t})\n\n\treturn serviceImport.Spec.IPFamilies\n}\n\nfunc (t *testDriver) awaitNoServiceImport(ctx context.Context, c *clusterClients, name, nonConformanceMsg string) {\n\tEventually(func() bool {\n\t\t_, err := c.mcs.MulticlusterV1beta1().ServiceImports(t.namespace).Get(ctx, name, metav1.GetOptions{})\n\t\tif apierrors.IsNotFound(err) {\n\t\t\treturn true\n\t\t}\n\n\t\tExpect(err).ToNot(HaveOccurred())\n\n\t\treturn false\n\t}, 20*time.Second, 100*time.Millisecond).Should(BeTrue(), reportNonConformant(nonConformanceMsg))\n}\n\nfunc (t *testDriver) ensureServiceImport(ctx context.Context, c *clusterClients, name, nonConformanceMsg string) {\n\tConsistently(func() error {\n\t\t_, err := c.mcs.MulticlusterV1beta1().ServiceImports(t.namespace).Get(ctx, name, metav1.GetOptions{})\n\t\treturn err\n\t}, 5*time.Second, 100*time.Millisecond).ShouldNot(HaveOccurred(), reportNonConformant(nonConformanceMsg))\n}\n\nfunc (t *testDriver) ensureNoServiceImport(ctx context.Context, c *clusterClients, name, nonConformanceMsg string) {\n\tConsistently(func() bool {\n\t\t_, err := c.mcs.MulticlusterV1beta1().ServiceImports(t.namespace).Get(ctx, name, metav1.GetOptions{})\n\t\treturn apierrors.IsNotFound(err)\n\t}, 5*time.Second, 100*time.Millisecond).Should(BeTrue(), reportNonConformant(nonConformanceMsg))\n}\n\nfunc (t *testDriver) awaitServiceExportCondition(ctx context.Context, c *clusterClients, condType v1beta1.ServiceExportConditionType,\n\twantStatus metav1.ConditionStatus) {\n\tEventually(func() bool {\n\t\tse, err := c.mcs.MulticlusterV1beta1().ServiceExports(t.namespace).Get(ctx, helloServiceName, metav1.GetOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\n\t\tcond := meta.FindStatusCondition(se.Status.Conditions, string(condType))\n\t\treturn cond != nil && cond.Status == wantStatus\n\t}, 20*time.Second, 100*time.Millisecond).Should(BeTrue(),\n\t\treportNonConformant(fmt.Sprintf(\"The %s condition was not set to %s\", condType, wantStatus)))\n}\n\nfunc (t *testDriver) startRequestPod(ctx context.Context, client clusterClients) {\n\t_, err := client.k8s.CoreV1().Pods(t.namespace).Create(ctx, t.requestPod, metav1.CreateOptions{})\n\tExpect(err).ToNot(HaveOccurred())\n\n\tEventually(func() error {\n\t\tpod, err := client.k8s.CoreV1().Pods(t.namespace).Get(ctx, t.requestPod.Name, metav1.GetOptions{})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif pod.Status.Phase != corev1.PodRunning {\n\t\t\treturn fmt.Errorf(\"pod is not running yet, current status %v\", pod.Status.Phase)\n\t\t}\n\n\t\treturn nil\n\t}, 20, 1).Should(Succeed())\n}\n\nfunc (t *testDriver) execCmdOnRequestPod(c *clusterClients, command []string) string {\n\tstdout, _, _ := execCmd(c.k8s, c.rest, t.requestPod.Name, t.namespace, command)\n\treturn string(stdout)\n}\n\nfunc (t *testDriver) awaitCmdOutputMatches(c *clusterClients, command []string, expected any, nIter int, msg func() string) {\n\tvar matcher types.GomegaMatcher\n\n\tswitch v := expected.(type) {\n\tcase string:\n\t\tmatcher = ContainSubstring(v)\n\tcase types.GomegaMatcher:\n\t\tmatcher = v\n\t}\n\n\tEventually(func(g Gomega) {\n\t\toutput := t.execCmdOnRequestPod(c, command)\n\t\tg.Expect(output).To(matcher, \"Command output\")\n\t}).Within(time.Duration(20*int64(nIter))*time.Second).ProbeEvery(time.Second).MustPassRepeatedly(nIter).Should(Succeed(), msg)\n}\n\nfunc (t *testDriver) awaitServicePodIP(ctx context.Context, c *clusterClients) string {\n\tBy(fmt.Sprintf(\"Awaiting service deployment pod IP on cluster %q\", c.name))\n\n\tservicePodIP := \"\"\n\n\tEventually(func(g Gomega, ctx context.Context) {\n\t\tpods, err := c.k8s.CoreV1().Pods(t.namespace).List(ctx, metav1.ListOptions{\n\t\t\tLabelSelector: metav1.FormatLabelSelector(newHelloDeployment().Spec.Selector),\n\t\t})\n\n\t\tg.Expect(err).NotTo(HaveOccurred())\n\t\tg.Expect(pods.Items).NotTo(BeEmpty())\n\n\t\tservicePodIP = pods.Items[0].Status.PodIP\n\t\tg.Expect(servicePodIP).NotTo(BeEmpty(), \"Service deployment pod was not allocated an IP\")\n\t}).WithContext(ctx).Within(20 * time.Second).WithPolling(100 * time.Millisecond).Should(Succeed())\n\n\tBy(fmt.Sprintf(\"Retrieved service deployment pod IP %q\", servicePodIP))\n\n\treturn servicePodIP\n}\n\nfunc (t *testDriver) execPortConnectivityCommand(ctx context.Context, port int, matchStr string, nIter int) {\n\tfor _, client := range clients {\n\t\tfor _, ipFamily := range t.awaitServiceImportIPFamilies(ctx, &client) {\n\t\t\tserviceFQDN := fmt.Sprintf(\"%s.%s.svc.%s\", t.helloService.Name, t.namespace, dnsDomain)\n\t\t\tcommand := []string{\"sh\", \"-c\", ncCommand(ipFamily, serviceFQDN, port)}\n\n\t\t\tBy(fmt.Sprintf(\"Executing %s command %q on cluster %q\", ipFamily, strings.Join(command, \" \"), client.name))\n\n\t\t\tt.awaitCmdOutputMatches(&client, command, matchStr, nIter, reportNonConformant(\"\"))\n\t\t}\n\t}\n}\n\ntype twoClusterTestDriver struct {\n\t*testDriver\n\thelloService2       *corev1.Service\n\thelloServiceExport2 *v1beta1.ServiceExport\n}\n\nfunc newTwoClusterTestDriver(t *testDriver) *twoClusterTestDriver {\n\ttt := &twoClusterTestDriver{testDriver: t}\n\n\tBeforeEach(func() {\n\t\trequireTwoClusters()\n\n\t\ttt.helloService2 = newHelloService()\n\t\ttt.helloServiceExport2 = newHelloServiceExport()\n\t\tt.autoExportService = false\n\t})\n\n\tJustBeforeEach(func(ctx context.Context) {\n\t\tt.createServiceExport(ctx, &clients[0], t.helloServiceExport)\n\n\t\t// The conflict resolution policy in the MCS spec (KEP 1645) allows an implementation to favor maintaining\n\t\t// service continuity and avoiding potentially disruptive changes, as such, an implementation may choose the\n\t\t// first observed exported service when resolving conflicts. To support this, verify the ServiceImport is\n\t\t// created on the first cluster prior to deploying on the second cluster.\n\t\tt.awaitServiceImport(ctx, &clients[0], helloServiceName, false, nil)\n\n\t\t// Delay a little before deploying on the second cluster to ensure the first cluster's ServiceExport timestamp\n\t\t// is older so conflict checking is deterministic for implementations that use the timestamp when resolving conflicts.\n\t\t// Make the delay at least 1 sec as creation timestamps have seconds granularity.\n\t\ttime.Sleep(1100 * time.Millisecond)\n\n\t\tt.deployHelloService(ctx, &clients[1], tt.helloService2)\n\t\tt.createServiceExport(ctx, &clients[1], tt.helloServiceExport2)\n\t})\n\n\treturn tt\n}\n\nfunc toMCSPorts(from []corev1.ServicePort) []v1beta1.ServicePort {\n\tvar mcsPorts []v1beta1.ServicePort\n\n\tfor _, port := range from {\n\t\tmcsPorts = append(mcsPorts, v1beta1.ServicePort{\n\t\t\tName:        port.Name,\n\t\t\tProtocol:    port.Protocol,\n\t\t\tPort:        port.Port,\n\t\t\tAppProtocol: port.AppProtocol,\n\t\t})\n\t}\n\n\treturn sortMCSPorts(mcsPorts)\n}\n\nfunc sortMCSPorts(p []v1beta1.ServicePort) []v1beta1.ServicePort {\n\tslices.SortFunc(p, func(a, b v1beta1.ServicePort) int {\n\t\treturn cmp.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name))\n\t})\n\n\treturn p\n}\n\nfunc requireTwoClusters() {\n\tif len(clients) < 2 {\n\t\tSkip(\"This test requires at least 2 clusters - skipping\")\n\t}\n}\n\nfunc addressTypeOf(f corev1.IPFamily) discoveryv1.AddressType {\n\tif f == corev1.IPv4Protocol {\n\t\treturn discoveryv1.AddressTypeIPv4\n\t}\n\n\treturn discoveryv1.AddressTypeIPv6\n}\n\nfunc dnsRecordTypeOf(f corev1.IPFamily) string {\n\tif f == corev1.IPv4Protocol {\n\t\treturn \"A\"\n\t}\n\n\treturn \"AAAA\"\n}\n\nfunc ipFamilyOf(ip string) corev1.IPFamily {\n\tf := k8snet.IPFamilyOfString(ip)\n\tExpect(f).NotTo(Equal(k8snet.IPFamilyUnknown))\n\n\tif f == k8snet.IPv4 {\n\t\treturn corev1.IPv4Protocol\n\t}\n\n\treturn corev1.IPv6Protocol\n}\n\n// ncCommand creates a shell command that connects to a service using nc with the specified IP family.\n// The netshoot image provides nc which supports -4/-6 flags for forcing IP family.\nfunc ncCommand(ipFamily corev1.IPFamily, serviceFQDN string, port int) string {\n\tipFlag := \"-4\"\n\tif ipFamily == corev1.IPv6Protocol {\n\t\tipFlag = \"-6\"\n\t}\n\n\t// For IPv6, nc -6 with hostname seems to hang in IPv6-only environments\n\t// Fall back to explicit DNS resolution and pass IP directly to nc\n\tif ipFamily == corev1.IPv6Protocol {\n\t\tfqdnWithDot := serviceFQDN + \".\"\n\t\treturn fmt.Sprintf(\n\t\t\t\"addr=$(nslookup -type=AAAA %s 2>&1 | grep '^Address:' | grep -v '#' | tail -1 | awk '{print $2}'); \"+\n\t\t\t\t\"if [ -z \\\"$addr\\\" ]; then echo 'No IPv6 address found'; exit 1; fi; \"+\n\t\t\t\t\"echo hi | nc -v -w 2 $addr %d 2>&1\",\n\t\t\tfqdnWithDot, port)\n\t}\n\n\treturn fmt.Sprintf(\"echo hi | nc -v -w 2 %s %s %d 2>&1\", ipFlag, serviceFQDN, port)\n}\n"
  },
  {
    "path": "conformance/conformance_suite_test.go",
    "content": "/*\nCopyright 2024 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage conformance_test\n\nimport (\n\t\"testing\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n)\n\nfunc TestConformance(t *testing.T) {\n\tRegisterFailHandler(Fail)\n\tRunSpecs(t, \"Conformance Suite\")\n}\n"
  },
  {
    "path": "conformance/connectivity.go",
    "content": "/*\nCopyright 2023 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage conformance\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/util/intstr\"\n)\n\nvar _ = Describe(\"\", func() {\n\tt := newTestDriver()\n\n\tBeforeEach(func() {\n\t\tt.autoExportService = false\n\t})\n\n\tContext(\"Connectivity to a service that is not exported\", func() {\n\t\tIt(\"should be inaccessible\", Label(RequiredLabel), func() {\n\t\t\tAddReportEntry(SpecRefReportEntry, \"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#exporting-services\")\n\t\t\tBy(\"attempting to access the remote service\", func() {\n\t\t\t\tBy(\"issuing a request from all clusters\", func() {\n\t\t\t\t\tserviceFQDN := fmt.Sprintf(\"%s.%s.svc.%s\", t.helloService.Name, t.namespace, dnsDomain)\n\n\t\t\t\t\t// Test all IP families for completeness\n\t\t\t\t\tfor _, ipFamily := range t.helloService.Spec.IPFamilies {\n\t\t\t\t\t\tcommand := []string{\"sh\", \"-c\", ncCommand(ipFamily, serviceFQDN, 42)}\n\n\t\t\t\t\t\t// Run on all clusters\n\t\t\t\t\t\tfor _, client := range clients {\n\t\t\t\t\t\t\t// Repeat multiple times\n\t\t\t\t\t\t\tfor i := 0; i < 20; i++ {\n\t\t\t\t\t\t\t\tExpect(t.execCmdOnRequestPod(&client, command)).NotTo(ContainSubstring(\"pod ip\"), reportNonConformant(\"\"))\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\tContext(\"Connectivity to an exported ClusterIP service\", func() {\n\t\tIt(\"should be accessible through DNS\", Label(OptionalLabel, ConnectivityLabel, ClusterIPLabel), func(ctx context.Context) {\n\t\t\tAddReportEntry(SpecRefReportEntry, \"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#dns\")\n\t\t\tBy(\"Exporting the service\", func() {\n\t\t\t\t// On the \"remote\" cluster\n\t\t\t\tt.createServiceExport(ctx, &clients[0], newHelloServiceExport())\n\t\t\t})\n\t\t\tBy(\"Issuing a request from all clusters\", func() {\n\t\t\t\tt.execPortConnectivityCommand(ctx, 42, \"pod ip\", 1)\n\t\t\t})\n\t\t})\n\t})\n\n\tContext(\"Connectivity to a ClusterIP service existing in two clusters but exported from one\", func() {\n\t\tBeforeEach(func() {\n\t\t\trequireTwoClusters()\n\t\t})\n\n\t\tJustBeforeEach(func(ctx context.Context) {\n\t\t\tt.deployHelloService(ctx, &clients[1], newHelloService())\n\t\t})\n\n\t\tIt(\"should only access the exporting cluster\", Label(OptionalLabel, ConnectivityLabel, ClusterIPLabel), func(ctx context.Context) {\n\t\t\tAddReportEntry(SpecRefReportEntry, \"https://github.com/kubernetes/enhancements/blob/master/keps/sig-multicluster/1645-multi-cluster-services-api/README.md#exporting-services\")\n\n\t\t\tBy(fmt.Sprintf(\"Exporting the service on cluster %q\", clients[0].name))\n\n\t\t\tt.createServiceExport(ctx, &clients[0], newHelloServiceExport())\n\n\t\t\tservicePodIP := t.awaitServicePodIP(ctx, &clients[0])\n\n\t\t\tt.execPortConnectivityCommand(ctx, 42, servicePodIP, 10)\n\t\t})\n\t})\n\n\tContext(\"Connectivity to exported services with same port name but different port numbers on each cluster\", func() {\n\t\ttt := newTwoClusterTestDriver(t)\n\n\t\tBeforeEach(func() {\n\t\t\ttt.helloService2.Spec.Ports = []corev1.ServicePort{\n\t\t\t\t{Name: \"tcp\", Port: 4242, Protocol: corev1.ProtocolTCP, TargetPort: intstr.FromInt32(42)},\n\t\t\t}\n\t\t})\n\n\t\tIt(\"should only route traffic to the cluster that exposes the port\", Label(OptionalLabel, ConnectivityLabel, ClusterIPLabel, StrictPortConflictLabel), func(ctx context.Context) {\n\t\t\tAddReportEntry(SpecRefReportEntry, \"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#service-port\")\n\n\t\t\tservicePodIP := t.awaitServicePodIP(ctx, &clients[0])\n\n\t\t\tt.execPortConnectivityCommand(ctx, 42, servicePodIP, 10)\n\t\t})\n\t})\n\n\tContext(\"Connectivity to exported services with different port name on each cluster\", func() {\n\t\ttt := newTwoClusterTestDriver(t)\n\n\t\tBeforeEach(func() {\n\t\t\ttt.helloService2.Spec.Ports = []corev1.ServicePort{\n\t\t\t\t{Name: \"tcp2\", Port: 4242, Protocol: corev1.ProtocolTCP, TargetPort: intstr.FromInt32(42)},\n\t\t\t}\n\t\t})\n\n\t\tIt(\"should route traffic to each port only to the cluster that exposes it\", Label(OptionalLabel, ConnectivityLabel, ClusterIPLabel, StrictPortConflictLabel), func(ctx context.Context) {\n\t\t\tAddReportEntry(SpecRefReportEntry, \"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#service-port\")\n\n\t\t\tcluster1PodIP := t.awaitServicePodIP(ctx, &clients[0])\n\t\t\tcluster2PodIP := t.awaitServicePodIP(ctx, &clients[1])\n\n\t\t\tt.execPortConnectivityCommand(ctx, 42, cluster1PodIP, 10)\n\t\t\tt.execPortConnectivityCommand(ctx, 4242, cluster2PodIP, 10)\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "conformance/endpoint_slice.go",
    "content": "/*\nCopyright 2024 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage conformance\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n\tdiscoveryv1 \"k8s.io/api/discovery/v1\"\n\tapierrors \"k8s.io/apimachinery/pkg/api/errors\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\n// K8sEndpointSliceManagedByName is the name used for endpoint slices managed by the Kubernetes controller\nconst K8sEndpointSliceManagedByName = \"endpointslice-controller.k8s.io\"\n\nvar _ = Describe(\"\", Label(OptionalLabel, EndpointSliceLabel), func() {\n\tt := newTestDriver()\n\n\tSpecifyWithSpecRef(\"Exporting a service should create an MCS EndpointSlice in the service's namespace in each cluster with the \"+\n\t\t\"required MCS labels. Unexporting should delete the EndpointSlice.\",\n\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#using-endpointslice-objects-to-track-endpoints\",\n\t\tfunc(ctx context.Context) {\n\t\t\tendpointSliceNames := make([][]string, len(clients))\n\n\t\t\tfor i, client := range clients {\n\t\t\t\tfor _, ipFamily := range t.awaitServiceImportIPFamilies(ctx, &client) {\n\t\t\t\t\teps := t.awaitMCSEndpointSlice(ctx, &client, addressTypeOf(ipFamily), nil, reportNonConformant(fmt.Sprintf(\n\t\t\t\t\t\t\"an MCS EndpointSlice was not found on cluster %q. An MCS EndpointSlice is identified by the presence \"+\n\t\t\t\t\t\t\t\"of the required MCS labels (%q and %q). \"+\n\t\t\t\t\t\t\t\"If the MCS implementation does not use MCS EndpointSlices, you can specify a Ginkgo label filter using \"+\n\t\t\t\t\t\t\t\"the %q label where appropriate to skip this test.\",\n\t\t\t\t\t\tclient.name, v1beta1.LabelServiceName, v1beta1.LabelSourceCluster, EndpointSliceLabel)))\n\n\t\t\t\t\tendpointSliceNames[i] = append(endpointSliceNames[i], eps.Name)\n\n\t\t\t\t\tExpect(eps.Labels).To(HaveKeyWithValue(v1beta1.LabelServiceName, t.helloService.Name),\n\t\t\t\t\t\treportNonConformant(fmt.Sprintf(\"the MCS EndpointSlice %q does not contain the %q label referencing the service name\",\n\t\t\t\t\t\t\teps.Name, v1beta1.LabelServiceName)))\n\n\t\t\t\t\tExpect(eps.Labels).To(HaveKey(discoveryv1.LabelManagedBy),\n\t\t\t\t\t\treportNonConformant(fmt.Sprintf(\"the MCS EndpointSlice %q does not contain the %q label\",\n\t\t\t\t\t\t\teps.Name, discoveryv1.LabelManagedBy)))\n\n\t\t\t\t\tif !skipVerifyEndpointSliceManagedBy {\n\t\t\t\t\t\tExpect(eps.Labels[discoveryv1.LabelManagedBy]).ToNot(Equal(K8sEndpointSliceManagedByName),\n\t\t\t\t\t\t\treportNonConformant(fmt.Sprintf(\"the MCS EndpointSlice's %q label must not reference %q\",\n\t\t\t\t\t\t\t\tdiscoveryv1.LabelManagedBy, K8sEndpointSliceManagedByName)))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tt.deleteServiceExport(ctx, &clients[0])\n\n\t\t\tfor i, client := range clients {\n\t\t\t\tfor _, name := range endpointSliceNames[i] {\n\t\t\t\t\tEventually(func(ctx context.Context) bool {\n\t\t\t\t\t\t_, err := client.k8s.DiscoveryV1().EndpointSlices(t.namespace).Get(ctx, name, metav1.GetOptions{})\n\t\t\t\t\t\treturn apierrors.IsNotFound(err)\n\t\t\t\t\t}).WithContext(ctx).Within(20*time.Second).ProbeEvery(100*time.Millisecond).Should(BeTrue(),\n\t\t\t\t\t\treportNonConformant(fmt.Sprintf(\"the EndpointSlice %q was not deleted on unexport from cluster %q\",\n\t\t\t\t\t\t\tname, client.name)))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n})\n\nfunc (t *testDriver) awaitMCSEndpointSlice(ctx context.Context, c *clusterClients, addressType discoveryv1.AddressType,\n\tverify func(Gomega, *discoveryv1.EndpointSlice), desc ...any) *discoveryv1.EndpointSlice {\n\tBy(fmt.Sprintf(\"Retrieving %s MCS EndpointSlice for the service on cluster %q\", addressType, c.name))\n\n\tvar endpointSlice *discoveryv1.EndpointSlice\n\n\thasLabel := func(eps *discoveryv1.EndpointSlice, label string) bool {\n\t\t_, exists := eps.Labels[label]\n\t\treturn exists\n\t}\n\n\tEventually(func(g Gomega, ctx context.Context) {\n\t\tlist, err := c.k8s.DiscoveryV1().EndpointSlices(t.namespace).List(ctx, metav1.ListOptions{})\n\t\tg.Expect(err).ToNot(HaveOccurred(), \"Error retrieving EndpointSlices\")\n\n\t\tendpointSlice = nil\n\n\t\tfor i := range list.Items {\n\t\t\teps := &list.Items[i]\n\n\t\t\tif hasLabel(eps, v1beta1.LabelServiceName) && hasLabel(eps, v1beta1.LabelSourceCluster) && eps.AddressType == addressType && len(eps.Endpoints) > 0 {\n\t\t\t\tendpointSlice = eps\n\n\t\t\t\tif verify != nil {\n\t\t\t\t\tverify(g, endpointSlice)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tg.Expect(endpointSlice).ToNot(BeNil(), desc...)\n\n\t\t// The final run succeeded so cancel any prior non-conformance reported.\n\t\tcancelNonConformanceReport()\n\t}).WithContext(ctx).Within(20 * time.Second).ProbeEvery(100 * time.Millisecond).Should(Succeed())\n\n\treturn endpointSlice\n}\n"
  },
  {
    "path": "conformance/framework.go",
    "content": "/*\nCopyright 2023 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage conformance\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"time\"\n\n\tv1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/client-go/kubernetes\"\n\t\"k8s.io/client-go/kubernetes/scheme\"\n\trest \"k8s.io/client-go/rest\"\n\t\"k8s.io/client-go/tools/remotecommand\"\n)\n\nfunc execCmd(k8s kubernetes.Interface, config *rest.Config, podName string, podNamespace string, command []string) ([]byte, []byte, error) {\n\treq := k8s.CoreV1().RESTClient().Post().Resource(\"pods\").Name(podName).Namespace(podNamespace).SubResource(\"exec\")\n\treq.VersionedParams(&v1.PodExecOptions{\n\t\tCommand: command,\n\t\tStdin:   false,\n\t\tStdout:  true,\n\t\tStderr:  true,\n\t\tTTY:     true,\n\t}, scheme.ParameterCodec)\n\n\texec, err := remotecommand.NewSPDYExecutor(config, \"POST\", req.URL())\n\tif err != nil {\n\t\treturn []byte{}, []byte{}, err\n\t}\n\n\tvar stdout, stderr bytes.Buffer\n\n\tctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)\n\tdefer cancel()\n\n\terr = exec.StreamWithContext(ctx, remotecommand.StreamOptions{\n\t\tStdin:  nil,\n\t\tStdout: &stdout,\n\t\tStderr: &stderr,\n\t})\n\tif err != nil {\n\t\treturn []byte{}, []byte{}, err\n\t}\n\n\treturn stdout.Bytes(), stderr.Bytes(), nil\n}\n"
  },
  {
    "path": "conformance/go.mod",
    "content": "module sigs.k8s.io/mcs-api/conformance\n\ngo 1.23.0\n\nrequire (\n\tgithub.com/onsi/ginkgo/v2 v2.21.0\n\tgithub.com/onsi/gomega v1.35.1\n\tgopkg.in/yaml.v3 v3.0.1\n\tk8s.io/api v0.32.5\n\tk8s.io/apimachinery v0.32.5\n\tk8s.io/client-go v0.32.5\n\tk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738\n\tsigs.k8s.io/mcs-api v0.3.0\n)\n\nreplace sigs.k8s.io/mcs-api => ..\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect\n\tgithub.com/emicklei/go-restful/v3 v3.11.0 // indirect\n\tgithub.com/fxamacker/cbor/v2 v2.7.0 // indirect\n\tgithub.com/go-logr/logr v1.4.2 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.21.0 // indirect\n\tgithub.com/go-openapi/jsonreference v0.20.2 // indirect\n\tgithub.com/go-openapi/swag v0.23.0 // indirect\n\tgithub.com/go-task/slim-sprig/v3 v3.0.0 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/google/gnostic-models v0.6.8 // indirect\n\tgithub.com/google/go-cmp v0.6.0 // indirect\n\tgithub.com/google/gofuzz v1.2.0 // indirect\n\tgithub.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/websocket v1.5.0 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/mailru/easyjson v0.7.7 // indirect\n\tgithub.com/moby/spdystream v0.5.0 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgithub.com/x448/float16 v0.8.4 // indirect\n\tgolang.org/x/net v0.30.0 // indirect\n\tgolang.org/x/oauth2 v0.23.0 // indirect\n\tgolang.org/x/sys v0.26.0 // indirect\n\tgolang.org/x/term v0.25.0 // indirect\n\tgolang.org/x/text v0.19.0 // indirect\n\tgolang.org/x/time v0.7.0 // indirect\n\tgolang.org/x/tools v0.26.0 // indirect\n\tgoogle.golang.org/protobuf v1.35.1 // indirect\n\tgopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect\n\tgopkg.in/inf.v0 v0.9.1 // indirect\n\tk8s.io/klog/v2 v2.130.1 // indirect\n\tk8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect\n\tsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect\n\tsigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect\n\tsigs.k8s.io/yaml v1.4.0 // indirect\n)\n"
  },
  {
    "path": "conformance/go.sum",
    "content": "github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=\ngithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=\ngithub.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=\ngithub.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=\ngithub.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=\ngithub.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=\ngithub.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=\ngithub.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=\ngithub.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=\ngithub.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=\ngithub.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=\ngithub.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=\ngithub.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=\ngithub.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=\ngithub.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=\ngithub.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=\ngithub.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\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-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=\ngithub.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=\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/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=\ngithub.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=\ngithub.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=\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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=\ngithub.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=\ngithub.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=\ngithub.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=\ngithub.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=\ngithub.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=\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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=\ngithub.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=\ngithub.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=\ngolang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=\ngolang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=\ngolang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=\ngolang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=\ngolang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=\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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=\ngolang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=\ngolang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=\ngolang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=\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-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=\ngolang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=\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=\ngoogle.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=\ngoogle.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=\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.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=\ngopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=\ngopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nk8s.io/api v0.32.5 h1:uqjjsYo1kTJr5NIcoIaP9F+TgXgADH7nKQx91FDAhtk=\nk8s.io/api v0.32.5/go.mod h1:bXXFU3fGCZ/eFMZvfHZC69PeGbXEL4zzjuPVzOxHF64=\nk8s.io/apimachinery v0.32.5 h1:6We3aJ6crC0ap8EhsEXcgX3LpI6SEjubpiOMXLROwPM=\nk8s.io/apimachinery v0.32.5/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=\nk8s.io/client-go v0.32.5 h1:huFmQMzgWu0z4kbWsuZci+Gt4Fo72I4CcrvhToZ/Qp0=\nk8s.io/client-go v0.32.5/go.mod h1:Qchw6f9WIVrur7DKojAHpRgGLcANT0RLIvF39Jz58xA=\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-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=\nk8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=\nk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=\nk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=\nsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=\nsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=\nsigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=\nsigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=\nsigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=\nsigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=\n"
  },
  {
    "path": "conformance/headless_service_dns.go",
    "content": "/*\nCopyright 2025 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage conformance\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n\t\"github.com/onsi/gomega/format\"\n\t\"github.com/onsi/gomega/types\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tdiscovery \"k8s.io/api/discovery/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n\t\"k8s.io/utils/ptr\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\nvar _ = Describe(\"\", Label(OptionalLabel, DNSLabel, HeadlessLabel), func() {\n\tconst replicas = 2\n\n\tt := newTestDriver()\n\n\tBeforeEach(func() {\n\t\tt.helloService.Spec.ClusterIP = corev1.ClusterIPNone\n\t\tt.helloDeployment.Spec.Replicas = ptr.To(int32(replicas))\n\t})\n\n\tSpecify(\"A DNS query of the <service>.<ns>.svc.\"+dnsDomain+\" domain for a headless service should return the \"+\n\t\t\"ready endpoint addresses of all the backing pods\", func(ctx context.Context) {\n\t\tAddReportEntry(SpecRefReportEntry, \"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#dns\")\n\n\t\tfor _, client := range clients {\n\t\t\tfor _, ipFamily := range t.awaitServiceImportIPFamilies(ctx, &client) {\n\t\t\t\tcommand := []string{\"sh\", \"-c\", fmt.Sprintf(\"nslookup -type=%s %s.%s.svc.%s.\", dnsRecordTypeOf(ipFamily),\n\t\t\t\t\tt.helloService.Name, t.namespace, dnsDomain)}\n\n\t\t\t\tendpoints := t.awaitK8sEndpoints(ctx, &clients[0], addressTypeOf(ipFamily))\n\n\t\t\t\tvar addresses []string\n\t\t\t\tfor _, ep := range endpoints {\n\t\t\t\t\taddresses = append(addresses, ep.address)\n\t\t\t\t}\n\n\t\t\t\tBy(fmt.Sprintf(\"Executing %s command %q on cluster %q\", ipFamily, strings.Join(command, \" \"), client.name))\n\n\t\t\t\tt.awaitCmdOutputMatches(&client, command, HaveAddresses(addresses), 1, reportNonConformant(\"\"))\n\t\t\t}\n\t\t}\n\t})\n\n\tContext(\"\", func() {\n\t\tBeforeEach(func() {\n\t\t\tt.helloDeployment = nil\n\t\t})\n\n\t\tJustBeforeEach(func(ctx context.Context) {\n\t\t\t_, err := clients[0].k8s.AppsV1().StatefulSets(t.namespace).Create(ctx, newStatefulSet(replicas), metav1.CreateOptions{})\n\t\t\tExpect(err).ToNot(HaveOccurred())\n\t\t})\n\n\t\tSpecify(\"A DNS query of the <hostname>.<clusterid>.<service>.<ns>.svc.\"+dnsDomain+\" domain for a headless StatefulSet \"+\n\t\t\t\"service should return the requested pod's endpoint address\", Label(EndpointSliceLabel), func(ctx context.Context) {\n\t\t\tAddReportEntry(SpecRefReportEntry, \"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#dns\")\n\n\t\t\tfor _, client := range clients {\n\t\t\t\tfor _, ipFamily := range t.awaitServiceImportIPFamilies(ctx, &client) {\n\t\t\t\t\teps := t.awaitMCSEndpointSlice(ctx, &client, addressTypeOf(ipFamily), func(g Gomega, eps *discovery.EndpointSlice) {\n\t\t\t\t\t\tg.Expect(eps.Endpoints).To(HaveLen(replicas),\n\t\t\t\t\t\t\t\"the MCS EndpointSlice %q does not contain the expected number of endpoints %d\",\n\t\t\t\t\t\t\teps.Name, replicas)\n\n\t\t\t\t\t\tfor i := range eps.Endpoints {\n\t\t\t\t\t\t\tep := eps.Endpoints[i]\n\n\t\t\t\t\t\t\tg.Expect(ptr.Deref(ep.Conditions.Ready, true)).To(BeTrue(),\n\t\t\t\t\t\t\t\t\"the endpoint address %s in the MCS EndpointSlice %q is not ready\",\n\t\t\t\t\t\t\t\tstrings.Join(ep.Addresses, \",\"), eps.Name)\n\n\t\t\t\t\t\t\tg.Expect(ptr.Deref(ep.Hostname, \"\")).ToNot(BeEmpty(),\n\t\t\t\t\t\t\t\t\"the hostname field for endpoint address %s in the MCS EndpointSlice %q is not set\",\n\t\t\t\t\t\t\t\tstrings.Join(ep.Addresses, \",\"), eps.Name)\n\t\t\t\t\t\t}\n\t\t\t\t\t}, \"an MCS EndpointSlice was not found on cluster %q\", client.name)\n\n\t\t\t\t\tclusterID := eps.Labels[v1beta1.LabelSourceCluster]\n\n\t\t\t\t\tfor i := range eps.Endpoints {\n\t\t\t\t\t\tep := &eps.Endpoints[i]\n\n\t\t\t\t\t\t// Add trailing dot to prevent search domain from being appended\n\t\t\t\t\t\tcommand := []string{\"sh\", \"-c\", fmt.Sprintf(\"nslookup -type=%s %s.%s.%s.%s.svc.%s.\",\n\t\t\t\t\t\t\tdnsRecordTypeOf(ipFamily), ptr.Deref(ep.Hostname, \"\"), clusterID, t.helloService.Name,\n\t\t\t\t\t\t\tt.namespace, dnsDomain)}\n\n\t\t\t\t\t\tBy(fmt.Sprintf(\"Executing command %q on cluster %q\", strings.Join(command, \" \"), client.name))\n\n\t\t\t\t\t\tt.awaitCmdOutputMatches(&client, command, HaveAddresses(ep.Addresses), 1, reportNonConformant(\"\"))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\n\tSpecify(\"A DNS SRV query of the <service>.<ns>.svc.\"+dnsDomain+\" domain for a headless service should return valid SRV \"+\n\t\t\"records\", func(ctx context.Context) {\n\t\tAddReportEntry(SpecRefReportEntry, \"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#dns\")\n\n\t\t// Collect endpoints for all IP families in the service (for dual-stack support)\n\t\tvar allEndpoints []endpointInfo\n\t\tfor _, ipFamily := range t.helloService.Spec.IPFamilies {\n\t\t\tendpoints := t.awaitK8sEndpoints(ctx, &clients[0], addressTypeOf(ipFamily))\n\t\t\tallEndpoints = append(allEndpoints, endpoints...)\n\t\t}\n\n\t\tdomainName := fmt.Sprintf(\"%s.%s.svc.%s\", t.helloService.Name, t.namespace, dnsDomain)\n\n\t\tfor _, client := range clients {\n\t\t\tsrvRecs := t.expectSRVRecords(&client, domainName, len(allEndpoints))\n\n\t\t\t// Verify each endpoint has a corresponding SRV record\n\t\t\tfor _, ep := range allEndpoints {\n\t\t\t\tindex := slices.IndexFunc(srvRecs, func(r srvRecord) bool {\n\t\t\t\t\treturn strings.HasPrefix(r.domainName, ep.hostName)\n\t\t\t\t})\n\n\t\t\t\tExpect(index).To(BeNumerically(\">=\", 0), reportNonConformant(\n\t\t\t\t\tfmt.Sprintf(\"SRV record for endpoint host name %q not received. Actual records received: %v\",\n\t\t\t\t\t\tep.hostName, srvRecs)))\n\n\t\t\t\tExpect(srvRecs[index].port).To(Equal(t.helloService.Spec.Ports[0].Port))\n\t\t\t}\n\t\t}\n\t})\n})\n\ntype endpointInfo struct {\n\taddress  string\n\thostName string\n}\n\nfunc (e endpointInfo) String() string {\n\treturn fmt.Sprintf(\"address:%q, hostName:%q\", e.address, e.hostName)\n}\n\nfunc (t *testDriver) awaitK8sEndpoints(ctx context.Context, c *clusterClients, addressType discovery.AddressType) []endpointInfo {\n\tBy(fmt.Sprintf(\"Retrieving %s K8s endpoint addresses for the service on cluster %q\", addressType, c.name))\n\n\tvar endpoints []endpointInfo\n\n\tEventually(func(g Gomega, ctx context.Context) {\n\t\tepsList, err := c.k8s.DiscoveryV1().EndpointSlices(t.namespace).List(ctx, metav1.ListOptions{\n\t\t\tLabelSelector: labels.SelectorFromSet(map[string]string{\n\t\t\t\tdiscovery.LabelServiceName: t.helloService.Name,\n\t\t\t}).String(),\n\t\t})\n\t\tg.Expect(err).ToNot(HaveOccurred())\n\n\t\tendpoints = nil\n\n\t\tfor i := range epsList.Items {\n\t\t\teps := &epsList.Items[i]\n\n\t\t\tif eps.AddressType != addressType {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfor j := range epsList.Items[i].Endpoints {\n\t\t\t\tep := &epsList.Items[i].Endpoints[j]\n\n\t\t\t\tg.Expect(ptr.Deref(ep.Conditions.Ready, true)).To(BeTrue(),\n\t\t\t\t\t\"the endpoint address %s in the K8s EndpointSlice %q is not ready\",\n\t\t\t\t\tstrings.Join(ep.Addresses, \",\"), eps.Name)\n\n\t\t\t\tfor _, addr := range ep.Addresses {\n\t\t\t\t\tepi := endpointInfo{address: addr}\n\n\t\t\t\t\tswitch {\n\t\t\t\t\tcase ptr.Deref(ep.Hostname, \"\") != \"\":\n\t\t\t\t\t\tepi.hostName = *ep.Hostname\n\t\t\t\t\tcase strings.Contains(addr, \".\"):\n\t\t\t\t\t\tepi.hostName = strings.ReplaceAll(addr, \".\", \"-\")\n\t\t\t\t\tcase strings.Contains(addr, \":\"):\n\t\t\t\t\t\tepi.hostName = strings.ReplaceAll(addr, \":\", \"-\")\n\t\t\t\t\t}\n\n\t\t\t\t\tendpoints = append(endpoints, epi)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\texpCount := int(ptr.Deref(t.helloDeployment.Spec.Replicas, 1))\n\n\t\tg.Expect(endpoints).To(HaveLen(expCount),\n\t\t\t\"the K8s EndpointSlice does not contain the expected number of ready endpoints %d\", expCount)\n\n\t\t// The final run succeeded so cancel any prior non-conformance reported.\n\t\tcancelNonConformanceReport()\n\t}).WithContext(ctx).Within(20 * time.Second).ProbeEvery(100 * time.Millisecond).Should(Succeed())\n\n\tBy(fmt.Sprintf(\"Found endpoints %v\", endpoints))\n\n\treturn endpoints\n}\n\n// Match DNS records of type A from nslookup output of the form:\n//\n//\tServer:\t\t10.96.0.10\n//\tAddress:\t10.96.0.10:53\n//\n//\tName:\thello.mcs-conformance-2021198391.svc.clusterset.local\n//\tAddress: 10.244.0.52\n//\tName:\thello.mcs-conformance-2021198391.svc.clusterset.local\n//\tAddress: 10.244.0.51\n//\n// to extract the domain addresses (in this case \"10.244.0.52\" and \"10.244.0.51\")\nvar addressesRegEx = regexp.MustCompile(`Name:.*\\s*Address:\\s*(.*)`)\n\ntype haveAddressesMatcher struct {\n\texpected []string\n}\n\nfunc (m *haveAddressesMatcher) Match(v interface{}) (bool, error) {\n\tmatches := addressesRegEx.FindAllStringSubmatch(v.(string), -1)\n\n\tvar actual []string\n\n\tfor i := range matches {\n\t\tactual = append(actual, strings.TrimSpace(matches[i][1]))\n\t}\n\n\tslices.Sort(actual)\n\n\treturn slices.Equal(actual, m.expected), nil\n}\n\nfunc (m *haveAddressesMatcher) FailureMessage(actual interface{}) string {\n\treturn format.Message(actual, \"to have addresses\", m.expected)\n}\n\nfunc (m *haveAddressesMatcher) NegatedFailureMessage(actual interface{}) string {\n\treturn format.Message(actual, \"to not have addresses\", m.expected)\n}\n\nfunc HaveAddresses(expected []string) types.GomegaMatcher {\n\tslices.Sort(expected)\n\n\treturn &haveAddressesMatcher{\n\t\texpected: expected,\n\t}\n}\n"
  },
  {
    "path": "conformance/k8s_objects.go",
    "content": "/*\nCopyright 2023 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage conformance\n\nimport (\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/utils/ptr\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\nconst helloServiceName = \"hello\"\n\nfunc newHelloService() *corev1.Service {\n\treturn &corev1.Service{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:        helloServiceName,\n\t\t\tAnnotations: map[string]string{\"dummy-svc\": \"dummy\"},\n\t\t\tLabels:      map[string]string{\"dummy-svc\": \"dummy\"},\n\t\t},\n\t\tSpec: corev1.ServiceSpec{\n\t\t\tSelector: map[string]string{\n\t\t\t\t\"app\": helloServiceName,\n\t\t\t},\n\t\t\tPorts: []corev1.ServicePort{\n\t\t\t\t{\n\t\t\t\t\tName:     \"tcp\",\n\t\t\t\t\tPort:     42,\n\t\t\t\t\tProtocol: corev1.ProtocolTCP,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName:     \"udp\",\n\t\t\t\t\tPort:     42,\n\t\t\t\t\tProtocol: corev1.ProtocolUDP,\n\t\t\t\t},\n\t\t\t},\n\t\t\tSessionAffinity: corev1.ServiceAffinityClientIP,\n\t\t\tSessionAffinityConfig: &corev1.SessionAffinityConfig{\n\t\t\t\tClientIP: &corev1.ClientIPConfig{TimeoutSeconds: ptr.To(int32(10))},\n\t\t\t},\n\t\t\tIPFamilyPolicy: ptr.To(corev1.IPFamilyPolicyPreferDualStack),\n\t\t},\n\t}\n}\n\nfunc newHelloServiceExport() *v1beta1.ServiceExport {\n\treturn &v1beta1.ServiceExport{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:        helloServiceName,\n\t\t\tAnnotations: map[string]string{\"dummy-svcexport\": \"dummy\"},\n\t\t\tLabels:      map[string]string{\"dummy-svcexport\": \"dummy\"},\n\t\t},\n\t}\n}\n\n// socatListenerScript generates a shell script that detects the pod's IP family configuration\n// and starts the appropriate socat listener (IPv4, IPv6, or dual-stack).\nfunc socatListenerScript(protocol string) string {\n\treturn `# Detect IP families available on the pod from podIPs\n# The downward API provides podIPs as a plain string (single IP or space/comma-separated list)\nhas_ipv4=false\nhas_ipv6=false\n\n# Check for IPv4 pattern (dotted decimal notation)\nif echo \"$MY_POD_IPS\" | grep -qE '([^0-9]|^)[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}([^0-9]|$)'; then has_ipv4=true; fi\n\n# Check for IPv6 pattern (hex digits with at least two colons)\nif echo \"$MY_POD_IPS\" | grep -qE '[0-9a-fA-F]*:[0-9a-fA-F]*:[0-9a-fA-F:]*'; then has_ipv6=true; fi\n\n# Debug: log what we detected\necho \"DEBUG: MY_POD_IPS=$MY_POD_IPS\" >&2\necho \"DEBUG: has_ipv4=$has_ipv4, has_ipv6=$has_ipv6\" >&2\n\n# Extract first IP address (may be space or comma separated)\nfirst_ip=$(echo \"$MY_POD_IPS\" | tr ',' ' ' | awk '{print $1}')\necho \"DEBUG: first_ip=$first_ip\" >&2\n\n# Choose socat listener based on available IP families\n# Export first_ip so it's available to SYSTEM subprocesses\nexport first_ip\n\nif $has_ipv6 && $has_ipv4; then\n\t# Dual-stack: use IPv6 socket that accepts both\n\techo \"DEBUG: Starting dual-stack listener\" >&2\n\tsocat -v -v ` + protocol + `6-LISTEN:42,crlf,reuseaddr,fork,ipv6only=0 SYSTEM:'echo \"pod ip $first_ip\"'\nelif $has_ipv6; then\n\t# IPv6 only\n\techo \"DEBUG: Starting IPv6-only listener\" >&2\n\tsocat -v -v ` + protocol + `6-LISTEN:42,crlf,reuseaddr,fork SYSTEM:'echo \"pod ip $first_ip\"'\nelse\n\t# IPv4 only\n\techo \"DEBUG: Starting IPv4-only listener\" >&2\n\tsocat -v -v ` + protocol + `-LISTEN:42,crlf,reuseaddr,fork SYSTEM:'echo \"pod ip $first_ip\"'\nfi`\n}\n\nfunc podContainers() []corev1.Container {\n\treturn []corev1.Container{\n\t\t{\n\t\t\tName:  \"hello-tcp\",\n\t\t\tImage: \"alpine/socat:1.7.4.4\",\n\t\t\t// Detect if pod has IPv4, IPv6, or both and use appropriate socat listener\n\t\t\t// - IPv4 only: use TCP-LISTEN\n\t\t\t// - IPv6 only: use TCP6-LISTEN (no ipv6only flag needed)\n\t\t\t// - Dual-stack: use TCP6-LISTEN with ipv6only=0 to handle both\n\t\t\tCommand: []string{\"/bin/sh\"},\n\t\t\tArgs:    []string{\"-c\", socatListenerScript(\"TCP\")},\n\t\t\tEnv: []corev1.EnvVar{\n\t\t\t\t{\n\t\t\t\t\tName: \"MY_POD_IPS\",\n\t\t\t\t\tValueFrom: &corev1.EnvVarSource{\n\t\t\t\t\t\tFieldRef: &corev1.ObjectFieldSelector{\n\t\t\t\t\t\t\tFieldPath: \"status.podIPs\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName:  \"hello-udp\",\n\t\t\tImage: \"alpine/socat:1.7.4.4\",\n\t\t\t// Detect if pod has IPv4, IPv6, or both and use appropriate socat listener\n\t\t\t// - IPv4 only: use UDP-LISTEN\n\t\t\t// - IPv6 only: use UDP6-LISTEN (no ipv6only flag needed)\n\t\t\t// - Dual-stack: use UDP6-LISTEN with ipv6only=0 to handle both\n\t\t\tCommand: []string{\"/bin/sh\"},\n\t\t\tArgs:    []string{\"-c\", socatListenerScript(\"UDP\")},\n\t\t\tEnv: []corev1.EnvVar{\n\t\t\t\t{\n\t\t\t\t\tName: \"MY_POD_IPS\",\n\t\t\t\t\tValueFrom: &corev1.EnvVarSource{\n\t\t\t\t\t\tFieldRef: &corev1.ObjectFieldSelector{\n\t\t\t\t\t\t\tFieldPath: \"status.podIPs\",\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\nfunc newHelloDeployment() *appsv1.Deployment {\n\treturn &appsv1.Deployment{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName: helloServiceName,\n\t\t},\n\t\tSpec: appsv1.DeploymentSpec{\n\t\t\tReplicas: ptr.To(int32(1)),\n\t\t\tSelector: &metav1.LabelSelector{\n\t\t\t\tMatchLabels: map[string]string{\n\t\t\t\t\t\"app\": helloServiceName,\n\t\t\t\t},\n\t\t\t},\n\t\t\tTemplate: corev1.PodTemplateSpec{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tLabels: map[string]string{\"app\": helloServiceName},\n\t\t\t\t},\n\t\t\t\tSpec: corev1.PodSpec{\n\t\t\t\t\tContainers: podContainers(),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc newStatefulSet(replicas int) *appsv1.StatefulSet {\n\treturn &appsv1.StatefulSet{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName: helloServiceName + \"-ss\",\n\t\t},\n\t\tSpec: appsv1.StatefulSetSpec{\n\t\t\tSelector: &metav1.LabelSelector{\n\t\t\t\tMatchLabels: map[string]string{\n\t\t\t\t\t\"app\": helloServiceName,\n\t\t\t\t},\n\t\t\t},\n\t\t\tServiceName: helloServiceName,\n\t\t\tReplicas:    ptr.To(int32(replicas)),\n\t\t\tTemplate: corev1.PodTemplateSpec{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\t\"app\": helloServiceName,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tSpec: corev1.PodSpec{\n\t\t\t\t\tContainers:    podContainers(),\n\t\t\t\t\tRestartPolicy: corev1.RestartPolicyAlways,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc newRequestPod() *corev1.Pod {\n\treturn &corev1.Pod{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:   \"request\",\n\t\t\tLabels: map[string]string{\"app\": \"request\"},\n\t\t},\n\t\tSpec: corev1.PodSpec{\n\t\t\tContainers: []corev1.Container{\n\t\t\t\t{\n\t\t\t\t\tName:  \"request\",\n\t\t\t\t\tImage: \"nicolaka/netshoot:v0.15\",\n\t\t\t\t\tArgs:  []string{\"/bin/sh\", \"-ec\", \"while :; do echo '.'; sleep 5 ; done\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "conformance/report.go",
    "content": "/*\nCopyright 2024 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage conformance\n\nimport (\n\t_ \"embed\" // Needed for go:embed\n\t\"errors\"\n\t\"fmt\"\n\t\"html/template\"\n\t\"os\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strings\"\n\t\"sync/atomic\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t\"github.com/onsi/ginkgo/v2/types\"\n\t. \"github.com/onsi/gomega\"\n\t\"github.com/onsi/gomega/matchers\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nconst (\n\tOptionalLabel            = \"Optional\"\n\tRequiredLabel            = \"Required\"\n\tDNSLabel                 = \"DNS\"\n\tConnectivityLabel        = \"Connectivity\"\n\tClusterIPLabel           = \"ClusterIP\"\n\tHeadlessLabel            = \"Headless\"\n\tExternalNameLabel        = \"ExternalName\"\n\tEndpointSliceLabel       = \"EndpointSlice\"\n\tExportedLabelsLabel      = \"ExportedLabels\"\n\tStrictPortConflictLabel  = \"StrictPortConflict\"\n\tSpecRefReportEntry       = \"spec-ref\"\n\tNonConformantReportEntry = \"non-conformant\"\n)\n\nvar reportingLabels = []string{\n\tRequiredLabel,\n\tOptionalLabel,\n}\n\n//go:embed report_template.gohtml\nvar reportHTML string\n\ntype testInfo struct {\n\tDesc       string\n\tRef        string\n\tLabels     []string\n\tPassed     bool\n\tFailed     bool\n\tSkipped    bool\n\tConformant bool\n\tMessage    string\n}\n\ntype testGrouping struct {\n\tName  string\n\tTests []testInfo\n}\n\ntype implementationInfo struct {\n\tOrganization string\n\tProject      string\n\tVersion      string\n\tURL          string\n}\n\nvar (\n\terrorRegEx *regexp.Regexp\n\t// currentSpecNonConformanceMsg holds the last non-conformance message emitted by the current spec. Using\n\t// Eventually with a function that takes a Gomega, different assertions could report non-conformance on\n\t// successive retries, so we want to just report the last one. This also allows the last message to be cleared\n\t// (via cancelNonConformanceReport()) if it eventually succeeded.\n\tcurrentSpecNonConformanceMsg atomic.Value\n\n\t// specRefRegistry maps test description substrings to the KEP spec reference\n\tspecRefRegistry = map[string]string{}\n)\n\n// SpecifyWithSpecRef exist to be able to register a spec to the KEP alongside\n// a Specify. This is important to be able to correctly attach this spec ref\n// even if a test is skipped\nfunc SpecifyWithSpecRef(text string, specRef string, args ...interface{}) bool {\n\tspecRefRegistry[text] = specRef\n\treturn Specify(text, args...)\n}\n\nfunc lookupSpecRef(fullText string) string {\n\tfor desc, ref := range specRefRegistry {\n\t\tif strings.Contains(fullText, desc) {\n\t\t\treturn ref\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc init() {\n\tdummyErr := errors.New(\"dummy\")\n\n\t// Matches gomega output for an error rendered by either the HaveOccurred or Success matchers, eg\n\t//\n\t// \"Error retrieving resource\n\t// Unexpected error:\n\t//      <*errors.StatusError | 0x400024e820>:\n\t//       \"foo\" not found\n\t//      {\n\t//          ...\n\t//      }\n\t//  occurred\"\n\t//\n\t// In this case, we want to match and extract: \"Error retrieving resource\" and \"\"foo\" not found\".\n\terrorRegEx = regexp.MustCompile(fmt.Sprintf(`\\s*(.*)\\s*(?:%s|%s|The function passed to)\\s*.*\\s*(.*)`,\n\t\tfirstLine((&matchers.HaveOccurredMatcher{}).NegatedFailureMessage(dummyErr)),\n\t\tfirstLine((&matchers.SucceedMatcher{}).FailureMessage(dummyErr))))\n}\n\nvar _ = ReportBeforeEach(func(specReport SpecReport) {\n\tcancelNonConformanceReport()\n\n\tif ref := lookupSpecRef(specReport.FullText()); ref != \"\" {\n\t\tAddReportEntry(SpecRefReportEntry, ref)\n\t}\n})\n\nvar _ = ReportAfterEach(func(specReport SpecReport) {\n\tif specReport.LeafNodeType != types.NodeTypeIt || specReport.State == types.SpecStatePending ||\n\t\tspecReport.State == types.SpecStateSkipped {\n\t\treturn\n\t}\n\n\tmsg := currentSpecNonConformanceMsg.Swap(\"\")\n\tif msg != \"\" {\n\t\tAddReportEntry(NonConformantReportEntry, msg, types.ReportEntryVisibilityNever)\n\t}\n})\n\nvar _ = ReportAfterSuite(\"MCS conformance report\", func(report Report) {\n\ttestGroupMap := map[string]*testGrouping{}\n\tsuiteFailure := \"\"\n\n\tfor _, specReport := range report.SpecReports {\n\t\tif specReport.LeafNodeType == types.NodeTypeBeforeSuite && specReport.State == types.SpecStateFailed {\n\t\t\tsuiteFailure = parseFailureMessage(specReport.FailureMessage())\n\t\t\tcontinue\n\t\t}\n\n\t\tif specReport.LeafNodeType != types.NodeTypeIt || specReport.State == types.SpecStatePending {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, label := range reportingLabels {\n\t\t\tif !slices.Contains(specReport.Labels(), label) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif testGroupMap[label] == nil {\n\t\t\t\ttestGroupMap[label] = &testGrouping{\n\t\t\t\t\tName: label,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tinfo := testInfo{\n\t\t\t\tDesc:       strings.TrimSpace(specReport.FullText()),\n\t\t\t\tConformant: true,\n\t\t\t}\n\n\t\t\tfor _, currLabel := range specReport.Labels() {\n\t\t\t\tif currLabel != label {\n\t\t\t\t\tinfo.Labels = append(info.Labels, currLabel)\n\t\t\t\t}\n\t\t\t}\n\t\t\tslices.Sort(info.Labels)\n\n\t\t\tfor i := range specReport.ReportEntries {\n\t\t\t\tswitch specReport.ReportEntries[i].Name {\n\t\t\t\tcase SpecRefReportEntry:\n\t\t\t\t\tinfo.Ref = specReport.ReportEntries[i].GetRawValue().(string)\n\t\t\t\tcase NonConformantReportEntry:\n\t\t\t\t\t// An assertion reporting non-conformance may have failed initially but eventually succeeded after retries\n\t\t\t\t\t// so only report non-conformance if the spec actually failed.\n\t\t\t\t\tif specReport.State != types.SpecStatePassed {\n\t\t\t\t\t\tinfo.Conformant = false\n\t\t\t\t\t\tinfo.Message = specReport.ReportEntries[i].GetRawValue().(string)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif specReport.State == types.SpecStateSkipped {\n\t\t\t\tinfo.Skipped = true\n\t\t\t\tinfo.Message = parseFailureMessage(specReport.FailureMessage())\n\t\t\t} else if specReport.State != types.SpecStatePassed && info.Conformant {\n\t\t\t\t// If the spec failed (ie didn't pass) not due to non-conformance then we assume it encountered\n\t\t\t\t// an unexpected error preventing conformance from being determined, and thus we'll report the\n\t\t\t\t// conformance status as unknown.\n\t\t\t\tinfo.Failed = true\n\t\t\t\tinfo.Message = parseFailureMessage(specReport.FailureMessage())\n\t\t\t}\n\n\t\t\tinfo.Passed = !info.Failed && !info.Skipped && info.Conformant\n\n\t\t\tif info.Message != \"\" {\n\t\t\t\tinfo.Message = \" - \" + info.Message\n\t\t\t}\n\n\t\t\ttestGroupMap[label].Tests = append(testGroupMap[label].Tests, info)\n\t\t}\n\t}\n\n\ttestGroups := []testGrouping{}\n\tfor _, l := range reportingLabels {\n\t\tif testGroupMap[l] != nil {\n\t\t\tslices.SortFunc(testGroupMap[l].Tests, func(a, b testInfo) int {\n\t\t\t\tif cmp := slices.Compare(a.Labels, b.Labels); cmp != 0 {\n\t\t\t\t\treturn cmp\n\t\t\t\t}\n\t\t\t\treturn strings.Compare(strings.TrimSpace(a.Desc), strings.TrimSpace(b.Desc))\n\t\t\t})\n\t\t\ttestGroups = append(testGroups, *testGroupMap[l])\n\t\t}\n\t}\n\n\ttotalTests := 0\n\tpassedTests := 0\n\tfor _, g := range testGroups {\n\t\tfor _, t := range g.Tests {\n\t\t\ttotalTests++\n\t\t\tif t.Passed {\n\t\t\t\tpassedTests++\n\t\t\t}\n\t\t}\n\t}\n\n\tdata := struct {\n\t\tGroups         []testGrouping\n\t\tSuiteFailure   string\n\t\tDNSDomain      string\n\t\tPassed         int\n\t\tTotal          int\n\t\tImplementation implementationInfo\n\t}{\n\t\tGroups:       testGroups,\n\t\tSuiteFailure: suiteFailure,\n\t\tDNSDomain:    dnsDomain,\n\t\tPassed:       passedTests,\n\t\tTotal:        totalTests,\n\t\tImplementation: implementationInfo{\n\t\t\tOrganization: organization,\n\t\t\tProject:      project,\n\t\t\tVersion:      version,\n\t\t\tURL:          url,\n\t\t},\n\t}\n\n\tout, err := os.Create(\"report.html\")\n\tExpect(err).To(Succeed())\n\n\ttmpl, err := template.New(\"report\").Parse(reportHTML)\n\tExpect(err).To(Succeed())\n\n\terr = tmpl.Execute(out, data)\n\tExpect(err).To(Succeed())\n\n\tyamlOut, err := os.Create(\"report.yaml\")\n\tExpect(err).To(Succeed())\n\n\terr = yaml.NewEncoder(yamlOut).Encode(data)\n\tExpect(err).To(Succeed())\n})\n\nfunc parseFailureMessage(s string) string {\n\t// First see if the message represents an error formatted by a gomega matcher - we're interested in\n\t// extracting the optional user description passed to the gomega assertion and the actual error\n\t// string as these are the useful parts conducive for formatting in the report table.\n\tmatches := errorRegEx.FindStringSubmatch(s)\n\tif len(matches) > 0 {\n\t\t// First match at index 0 is the full text that was matched; index 1 will be the user description\n\t\t// and index 2 the error string. We concatenate the latter two.\n\t\tmsg := strings.TrimSpace(matches[1])\n\t\tif msg == \"\" {\n\t\t\tmsg = strings.TrimSpace(matches[2])\n\t\t} else {\n\t\t\tmsg = strings.TrimSuffix(msg, \".\") + \": \" + strings.TrimSpace(matches[2])\n\t\t}\n\n\t\treturn msg\n\t}\n\n\t// Fallback - just take the first line in the message.\n\treturn firstLine(s)\n}\n\nfunc firstLine(s string) string {\n\tfirst, _, _ := strings.Cut(s, \"\\n\")\n\treturn strings.TrimSpace(first)\n}\n\n// reportNonConformant is intended for use as an optional description in a gomega assertion. It returns\n// a function that is lazily evaluated by the assertion only if a failure occurs. We take advantage of\n// that to add a report entry indicating non-conformance.\nfunc reportNonConformant(msg string) func() string {\n\treturn func() string {\n\t\tcurrentSpecNonConformanceMsg.Store(msg)\n\t\treturn msg\n\t}\n}\n\nfunc cancelNonConformanceReport() {\n\tcurrentSpecNonConformanceMsg.Store(\"\")\n}\n"
  },
  {
    "path": "conformance/report_template.gohtml",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n    <title>MCS API conformance report</title>\n    <style>\n        table, th, td {\n            border: 1px solid black;\n        }\n        td {\n            padding: 2px;\n        }\n    </style>\n</head>\n<body>\n<h2>MCS Conformance Report</h2>\n<p><strong>{{.Passed}}</strong> of <strong>{{.Total}}</strong> tests passed</p>\n\n{{if and .DNSDomain (ne .DNSDomain \"clusterset.local\")}}\n    <p>DNS domain suffix: <strong>{{.DNSDomain}}</strong></p>\n{{end}}\n\n{{if .SuiteFailure }}\n    <p style=\"color: red\">{{.SuiteFailure}}</p>\n{{end}}\n\n{{range .Groups}}\n<h3>{{.Name}}</h3>\n<table>\n    <thead>\n        <tr>\n            <th>Conformant</th>\n            <th>Labels</th>\n            <th>Description</th>\n        </tr>\n    </thead>\n    {{range .Tests}}\n    <tr>\n        {{ if .Skipped }}\n            <td style=\"color:gray\">Skipped{{.Message}}</td>\n        {{ else if .Failed }}\n            <td style=\"color:orange\">Unknown{{.Message}}</td>\n        {{ else if .Conformant }}\n            <td style=\"color:green\">Yes{{.Message}}</td>\n        {{ else }}\n            <td style=\"color:red\">No{{.Message}}</td>\n        {{end}}\n        <td>{{range $i, $l := .Labels}}{{if $i}}, {{end}}{{$l}}{{end}}</td>\n        <td><a href=\"{{.Ref}}\">{{.Desc}}</a></td>\n    </tr>\n    {{end}}\n</table>\n{{end}}\n</body>\n</html>\n"
  },
  {
    "path": "conformance/service_import.go",
    "content": "/*\nCopyright 2024 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage conformance\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"time\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/utils/ptr\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\nvar (\n\t_ = Describe(\"\", testGeneralServiceImport)\n\t_ = Describe(\"\", Label(ClusterIPLabel), testClusterIPServiceImport)\n\t_ = Describe(\"\", Label(HeadlessLabel), testHeadlessServiceImport)\n\t_ = Describe(\"\", Label(ExternalNameLabel), testExternalNameService)\n\t_ = Describe(\"\", testServiceTypeConflict)\n)\n\nfunc testGeneralServiceImport() {\n\tt := newTestDriver()\n\n\tassertHasKeyValues := func(g Gomega, actual, expected map[string]string) {\n\t\tfor k, v := range expected {\n\t\t\tg.Expect(actual).To(HaveKeyWithValue(k, v), reportNonConformant(\"\"))\n\t\t}\n\t}\n\n\tassertNotHasKeyValues := func(g Gomega, actual, expected map[string]string) {\n\t\tfor k, v := range expected {\n\t\t\tg.Expect(actual).ToNot(HaveKeyWithValue(k, v), reportNonConformant(\"\"))\n\t\t}\n\t}\n\n\t// Other tests also unexport the service and verifies the ServiceImport is deleted, but it doesn't require more than one\n\t// cluster, so it provides basic coverage in a simple single cluster environment. The following test is more comprehensive in that it\n\t// exports a service from two clusters and ensures proper behavior when unexported from both.\n\tSpecifyWithSpecRef(\"A ServiceImport should only exist as long as there's at least one exporting cluster\",\n\t\t\"https://github.com/kubernetes/enhancements/blob/master/keps/sig-multicluster/1645-multi-cluster-services-api/README.md#importing-services\",\n\t\tLabel(RequiredLabel), func(ctx context.Context) {\n\t\t\trequireTwoClusters()\n\n\t\t\tt.awaitServiceImport(ctx, &clients[0], t.helloService.Name, false, nil)\n\n\t\t\tBy(fmt.Sprintf(\"Exporting the service on the second cluster %q\", clients[1].name))\n\n\t\t\tt.deployHelloService(ctx, &clients[1], newHelloService())\n\t\t\tt.createServiceExport(ctx, &clients[1], newHelloServiceExport())\n\n\t\t\t// Sanity check and to also wait a bit for the second cluster to export. There's no deterministic way to tell if/when\n\t\t\t// the second cluster has finished exporting other than utilizing different service ports in each cluster but service\n\t\t\t// port merging behavior is already covered in another test case, so it's ideal not to rely on behavior that could\n\t\t\t// cause orthogonal failures and to avoid duplicate testing.\n\t\t\tt.ensureServiceImport(ctx, &clients[0], t.helloService.Name, fmt.Sprintf(\n\t\t\t\t\"the ServiceImport no longer exists after exporting on cluster %q\", clients[1].name))\n\n\t\t\tt.deleteServiceExport(ctx, &clients[0])\n\n\t\t\tt.ensureServiceImport(ctx, &clients[0], t.helloService.Name, fmt.Sprintf(\n\t\t\t\t\"the ServiceImport no longer exists after unexporting the service on cluster %q while still exported on cluster %q\",\n\t\t\t\tclients[0].name, clients[1].name))\n\n\t\t\tt.deleteServiceExport(ctx, &clients[1])\n\n\t\t\tt.awaitNoServiceImport(ctx, &clients[0], helloServiceName,\n\t\t\t\t\"the ServiceImport still exists after unexporting the service on all clusters\")\n\t\t})\n\n\tContext(\"\", func() {\n\t\tBeforeEach(func() {\n\t\t\tt.helloServiceExport.Spec.ExportedAnnotations = map[string]string{\"dummy-annotation\": \"true\"}\n\t\t\tt.helloServiceExport.Spec.ExportedLabels = map[string]string{\"dummy-label\": \"true\"}\n\t\t})\n\n\t\tSpecifyWithSpecRef(\"Only labels and annotations specified as exported in the ServiceExport should be propagated to the ServiceImport\",\n\t\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#labels-and-annotations\",\n\t\t\tLabel(OptionalLabel), Label(ExportedLabelsLabel), func(ctx context.Context) {\n\t\t\t\tt.awaitServiceImport(ctx, &clients[0], helloServiceName, false,\n\t\t\t\t\tfunc(g Gomega, serviceImport *v1beta1.ServiceImport) {\n\t\t\t\t\t\tassertHasKeyValues(g, serviceImport.Annotations, t.helloServiceExport.Spec.ExportedAnnotations)\n\t\t\t\t\t\tassertNotHasKeyValues(g, serviceImport.Annotations, t.helloService.Annotations)\n\n\t\t\t\t\t\tassertHasKeyValues(g, serviceImport.Labels, t.helloServiceExport.Spec.ExportedLabels)\n\t\t\t\t\t\tassertNotHasKeyValues(g, serviceImport.Labels, t.helloService.Labels)\n\t\t\t\t\t})\n\t\t\t})\n\t})\n\n\tContext(\"A service exported on two clusters\", func() {\n\t\ttt := newTwoClusterTestDriver(t)\n\n\t\tContext(\"with conflicting annotations and labels\", func() {\n\t\t\tBeforeEach(func() {\n\t\t\t\tt.helloServiceExport.Spec.ExportedAnnotations = map[string]string{\"dummy-annotation\": \"true\"}\n\t\t\t\tt.helloServiceExport.Spec.ExportedLabels = map[string]string{\"dummy-label\": \"true\"}\n\n\t\t\t\ttt.helloServiceExport2.Spec.ExportedAnnotations = map[string]string{\"dummy-annotation2\": \"true\"}\n\t\t\t\ttt.helloServiceExport2.Spec.ExportedLabels = map[string]string{\"dummy-label2\": \"true\"}\n\t\t\t})\n\n\t\t\tSpecifyWithSpecRef(\"should apply the conflict resolution policy and report a Conflict condition on each ServiceExport\",\n\t\t\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#labels-and-annotations\",\n\t\t\t\tLabel(OptionalLabel), Label(ExportedLabelsLabel), func(ctx context.Context) {\n\t\t\t\t\tt.awaitServiceExportCondition(ctx, &clients[0], v1beta1.ServiceExportConditionConflict, metav1.ConditionTrue)\n\t\t\t\t\tt.awaitServiceExportCondition(ctx, &clients[1], v1beta1.ServiceExportConditionConflict, metav1.ConditionTrue)\n\n\t\t\t\t\tt.awaitServiceImport(ctx, &clients[0], t.helloService.Name, false,\n\t\t\t\t\t\tfunc(g Gomega, serviceImport *v1beta1.ServiceImport) {\n\t\t\t\t\t\t\tassertHasKeyValues(g, serviceImport.Annotations, t.helloServiceExport.Spec.ExportedAnnotations)\n\t\t\t\t\t\t\tassertNotHasKeyValues(g, serviceImport.Annotations, tt.helloServiceExport2.Spec.ExportedAnnotations)\n\n\t\t\t\t\t\t\tassertHasKeyValues(g, serviceImport.Labels, t.helloServiceExport.Spec.ExportedLabels)\n\t\t\t\t\t\t\tassertNotHasKeyValues(g, serviceImport.Labels, tt.helloServiceExport2.Spec.ExportedLabels)\n\t\t\t\t\t\t})\n\t\t\t\t})\n\t\t})\n\t})\n}\n\nfunc testClusterIPServiceImport() {\n\tt := newTestDriver()\n\n\tSpecifyWithSpecRef(\"Exporting a ClusterIP service should create a ServiceImport of type ClusterSetIP in the service's namespace in each cluster. \"+\n\t\t\"Unexporting should delete the ServiceImport\",\n\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#importing-services\",\n\t\tLabel(RequiredLabel), func(ctx context.Context) {\n\t\t\tt.awaitServiceExportCondition(ctx, &clients[0], v1beta1.ServiceExportConditionValid, metav1.ConditionTrue)\n\n\t\t\tfor i := range clients {\n\t\t\t\tserviceImport := t.awaitServiceImport(ctx, &clients[i], helloServiceName, true, nil)\n\n\t\t\t\tExpect(serviceImport.Spec.Type).To(Equal(v1beta1.ClusterSetIP), reportNonConformant(\n\t\t\t\t\tfmt.Sprintf(\"ServiceImport on cluster %q has type %q\", clients[i].name, serviceImport.Spec.Type)))\n\t\t\t}\n\n\t\t\tt.deleteServiceExport(ctx, &clients[0])\n\n\t\t\tfor i := range clients {\n\t\t\t\tt.awaitNoServiceImport(ctx, &clients[i], helloServiceName, fmt.Sprintf(\n\t\t\t\t\t\"the ServiceImport still exists on cluster %q after unexporting the service\", clients[i].name))\n\t\t\t}\n\t\t})\n\n\tSpecifyWithSpecRef(\"The SessionAffinity for a ClusterSetIP ServiceImport should match the exported service's SessionAffinity\",\n\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#session-affinity\",\n\t\tLabel(RequiredLabel), func(ctx context.Context) {\n\t\t\tt.awaitServiceImport(ctx, &clients[0], helloServiceName, false, func(g Gomega, serviceImport *v1beta1.ServiceImport) {\n\t\t\t\tg.Expect(serviceImport.Spec.SessionAffinity).To(Equal(t.helloService.Spec.SessionAffinity), reportNonConformant(\"\"))\n\n\t\t\t\tg.Expect(serviceImport.Spec.SessionAffinityConfig).To(Equal(t.helloService.Spec.SessionAffinityConfig), reportNonConformant(\n\t\t\t\t\t\"The SessionAffinityConfig of the ServiceImport does not match the exported Service's SessionAffinityConfig\"))\n\t\t\t})\n\t\t})\n\n\tContext(\"\", func() {\n\t\tBeforeEach(func() {\n\t\t\tt.helloService.Spec.InternalTrafficPolicy = ptr.To(corev1.ServiceInternalTrafficPolicyCluster)\n\t\t})\n\t\tSpecifyWithSpecRef(\"The InternalTrafficPolicy for a ClusterSetIP ServiceImport should match the exported service's InternalTrafficPolicy\",\n\t\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#internal-traffic-policy\",\n\t\t\tLabel(RequiredLabel), func(ctx context.Context) {\n\t\t\t\tt.awaitServiceImport(ctx, &clients[0], helloServiceName, false, func(g Gomega, serviceImport *v1beta1.ServiceImport) {\n\t\t\t\t\tg.Expect(serviceImport.Spec.InternalTrafficPolicy).To(Equal(t.helloService.Spec.InternalTrafficPolicy), reportNonConformant(\n\t\t\t\t\t\t\"The InternalTrafficPolicy of the ServiceImport does not match the exported Service's InternalTrafficPolicy\"))\n\t\t\t\t})\n\t\t\t},\n\t\t)\n\t})\n\n\tContext(\"\", func() {\n\t\tBeforeEach(func() {\n\t\t\tt.helloService.Spec.TrafficDistribution = ptr.To(corev1.ServiceTrafficDistributionPreferClose)\n\t\t})\n\t\tSpecifyWithSpecRef(\"The TrafficDistribution for a ClusterSetIP ServiceImport should match the exported service's TrafficDistribution\",\n\t\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#traffic-distribution\",\n\t\t\tLabel(RequiredLabel), func(ctx context.Context) {\n\t\t\t\tt.awaitServiceImport(ctx, &clients[0], helloServiceName, false, func(g Gomega, serviceImport *v1beta1.ServiceImport) {\n\t\t\t\t\tg.Expect(serviceImport.Spec.TrafficDistribution).To(Equal(t.helloService.Spec.TrafficDistribution), reportNonConformant(\n\t\t\t\t\t\t\"The TrafficDistribution of the ServiceImport does not match the exported Service's TrafficDistribution\"))\n\t\t\t\t})\n\t\t\t},\n\t\t)\n\t})\n\n\tSpecifyWithSpecRef(\"An IP should be allocated for a ClusterSetIP ServiceImport for each IP family\",\n\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#clustersetip\",\n\t\tLabel(RequiredLabel), func(ctx context.Context) {\n\t\t\tserviceImport := t.awaitServiceImport(ctx, &clients[0], t.helloService.Name, false,\n\t\t\t\tfunc(g Gomega, serviceImport *v1beta1.ServiceImport) {\n\t\t\t\t\tg.Expect(serviceImport.Spec.IPs).ToNot(BeEmpty(), reportNonConformant(\"\"))\n\t\t\t\t})\n\n\t\t\tExpect(serviceImport.Spec.IPs).To(HaveLen(len(serviceImport.Spec.IPFamilies)), reportNonConformant(\n\t\t\t\t\"The ServiceImport IPs field must have the same number of IPs as ServiceImport IPFamilies\"))\n\n\t\t\t// Verify the IPs are valid and are in the same order as IPFamilies.\n\t\t\tfor i := range serviceImport.Spec.IPs {\n\t\t\t\tExpect(net.ParseIP(serviceImport.Spec.IPs[i])).ToNot(BeNil(),\n\t\t\t\t\treportNonConformant(fmt.Sprintf(\"The value %q is not a valid IP\", serviceImport.Spec.IPs[i])))\n\n\t\t\t\tExpect(ipFamilyOf(serviceImport.Spec.IPs[i])).To(Equal(serviceImport.Spec.IPFamilies[i]),\n\t\t\t\t\treportNonConformant(fmt.Sprintf(\n\t\t\t\t\t\t\"The IP family of ServiceImport.Spec.IPs[%d] (%q) must match ServiceImport.Spec.IPFamilies[%d] (%q)\",\n\t\t\t\t\t\ti, serviceImport.Spec.IPs[i], i, serviceImport.Spec.IPFamilies[i])))\n\t\t\t}\n\t\t})\n\n\tSpecifyWithSpecRef(\"The ports for a ClusterSetIP ServiceImport should match those of the exported service\",\n\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#service-port\",\n\t\tLabel(RequiredLabel), func(ctx context.Context) {\n\t\t\tt.awaitServiceImport(ctx, &clients[0], helloServiceName, false, func(g Gomega, serviceImport *v1beta1.ServiceImport) {\n\t\t\t\tg.Expect(sortMCSPorts(serviceImport.Spec.Ports)).To(Equal(toMCSPorts(t.helloService.Spec.Ports)), reportNonConformant(\"\"))\n\t\t\t})\n\t\t})\n\n\tContext(\"A ClusterIP service exported on two clusters\", func() {\n\t\ttt := newTwoClusterTestDriver(t)\n\n\t\tContext(\"\", func() {\n\t\t\tBeforeEach(func() {\n\t\t\t\ttt.helloService2.Spec.Ports = []corev1.ServicePort{\n\t\t\t\t\tt.helloService.Spec.Ports[0],\n\t\t\t\t\t{\n\t\t\t\t\t\tName:     \"stcp\",\n\t\t\t\t\t\tPort:     142,\n\t\t\t\t\t\tProtocol: corev1.ProtocolSCTP,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tSpecifyWithSpecRef(\"should expose the union of the constituent service ports and raise a conflict\",\n\t\t\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#service-port\",\n\t\t\t\tLabel(RequiredLabel), func(ctx context.Context) {\n\t\t\t\t\tt.awaitServiceExportCondition(ctx, &clients[0], v1beta1.ServiceExportConditionConflict, metav1.ConditionTrue)\n\t\t\t\t\tt.awaitServiceExportCondition(ctx, &clients[1], v1beta1.ServiceExportConditionConflict, metav1.ConditionTrue)\n\n\t\t\t\t\tt.awaitServiceImport(ctx, &clients[0], t.helloService.Name, false,\n\t\t\t\t\t\tfunc(g Gomega, serviceImport *v1beta1.ServiceImport) {\n\t\t\t\t\t\t\tg.Expect(sortMCSPorts(serviceImport.Spec.Ports)).To(Equal(toMCSPorts(\n\t\t\t\t\t\t\t\tappend(t.helloService.Spec.Ports, tt.helloService2.Spec.Ports[1]))), reportNonConformant(\"\"))\n\t\t\t\t\t\t})\n\t\t\t\t})\n\t\t})\n\n\t\tContext(\"with conflicting ports\", Label(RequiredLabel), func() {\n\t\t\tBeforeEach(func() {\n\t\t\t\ttt.helloService2.Spec.Ports = []corev1.ServicePort{t.helloService.Spec.Ports[0]}\n\t\t\t\ttt.helloService2.Spec.Ports[0].Port = t.helloService.Spec.Ports[0].Port + 1\n\t\t\t})\n\n\t\t\tSpecifyWithSpecRef(\"should apply the conflict resolution policy and report a Conflict condition on each ServiceExport\",\n\t\t\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#service-port\",\n\t\t\t\tfunc(ctx context.Context) {\n\t\t\t\t\tt.awaitServiceExportCondition(ctx, &clients[0], v1beta1.ServiceExportConditionConflict, metav1.ConditionTrue)\n\t\t\t\t\tt.awaitServiceExportCondition(ctx, &clients[1], v1beta1.ServiceExportConditionConflict, metav1.ConditionTrue)\n\n\t\t\t\t\tt.awaitServiceImport(ctx, &clients[0], t.helloService.Name, false,\n\t\t\t\t\t\tfunc(g Gomega, serviceImport *v1beta1.ServiceImport) {\n\t\t\t\t\t\t\tg.Expect(sortMCSPorts(serviceImport.Spec.Ports)).To(Equal(toMCSPorts(t.helloService.Spec.Ports)),\n\t\t\t\t\t\t\t\treportNonConformant(\"The service ports were not resolved correctly\"))\n\t\t\t\t\t\t})\n\t\t\t\t})\n\t\t})\n\t})\n}\n\nfunc testHeadlessServiceImport() {\n\tt := newTestDriver()\n\n\tBeforeEach(func() {\n\t\tt.helloService.Spec.ClusterIP = corev1.ClusterIPNone\n\t})\n\n\tSpecifyWithSpecRef(\"Exporting a headless service should create a ServiceImport of type Headless in the service's namespace in each cluster. \"+\n\t\t\"Unexporting should delete the ServiceImport\",\n\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#service-types\",\n\t\tLabel(RequiredLabel), func(ctx context.Context) {\n\t\t\tfor i := range clients {\n\t\t\t\tserviceImport := t.awaitServiceImport(ctx, &clients[i], helloServiceName, true, nil)\n\n\t\t\t\tExpect(serviceImport.Spec.Type).To(Equal(v1beta1.Headless), reportNonConformant(\n\t\t\t\t\tfmt.Sprintf(\"ServiceImport on cluster %q has type %q\", clients[i].name, serviceImport.Spec.Type)))\n\t\t\t}\n\n\t\t\tt.deleteServiceExport(ctx, &clients[0])\n\n\t\t\tfor i := range clients {\n\t\t\t\tt.awaitNoServiceImport(ctx, &clients[i], helloServiceName, fmt.Sprintf(\n\t\t\t\t\t\"the ServiceImport still exists on cluster %q after unexporting the service\", clients[i].name))\n\t\t\t}\n\t\t})\n\n\tSpecifyWithSpecRef(\"No clusterset IP should be allocated for a Headless ServiceImport\",\n\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#clustersetip\",\n\t\tLabel(RequiredLabel), func(ctx context.Context) {\n\t\t\tt.awaitServiceImport(ctx, &clients[0], t.helloService.Name, false, nil)\n\n\t\t\tConsistently(func() []string {\n\t\t\t\treturn t.getServiceImport(ctx, &clients[0], t.helloService.Name).Spec.IPs\n\t\t\t}).Within(5*time.Second).ProbeEvery(time.Second).Should(BeEmpty(), reportNonConformant(\"\"))\n\t\t})\n}\n\nfunc testExternalNameService() {\n\tt := newTestDriver()\n\n\tBeforeEach(func() {\n\t\tt.helloService.Spec.Type = corev1.ServiceTypeExternalName\n\t\tt.helloService.Spec.ExternalName = \"example.com\"\n\t\tt.helloService.Spec.IPFamilyPolicy = nil\n\t})\n\n\tSpecifyWithSpecRef(\"Exporting an ExternalName service should set ServiceExport Valid condition to False\",\n\t\t\"https://github.com/kubernetes/enhancements/blob/master/keps/sig-multicluster/1645-multi-cluster-services-api/README.md#service-types\",\n\t\tLabel(RequiredLabel), func(ctx context.Context) {\n\t\t\tt.awaitServiceExportCondition(ctx, &clients[0], v1beta1.ServiceExportConditionValid, metav1.ConditionFalse)\n\t\t\tt.ensureNoServiceImport(ctx, &clients[0], helloServiceName,\n\t\t\t\t\"the ServiceImport should not exist for an ExternalName service\")\n\t\t})\n}\n\nfunc testServiceTypeConflict() {\n\tt := newTwoClusterTestDriver(newTestDriver())\n\n\tBeforeEach(func() {\n\t\tt.helloService2.Spec.ClusterIP = corev1.ClusterIPNone\n\t})\n\n\tSpecifyWithSpecRef(\"A service exported on two clusters with conflicting headlessness should apply the conflict resolution policy and \"+\n\t\t\"report a Conflict condition on the ServiceExport\",\n\t\t\"https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#headlessness\",\n\t\tLabel(RequiredLabel), func(ctx context.Context) {\n\t\t\tt.awaitServiceExportCondition(ctx, &clients[0], v1beta1.ServiceExportConditionConflict, metav1.ConditionTrue)\n\t\t\tt.awaitServiceExportCondition(ctx, &clients[1], v1beta1.ServiceExportConditionConflict, metav1.ConditionTrue)\n\n\t\t\tfor i := range clients {\n\t\t\t\tserviceImport := t.awaitServiceImport(ctx, &clients[i], helloServiceName, true, nil)\n\n\t\t\t\tExpect(serviceImport.Spec.Type).To(Equal(v1beta1.ClusterSetIP), reportNonConformant(\n\t\t\t\t\tfmt.Sprintf(\"ServiceImport on cluster %q has type %q\", clients[i].name, serviceImport.Spec.Type)))\n\t\t\t}\n\t\t})\n}\n"
  },
  {
    "path": "controllers/cmd/servicecontroller/servicecontroller.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"os\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\tclientgoscheme \"k8s.io/client-go/kubernetes/scheme\"\n\t_ \"k8s.io/client-go/plugin/pkg/client/auth/gcp\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/log/zap\"\n\t\"sigs.k8s.io/controller-runtime/pkg/metrics/server\"\n\t\"sigs.k8s.io/controller-runtime/pkg/webhook\"\n\t\"sigs.k8s.io/mcs-api/controllers\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\nvar (\n\tscheme   = runtime.NewScheme()\n\tsetupLog = ctrl.Log.WithName(\"setup\")\n)\n\nfunc init() {\n\tclientgoscheme.AddToScheme(scheme)\n\tv1beta1.AddToScheme(scheme)\n}\n\nfunc main() {\n\tvar metricsAddr string\n\tvar enableLeaderElection bool\n\tflag.StringVar(&metricsAddr, \"metrics-addr\", \":8080\", \"The address the metric endpoint binds to.\")\n\tflag.BoolVar(&enableLeaderElection, \"enable-leader-election\", false,\n\t\t\"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.\")\n\tflag.Parse()\n\topts := ctrl.Options{\n\t\tScheme: scheme,\n\t\tMetrics: server.Options{\n\t\t\tBindAddress: metricsAddr,\n\t\t},\n\t\tLeaderElection: enableLeaderElection,\n\t\tWebhookServer: webhook.NewServer(webhook.Options{\n\t\t\tPort: 9443,\n\t\t}),\n\t}\n\tctrl.SetLogger(zap.New(zap.UseDevMode(true)))\n\n\tif err := controllers.Start(ctrl.SetupSignalHandler(), ctrl.GetConfigOrDie(), setupLog, opts); err != nil {\n\t\tsetupLog.Error(err, \"problem running controllers\")\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "controllers/common.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage controllers\n\nimport (\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"encoding/base32\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/go-logr/logr\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"k8s.io/client-go/rest\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n)\n\nconst (\n\t// DerivedServiceAnnotation is set on a ServiceImport to reference the\n\t// derived Service that represents the imported service for kube-proxy.\n\tDerivedServiceAnnotation = \"multicluster.kubernetes.io/derived-service\"\n\tserviceImportKind        = \"ServiceImport\"\n)\n\nfunc derivedName(name types.NamespacedName) string {\n\thash := sha256.New()\n\thash.Write([]byte(name.String()))\n\treturn \"derived-\" + strings.ToLower(base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString(hash.Sum(nil)))[:10]\n}\n\n// Start the controllers with the supplied config\nfunc Start(ctx context.Context, cfg *rest.Config, setupLog logr.Logger, opts ctrl.Options) error {\n\tmgr, err := ctrl.NewManager(cfg, opts)\n\tif err != nil {\n\t\tsetupLog.Error(err, \"unable to start manager\")\n\t\tos.Exit(1)\n\t}\n\n\tif err = (&ServiceImportReconciler{\n\t\tClient: mgr.GetClient(),\n\t\tLog:    ctrl.Log.WithName(\"controllers\").WithName(\"ServiceImport\"),\n\t}).SetupWithManager(mgr); err != nil {\n\t\tsetupLog.Error(err, \"unable to create controller\", \"controller\", \"ServiceImport\")\n\t\treturn err\n\t}\n\tif err = (&ServiceReconciler{\n\t\tClient: mgr.GetClient(),\n\t\tLog:    ctrl.Log.WithName(\"controllers\").WithName(\"Service\"),\n\t}).SetupWithManager(mgr); err != nil {\n\t\tsetupLog.Error(err, \"unable to create controller\", \"controller\", \"Service\")\n\t\treturn err\n\t}\n\tif err = (&EndpointSliceReconciler{\n\t\tClient: mgr.GetClient(),\n\t\tLog:    ctrl.Log.WithName(\"controllers\").WithName(\"EndpointSlice\"),\n\t}).SetupWithManager(mgr); err != nil {\n\t\tsetupLog.Error(err, \"unable to create controller\", \"controller\", \"EndpointSlice\")\n\t\treturn err\n\t}\n\n\tsetupLog.Info(\"starting manager\")\n\tif err := mgr.Start(ctx); err != nil {\n\t\tsetupLog.Error(err, \"problem running manager\")\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "controllers/controllers_suite_test.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage controllers\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\tclientgoscheme \"k8s.io/client-go/kubernetes/scheme\"\n\t\"k8s.io/client-go/rest\"\n\t\"k8s.io/client-go/tools/clientcmd\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/envtest\"\n\t\"sigs.k8s.io/controller-runtime/pkg/log\"\n\t\"sigs.k8s.io/controller-runtime/pkg/log/zap\"\n\t\"sigs.k8s.io/kind/pkg/cluster\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\nconst (\n\tclusterName = \"test-cluster\"\n)\n\nvar (\n\tcfg             *rest.Config\n\tk8s             client.Client\n\tenv             *envtest.Environment\n\tclusterProvider *cluster.Provider\n\ttestNS          string\n)\n\nvar _ = BeforeSuite(func(done Done) {\n\trand.Seed(GinkgoRandomSeed())\n\tlog.SetLogger(zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter)))\n\t// Use Kind for a more up-to-date K8s\n\tclusterProvider = cluster.NewProvider()\n\tExpect(clusterProvider.Create(clusterName)).To(Succeed())\n\tkubeconfig, err := clusterProvider.KubeConfig(clusterName, false)\n\tExpect(err).ToNot(HaveOccurred())\n\n\tcfg, err := clientcmd.RESTConfigFromKubeConfig([]byte(kubeconfig))\n\tExpect(err).ToNot(HaveOccurred())\n\tscheme := runtime.NewScheme()\n\tExpect(clientgoscheme.AddToScheme(scheme)).To(Succeed())\n\tExpect(v1beta1.AddToScheme(scheme)).To(Succeed())\n\tExpect(err).ToNot(HaveOccurred())\n\texistingCluster := true\n\tenv = &envtest.Environment{\n\t\tCRDDirectoryPaths:  []string{filepath.Join(\"..\", \"..\", \"config\", \"crd\")},\n\t\tUseExistingCluster: &existingCluster,\n\t\tConfig:             cfg,\n\t}\n\tcfg, err = env.Start()\n\tExpect(err).ToNot(HaveOccurred())\n\tExpect(cfg).ToNot(BeNil())\n\n\tk8s, err = client.New(cfg, client.Options{Scheme: scheme})\n\tExpect(err).ToNot(HaveOccurred())\n\tExpect(k8s).ToNot(BeNil())\n\n\ttestNS = fmt.Sprintf(\"test-%v\", time.Now().Unix())\n\tExpect(k8s.Create(context.Background(), &v1.Namespace{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName: testNS,\n\t\t},\n\t})).To(Succeed())\n\n\topts := ctrl.Options{\n\t\tScheme: scheme,\n\t}\n\n\tgo Start(context.TODO(), cfg, log.Log, opts)\n\tclose(done)\n})\n\nvar _ = AfterSuite(func() {\n\tExpect(clusterProvider.Delete(clusterName, \"\")).To(Succeed())\n\terr := env.Stop()\n\tExpect(err).ToNot(HaveOccurred())\n})\n\nfunc TestControllers(t *testing.T) {\n\tRegisterFailHandler(Fail)\n}\n"
  },
  {
    "path": "controllers/endpointslice.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage controllers\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-logr/logr\"\n\tdiscoveryv1 \"k8s.io/api/discovery/v1\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\n// EndpointSliceReconciler reconciles a EndpointSlice object\ntype EndpointSliceReconciler struct {\n\tclient.Client\n\tLog logr.Logger\n}\n\n// +kubebuilder:rbac:groups=discovery.k8s.io,resources=endpointslices,verbs=get;list;watch;update;patch\n\nfunc shouldIgnoreEndpointSlice(epSlice *discoveryv1.EndpointSlice) bool {\n\tif epSlice.DeletionTimestamp != nil {\n\t\treturn true\n\t}\n\tif epSlice.Labels[v1beta1.LabelServiceName] == \"\" {\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Reconcile the changes.\nfunc (r *EndpointSliceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {\n\tlog := r.Log.WithValues(\"endpointslice\", req.NamespacedName)\n\n\tvar epSlice discoveryv1.EndpointSlice\n\tif err := r.Client.Get(ctx, req.NamespacedName, &epSlice); err != nil {\n\t\treturn ctrl.Result{}, client.IgnoreNotFound(err)\n\t}\n\tif shouldIgnoreEndpointSlice(&epSlice) {\n\t\treturn ctrl.Result{}, nil\n\t}\n\t// Ensure the EndpointSlice is labelled to match the ServiceImport's derived\n\t// Service.\n\tserviceName := derivedName(types.NamespacedName{Namespace: epSlice.Namespace, Name: epSlice.Labels[v1beta1.LabelServiceName]})\n\tif epSlice.Labels[discoveryv1.LabelServiceName] == serviceName {\n\t\treturn ctrl.Result{}, nil\n\t}\n\tepSlice.Labels[discoveryv1.LabelServiceName] = serviceName\n\tif err := r.Client.Update(ctx, &epSlice); err != nil {\n\t\treturn ctrl.Result{}, err\n\t}\n\tlog.Info(\"added label\", discoveryv1.LabelServiceName, serviceName)\n\treturn ctrl.Result{}, nil\n}\n\n// SetupWithManager wires up the controller.\nfunc (r *EndpointSliceReconciler) SetupWithManager(mgr ctrl.Manager) error {\n\treturn ctrl.NewControllerManagedBy(mgr).For(&discoveryv1.EndpointSlice{}).Complete(r)\n}\n"
  },
  {
    "path": "controllers/endpointslice_test.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage controllers\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n\tdiscoveryv1 \"k8s.io/api/discovery/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\nvar _ = Describe(\"EndpointSlice\", func() {\n\tctx := context.Background()\n\tContext(\"should be ignored\", func() {\n\t\tSpecify(\"when not multi-cluster\", func() {\n\t\t\tExpect(shouldIgnoreEndpointSlice(&discoveryv1.EndpointSlice{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tNamespace: testNS,\n\t\t\t\t\tName:      \"no-mc-service\",\n\t\t\t\t},\n\t\t\t\tAddressType: discoveryv1.AddressTypeIPv4,\n\t\t\t})).To(BeTrue())\n\t\t})\n\t\tSpecify(\"when deleted\", func() {\n\t\t\tExpect(shouldIgnoreEndpointSlice(&discoveryv1.EndpointSlice{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tNamespace:         testNS,\n\t\t\t\t\tName:              \"deleted\",\n\t\t\t\t\tDeletionTimestamp: &metav1.Time{Time: time.Now()},\n\t\t\t\t},\n\t\t\t\tAddressType: discoveryv1.AddressTypeIPv4,\n\t\t\t})).To(BeTrue())\n\t\t})\n\t})\n\tContext(\"created with mc label\", func() {\n\t\tvar (\n\t\t\tserviceName        types.NamespacedName\n\t\t\tderivedServiceName types.NamespacedName\n\t\t\tsliceName          types.NamespacedName\n\t\t\tepSlice            discoveryv1.EndpointSlice\n\t\t)\n\t\tBeforeEach(func() {\n\t\t\tserviceName = types.NamespacedName{Namespace: testNS, Name: fmt.Sprintf(\"svc-%v\", rand.Uint64())}\n\t\t\tderivedServiceName = types.NamespacedName{Namespace: testNS, Name: derivedName(serviceName)}\n\t\t\tsliceName = types.NamespacedName{Namespace: testNS, Name: fmt.Sprintf(\"slice-%v\", rand.Uint64())}\n\t\t\tepSlice = discoveryv1.EndpointSlice{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tNamespace: testNS,\n\t\t\t\t\tName:      sliceName.Name,\n\t\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\tv1beta1.LabelServiceName: serviceName.Name,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tAddressType: discoveryv1.AddressTypeIPv4,\n\t\t\t}\n\t\t\tExpect(k8s.Create(ctx, &epSlice)).To(Succeed())\n\t\t})\n\t\tIt(\"has correct label\", func() {\n\t\t\tEventually(func() string {\n\t\t\t\tvar eps discoveryv1.EndpointSlice\n\t\t\t\tExpect(k8s.Get(ctx, sliceName, &eps)).Should(Succeed())\n\t\t\t\treturn eps.Labels[discoveryv1.LabelServiceName]\n\t\t\t}).Should(Equal(derivedServiceName.Name))\n\t\t})\n\t})\n\tContext(\"created with wrong label\", func() {\n\t\tvar (\n\t\t\tserviceName        types.NamespacedName\n\t\t\tderivedServiceName types.NamespacedName\n\t\t\tsliceName          types.NamespacedName\n\t\t\tepSlice            discoveryv1.EndpointSlice\n\t\t)\n\t\tBeforeEach(func() {\n\t\t\tserviceName = types.NamespacedName{Namespace: testNS, Name: fmt.Sprintf(\"svc-%v\", rand.Uint64())}\n\t\t\tderivedServiceName = types.NamespacedName{Namespace: testNS, Name: derivedName(serviceName)}\n\t\t\tsliceName = types.NamespacedName{Namespace: testNS, Name: fmt.Sprintf(\"slice-%v\", rand.Uint64())}\n\t\t\tepSlice = discoveryv1.EndpointSlice{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tNamespace: testNS,\n\t\t\t\t\tName:      sliceName.Name,\n\t\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\tv1beta1.LabelServiceName:     serviceName.Name,\n\t\t\t\t\t\tdiscoveryv1.LabelServiceName: serviceName.Name,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tAddressType: discoveryv1.AddressTypeIPv4,\n\t\t\t}\n\t\t\tExpect(k8s.Create(ctx, &epSlice)).To(Succeed())\n\t\t})\n\t\tIt(\"has correct label\", func() {\n\t\t\tEventually(func() string {\n\t\t\t\tvar eps discoveryv1.EndpointSlice\n\t\t\t\tExpect(k8s.Get(ctx, sliceName, &eps)).Should(Succeed())\n\t\t\t\treturn eps.Labels[discoveryv1.LabelServiceName]\n\t\t\t}).Should(Equal(derivedServiceName.Name))\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "controllers/go.mod",
    "content": "module sigs.k8s.io/mcs-api/controllers\n\ngo 1.23.0\n\nrequire (\n\tgithub.com/go-logr/logr v1.4.2\n\tgithub.com/onsi/ginkgo/v2 v2.22.0\n\tgithub.com/onsi/gomega v1.36.1\n\tk8s.io/api v0.32.5\n\tk8s.io/apimachinery v0.32.5\n\tk8s.io/client-go v0.32.5\n\tsigs.k8s.io/controller-runtime v0.20.4\n\tsigs.k8s.io/kind v0.23.0\n\tsigs.k8s.io/mcs-api v0.3.0\n)\n\nreplace sigs.k8s.io/mcs-api => ..\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.0.0 // indirect\n\tgithub.com/alessio/shellescape v1.4.1 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect\n\tgithub.com/emicklei/go-restful/v3 v3.11.0 // indirect\n\tgithub.com/evanphx/json-patch/v5 v5.9.11 // indirect\n\tgithub.com/fsnotify/fsnotify v1.7.0 // indirect\n\tgithub.com/fxamacker/cbor/v2 v2.7.0 // indirect\n\tgithub.com/go-logr/zapr v1.3.0 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.21.0 // indirect\n\tgithub.com/go-openapi/jsonreference v0.20.2 // indirect\n\tgithub.com/go-openapi/swag v0.23.0 // indirect\n\tgithub.com/go-task/slim-sprig/v3 v3.0.0 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/google/btree v1.1.3 // indirect\n\tgithub.com/google/gnostic-models v0.6.8 // indirect\n\tgithub.com/google/go-cmp v0.6.0 // indirect\n\tgithub.com/google/gofuzz v1.2.0 // indirect\n\tgithub.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect\n\tgithub.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/mailru/easyjson v0.7.7 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/pelletier/go-toml v1.9.4 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/prometheus/client_golang v1.19.1 // indirect\n\tgithub.com/prometheus/client_model v0.6.1 // indirect\n\tgithub.com/prometheus/common v0.55.0 // indirect\n\tgithub.com/prometheus/procfs v0.15.1 // indirect\n\tgithub.com/spf13/cobra v1.8.1 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgithub.com/x448/float16 v0.8.4 // indirect\n\tgo.uber.org/multierr v1.11.0 // indirect\n\tgo.uber.org/zap v1.27.0 // indirect\n\tgolang.org/x/net v0.30.0 // indirect\n\tgolang.org/x/oauth2 v0.23.0 // indirect\n\tgolang.org/x/sync v0.8.0 // indirect\n\tgolang.org/x/sys v0.26.0 // indirect\n\tgolang.org/x/term v0.25.0 // indirect\n\tgolang.org/x/text v0.19.0 // indirect\n\tgolang.org/x/time v0.7.0 // indirect\n\tgolang.org/x/tools v0.26.0 // indirect\n\tgomodules.xyz/jsonpatch/v2 v2.4.0 // indirect\n\tgoogle.golang.org/protobuf v1.35.1 // indirect\n\tgopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect\n\tgopkg.in/inf.v0 v0.9.1 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tk8s.io/apiextensions-apiserver v0.32.1 // indirect\n\tk8s.io/klog/v2 v2.130.1 // indirect\n\tk8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect\n\tk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect\n\tsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect\n\tsigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect\n\tsigs.k8s.io/yaml v1.4.0 // indirect\n)\n"
  },
  {
    "path": "controllers/go.sum",
    "content": "github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=\ngithub.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=\ngithub.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=\ngithub.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=\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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=\ngithub.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=\ngithub.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k=\ngithub.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=\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/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=\ngithub.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=\ngithub.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=\ngithub.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=\ngithub.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=\ngithub.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\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/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=\ngithub.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=\ngithub.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=\ngithub.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=\ngithub.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=\ngithub.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=\ngithub.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=\ngithub.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=\ngithub.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=\ngithub.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=\ngithub.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=\ngithub.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=\ngithub.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\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-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=\ngithub.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=\ngithub.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 h1:SJ+NtwL6QaZ21U+IrK7d0gGgpjGGvd2kz+FzTHVzdqI=\ngithub.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2/go.mod h1:Tv1PlzqC9t8wNnpPdctvtSUOPUUg4SHeE6vR1Ir2hmg=\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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\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/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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=\ngithub.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=\ngithub.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=\ngithub.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=\ngithub.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=\ngithub.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\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.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=\ngithub.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=\ngithub.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=\ngithub.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=\ngithub.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=\ngithub.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=\ngithub.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=\ngithub.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=\ngithub.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=\ngithub.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=\ngithub.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=\ngithub.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\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.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=\ngo.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=\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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=\ngolang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=\ngolang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=\ngolang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=\ngolang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=\ngolang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=\ngolang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=\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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=\ngolang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=\ngolang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=\ngolang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=\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-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=\ngolang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=\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=\ngomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=\ngomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=\ngoogle.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=\ngoogle.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=\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.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=\ngopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=\ngopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nk8s.io/api v0.32.5 h1:uqjjsYo1kTJr5NIcoIaP9F+TgXgADH7nKQx91FDAhtk=\nk8s.io/api v0.32.5/go.mod h1:bXXFU3fGCZ/eFMZvfHZC69PeGbXEL4zzjuPVzOxHF64=\nk8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw=\nk8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto=\nk8s.io/apimachinery v0.32.5 h1:6We3aJ6crC0ap8EhsEXcgX3LpI6SEjubpiOMXLROwPM=\nk8s.io/apimachinery v0.32.5/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=\nk8s.io/client-go v0.32.5 h1:huFmQMzgWu0z4kbWsuZci+Gt4Fo72I4CcrvhToZ/Qp0=\nk8s.io/client-go v0.32.5/go.mod h1:Qchw6f9WIVrur7DKojAHpRgGLcANT0RLIvF39Jz58xA=\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-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=\nk8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=\nk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=\nk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=\nsigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU=\nsigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY=\nsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=\nsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=\nsigs.k8s.io/kind v0.23.0 h1:8fyDGWbWTeCcCTwA04v4Nfr45KKxbSPH1WO9K+jVrBg=\nsigs.k8s.io/kind v0.23.0/go.mod h1:ZQ1iZuJLh3T+O8fzhdi3VWcFTzsdXtNv2ppsHc8JQ7s=\nsigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=\nsigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=\nsigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=\nsigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=\n"
  },
  {
    "path": "controllers/service.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage controllers\n\nimport (\n\t\"context\"\n\t\"slices\"\n\n\t\"github.com/go-logr/logr\"\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\n// ServiceReconciler reconciles a Service object\ntype ServiceReconciler struct {\n\tclient.Client\n\tLog logr.Logger\n}\n\n// +kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch\n\nfunc serviceImportOwner(refs []metav1.OwnerReference) string {\n\tfor _, ref := range refs {\n\t\tif ref.APIVersion == v1beta1.GroupVersion.String() && ref.Kind == serviceImportKind {\n\t\t\treturn ref.Name\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// Reconcile the changes.\nfunc (r *ServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {\n\tlog := r.Log.WithValues(\"service\", req.NamespacedName)\n\tvar service v1.Service\n\tif err := r.Client.Get(ctx, req.NamespacedName, &service); err != nil {\n\t\treturn ctrl.Result{}, client.IgnoreNotFound(err)\n\t}\n\tif service.DeletionTimestamp != nil {\n\t\treturn ctrl.Result{}, nil\n\t}\n\timportName := serviceImportOwner(service.OwnerReferences)\n\tif importName == \"\" {\n\t\treturn ctrl.Result{}, nil\n\t}\n\tvar svcImport v1beta1.ServiceImport\n\tif err := r.Client.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: importName}, &svcImport); err != nil {\n\t\treturn ctrl.Result{}, err\n\t}\n\n\tipsLen := min(2, len(service.Spec.ClusterIPs))\n\tdesiredIPs := service.Spec.ClusterIPs[:ipsLen]\n\tif service.Spec.ClusterIP == v1.ClusterIPNone {\n\t\tdesiredIPs = []string{}\n\t}\n\tif slices.Equal(desiredIPs, svcImport.Spec.IPs) {\n\t\treturn ctrl.Result{}, nil\n\t}\n\n\tsvcImport.Spec.IPs = desiredIPs\n\tif err := r.Client.Update(ctx, &svcImport); err != nil {\n\t\treturn ctrl.Result{}, err\n\t}\n\tlog.Info(\"updated serviceimport ip\", \"ip\", service.Spec.ClusterIP)\n\treturn ctrl.Result{}, nil\n}\n\n// SetupWithManager wires up the controller.\nfunc (r *ServiceReconciler) SetupWithManager(mgr ctrl.Manager) error {\n\treturn ctrl.NewControllerManagedBy(mgr).For(&v1.Service{}).Complete(r)\n}\n"
  },
  {
    "path": "controllers/serviceimport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage controllers\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-logr/logr\"\n\tv1 \"k8s.io/api/core/v1\"\n\tapierrors \"k8s.io/apimachinery/pkg/api/errors\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\n// ServiceImportReconciler reconciles a ServiceImport object\ntype ServiceImportReconciler struct {\n\tclient.Client\n\tLog logr.Logger\n}\n\n// +kubebuilder:rbac:groups=multicluster.x-k8s.io,resources=serviceimports,verbs=get;list;watch;update;patch\n\nfunc servicePorts(svcImport *v1beta1.ServiceImport) []v1.ServicePort {\n\tports := make([]v1.ServicePort, len(svcImport.Spec.Ports))\n\tfor i, p := range svcImport.Spec.Ports {\n\t\tports[i] = v1.ServicePort{\n\t\t\tName:        p.Name,\n\t\t\tProtocol:    p.Protocol,\n\t\t\tPort:        p.Port,\n\t\t\tAppProtocol: p.AppProtocol,\n\t\t}\n\t}\n\treturn ports\n}\n\nfunc shouldIgnoreImport(svcImport *v1beta1.ServiceImport) bool {\n\tif svcImport.DeletionTimestamp != nil {\n\t\treturn true\n\t}\n\tif svcImport.Spec.Type != v1beta1.ClusterSetIP {\n\t\treturn true\n\t}\n\treturn false\n}\n\n// +kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch\n// +kubebuilder:rbac:groups=core,resources=services/status,verbs=get;list;watch;create;update;patch\n\n// Reconcile the changes.\nfunc (r *ServiceImportReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {\n\tserviceName := derivedName(req.NamespacedName)\n\tlog := r.Log.WithValues(\"serviceimport\", req.NamespacedName, \"derived\", serviceName)\n\tvar svcImport v1beta1.ServiceImport\n\tif err := r.Client.Get(ctx, req.NamespacedName, &svcImport); err != nil {\n\t\treturn ctrl.Result{}, client.IgnoreNotFound(err)\n\t}\n\tif shouldIgnoreImport(&svcImport) {\n\t\treturn ctrl.Result{}, nil\n\t}\n\n\t// Ensure the existence of the derived service\n\tvar svc v1.Service\n\tif svcImport.Annotations[DerivedServiceAnnotation] == \"\" {\n\t\tif svcImport.Annotations == nil {\n\t\t\tsvcImport.Annotations = map[string]string{}\n\t\t}\n\t\tsvcImport.Annotations[DerivedServiceAnnotation] = derivedName(req.NamespacedName)\n\t\tif err := r.Client.Update(ctx, &svcImport); err != nil {\n\t\t\treturn ctrl.Result{}, err\n\t\t}\n\t\tlog.Info(\"added annotation\", DerivedServiceAnnotation, svcImport.Annotations[DerivedServiceAnnotation])\n\t\treturn ctrl.Result{}, nil\n\t}\n\tif err := r.Client.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: svcImport.Annotations[DerivedServiceAnnotation]}, &svc); err == nil {\n\t\treturn ctrl.Result{}, nil\n\t} else if !apierrors.IsNotFound(err) {\n\t\treturn ctrl.Result{}, err\n\t}\n\n\tsvc = v1.Service{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tNamespace: req.Namespace,\n\t\t\tName:      svcImport.Annotations[DerivedServiceAnnotation],\n\t\t\tOwnerReferences: []metav1.OwnerReference{\n\t\t\t\t{\n\t\t\t\t\tName:       req.Name,\n\t\t\t\t\tKind:       serviceImportKind,\n\t\t\t\t\tAPIVersion: v1beta1.GroupVersion.String(),\n\t\t\t\t\tUID:        svcImport.UID,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tSpec: v1.ServiceSpec{\n\t\t\tType:  v1.ServiceTypeClusterIP,\n\t\t\tPorts: servicePorts(&svcImport),\n\t\t},\n\t}\n\tif err := r.Client.Create(ctx, &svc); err != nil {\n\t\treturn ctrl.Result{}, err\n\t}\n\tlog.Info(\"created service\")\n\n\tif len(svcImport.Spec.IPs) == 0 {\n\t\treturn ctrl.Result{}, nil\n\t}\n\n\t// update loadbalanacer status with provided clustersetIPs\n\tingress := []v1.LoadBalancerIngress{}\n\tfor _, ip := range svcImport.Spec.IPs {\n\t\tingress = append(ingress, v1.LoadBalancerIngress{\n\t\t\tIP: ip,\n\t\t})\n\t}\n\n\tsvc.Status = v1.ServiceStatus{\n\t\tLoadBalancer: v1.LoadBalancerStatus{\n\t\t\tIngress: ingress,\n\t\t},\n\t}\n\n\tif err := r.Client.Status().Update(ctx, &svc); err != nil {\n\t\treturn ctrl.Result{}, err\n\t}\n\n\treturn ctrl.Result{}, nil\n}\n\n// SetupWithManager wires up the controller.\nfunc (r *ServiceImportReconciler) SetupWithManager(mgr ctrl.Manager) error {\n\treturn ctrl.NewControllerManagedBy(mgr).For(&v1beta1.ServiceImport{}).Complete(r)\n}\n"
  },
  {
    "path": "controllers/serviceimport_test.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage controllers\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\nvar _ = Describe(\"ServiceImport\", func() {\n\tvar (\n\t\tserviceImport      v1beta1.ServiceImport\n\t\tserviceName        types.NamespacedName\n\t\tderivedServiceName types.NamespacedName\n\t)\n\tctx := context.Background()\n\tContext(\"should be ignored\", func() {\n\t\tSpecify(\"when headless\", func() {\n\t\t\tExpect(shouldIgnoreImport(&v1beta1.ServiceImport{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tNamespace: testNS,\n\t\t\t\t\tName:      \"headless\",\n\t\t\t\t},\n\t\t\t\tSpec: v1beta1.ServiceImportSpec{\n\t\t\t\t\tType: v1beta1.Headless,\n\t\t\t\t\tPorts: []v1beta1.ServicePort{\n\t\t\t\t\t\t{Port: 80},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t})).To(BeTrue())\n\t\t})\n\t\tSpecify(\"when deleted\", func() {\n\t\t\tExpect(shouldIgnoreImport(&v1beta1.ServiceImport{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tNamespace:         testNS,\n\t\t\t\t\tName:              \"deleted\",\n\t\t\t\t\tDeletionTimestamp: &metav1.Time{Time: time.Now()},\n\t\t\t\t},\n\t\t\t\tSpec: v1beta1.ServiceImportSpec{\n\t\t\t\t\tType: v1beta1.ClusterSetIP,\n\t\t\t\t\tPorts: []v1beta1.ServicePort{\n\t\t\t\t\t\t{Port: 80},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t})).To(BeTrue())\n\t\t})\n\t})\n\tContext(\"created\", func() {\n\t\tBeforeEach(func() {\n\t\t\tserviceName = types.NamespacedName{Namespace: testNS, Name: fmt.Sprintf(\"svc-%v\", rand.Uint64())}\n\t\t\tderivedServiceName = types.NamespacedName{Namespace: testNS, Name: derivedName(serviceName)}\n\t\t\tserviceImport = v1beta1.ServiceImport{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tNamespace: testNS,\n\t\t\t\t\tName:      serviceName.Name,\n\t\t\t\t},\n\t\t\t\tSpec: v1beta1.ServiceImportSpec{\n\t\t\t\t\tType: v1beta1.ClusterSetIP,\n\t\t\t\t\tPorts: []v1beta1.ServicePort{\n\t\t\t\t\t\t{Port: 80},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t\tExpect(k8s.Create(ctx, &serviceImport)).To(Succeed())\n\t\t})\n\t\tIt(\"has derived service annotation\", func() {\n\t\t\tEventually(func() string {\n\t\t\t\tvar s v1beta1.ServiceImport\n\t\t\t\tExpect(k8s.Get(ctx, serviceName, &s)).To(Succeed())\n\t\t\t\treturn s.Annotations[DerivedServiceAnnotation]\n\t\t\t}, 10).Should(Equal(derivedName(serviceName)))\n\t\t}, 10)\n\t\tIt(\"has derived service IP\", func() {\n\t\t\tvar s v1beta1.ServiceImport\n\t\t\tEventually(func() string {\n\t\t\t\tExpect(k8s.Get(ctx, serviceName, &s)).To(Succeed())\n\t\t\t\tif len(s.Spec.IPs) > 0 {\n\t\t\t\t\treturn s.Spec.IPs[0]\n\t\t\t\t}\n\t\t\t\treturn \"\"\n\t\t\t}, 10).ShouldNot(BeEmpty())\n\t\t}, 15)\n\t\tIt(\"created derived service\", func() {\n\t\t\tvar s v1.Service\n\t\t\tEventually(func() error {\n\t\t\t\treturn k8s.Get(ctx, derivedServiceName, &s)\n\t\t\t}, 10).Should(Succeed())\n\t\t\tExpect(len(s.OwnerReferences)).To(Equal(1))\n\t\t\tExpect(s.OwnerReferences[0].UID).To(Equal(serviceImport.UID))\n\t\t}, 15)\n\t\tIt(\"removes derived service\", func() {\n\t\t\tvar s v1.Service\n\t\t\tEventually(func() error {\n\t\t\t\treturn k8s.Get(ctx, derivedServiceName, &s)\n\t\t\t}, 10).Should(Succeed())\n\t\t\tvar imp v1beta1.ServiceImport\n\t\t\tExpect(k8s.Get(ctx, serviceName, &imp)).To(Succeed())\n\t\t\tExpect(k8s.Delete(ctx, &imp)).To(Succeed())\n\t\t\tEventually(func() error {\n\t\t\t\treturn k8s.Get(ctx, derivedServiceName, &s)\n\t\t\t}, 15).ShouldNot(Succeed())\n\t\t}, 15)\n\t})\n\tContext(\"created with IP\", func() {\n\t\tBeforeEach(func() {\n\t\t\tserviceName = types.NamespacedName{Namespace: testNS, Name: fmt.Sprintf(\"svc-%v\", rand.Uint64())}\n\t\t\tderivedServiceName = types.NamespacedName{Namespace: testNS, Name: derivedName(serviceName)}\n\t\t\tserviceImport = v1beta1.ServiceImport{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tNamespace: testNS,\n\t\t\t\t\tName:      serviceName.Name,\n\t\t\t\t},\n\t\t\t\tSpec: v1beta1.ServiceImportSpec{\n\t\t\t\t\tType: v1beta1.ClusterSetIP,\n\t\t\t\t\tPorts: []v1beta1.ServicePort{\n\t\t\t\t\t\t{Port: 80},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t\tExpect(k8s.Create(ctx, &serviceImport)).To(Succeed())\n\t\t})\n\t\tIt(\"updates derived service IP\", func() {\n\t\t\tvar svcImport v1beta1.ServiceImport\n\t\t\tvar s v1.Service\n\t\t\tEventually(func() error {\n\t\t\t\treturn k8s.Get(ctx, derivedServiceName, &s)\n\t\t\t}, 10).Should(Succeed())\n\t\t\tEventually(func() string {\n\t\t\t\tExpect(k8s.Get(ctx, serviceName, &svcImport)).To(Succeed())\n\t\t\t\tif len(svcImport.Spec.IPs) > 0 {\n\t\t\t\t\treturn svcImport.Spec.IPs[0]\n\t\t\t\t}\n\t\t\t\treturn \"\"\n\t\t\t}, 10).Should(Equal(s.Spec.ClusterIP))\n\t\t}, 15)\n\t})\n\tContext(\"created with existing clustersetIP\", func() {\n\t\tBeforeEach(func() {\n\t\t\tserviceName = types.NamespacedName{Namespace: testNS, Name: fmt.Sprintf(\"svc-%v\", rand.Uint64())}\n\t\t\tderivedServiceName = types.NamespacedName{Namespace: testNS, Name: derivedName(serviceName)}\n\t\t\tserviceImport = v1beta1.ServiceImport{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tNamespace: testNS,\n\t\t\t\t\tName:      serviceName.Name,\n\t\t\t\t},\n\t\t\t\tSpec: v1beta1.ServiceImportSpec{\n\t\t\t\t\tType: v1beta1.ClusterSetIP,\n\t\t\t\t\tPorts: []v1beta1.ServicePort{\n\t\t\t\t\t\t{Port: 80},\n\t\t\t\t\t},\n\t\t\t\t\tIPs: []string{\"10.42.42.42\"},\n\t\t\t\t},\n\t\t\t}\n\t\t\tExpect(k8s.Create(ctx, &serviceImport)).To(Succeed())\n\t\t})\n\t\tIt(\"updates service loadbalancer status with service import IPs\", func() {\n\t\t\tvar svcImport v1beta1.ServiceImport\n\t\t\tvar s v1.Service\n\t\t\tEventually(func() error {\n\t\t\t\treturn k8s.Get(ctx, derivedServiceName, &s)\n\t\t\t}, 10).Should(Succeed())\n\t\t\tEventually(func() string {\n\t\t\t\tExpect(k8s.Get(ctx, serviceName, &svcImport)).To(Succeed())\n\t\t\t\tif len(svcImport.Spec.IPs) > 0 {\n\t\t\t\t\treturn svcImport.Spec.IPs[0]\n\t\t\t\t}\n\t\t\t\treturn \"\"\n\t\t\t}, 10).Should(Equal(s.Status.LoadBalancer.Ingress[0].IP))\n\t\t}, 15)\n\t})\n})\n"
  },
  {
    "path": "demo/.gitignore",
    "content": "*.kubeconfig\n*.tmp"
  },
  {
    "path": "demo/demo.sh",
    "content": "#!/bin/bash\n\n# Copyright 2020 The Kubernetes 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# Resolve the absolute path to this script directory.\ncd $(dirname \"${BASH_SOURCE[0]}\")\ndemo_dir=$(realpath \"$(pwd)\")\nscripts_dir=$(realpath \"$(pwd)/../scripts\")\n\n. \"${demo_dir}/udemo.sh\"\n. \"${scripts_dir}/util.sh\"\n\nDEMO_AUTO_RUN=true\n\nkubeconfig1=\"${KUBECONFIG1:-${scripts_dir}/c1.kubeconfig}\"\nkubeconfig2=\"${KUBECONFIG2:-${scripts_dir}/c2.kubeconfig}\"\n\nk1=\"kubectl --kubeconfig ${kubeconfig1}\"\nk2=\"kubectl --kubeconfig ${kubeconfig2}\"\n\ndesc \"Setup our demo namespace\"\nrun \"${k1} create ns demo\"\nrun \"${k2} create ns demo\"\n\nc1_pane=$(tmux split-window -h -d -P)\n\nfunction cleanup() {\n    tmux kill-pane -t \"$c1_pane\"\n}\ntrap cleanup EXIT\n\ntmux send -t \"$c1_pane\" \"${k1} logs -f mcs-api-controller\" Enter\n\ndesc \"Create our service in each cluster\"\nrun \"${k1} apply -f ${demo_dir}/yaml/dep1.yaml -f ${demo_dir}/yaml/svc.yaml\"\nrun \"${k2} apply -f ${demo_dir}/yaml/dep2.yaml -f ${demo_dir}/yaml/svc.yaml\"\nrun \"${k1} get endpointslice -n demo\"\n\ndesc \"Lets look at some requests to the service in cluster 1\"\nrun \"${k1} -n demo run -i --rm --restart=Never --image=jeremyot/request:0a40de8 request -- --duration=5s --address=serve.demo.svc.cluster.local\"\n\ndesc \"Ok, looks normal. Let's import the service from our other cluster\"\nep_1=$(${k1} get endpointslice -n demo -l 'kubernetes.io/service-name=serve' --template=\"{{(index .items 0).metadata.name}}\")\nep_2=$(${k2} get endpointslice -n demo -l 'kubernetes.io/service-name=serve' --template=\"{{(index .items 0).metadata.name}}\")\n\nrun \"${k1} get endpointslice -n demo ${ep_1} -o yaml | ${demo_dir}/edit-meta --metadata '{name: imported-${ep_1}, namespace: demo, labels: {multicluster.kubernetes.io/service-name: serve}}' > ${demo_dir}/yaml/slice-1.tmp\"\nrun \"${k2} get endpointslice -n demo ${ep_2} -o yaml | ${demo_dir}/edit-meta --metadata '{name: imported-${ep_2}, namespace: demo, labels: {multicluster.kubernetes.io/service-name: serve}}' > ${demo_dir}/yaml/slice-2.tmp\"\nrun \"${k1} apply -f ${demo_dir}/yaml/serviceimport.yaml -f ${demo_dir}/yaml/slice-1.tmp -f ${demo_dir}/yaml/slice-2.tmp\"\nrun \"${k2} apply -f ${demo_dir}/yaml/serviceimport.yaml -f ${demo_dir}/yaml/slice-1.tmp -f ${demo_dir}/yaml/slice-2.tmp\"\nrun \"${k1} apply -f ${demo_dir}/yaml/serviceimport-with-vip.yaml -f ${demo_dir}/yaml/slice-1.tmp -f ${demo_dir}/yaml/slice-2.tmp\"\nrun \"${k2} apply -f ${demo_dir}/yaml/serviceimport-with-vip.yaml -f ${demo_dir}/yaml/slice-1.tmp -f ${demo_dir}/yaml/slice-2.tmp\"\n\ndesc \"See what we've created...\"\nrun \"${k1} get -n demo serviceimports\"\nrun \"${k1} get -n demo endpointslice\"\nrun \"${k1} get -n demo service\"\n\nfunction import_ip() {\n    ${k1} get serviceimport -n demo -o go-template --template='{{index (index .items 0).spec.ips 0}}'\n}\n\nwaitfor import_ip\n\nvip=$(${k1} get serviceimport -n demo -o go-template --template='{{index (index .items 0).spec.ips 0}}')\ndesc \"Now grab the multi-cluster VIP from the serviceimport...\"\nrun \"${k1} get serviceimport -n demo -o go-template --template='{{index (index .items 0).spec.ips 0}}{{\\\"\\n\\\"}}'\"\ndesc \"...and connect to it\"\nrun \"${k1} -n demo run -i --rm --restart=Never --image=jeremyot/request:0a40de8 request -- --duration=10s --address=${vip}\"\nrun \"${k1} -n demo run -i --rm --restart=Never --image=jeremyot/request:0a40de8 request -- --duration=10s --address=serve.demo.svc.clusterset.local\"\ndesc \"We have a multi-cluster service!\"\ndesc \"See for yourself\"\ndesc \"Cluster 1: kubectl --kubeconfig ${kubeconfig1} -n demo\"\ndesc \"Cluster 2: kubectl --kubeconfig ${kubeconfig2} -n demo\"\ndesc \"(Enter to exit)\"\nread -s\n"
  },
  {
    "path": "demo/edit-meta",
    "content": "#!/usr/bin/env python3\n\nimport sys\nimport argparse\nimport yaml\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser()\n    parser.add_argument('-m', '--metadata', dest='metadata', help=\"Overwrite the resource's metadata with this value, may be any valid YAML\")\n    args = parser.parse_args()\n\n    data = yaml.load(sys.stdin, Loader=yaml.Loader)\n    if args.metadata:\n        value = yaml.load(args.metadata, Loader=yaml.Loader)\n        data['metadata'] = value\n\n    print(yaml.dump(data, indent=2, default_flow_style=False, Dumper=yaml.Dumper))\n"
  },
  {
    "path": "demo/reset.sh",
    "content": "#!/bin/bash\n\n# Copyright 2020 The Kubernetes 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\ncd $(dirname ${BASH_SOURCE})\n. ./udemo.sh\n\nc1=c1\nc2=c2\nk1=\"kubectl --kubeconfig ${c1}.kubeconfig\"\nk2=\"kubectl --kubeconfig ${c2}.kubeconfig\"\n\n${k1} delete ns demo\n${k2} delete ns demo"
  },
  {
    "path": "demo/udemo.sh",
    "content": "#!/bin/bash\n# Copyright 2016 The Kubernetes 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\nreadonly  reset=$(tput sgr0)\nreadonly  green=$(tput bold; tput setaf 2)\nreadonly yellow=$(tput bold; tput setaf 3)\nreadonly   blue=$(tput bold; tput setaf 6)\nreadonly timeout=$(if [ \"$(uname)\" == \"Darwin\" ]; then echo \"1\"; else echo \"0.1\"; fi)\n\nfunction desc() {\n    maybe_first_prompt\n    echo \"$blue# $@$reset\"\n    prompt\n}\n\nfunction prompt() {\n    echo -n \"$yellow\\$ $reset\"\n}\n\nstarted=\"\"\nfunction maybe_first_prompt() {\n    if [ -z \"$started\" ]; then\n        prompt\n        started=true\n    fi\n}\n\n# After a `run` this variable will hold the stdout of the command that was run.\n# If the command was interactive, this will likely be garbage.\nDEMO_RUN_STDOUT=\"\"\n\nfunction run() {\n    maybe_first_prompt\n    rate=25\n    if [ -n \"$DEMO_RUN_FAST\" ]; then\n      rate=1000\n    fi\n    echo \"$green$1$reset\" | pv -qL $rate\n    if [ -n \"$DEMO_RUN_FAST\" ]; then\n      sleep 0.5\n    fi\n    OFILE=\"$(mktemp -t $(basename $0).XXXXXX)\"\n    if [[ \"$OSTYPE\" == \"darwin\"* ]]; then\n        script -q -a \"$OFILE\" bash -c \"$1\"\n    else\n        script -eq -c \"$1\" -f \"$OFILE\"\n    fi\n    r=$?\n    read -d '' -t \"${timeout}\" -n 10000 # clear stdin\n    prompt\n    if [ -z \"$DEMO_AUTO_RUN\" ]; then\n      read -s\n    fi\n    DEMO_RUN_STDOUT=\"$(tail -n +2 $OFILE | sed 's/\\r//g')\"\n    return $r\n}\n\nfunction relative() {\n    for arg; do\n        echo \"$(realpath $(dirname $(which $0)))/$arg\" | sed \"s|$(realpath $(pwd))|.|\"\n    done\n}\n\nSSH_NODE=$(kubectl get nodes | tail -1 | cut -f1 -d' ')\n\ntrap \"echo\" EXIT\n"
  },
  {
    "path": "demo/yaml/dep1.yaml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: serve\n  namespace: demo\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: serve\n  template:\n    metadata:\n      labels:\n        app: serve\n    spec:\n      containers:\n      - name: serve\n        image: jeremyot/serve:0a40de8\n        args:\n        - \"--message='hello from cluster 1 (Node: {{env \\\"NODE_NAME\\\"}} Pod: {{env \\\"POD_NAME\\\"}} Address: {{addr}})'\"\n        env:\n          - name: NODE_NAME\n            valueFrom:\n              fieldRef:\n                fieldPath: spec.nodeName\n          - name: POD_NAME\n            valueFrom:\n              fieldRef:\n                fieldPath: metadata.name"
  },
  {
    "path": "demo/yaml/dep2.yaml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: serve\n  namespace: demo\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: serve\n  template:\n    metadata:\n      labels:\n        app: serve\n    spec:\n      containers:\n      - name: serve\n        image: jeremyot/serve:0a40de8\n        args:\n        - \"--message='hello from cluster 2 (Node: {{env \\\"NODE_NAME\\\"}} Pod: {{env \\\"POD_NAME\\\"}} Address: {{addr}})'\"\n        env:\n          - name: NODE_NAME\n            valueFrom:\n              fieldRef:\n                fieldPath: spec.nodeName\n          - name: POD_NAME\n            valueFrom:\n              fieldRef:\n                fieldPath: metadata.name"
  },
  {
    "path": "demo/yaml/serviceimport-with-vip.yaml",
    "content": "apiVersion: multicluster.x-k8s.io/v1beta1\nkind: ServiceImport\nmetadata:\n  name: serve-with-vip\n  namespace: demo\nspec:\n  type: ClusterSetIP\n  ips:\n  - 1.2.3.4\n  ports:\n  - port: 80\n    protocol: TCP\n"
  },
  {
    "path": "demo/yaml/serviceimport.yaml",
    "content": "apiVersion: multicluster.x-k8s.io/v1beta1\nkind: ServiceImport\nmetadata:\n  name: serve\n  namespace: demo\nspec:\n  type: ClusterSetIP\n  ports:\n  - port: 80\n    protocol: TCP\n"
  },
  {
    "path": "demo/yaml/svc.yaml",
    "content": "apiVersion: v1\nkind: Service\nmetadata:\n  name: serve\n  namespace: demo\nspec:\n  ports:\n  - port: 80\n    targetPort: 8080\n  selector:\n    app: serve\n"
  },
  {
    "path": "e2e/connectivity_test.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage e2etest\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"strings\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\nvar (\n\treplicaCount = int32(1)\n\thelloService = v1.Service{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName: \"hello\",\n\t\t},\n\t\tSpec: v1.ServiceSpec{\n\t\t\tSelector: map[string]string{\n\t\t\t\t\"app\": \"hello\",\n\t\t\t},\n\t\t\tPorts: []v1.ServicePort{\n\t\t\t\t{\n\t\t\t\t\tName:     \"tcp\",\n\t\t\t\t\tPort:     42,\n\t\t\t\t\tProtocol: v1.ProtocolTCP,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName:     \"udp\",\n\t\t\t\t\tPort:     42,\n\t\t\t\t\tProtocol: v1.ProtocolUDP,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\thelloServiceImport = v1beta1.ServiceImport{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName: \"hello\",\n\t\t},\n\t\tSpec: v1beta1.ServiceImportSpec{\n\t\t\tType: v1beta1.ClusterSetIP,\n\t\t\tPorts: []v1beta1.ServicePort{\n\t\t\t\t{\n\t\t\t\t\tName:     \"tcp\",\n\t\t\t\t\tPort:     42,\n\t\t\t\t\tProtocol: v1.ProtocolTCP,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName:     \"udp\",\n\t\t\t\t\tPort:     42,\n\t\t\t\t\tProtocol: v1.ProtocolUDP,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\thelloDeployment = appsv1.Deployment{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName: \"hello\",\n\t\t},\n\t\tSpec: appsv1.DeploymentSpec{\n\t\t\tReplicas: &replicaCount,\n\t\t\tSelector: &metav1.LabelSelector{\n\t\t\t\tMatchLabels: map[string]string{\n\t\t\t\t\t\"app\": \"hello\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tTemplate: v1.PodTemplateSpec{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tLabels: map[string]string{\"app\": \"hello\"},\n\t\t\t\t},\n\t\t\t\tSpec: v1.PodSpec{\n\t\t\t\t\tContainers: []v1.Container{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:  \"hello-tcp\",\n\t\t\t\t\t\t\tImage: \"alpine/socat:1.7.4.4\",\n\t\t\t\t\t\t\tArgs:  []string{\"-v\", \"-v\", \"TCP-LISTEN:42,crlf,reuseaddr,fork\", \"SYSTEM:echo $(MY_POD_IP)\"},\n\t\t\t\t\t\t\tEnv: []v1.EnvVar{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName: \"MY_POD_IP\",\n\t\t\t\t\t\t\t\t\tValueFrom: &v1.EnvVarSource{\n\t\t\t\t\t\t\t\t\t\tFieldRef: &v1.ObjectFieldSelector{\n\t\t\t\t\t\t\t\t\t\t\tFieldPath: \"status.podIP\",\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\t{\n\t\t\t\t\t\t\tName:  \"hello-udp\",\n\t\t\t\t\t\t\tImage: \"alpine/socat:1.7.4.4\",\n\t\t\t\t\t\t\tArgs:  []string{\"-v\", \"-v\", \"UDP-LISTEN:42,crlf,reuseaddr,fork\", \"SYSTEM:echo $(MY_POD_IP)\"},\n\t\t\t\t\t\t\tEnv: []v1.EnvVar{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName: \"MY_POD_IP\",\n\t\t\t\t\t\t\t\t\tValueFrom: &v1.EnvVarSource{\n\t\t\t\t\t\t\t\t\t\tFieldRef: &v1.ObjectFieldSelector{\n\t\t\t\t\t\t\t\t\t\t\tFieldPath: \"status.podIP\",\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\trequestPod = v1.Pod{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName:   \"request\",\n\t\t\tLabels: map[string]string{\"app\": \"request\"},\n\t\t},\n\t\tSpec: v1.PodSpec{\n\t\t\tContainers: []v1.Container{\n\t\t\t\t{\n\t\t\t\t\tName:  \"request\",\n\t\t\t\t\tImage: \"busybox\",\n\t\t\t\t\tArgs:  []string{\"/bin/sh\", \"-ec\", \"while :; do echo '.'; sleep 5 ; done\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n)\n\nvar _ = Describe(\"Connectivity\", func() {\n\tvar (\n\t\tnamespace string\n\n\t\tctx           = context.Background()\n\t\tserviceImport *v1beta1.ServiceImport\n\t\tpodIPs        []string\n\t\treqPod        *v1.Pod\n\n\t\tverifyConnectivity = func() {\n\t\t\tcheckPodReachable := func(command []string) {\n\t\t\t\tips := map[string]int{}\n\t\t\t\tEventually(func(g Gomega) {\n\t\t\t\t\tstdout, _, err := execCmd(cluster1.k8s, restcfg1, reqPod.Name, reqPod.Namespace, command)\n\t\t\t\t\tg.Expect(err).ToNot(HaveOccurred())\n\t\t\t\t\tip := strings.TrimSpace(string(stdout))\n\t\t\t\t\tg.Expect(ip).To(BeElementOf(podIPs))\n\t\t\t\t\tips[ip]++\n\t\t\t\t}, \"5m\").MustPassRepeatedly(50).Should(Succeed())\n\t\t\t\tExpect(ips).To(HaveEach(Not(BeZero())))\n\t\t\t}\n\n\t\t\tSpecify(\"UDP connects across clusters using the VIP\", func() {\n\t\t\t\tcommand := []string{\"sh\", \"-c\", fmt.Sprintf(\"echo hi | nc -uw1 %s 42\", serviceImport.Spec.IPs[0])}\n\t\t\t\tcheckPodReachable(command)\n\t\t\t})\n\t\t\tSpecify(\"TCP connects across clusters using the VIP\", func() {\n\t\t\t\tcommand := []string{\"sh\", \"-c\", fmt.Sprintf(\"echo hi | nc %s 42\", serviceImport.Spec.IPs[0])}\n\t\t\t\tcheckPodReachable(command)\n\t\t\t})\n\t\t\tSpecify(\"UDP connects across clusters using the DNS name\", func() {\n\t\t\t\tcommand := []string{\"sh\", \"-c\", fmt.Sprintf(\"echo hi | nc -uw1 %s.%s.svc.clusterset.local 42\", serviceImport.Name, serviceImport.Namespace)}\n\t\t\t\tcheckPodReachable(command)\n\t\t\t})\n\t\t\tSpecify(\"TCP connects across clusters using the DNS name\", func() {\n\t\t\t\tcommand := []string{\"sh\", \"-c\", fmt.Sprintf(\"echo hi | nc %s.%s.svc.clusterset.local 42\", serviceImport.Name, serviceImport.Namespace)}\n\t\t\t\tcheckPodReachable(command)\n\t\t\t})\n\t\t}\n\t)\n\tBeforeEach(func() {\n\t\tnamespace = fmt.Sprintf(\"mcse2e-conformance-%v\", rand.Uint32())\n\t\t_, err := cluster1.k8s.CoreV1().Namespaces().Create(ctx, &v1.Namespace{\n\t\t\tObjectMeta: metav1.ObjectMeta{Name: namespace},\n\t\t}, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\t_, err = cluster2.k8s.CoreV1().Namespaces().Create(ctx, &v1.Namespace{\n\t\t\tObjectMeta: metav1.ObjectMeta{Name: namespace},\n\t\t}, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\tpod := requestPod\n\t\t_, err = cluster1.k8s.CoreV1().Pods(namespace).Create(ctx, &pod, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\tdep := helloDeployment\n\t\t_, err = cluster2.k8s.AppsV1().Deployments(namespace).Create(ctx, &dep, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\tsvc := helloService\n\t\t_, err = cluster2.k8s.CoreV1().Services(namespace).Create(ctx, &svc, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\timp := helloServiceImport\n\t\t_, err = cluster1.mcs.MulticlusterV1beta1().ServiceImports(namespace).Create(ctx, &imp, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\tEventually(func() string {\n\t\t\trp, err := cluster1.k8s.CoreV1().Pods(namespace).Get(ctx, requestPod.Name, metav1.GetOptions{})\n\t\t\tExpect(err).ToNot(HaveOccurred())\n\t\t\treturn rp.Name\n\t\t}).ShouldNot(BeEmpty())\n\t\tEventually(func() string {\n\t\t\tpods, err := cluster2.k8s.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{\n\t\t\t\tLabelSelector: metav1.FormatLabelSelector(helloDeployment.Spec.Selector),\n\t\t\t})\n\t\t\tExpect(err).ToNot(HaveOccurred())\n\t\t\tif len(pods.Items) > 0 {\n\t\t\t\treturn pods.Items[0].Status.PodIP\n\t\t\t}\n\t\t\treturn \"\"\n\t\t}, 30).ShouldNot(BeEmpty())\n\t\tpods, err := cluster2.k8s.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{\n\t\t\tLabelSelector: metav1.FormatLabelSelector(helloDeployment.Spec.Selector),\n\t\t})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\tfor _, pod := range pods.Items {\n\t\t\tpodIPs = append(podIPs, pod.Status.PodIP)\n\t\t}\n\n\t\texportService(ctx, cluster2, cluster1, namespace, svc.Name)\n\n\t\tEventually(func() []string {\n\t\t\tsvcImport, err := cluster1.mcs.MulticlusterV1beta1().ServiceImports(namespace).Get(ctx, helloServiceImport.Name, metav1.GetOptions{})\n\t\t\tExpect(err).ToNot(HaveOccurred())\n\t\t\treturn svcImport.Spec.IPs\n\t\t}).ShouldNot(BeEmpty())\n\t\tserviceImport, err = cluster1.mcs.MulticlusterV1beta1().ServiceImports(namespace).Get(ctx, helloServiceImport.Name, metav1.GetOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\n\t\treqPod, err = cluster1.k8s.CoreV1().Pods(namespace).Get(ctx, requestPod.Name, metav1.GetOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\tBy(\"Created all in \" + namespace)\n\t})\n\tAfterEach(func() {\n\t\tif *noTearDown {\n\t\t\tBy(fmt.Sprintf(\"Skipping teardown. Test namespace %q\", namespace))\n\t\t\tBy(fmt.Sprintf(\"Cluster 1: kubectl --kubeconfig %q -n %q\", *kubeconfig1, namespace))\n\t\t\tBy(fmt.Sprintf(\"Cluster 2: kubectl --kubeconfig %q -n %q\", *kubeconfig2, namespace))\n\t\t\treturn\n\t\t}\n\t\tExpect(cluster1.k8s.CoreV1().Namespaces().Delete(ctx, namespace, metav1.DeleteOptions{})).To(Succeed())\n\t\tExpect(cluster2.k8s.CoreV1().Namespaces().Delete(ctx, namespace, metav1.DeleteOptions{})).To(Succeed())\n\t})\n\n\tWhen(\"trying to reach a service exported from only the remote cluster\", func() {\n\t\tverifyConnectivity()\n\t})\n\n\tWhen(\"trying to reach a service exported by both the local and remote clusters\", func() {\n\t\tBeforeEach(func() {\n\t\t\tdep := helloDeployment\n\t\t\t_, err := cluster1.k8s.AppsV1().Deployments(namespace).Create(ctx, &dep, metav1.CreateOptions{})\n\t\t\tExpect(err).ToNot(HaveOccurred())\n\t\t\tsvc := helloService\n\t\t\t_, err = cluster1.k8s.CoreV1().Services(namespace).Create(ctx, &svc, metav1.CreateOptions{})\n\t\t\tExpect(err).ToNot(HaveOccurred())\n\t\t\tEventually(func() string {\n\t\t\t\tpods, err := cluster1.k8s.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{\n\t\t\t\t\tLabelSelector: metav1.FormatLabelSelector(helloDeployment.Spec.Selector),\n\t\t\t\t})\n\t\t\t\tExpect(err).ToNot(HaveOccurred())\n\t\t\t\tif len(pods.Items) > 0 {\n\t\t\t\t\treturn pods.Items[0].Status.PodIP\n\t\t\t\t}\n\t\t\t\treturn \"\"\n\t\t\t}, 30).ShouldNot(BeEmpty())\n\t\t\tpods, err := cluster1.k8s.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{\n\t\t\t\tLabelSelector: metav1.FormatLabelSelector(helloDeployment.Spec.Selector),\n\t\t\t})\n\t\t\tExpect(err).ToNot(HaveOccurred())\n\t\t\tfor _, pod := range pods.Items {\n\t\t\t\tpodIPs = append(podIPs, pod.Status.PodIP)\n\t\t\t}\n\t\t\texportService(ctx, cluster1, cluster1, namespace, svc.Name)\n\t\t})\n\n\t\tverifyConnectivity()\n\t})\n})\n"
  },
  {
    "path": "e2e/e2e_suite_test.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage e2etest\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"flag\"\n\t\"math/rand\"\n\t\"os\"\n\t\"strconv\"\n\t\"testing\"\n\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n\t\"k8s.io/client-go/kubernetes/scheme\"\n\trestclient \"k8s.io/client-go/rest\"\n\t\"k8s.io/client-go/tools/remotecommand\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n\tv1 \"k8s.io/api/core/v1\"\n\tdiscoveryv1 \"k8s.io/api/discovery/v1\"\n\t\"k8s.io/client-go/kubernetes\"\n\t\"k8s.io/client-go/tools/clientcmd\"\n\t\"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n\tmcsclient \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned\"\n)\n\nvar (\n\tkubeconfig1 = flag.String(\"kubeconfig1\", os.Getenv(\"KUBECONFIG1\"), \"The path to a kubeconfig for cluster 1\")\n\tkubeconfig2 = flag.String(\"kubeconfig2\", os.Getenv(\"KUBECONFIG2\"), \"The path to a kubeconfig for cluster 2\")\n\tnoTearDown  = flag.Bool(\"no-tear-down\", tryParseBool(os.Getenv(\"NO_TEAR_DOWN\")), \"Don't tear down after test (useful for debugging failures).\")\n\tcluster1    clusterClients\n\tcluster2    clusterClients\n\trestcfg1, _ = clientcmd.BuildConfigFromFlags(\"\", *kubeconfig1)\n\t//restcfg2, _ = clientcmd.BuildConfigFromFlags(\"\", *kubeconfig2)\n)\n\nfunc tryParseBool(s string) bool {\n\tb, _ := strconv.ParseBool(s)\n\treturn b\n}\n\ntype clusterClients struct {\n\tk8s kubernetes.Interface\n\tmcs mcsclient.Interface\n}\n\nfunc TestE2E(t *testing.T) {\n\tflag.Parse()\n\tRegisterFailHandler(Fail)\n\tRunSpecs(t, \"E2E Suite\")\n}\n\nvar _ = BeforeSuite(func() {\n\trand.Seed(GinkgoRandomSeed())\n\n\tExpect(*kubeconfig1).ToNot(BeEmpty(), \"either --kubeconfig1 or KUBECONFIG1 must be set\")\n\tExpect(*kubeconfig2).ToNot(BeEmpty(), \"either --kubeconfig2 or KUBECONFIG2 must be set\")\n\n\trestcfg1, err := clientcmd.BuildConfigFromFlags(\"\", *kubeconfig1)\n\tExpect(err).ToNot(HaveOccurred())\n\trestcfg2, err := clientcmd.BuildConfigFromFlags(\"\", *kubeconfig2)\n\tExpect(err).ToNot(HaveOccurred())\n\n\tcluster1 = clusterClients{\n\t\tk8s: kubernetes.NewForConfigOrDie(restcfg1),\n\t\tmcs: mcsclient.NewForConfigOrDie(restcfg1),\n\t}\n\tcluster2 = clusterClients{\n\t\tk8s: kubernetes.NewForConfigOrDie(restcfg2),\n\t\tmcs: mcsclient.NewForConfigOrDie(restcfg2),\n\t}\n})\n\nfunc execCmd(k8s kubernetes.Interface, config *restclient.Config, podName string, podNamespace string, command []string) ([]byte, []byte, error) {\n\treq := k8s.CoreV1().RESTClient().Post().Resource(\"pods\").Name(podName).Namespace(podNamespace).SubResource(\"exec\")\n\treq.VersionedParams(&v1.PodExecOptions{\n\t\tCommand: command,\n\t\tStdin:   false,\n\t\tStdout:  true,\n\t\tStderr:  true,\n\t\tTTY:     true,\n\t}, scheme.ParameterCodec)\n\texec, err := remotecommand.NewSPDYExecutor(config, \"POST\", req.URL())\n\tif err != nil {\n\t\treturn []byte{}, []byte{}, err\n\t}\n\tvar stdout, stderr bytes.Buffer\n\terr = exec.Stream(remotecommand.StreamOptions{\n\t\tStdin:  nil,\n\t\tStdout: &stdout,\n\t\tStderr: &stderr,\n\t})\n\tif err != nil {\n\t\treturn []byte{}, []byte{}, err\n\t}\n\treturn stdout.Bytes(), stderr.Bytes(), nil\n}\n\nfunc exportService(ctx context.Context, fromCluster, toCluster clusterClients, namespace string, svcName string) {\n\t_, err := fromCluster.mcs.MulticlusterV1beta1().ServiceExports(namespace).Create(ctx, &v1beta1.ServiceExport{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName: svcName,\n\t\t},\n\t}, metav1.CreateOptions{})\n\tExpect(err).ToNot(HaveOccurred())\n\tvar slices *discoveryv1.EndpointSliceList\n\tEventually(func() int {\n\t\teps := 0\n\t\tslices, err = fromCluster.k8s.DiscoveryV1().EndpointSlices(namespace).List(ctx, metav1.ListOptions{\n\t\t\tLabelSelector: labels.Set{discoveryv1.LabelServiceName: svcName}.AsSelector().String(),\n\t\t})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\tfor _, s := range slices.Items {\n\t\t\teps += len(s.Endpoints)\n\t\t}\n\t\treturn eps\n\t}, 30).Should(Equal(1))\n\timportedSlice := slices.Items[0] // This direct indexing is ok because we just asserted above that there is exactly one element here\n\timportedSlice.ObjectMeta = metav1.ObjectMeta{\n\t\tGenerateName: svcName + \"-\",\n\t\tLabels: map[string]string{\n\t\t\tv1beta1.LabelServiceName: svcName,\n\t\t},\n\t}\n\n\tcreatedSlice, err := toCluster.k8s.DiscoveryV1().EndpointSlices(namespace).Create(ctx, &importedSlice, metav1.CreateOptions{})\n\tExpect(err).ToNot(HaveOccurred())\n\n\tEventually(func() string {\n\t\tupdatedSlice, err := toCluster.k8s.DiscoveryV1().EndpointSlices(namespace).Get(ctx, createdSlice.Name, metav1.GetOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\treturn updatedSlice.Labels[discoveryv1.LabelServiceName]\n\t}).ShouldNot(BeEmpty())\n}\n"
  },
  {
    "path": "e2e/go.mod",
    "content": "module sigs.k8s.io/mcs-api/e2e\n\ngo 1.23.0\n\nrequire (\n\tgithub.com/onsi/ginkgo/v2 v2.21.0\n\tgithub.com/onsi/gomega v1.35.1\n\tk8s.io/api v0.32.5\n\tk8s.io/apimachinery v0.32.5\n\tk8s.io/client-go v0.32.5\n\tsigs.k8s.io/mcs-api v0.3.0\n)\n\nreplace sigs.k8s.io/mcs-api => ..\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect\n\tgithub.com/emicklei/go-restful/v3 v3.11.0 // indirect\n\tgithub.com/fxamacker/cbor/v2 v2.7.0 // indirect\n\tgithub.com/go-logr/logr v1.4.2 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.21.0 // indirect\n\tgithub.com/go-openapi/jsonreference v0.20.2 // indirect\n\tgithub.com/go-openapi/swag v0.23.0 // indirect\n\tgithub.com/go-task/slim-sprig/v3 v3.0.0 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/google/gnostic-models v0.6.8 // indirect\n\tgithub.com/google/go-cmp v0.6.0 // indirect\n\tgithub.com/google/gofuzz v1.2.0 // indirect\n\tgithub.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/websocket v1.5.0 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/mailru/easyjson v0.7.7 // indirect\n\tgithub.com/moby/spdystream v0.5.0 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgithub.com/x448/float16 v0.8.4 // indirect\n\tgolang.org/x/net v0.30.0 // indirect\n\tgolang.org/x/oauth2 v0.23.0 // indirect\n\tgolang.org/x/sys v0.26.0 // indirect\n\tgolang.org/x/term v0.25.0 // indirect\n\tgolang.org/x/text v0.19.0 // indirect\n\tgolang.org/x/time v0.7.0 // indirect\n\tgolang.org/x/tools v0.26.0 // indirect\n\tgoogle.golang.org/protobuf v1.35.1 // indirect\n\tgopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect\n\tgopkg.in/inf.v0 v0.9.1 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tk8s.io/klog/v2 v2.130.1 // indirect\n\tk8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect\n\tk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect\n\tsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect\n\tsigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect\n\tsigs.k8s.io/yaml v1.4.0 // indirect\n)\n"
  },
  {
    "path": "e2e/go.sum",
    "content": "github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=\ngithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=\ngithub.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=\ngithub.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=\ngithub.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=\ngithub.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=\ngithub.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=\ngithub.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=\ngithub.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=\ngithub.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=\ngithub.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=\ngithub.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=\ngithub.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=\ngithub.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=\ngithub.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=\ngithub.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=\ngithub.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\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-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=\ngithub.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=\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/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=\ngithub.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=\ngithub.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=\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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=\ngithub.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=\ngithub.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=\ngithub.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=\ngithub.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=\ngithub.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=\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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=\ngithub.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=\ngithub.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=\ngolang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=\ngolang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=\ngolang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=\ngolang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=\ngolang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=\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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=\ngolang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=\ngolang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=\ngolang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=\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-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=\ngolang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=\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=\ngoogle.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=\ngoogle.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=\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.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=\ngopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=\ngopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nk8s.io/api v0.32.5 h1:uqjjsYo1kTJr5NIcoIaP9F+TgXgADH7nKQx91FDAhtk=\nk8s.io/api v0.32.5/go.mod h1:bXXFU3fGCZ/eFMZvfHZC69PeGbXEL4zzjuPVzOxHF64=\nk8s.io/apimachinery v0.32.5 h1:6We3aJ6crC0ap8EhsEXcgX3LpI6SEjubpiOMXLROwPM=\nk8s.io/apimachinery v0.32.5/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=\nk8s.io/client-go v0.32.5 h1:huFmQMzgWu0z4kbWsuZci+Gt4Fo72I4CcrvhToZ/Qp0=\nk8s.io/client-go v0.32.5/go.mod h1:Qchw6f9WIVrur7DKojAHpRgGLcANT0RLIvF39Jz58xA=\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-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=\nk8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=\nk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=\nk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=\nsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=\nsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=\nsigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=\nsigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=\nsigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=\nsigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=\n"
  },
  {
    "path": "e2e/localserviceimpact_test.go",
    "content": "/*\nCopyright 2024 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage e2etest\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nvar _ = Describe(\"Local service not impacted\", func() {\n\thelloDeployment := appsv1.Deployment{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName: \"hello\",\n\t\t},\n\t\tSpec: appsv1.DeploymentSpec{\n\t\t\tReplicas: &replicaCount,\n\t\t\tSelector: &metav1.LabelSelector{\n\t\t\t\tMatchLabels: map[string]string{\n\t\t\t\t\t\"app\": \"hello\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tTemplate: v1.PodTemplateSpec{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tLabels: map[string]string{\"app\": \"hello\"},\n\t\t\t\t},\n\t\t\t\tSpec: v1.PodSpec{\n\t\t\t\t\tContainers: []v1.Container{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:  \"hello-tcp\",\n\t\t\t\t\t\t\tImage: \"alpine/socat:1.7.4.4\",\n\t\t\t\t\t\t\tArgs:  []string{\"-v\", \"-v\", \"TCP-LISTEN:42,crlf,reuseaddr,fork\", \"SYSTEM:echo $(CLUSTER_ID)\"},\n\t\t\t\t\t\t\tEnv: []v1.EnvVar{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName: \"CLUSTER_ID\",\n\t\t\t\t\t\t\t\t\tValueFrom: &v1.EnvVarSource{\n\t\t\t\t\t\t\t\t\t\tConfigMapKeyRef: &v1.ConfigMapKeySelector{\n\t\t\t\t\t\t\t\t\t\t\tLocalObjectReference: v1.LocalObjectReference{\n\t\t\t\t\t\t\t\t\t\t\t\tName: \"cluster-info\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tKey: \"clusterID\",\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\tvar (\n\t\tnamespace string\n\n\t\tctx    = context.Background()\n\t\treqPod *v1.Pod\n\t)\n\n\tBeforeEach(func() {\n\t\tnamespace = fmt.Sprintf(\"mcse2e-conformance-%v\", rand.Uint32())\n\t\t_, err := cluster1.k8s.CoreV1().Namespaces().Create(ctx, &v1.Namespace{\n\t\t\tObjectMeta: metav1.ObjectMeta{Name: namespace},\n\t\t}, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\t_, err = cluster2.k8s.CoreV1().Namespaces().Create(ctx, &v1.Namespace{\n\t\t\tObjectMeta: metav1.ObjectMeta{Name: namespace},\n\t\t}, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\t_, err = cluster1.k8s.CoreV1().ConfigMaps(namespace).Create(ctx, &v1.ConfigMap{\n\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\tName: \"cluster-info\",\n\t\t\t},\n\t\t\tData: map[string]string{\n\t\t\t\t\"clusterID\": \"cluster1\",\n\t\t\t},\n\t\t}, metav1.CreateOptions{})\n\t\tExpect(err).NotTo(HaveOccurred())\n\t\t_, err = cluster2.k8s.CoreV1().ConfigMaps(namespace).Create(ctx, &v1.ConfigMap{\n\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\tName: \"cluster-info\",\n\t\t\t},\n\t\t\tData: map[string]string{\n\t\t\t\t\"clusterID\": \"cluster2\",\n\t\t\t},\n\t\t}, metav1.CreateOptions{})\n\t\tExpect(err).NotTo(HaveOccurred())\n\t\tpod := requestPod\n\t\t_, err = cluster1.k8s.CoreV1().Pods(namespace).Create(ctx, &pod, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\tdep := helloDeployment\n\t\t_, err = cluster1.k8s.AppsV1().Deployments(namespace).Create(ctx, &dep, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\t_, err = cluster2.k8s.AppsV1().Deployments(namespace).Create(ctx, &dep, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\tsvc := helloService\n\t\t_, err = cluster1.k8s.CoreV1().Services(namespace).Create(ctx, &svc, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\t_, err = cluster2.k8s.CoreV1().Services(namespace).Create(ctx, &svc, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\t_, err = cluster1.mcs.MulticlusterV1beta1().ServiceImports(namespace).Create(ctx, &helloServiceImport, metav1.CreateOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\tdeploymentAvailable := func(clients clusterClients) func(Gomega) {\n\t\t\treturn func(g Gomega) {\n\t\t\t\tdeployment, err := clients.k8s.AppsV1().Deployments(namespace).Get(ctx, dep.Name, metav1.GetOptions{})\n\t\t\t\tg.Expect(err).NotTo(HaveOccurred())\n\t\t\t\tg.Expect(deployment.Status.Conditions).To(ContainElement(Satisfy(func(cond appsv1.DeploymentCondition) bool {\n\t\t\t\t\treturn cond.Type == appsv1.DeploymentAvailable &&\n\t\t\t\t\t\tcond.Status == v1.ConditionTrue\n\t\t\t\t})))\n\t\t\t}\n\t\t}\n\t\tEventually(deploymentAvailable(cluster1), 30).Should(Succeed())\n\t\tEventually(deploymentAvailable(cluster2), 30).Should(Succeed())\n\n\t\texportService(ctx, cluster2, cluster1, namespace, svc.Name)\n\n\t\tEventually(func() []string {\n\t\t\tsvcImport, err := cluster1.mcs.MulticlusterV1beta1().ServiceImports(namespace).Get(ctx, helloServiceImport.Name, metav1.GetOptions{})\n\t\t\tExpect(err).ToNot(HaveOccurred())\n\t\t\treturn svcImport.Spec.IPs\n\t\t}).ShouldNot(BeEmpty())\n\t\treqPod, err = cluster1.k8s.CoreV1().Pods(namespace).Get(ctx, requestPod.Name, metav1.GetOptions{})\n\t\tExpect(err).ToNot(HaveOccurred())\n\t\tBy(\"Created all in \" + namespace)\n\t})\n\tAfterEach(func() {\n\t\tif *noTearDown {\n\t\t\tBy(fmt.Sprintf(\"Skipping teardown. Test namespace %q\", namespace))\n\t\t\tBy(fmt.Sprintf(\"Cluster 1: kubectl --kubeconfig %q -n %q\", *kubeconfig1, namespace))\n\t\t\tBy(fmt.Sprintf(\"Cluster 2: kubectl --kubeconfig %q -n %q\", *kubeconfig2, namespace))\n\t\t\treturn\n\t\t}\n\t\tExpect(cluster1.k8s.CoreV1().Namespaces().Delete(ctx, namespace, metav1.DeleteOptions{})).To(Succeed())\n\t\tExpect(cluster2.k8s.CoreV1().Namespaces().Delete(ctx, namespace, metav1.DeleteOptions{})).To(Succeed())\n\t})\n\tSpecify(\"DNS resolves as expected\", func() {\n\t\tcheckAllClustersReachable := func(command []string, clusterIDs ...string) {\n\t\t\tclusters := map[string]int{}\n\t\t\tEventually(func(g Gomega) {\n\t\t\t\tstdout, _, err := execCmd(cluster1.k8s, restcfg1, reqPod.Name, reqPod.Namespace, command)\n\t\t\t\tg.Expect(err).ToNot(HaveOccurred())\n\t\t\t\tclusterID := strings.TrimSpace(string(stdout))\n\t\t\t\tg.Expect(clusterID).To(BeElementOf(clusterIDs))\n\t\t\t\tclusters[clusterID]++\n\t\t\t}).MustPassRepeatedly(20).Within(time.Second * 10).Should(Succeed())\n\t\t\tExpect(clusters).To(HaveEach(Not(BeZero())))\n\t\t}\n\n\t\tBy(\"verifying a local, unexported service is reachable via cluster.local\")\n\t\tcommand := []string{\"sh\", \"-c\", fmt.Sprintf(\"echo hi | nc %s.%s.svc.cluster.local 42\", helloService.Name, namespace)}\n\t\tcheckAllClustersReachable(command, \"cluster1\")\n\n\t\tBy(\"verifying a remote, exported service is reachable via clusterset.local\")\n\t\tcommand = []string{\"sh\", \"-c\", fmt.Sprintf(\"echo hi | nc %s.%s.svc.clusterset.local 42\", helloServiceImport.Name, namespace)}\n\t\tcheckAllClustersReachable(command, \"cluster2\")\n\n\t\tBy(\"exporting the service from cluster1\")\n\t\texportService(ctx, cluster1, cluster1, namespace, helloService.Name)\n\n\t\tBy(\"verifying a service exported from both local and remote clusters is reachable via clusterset.local\")\n\t\tcheckAllClustersReachable(command, \"cluster1\", \"cluster2\")\n\n\t\tBy(\"verifying a local, exported service is reachable via cluster.local\")\n\t\tcommand = []string{\"sh\", \"-c\", fmt.Sprintf(\"echo hi | nc %s.%s.svc.cluster.local 42\", helloService.Name, namespace)}\n\t\tcheckAllClustersReachable(command, \"cluster1\")\n\t})\n})\n"
  },
  {
    "path": "go.mod",
    "content": "module sigs.k8s.io/mcs-api\n\ngo 1.23.0\n\nrequire (\n\tk8s.io/api v0.32.5\n\tk8s.io/apimachinery v0.32.5\n\tk8s.io/client-go v0.32.5\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect\n\tgithub.com/emicklei/go-restful/v3 v3.11.0 // indirect\n\tgithub.com/fxamacker/cbor/v2 v2.7.0 // indirect\n\tgithub.com/go-logr/logr v1.4.2 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.21.0 // indirect\n\tgithub.com/go-openapi/jsonreference v0.20.2 // indirect\n\tgithub.com/go-openapi/swag v0.23.0 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/google/gnostic-models v0.6.8 // indirect\n\tgithub.com/google/go-cmp v0.6.0 // indirect\n\tgithub.com/google/gofuzz v1.2.0 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/mailru/easyjson v0.7.7 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/x448/float16 v0.8.4 // indirect\n\tgolang.org/x/net v0.30.0 // indirect\n\tgolang.org/x/oauth2 v0.23.0 // indirect\n\tgolang.org/x/sys v0.26.0 // indirect\n\tgolang.org/x/term v0.25.0 // indirect\n\tgolang.org/x/text v0.19.0 // indirect\n\tgolang.org/x/time v0.7.0 // indirect\n\tgoogle.golang.org/protobuf v1.35.1 // indirect\n\tgopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect\n\tgopkg.in/inf.v0 v0.9.1 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tk8s.io/klog/v2 v2.130.1 // indirect\n\tk8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect\n\tk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect\n\tsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect\n\tsigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect\n\tsigs.k8s.io/yaml v1.4.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=\ngithub.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=\ngithub.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=\ngithub.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=\ngithub.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=\ngithub.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=\ngithub.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=\ngithub.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=\ngithub.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=\ngithub.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=\ngithub.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=\ngithub.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=\ngithub.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=\ngithub.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=\ngithub.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=\ngithub.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\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-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=\ngithub.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=\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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=\ngithub.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=\ngithub.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=\ngithub.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=\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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=\ngithub.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=\ngithub.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=\ngolang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=\ngolang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=\ngolang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=\ngolang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=\ngolang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=\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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=\ngolang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=\ngolang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=\ngolang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=\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-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=\ngolang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=\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=\ngoogle.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=\ngoogle.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=\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.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=\ngopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=\ngopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nk8s.io/api v0.32.5 h1:uqjjsYo1kTJr5NIcoIaP9F+TgXgADH7nKQx91FDAhtk=\nk8s.io/api v0.32.5/go.mod h1:bXXFU3fGCZ/eFMZvfHZC69PeGbXEL4zzjuPVzOxHF64=\nk8s.io/apimachinery v0.32.5 h1:6We3aJ6crC0ap8EhsEXcgX3LpI6SEjubpiOMXLROwPM=\nk8s.io/apimachinery v0.32.5/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=\nk8s.io/client-go v0.32.5 h1:huFmQMzgWu0z4kbWsuZci+Gt4Fo72I4CcrvhToZ/Qp0=\nk8s.io/client-go v0.32.5/go.mod h1:Qchw6f9WIVrur7DKojAHpRgGLcANT0RLIvF39Jz58xA=\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-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=\nk8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=\nk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=\nk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=\nsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=\nsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=\nsigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=\nsigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=\nsigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=\nsigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=\n"
  },
  {
    "path": "hack/boilerplate/boilerplate.go.txt",
    "content": "/*\nCopyright YEAR The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/"
  },
  {
    "path": "hack/boilerplate/boilerplate.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright 2015 The Kubernetes 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\nfrom __future__ import print_function\n\nimport argparse\nimport difflib\nimport glob\nimport json\nimport mmap\nimport os\nimport re\nimport sys\nfrom datetime import date\n\nparser = argparse.ArgumentParser()\nparser.add_argument(\n    \"filenames\",\n    help=\"list of files to check, all files if unspecified\",\n    nargs='*')\n\nrootdir = os.path.dirname(__file__) + \"/../../\"\nrootdir = os.path.abspath(rootdir)\nparser.add_argument(\n    \"--rootdir\", default=rootdir, help=\"root directory to examine\")\n\ndefault_boilerplate_dir = os.path.join(rootdir, \"hack/boilerplate\")\nparser.add_argument(\n    \"--boilerplate-dir\", default=default_boilerplate_dir)\n\nparser.add_argument(\n    \"-v\", \"--verbose\",\n    help=\"give verbose output regarding why a file does not pass\",\n    action=\"store_true\")\n\nargs = parser.parse_args()\n\nverbose_out = sys.stderr if args.verbose else open(\"/dev/null\", \"w\")\n\n\ndef get_refs():\n    refs = {}\n\n    for path in glob.glob(os.path.join(args.boilerplate_dir, \"boilerplate.*.txt\")):\n        extension = os.path.basename(path).split(\".\")[1]\n\n        ref_file = open(path, 'r')\n        ref = ref_file.read().splitlines()\n        ref_file.close()\n        refs[extension] = ref\n\n    return refs\n\n\ndef file_passes(filename, refs, regexs):\n    try:\n        f = open(filename, 'r')\n    except Exception as exc:\n        print(\"Unable to open %s: %s\" % (filename, exc), file=verbose_out)\n        return False\n\n    data = f.read()\n    f.close()\n\n    basename = os.path.basename(filename)\n    extension = file_extension(filename)\n    if extension != \"\":\n        ref = refs[extension]\n    else:\n        ref = refs[basename]\n\n    # remove build tags from the top of Go files\n    if extension == \"go\":\n        p = regexs[\"go_build_constraints\"]\n        (data, found) = p.subn(\"\", data, 1)\n\n    # remove shebang from the top of shell files\n    if extension == \"sh\":\n        p = regexs[\"shebang\"]\n        (data, found) = p.subn(\"\", data, 1)\n\n    data = data.splitlines()\n\n    # if our test file is smaller than the reference it surely fails!\n    if len(ref) > len(data):\n        print('File %s smaller than reference (%d < %d)' %\n              (filename, len(data), len(ref)),\n              file=verbose_out)\n        return False\n\n    # trim our file to the same number of lines as the reference file\n    data = data[:len(ref)]\n\n    p = regexs[\"year\"]\n    for d in data:\n        if p.search(d):\n            print('File %s is missing the year' % filename, file=verbose_out)\n            return False\n\n    # Replace all occurrences of the regex \"CURRENT_YEAR|...|2016|2015|2014\" with \"YEAR\"\n    p = regexs[\"date\"]\n    for i, d in enumerate(data):\n        (data[i], found) = p.subn('YEAR', d)\n        if found != 0:\n            break\n\n    # if we don't match the reference at this point, fail\n    if ref != data:\n        print(\"Header in %s does not match reference, diff:\" %\n              filename, file=verbose_out)\n        if args.verbose:\n            print(file=verbose_out)\n            for line in difflib.unified_diff(ref, data, 'reference', filename, lineterm=''):\n                print(line, file=verbose_out)\n            print(file=verbose_out)\n        return False\n\n    return True\n\n\ndef file_extension(filename):\n    return os.path.splitext(filename)[1].split(\".\")[-1].lower()\n\n\nskipped_dirs = [\n    '.git',\n    \"vendor\",\n    \"test/e2e/framework/framework.go\",\n    \"images\"\n]\n\n\ndef normalize_files(files):\n    newfiles = []\n    for pathname in files:\n        if any(x in pathname for x in skipped_dirs):\n            continue\n        newfiles.append(pathname)\n    for i, pathname in enumerate(newfiles):\n        if not os.path.isabs(pathname):\n            newfiles[i] = os.path.join(args.rootdir, pathname)\n    return newfiles\n\n\ndef get_files(extensions):\n    files = []\n    if len(args.filenames) > 0:\n        files = args.filenames\n    else:\n        for root, dirs, walkfiles in os.walk(args.rootdir):\n            # don't visit certain dirs. This is just a performance improvement\n            # as we would prune these later in normalize_files(). But doing it\n            # cuts down the amount of filesystem walking we do and cuts down\n            # the size of the file list\n            for d in skipped_dirs:\n                if d in dirs:\n                    dirs.remove(d)\n\n            for name in walkfiles:\n                pathname = os.path.join(root, name)\n                files.append(pathname)\n\n    files = normalize_files(files)\n    outfiles = []\n    for pathname in files:\n        basename = os.path.basename(pathname)\n        extension = file_extension(pathname)\n        if extension in extensions or basename in extensions:\n            outfiles.append(pathname)\n    return outfiles\n\n\ndef get_regexs():\n    regexs = {}\n    # Search for \"YEAR\" which exists in the boilerplate, but shouldn't in the real thing\n    regexs[\"year\"] = re.compile('YEAR')\n    # dates can be 2014, 2015, 2016, ..., CURRENT_YEAR, company holder names can be anything\n    years = range(2014, date.today().year + 1)\n    regexs[\"date\"] = re.compile(\n        '(%s)' % \"|\".join(map(lambda l: str(l), years)))\n    # strip // +build \\n\\n build constraints\n    regexs[\"go_build_constraints\"] = re.compile(\n        r\"^(// ?(go:|\\+)build.*\\n)+\\n\", re.MULTILINE)\n    # strip #!.* from shell scripts\n    regexs[\"shebang\"] = re.compile(r\"^(#!.*\\n)\\n*\", re.MULTILINE)\n    return regexs\n\n\ndef main():\n    regexs = get_regexs()\n    refs = get_refs()\n    filenames = get_files(refs.keys())\n\n    for filename in filenames:\n        if not file_passes(filename, refs, regexs):\n            print(filename, file=sys.stdout)\n\n    return 0\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "hack/boilerplate/boilerplate.py.txt",
    "content": "#!/usr/bin/env python3\n\n# Copyright YEAR The Kubernetes 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"
  },
  {
    "path": "hack/boilerplate/boilerplate.sh.txt",
    "content": "# Copyright YEAR The Kubernetes 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"
  },
  {
    "path": "hack/boilerplate.go.txt",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n"
  },
  {
    "path": "hack/kube-env.sh",
    "content": "#!/bin/bash\n\n# Copyright 2014 The Kubernetes 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# Some useful colors.\nif [[ -z \"${color_start-}\" ]]; then\n  declare -r color_start=\"\\033[\"\n  declare -r color_red=\"${color_start}0;31m\"\n  declare -r color_yellow=\"${color_start}0;33m\"\n  declare -r color_green=\"${color_start}0;32m\"\n  declare -r color_norm=\"${color_start}0m\"\nfi\n\n# Returns the server version as MMmmpp, with MM as the major\n# component, mm the minor component, and pp as the patch\n# revision. e.g. 0.7.1 is echoed as 701, and 1.0.11 would be\n# 10011. (This makes for easy integer comparison in bash.)\nfunction kube_server_version() {\n  local server_version\n  local major\n  local minor\n  local patch\n\n  # This sed expression is the POSIX BRE to match strings like:\n  # Server Version: &version.Info{Major:\"0\", Minor:\"7+\", GitVersion:\"v0.7.0-dirty\", GitCommit:\"ad44234f7152e9c66bc2853575445c7071335e57\", GitTreeState:\"dirty\"}\n  # and capture the GitVersion portion (which has the patch level)\n  server_version=$(${KUBECTL} --match-server-version=false version | grep \"Server Version:\")\n  read major minor patch < <(\n    echo ${server_version} | \\\n      sed \"s/.*GitVersion:\\\"v\\([0-9]\\{1,\\}\\)\\.\\([0-9]\\{1,\\}\\)\\.\\([0-9]\\{1,\\}\\).*/\\1 \\2 \\3/\")\n  printf \"%02d%02d%02d\" ${major} ${minor} ${patch} | sed 's/^0*//'\n}\n"
  },
  {
    "path": "hack/update-codegen.sh",
    "content": "#!/usr/bin/env bash\n\n# Copyright 2020 The Kubernetes 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\nset -o errexit\nset -o nounset\nset -o pipefail\n\nSCRIPT_ROOT=$(dirname \"${BASH_SOURCE}\")/..\n\ngo -C tools install k8s.io/code-generator/cmd/{client-gen,lister-gen,informer-gen,deepcopy-gen,register-gen}\n\n# Go installs the above commands to get installed in $GOBIN if defined, and $GOPATH/bin otherwise:\nGOBIN=\"$(go env GOBIN)\"\ngobin=\"${GOBIN:-$(go env GOPATH)/bin}\"\n\nOUTPUT_PKG=sigs.k8s.io/mcs-api/pkg/client\nOUTPUT_DIR=$SCRIPT_ROOT/pkg/client\nFQ_APIS_V1ALPHA1=sigs.k8s.io/mcs-api/pkg/apis/v1alpha1\nFQ_APIS_V1BETA1=sigs.k8s.io/mcs-api/pkg/apis/v1beta1\nCLIENTSET_NAME=versioned\nCLIENTSET_PKG_NAME=clientset\n\nif [[ \"${VERIFY_CODEGEN:-}\" == \"true\" ]]; then\n  echo \"Running in verification mode\"\n  ORIG_OUTPUT_DIR=\"$OUTPUT_DIR\"\n  OUTPUT_DIR=$(mktemp -d)\n  trap \"rm -rf $OUTPUT_DIR\" EXIT\nelse\n  # Clear existing code before re-generating it\n  rm -rf \"$OUTPUT_DIR\"\nfi\nCOMMON_FLAGS=\"--go-header-file ${SCRIPT_ROOT}/hack/boilerplate.go.txt\"\n\necho \"Generating clientset at ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME}\"\n\"${gobin}/client-gen\" --clientset-name \"${CLIENTSET_NAME}\" --input-base \"\" --input \"${FQ_APIS_V1ALPHA1}\" --input \"${FQ_APIS_V1BETA1}\" --output-pkg \"${OUTPUT_PKG}/${CLIENTSET_PKG_NAME}\" --output-dir \"$OUTPUT_DIR/$CLIENTSET_PKG_NAME\" ${COMMON_FLAGS}\n\necho \"Generating listers at ${OUTPUT_PKG}/listers\"\n\"${gobin}/lister-gen\" \"${FQ_APIS_V1ALPHA1}\" \"${FQ_APIS_V1BETA1}\" --output-pkg \"${OUTPUT_PKG}/listers\" --output-dir \"${OUTPUT_DIR}/listers\" ${COMMON_FLAGS}\n\necho \"Generating informers at ${OUTPUT_PKG}/informers\"\n\"${gobin}/informer-gen\" \\\n         \"${FQ_APIS_V1ALPHA1}\" \"${FQ_APIS_V1BETA1}\" \\\n         --versioned-clientset-package \"${OUTPUT_PKG}/${CLIENTSET_PKG_NAME}/${CLIENTSET_NAME}\" \\\n         --listers-package \"${OUTPUT_PKG}/listers\" \\\n         --output-pkg \"${OUTPUT_PKG}/informers\" \\\n         --output-dir \"${OUTPUT_DIR}/informers\" \\\n         ${COMMON_FLAGS}\n\necho \"Generating register at ${FQ_APIS_V1ALPHA1} & ${FQ_APIS_V1BETA1}\"\n\"${gobin}/register-gen\" \"${FQ_APIS_V1ALPHA1}\" \"${FQ_APIS_V1BETA1}\" --output-file zz_generated.register.go ${COMMON_FLAGS}\n\nif [[ \"${VERIFY_CODEGEN:-}\" == \"true\" ]]; then\n  diff -urN \"$ORIG_OUTPUT_DIR\" \"$OUTPUT_DIR\"\nfi\n"
  },
  {
    "path": "hack/update-k8s.sh",
    "content": "#!/usr/bin/env bash\n\n# Copyright 2025 The Kubernetes 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# This script updates mcs-api dependencies to either the latest\n# k8s.io dependencies, or to the version given as argument\n# (with the @, e.g. @v0.32.5).\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nSCRIPT_ROOT=$(dirname \"${BASH_SOURCE}\")/..\n\nfor dir in \"$SCRIPT_ROOT\"{,/tools}; do\n    awk '/[^.]k8s.io[/][^ ]+ v[.0-9]+$/ { print $1 \"'\"$1\"'\" }' \"$dir/go.mod\" |\n\txargs -r -n 1 go -C \"$dir\" get\ndone\n\nfor mod in \"$SCRIPT_ROOT\"/go.mod \"$SCRIPT_ROOT\"/*/go.mod; do\n    go -C \"${mod%/*}\" mod tidy\ndone\n\nmake generate\n"
  },
  {
    "path": "hack/verify-all.sh",
    "content": "#!/bin/bash\n\n# Copyright 2014 The Kubernetes 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\nset -o errexit\nset -o nounset\nset -o pipefail\n\nSCRIPT_ROOT=$(dirname \"${BASH_SOURCE}\")/..\nsource \"${SCRIPT_ROOT}/hack/kube-env.sh\"\n\nSILENT=true\n\nfunction is-excluded {\n  for e in $EXCLUDE; do\n    if [[ $1 -ef ${BASH_SOURCE} ]]; then\n      return\n    fi\n    if [[ $1 -ef \"$SCRIPT_ROOT/hack/$e\" ]]; then\n      return\n    fi\n  done\n  return 1\n}\n\nwhile getopts \":v\" opt; do\n  case $opt in\n    v)\n      SILENT=false\n      ;;\n    \\?)\n      echo \"Invalid flag: -$OPTARG\" >&2\n      exit 1\n      ;;\n  esac\ndone\n\nif $SILENT ; then\n  echo \"Running in the silent mode, run with -v if you want to see script logs.\"\nfi\n\nEXCLUDE=\"verify-all.sh\"\n\nret=0\nfor t in \"$SCRIPT_ROOT\"/hack/verify-*.sh\ndo\n  if is-excluded $t ; then\n    echo \"Skipping $t\"\n    continue\n  fi\n  if $SILENT ; then\n    echo -e \"Verifying $t\"\n    if bash \"$t\" &> /dev/null; then\n      echo -e \"${color_green}SUCCESS${color_norm}\"\n    else\n      echo -e \"${color_red}FAILED${color_norm}\"\n      ret=1\n    fi\n  else\n    bash \"$t\" || ret=1\n  fi\ndone\n\nexit $ret\n\n# ex: ts=2 sw=2 et filetype=sh\n"
  },
  {
    "path": "hack/verify-boilerplate.sh",
    "content": "#!/bin/bash\n\n# Copyright 2014 The Kubernetes 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\nset -o errexit\nset -o nounset\nset -o pipefail\n\nSCRIPT_ROOT=$(dirname \"${BASH_SOURCE}\")/..\n\nboilerDir=\"${SCRIPT_ROOT}/hack/boilerplate\"\nboiler=\"${boilerDir}/boilerplate.py\"\n\nfiles_need_boilerplate=($(${boiler} \"$@\"))\n\n# Run boilerplate check\nif [[ ${#files_need_boilerplate[@]} -gt 0 ]]; then\n  for file in \"${files_need_boilerplate[@]}\"; do\n    echo \"Boilerplate header is wrong for: ${file}\"\n  done\n\n  exit 1\nfi\n"
  },
  {
    "path": "hack/verify-codegen.sh",
    "content": "#!/bin/bash\n\n# Copyright 2020 The Kubernetes 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\nset -o errexit\nset -o nounset\nset -o pipefail\n\nSCRIPT_ROOT=$(dirname \"${BASH_SOURCE}\")/..\n\ncd $SCRIPT_ROOT\nVERIFY_CODEGEN=true $SCRIPT_ROOT/hack/update-codegen.sh\n"
  },
  {
    "path": "hack/verify-crd-bump-revision.sh",
    "content": "#!/bin/bash\n\n# Copyright 2025 The Kubernetes 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# Check against master if PULL_BASE_SHA is not defined by prow\nBASE_REF=\"${PULL_BASE_SHA:-master}\"\n\ncrd_changed=\"$(git diff --name-only \"${BASE_REF}\" | grep -c \"^config/crd/.*\\.yaml$\")\"\nversion_label_changed=\"$(git diff -U0 \"${BASE_REF}\" -- \"config/crd-base/\" | grep -Ec \"multicluster.x-k8s.io/(release-version|crd-schema-revision)\")\"\n\nif [ \"${crd_changed}\" -gt 0 ] && [ \"${version_label_changed}\" -lt 4 ]; then\n\techo \"❌ CRDs were modified, but the CRD version/revision labels were not changed in 'config/crd-base/'. Please update them.\"\n\texit 1\nfi\n"
  },
  {
    "path": "hack/verify-crds.sh",
    "content": "#!/bin/bash\n\n# Copyright 2020 The Kubernetes 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\nset -o errexit\nset -o nounset\nset -o pipefail\n\nSCRIPT_ROOT=$(dirname \"${BASH_SOURCE}\")/..\nDIFFROOT=\"${SCRIPT_ROOT}/config\"\nTMP_DIFFROOT=\"${SCRIPT_ROOT}/_tmp/config\"\n_tmp=\"${SCRIPT_ROOT}/_tmp\"\n# The controller-gen command for generating CRDs from API definitions.\nCONTROLLER_GEN=\"go -C tools run sigs.k8s.io/controller-tools/cmd/controller-gen\"\n# Need v1 to support defaults in CRDs, unfortunately limiting us to k8s 1.16+\nCRD_OPTIONS=\"crd:crdVersions=v1\"\n\ncd \"${SCRIPT_ROOT}\"\n\ncleanup() {\n  rm -rf \"${_tmp}\"\n}\ntrap \"cleanup\" EXIT SIGINT\n\ncleanup\n\nmkdir -p \"${TMP_DIFFROOT}\"\ncp -a \"${DIFFROOT}\"/* \"${TMP_DIFFROOT}\"\n\n${CONTROLLER_GEN} ${CRD_OPTIONS} rbac:roleName=mcs-derived-service-manager webhook \\\npaths=\"${SCRIPT_ROOT}/...\" schemapatch:manifests=\"${SCRIPT_ROOT}/config/crd-base\" output:crd:none \\\noutput:schemapatch:dir=\"${TMP_DIFFROOT}/crd\" output:rbac:dir=\"${TMP_DIFFROOT}/rbac\"\n\necho \"diffing ${DIFFROOT} against freshly generated codegen in ${TMP_DIFFROOT}\"\nret=0\ndiff -Naupr \"${DIFFROOT}\" \"${TMP_DIFFROOT}\" || ret=$?\nif [[ $ret -eq 0 ]]\nthen\n  echo \"${DIFFROOT} up to date.\"\nelse\n  echo \"${DIFFROOT} is out of date. Please run 'make manifests'\"\n  exit 1\nfi\n"
  },
  {
    "path": "hack/verify-gofmt.sh",
    "content": "#!/bin/bash\n\n# Copyright 2014 The Kubernetes 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# GoFmt apparently is changing @ head...\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nKUBE_ROOT=$(dirname \"${BASH_SOURCE}\")/..\n\ncd \"${KUBE_ROOT}\"\n\nfind_files() {\n  find . -not \\( \\\n      \\( \\\n        -wholename './.git' \\\n        -o -wholename '*/vendor/*' \\\n      \\) -prune \\\n    \\) -name '*.go'\n}\n\nGOFMT=\"gofmt -s\"\nbad_files=$(find_files | xargs $GOFMT -l)\nif [[ -n \"${bad_files}\" ]]; then\n  echo \"!!! '$GOFMT' needs to be run on the following files: \"\n  echo \"${bad_files}\"\n  exit 1\nfi\n"
  },
  {
    "path": "hack/verify-golint.sh",
    "content": "#!/bin/bash\n\n# Copyright 2014 The Kubernetes 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\nset -o errexit\nset -o nounset\nset -o pipefail\n\nKUBE_ROOT=$(dirname \"${BASH_SOURCE}\")/..\n\ncd \"${KUBE_ROOT}\"\n\nPACKAGES=($(go list ./... | sed sXsigs.k8s.io/mcs-apiX..X))\nbad_files=()\nfor package in \"${PACKAGES[@]}\"; do\n  out=$(go -C tools run golang.org/x/lint/golint -min_confidence=0.9 \"${package}\" 2>&1 |\n        sed 'sX^../XX;/should not use dot imports/d;/exported const OptionalLabel/d;/^go: downloading/d' ||:)\n  if [[ -n \"${out}\" ]]; then\n    bad_files+=(\"${out}\")\n  fi\ndone\nif [[ \"${#bad_files[@]}\" -ne 0 ]]; then\n  echo \"!!! golint problems: \"\n  for err in \"${bad_files[@]}\"; do\n    echo \"$err\"\n  done\n  exit 1\nfi\n\n# ex: ts=2 sw=2 et filetype=sh\n"
  },
  {
    "path": "pkg/apis/v1alpha1/BUILD",
    "content": "load(\"@io_bazel_rules_go//go:def.bzl\", \"go_library\")\n\ngo_library(\n    name = \"go_default_library\",\n    srcs = [\n        \"doc.go\",\n        \"register.go\",\n        \"types.go\",\n        \"well_known_labels.go\",\n        \"zz_generated.deepcopy.go\",\n    ],\n    importmap = \"k8s.io/kubernetes/vendor/k8s.io/mcs-api/pkg/apis/v1alpha1\",\n    importpath = \"k8s.io/mcs-api/pkg/apis/v1alpha1\",\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"//staging/src/k8s.io/api/core/v1:go_default_library\",\n        \"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library\",\n        \"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library\",\n        \"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library\",\n    ],\n)\n\nfilegroup(\n    name = \"package-srcs\",\n    srcs = glob([\"**\"]),\n    tags = [\"automanaged\"],\n    visibility = [\"//visibility:private\"],\n)\n\nfilegroup(\n    name = \"all-srcs\",\n    srcs = [\":package-srcs\"],\n    tags = [\"automanaged\"],\n    visibility = [\"//visibility:public\"],\n)\n"
  },
  {
    "path": "pkg/apis/v1alpha1/doc.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Package v1alpha1 contains API schema definitions for the Multi-Cluster\n// Services v1alpha1 API group.\n// +kubebuilder:object:generate=true\n// +groupName=multicluster.x-k8s.io\npackage v1alpha1\n"
  },
  {
    "path": "pkg/apis/v1alpha1/serviceexport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage v1alpha1\n\nimport (\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nconst (\n\t// ServiceExportPluralName is the plural name of ServiceExport\n\tServiceExportPluralName = \"serviceexports\"\n\t// ServiceExportKindName is the kind name of ServiceExport\n\tServiceExportKindName = \"ServiceExport\"\n\t// ServiceExportFullName is the full name of ServiceExport\n\tServiceExportFullName = ServiceExportPluralName + \".\" + GroupName\n)\n\n// ServiceExportVersionedName is the versioned name of ServiceExport\nvar ServiceExportVersionedName = ServiceExportKindName + \"/\" + GroupVersion.Version\n\n// +genclient\n// +kubebuilder:object:root=true\n// +kubebuilder:resource:shortName={svcex,svcexport}\n\n// ServiceExport declares that the Service with the same name and namespace\n// as this export should be consumable from other clusters.\ntype ServiceExport struct {\n\tmetav1.TypeMeta `json:\",inline\"`\n\t// +optional\n\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\t// spec defines the behavior of a ServiceExport.\n\t// +optional\n\tSpec ServiceExportSpec `json:\"spec,omitempty\"`\n\t// status describes the current state of an exported service.\n\t// Service configuration comes from the Service that had the same\n\t// name and namespace as this ServiceExport.\n\t// Populated by the multi-cluster service implementation's controller.\n\t// +optional\n\tStatus ServiceExportStatus `json:\"status,omitempty\"`\n}\n\n// ServiceExportSpec describes an exported service extra information\ntype ServiceExportSpec struct {\n\t// exportedLabels describes the labels exported. It is optional for implementation.\n\t// +optional\n\tExportedLabels map[string]string `json:\"exportedLabels,omitempty\"`\n\t// exportedAnnotations describes the annotations exported. It is optional for implementation.\n\t// +optional\n\tExportedAnnotations map[string]string `json:\"exportedAnnotations,omitempty\"`\n}\n\n// ServiceExportStatus contains the current status of an export.\ntype ServiceExportStatus struct {\n\t// +optional\n\t// +patchStrategy=merge\n\t// +patchMergeKey=type\n\t// +listType=map\n\t// +listMapKey=type\n\tConditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"`\n}\n\nconst (\n\t// ServiceExportValid means that the service referenced by this\n\t// service export has been recognized as valid by an mcs-controller.\n\t// This will be false if the service is found to be unexportable\n\t// (ExternalName, not found).\n\t//\n\t// Deprecated: use ServiceExportConditionValid instead\n\tServiceExportValid = \"Valid\"\n\t// ServiceExportConflict means that there is a conflict between two\n\t// exports for the same Service. When \"True\", the condition message\n\t// should contain enough information to diagnose the conflict:\n\t// field(s) under contention, which cluster won, and why.\n\t// Users should not expect detailed per-cluster information in the\n\t// conflict message.\n\t//\n\t// Deprecated: use ServiceExportConditionConflict instead\n\tServiceExportConflict = \"Conflict\"\n)\n\n// +kubebuilder:object:root=true\n\n// ServiceExportList represents a list of endpoint slices\ntype ServiceExportList struct {\n\tmetav1.TypeMeta `json:\",inline\"`\n\t// Standard list metadata.\n\t// +optional\n\tmetav1.ListMeta `json:\"metadata,omitempty\"`\n\t// List of endpoint slices\n\t// +listType=set\n\tItems []ServiceExport `json:\"items\"`\n}\n\n// ServiceExportConditionType is a type of condition associated with a\n// ServiceExport. This type should be used with the ServiceExportStatus.Conditions\n// field.\ntype ServiceExportConditionType string\n\n// ServiceExportConditionReason defines the set of reasons that explain why a\n// particular ServiceExport condition type has been raised.\ntype ServiceExportConditionReason string\n\n// NewServiceExportCondition creates a new ServiceExport condition\nfunc NewServiceExportCondition(t ServiceExportConditionType, status metav1.ConditionStatus, reason ServiceExportConditionReason, msg string) metav1.Condition {\n\treturn metav1.Condition{\n\t\tType:               string(t),\n\t\tStatus:             status,\n\t\tReason:             string(reason),\n\t\tMessage:            msg,\n\t\tLastTransitionTime: metav1.Now(),\n\t}\n}\n\nconst (\n\t// ServiceExportConditionValid is true when the Service Export is valid.\n\t// This does not indicate whether or not the configuration has been exported\n\t// to a control plane / data plane.\n\t//\n\t//\n\t// Possible reasons for this condition to be true are:\n\t//\n\t// * \"Valid\"\n\t//\n\t// Possible reasons for this condition to be False are:\n\t//\n\t// * \"NoService\"\n\t// * \"InvalidServiceType\"\n\t//\n\t// Controllers may raise this condition with other reasons,\n\t// but should prefer to use the reasons listed above to improve\n\t// interoperability.\n\tServiceExportConditionValid ServiceExportConditionType = \"Valid\"\n\n\t// ServiceExportReasonValid is used with the \"Valid\" condition when the\n\t// condition is True.\n\tServiceExportReasonValid ServiceExportConditionReason = \"Valid\"\n\n\t// ServiceExportReasonNoService is used with the \"Valid\" condition when\n\t// the associated Service does not exist.\n\tServiceExportReasonNoService ServiceExportConditionReason = \"NoService\"\n\n\t// ServiceExportReasonInvalidServiceType is used with the \"Valid\"\n\t// condition when the associated Service has an invalid type\n\t// (per the KEP at least the ExternalName type).\n\tServiceExportReasonInvalidServiceType ServiceExportConditionReason = \"InvalidServiceType\"\n)\n\nconst (\n\t// ServiceExportConditionReady is true when the service is exported\n\t// to some control plane or data plane or ready to be pulled.\n\t//\n\t//\n\t// Possible reasons for this condition to be true are:\n\t//\n\t// * \"Exported\"\n\t// * \"Ready\"\n\t//\n\t// Possible reasons for this condition to be False are:\n\t//\n\t// * \"Pending\"\n\t// * \"Failed\"\n\t//\n\t// Possible reasons for this condition to be Unknown are:\n\t//\n\t// * \"Pending\"\n\t//\n\t// Controllers may raise this condition with other reasons,\n\t// but should prefer to use the reasons listed above to improve\n\t// interoperability.\n\tServiceExportConditionReady ServiceExportConditionType = \"Ready\"\n\n\t// ServiceExportReasonExported is used with the \"Ready\" condition\n\t// when the condition is True and the service has been exported.\n\t// This would be used when an implementation exports a service\n\t// to a control plane or data plane.\n\tServiceExportReasonExported ServiceExportConditionReason = \"Exported\"\n\n\t// ServiceExportReasonReady is used with the \"Ready\" condition\n\t// when the condition is True and the service has been exported.\n\t// This would typically be used in an implementation that uses a\n\t// pull model.\n\tServiceExportReasonReady ServiceExportConditionReason = \"Ready\"\n\n\t// ServiceExportReasonPending is used with the \"Ready\" condition\n\t// when the service is in the process of being exported.\n\tServiceExportReasonPending ServiceExportConditionReason = \"Pending\"\n\n\t// ServiceExportReasonFailed is used with the \"Ready\" condition\n\t// when the service failed to be exported with the message providing\n\t// the specific reason.\n\tServiceExportReasonFailed ServiceExportConditionReason = \"Failed\"\n)\n\nconst (\n\t// ServiceExportConditionConflict indicates that some property of an\n\t// exported service has conflicting values across the constituent\n\t// ServiceExports. This condition must be at least raised on the\n\t// conflicting ServiceExport and is recommended to be raised on all on\n\t// all the constituent ServiceExports if feasible.\n\t//\n\t//\n\t// Possible reasons for this condition to be true are:\n\t//\n\t// * \"PortConflict\"\n\t// * \"TypeConflict\"\n\t// * \"SessionAffinityConflict\"\n\t// * \"SessionAffinityConfigConflict\"\n\t// * \"AnnotationsConflict\"\n\t// * \"LabelsConflict\"\n\t//\n\t// When multiple conflicts occurs the above reasons may be combined\n\t// using commas.\n\t//\n\t// Possible reasons for this condition to be False are:\n\t//\n\t// * \"NoConflicts\"\n\t//\n\t// Controllers may raise this condition with other reasons,\n\t// but should prefer to use the reasons listed above to improve\n\t// interoperability.\n\tServiceExportConditionConflict ServiceExportConditionType = \"Conflict\"\n\n\t// ServiceExportReasonPortConflict is used with the \"Conflict\" condition\n\t// when the exported service has a conflict related to port configuration\n\t// if the ports are not identical in all the constituent Services.\n\tServiceExportReasonPortConflict ServiceExportConditionReason = \"PortConflict\"\n\n\t// ServiceExportReasonTypeConflict is used with the \"Conflict\" condition\n\t// when the exported service has a conflict related to the service type\n\t// (eg headless vs non-headless).\n\tServiceExportReasonTypeConflict ServiceExportConditionReason = \"TypeConflict\"\n\n\t// ServiceExportReasonSessionAffinityConflict is used with the \"Conflict\"\n\t// condition when the exported service has a conflict related to session affinity.\n\tServiceExportReasonSessionAffinityConflict ServiceExportConditionReason = \"SessionAffinityConflict\"\n\n\t// ServiceExportReasonSessionAffinityConfigConflict is used with the\n\t// \"Conflict\" condition when the exported service has a conflict related\n\t// to session affinity config.\n\tServiceExportReasonSessionAffinityConfigConflict ServiceExportConditionReason = \"SessionAffinityConfigConflict\"\n\n\t// ServiceExportReasonLabelsConflict is used with the \"Conflict\"\n\t// condition when the ServiceExport has a conflict related to exported\n\t// labels.\n\tServiceExportReasonLabelsConflict ServiceExportConditionReason = \"LabelsConflict\"\n\n\t// ServiceExportReasonAnnotationsConflict is used with the \"Conflict\"\n\t// condition when the ServiceExport has a conflict related to exported\n\t// annotations.\n\tServiceExportReasonAnnotationsConflict ServiceExportConditionReason = \"AnnotationsConflict\"\n\n\t// ServiceExportReasonInternalTrafficPolicyConflict is used with the \"Conflict\"\n\t// condition when the exported service has a conflict related to internal traffic policy.\n\tServiceExportReasonInternalTrafficPolicyConflict ServiceExportConditionReason = \"InternalTrafficPolicyConflict\"\n\n\t// ServiceExportReasonTrafficDistributionConflict is used with the \"Conflict\"\n\t// condition when the exported service has a conflict related to traffic distribution.\n\tServiceExportReasonTrafficDistributionConflict ServiceExportConditionReason = \"TrafficDistributionConflict\"\n\n\t// ServiceExportReasonIPFamilyConflict is used with the \"Conflict\" condition\n\t// when the exported service has a conflict related to IPFamilies.\n\t// The handling of IP families is implementation-specific but this condition\n\t// must be used if a conflicting IP family may result in network traffic reaching\n\t// only a subset of the backends depending on the IP protocol used.\n\tServiceExportReasonIPFamilyConflict ServiceExportConditionReason = \"IPFamilyConflict\"\n\n\t// ServiceExportReasonNoConflicts is used with the \"Conflict\" condition\n\t// when the condition is False.\n\tServiceExportReasonNoConflicts ServiceExportConditionReason = \"NoConflicts\"\n)\n"
  },
  {
    "path": "pkg/apis/v1alpha1/serviceimport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage v1alpha1\n\nimport (\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nconst (\n\t// ServiceImportPluralName is the plural name of ServiceImport\n\tServiceImportPluralName = \"serviceimports\"\n\t// ServiceImportKindName is the kind name of ServiceImport\n\tServiceImportKindName = \"ServiceImport\"\n\t// ServiceImportFullName is the full name of ServiceImport\n\tServiceImportFullName = ServiceImportPluralName + \".\" + GroupName\n)\n\n// ServiceImportVersionedName is the versioned name of ServiceImport\nvar ServiceImportVersionedName = ServiceImportKindName + \"/\" + GroupVersion.Version\n\n// +genclient\n// +kubebuilder:object:root=true\n// +kubebuilder:resource:shortName={svcim,svcimport}\n\n// ServiceImport describes a service imported from clusters in a ClusterSet.\ntype ServiceImport struct {\n\tmetav1.TypeMeta `json:\",inline\"`\n\t// +optional\n\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\t// spec defines the behavior of a ServiceImport.\n\t// +optional\n\tSpec ServiceImportSpec `json:\"spec,omitempty\"`\n\t// status contains information about the exported services that form\n\t// the multi-cluster service referenced by this ServiceImport.\n\t// +optional\n\tStatus ServiceImportStatus `json:\"status,omitempty\"`\n}\n\n// ServiceImportType designates the type of a ServiceImport\ntype ServiceImportType string\n\nconst (\n\t// ClusterSetIP are only accessible via the ClusterSet IP.\n\tClusterSetIP ServiceImportType = \"ClusterSetIP\"\n\t// Headless services allow backend pods to be addressed directly.\n\tHeadless ServiceImportType = \"Headless\"\n)\n\n// ServiceImportSpec describes an imported service and the information necessary to consume it.\ntype ServiceImportSpec struct {\n\t// +listType=atomic\n\tPorts []ServicePort `json:\"ports\"`\n\t// ip will be used as the VIP for this service when type is ClusterSetIP.\n\t// +kubebuilder:validation:MaxItems:=2\n\t// +optional\n\tIPs []string `json:\"ips,omitempty\"`\n\t// type defines the type of this service.\n\t// Must be ClusterSetIP or Headless.\n\t// +kubebuilder:validation:Enum=ClusterSetIP;Headless\n\tType ServiceImportType `json:\"type\"`\n\t// Supports \"ClientIP\" and \"None\". Used to maintain session affinity.\n\t// Enable client IP based session affinity.\n\t// Must be ClientIP or None.\n\t// Defaults to None.\n\t// Ignored when type is Headless\n\t// More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\n\t// +optional\n\tSessionAffinity v1.ServiceAffinity `json:\"sessionAffinity,omitempty\"`\n\t// sessionAffinityConfig contains session affinity configuration.\n\t// +optional\n\tSessionAffinityConfig *v1.SessionAffinityConfig `json:\"sessionAffinityConfig,omitempty\"`\n\t// IPFamilies identifies all the IPFamilies assigned for this ServiceImport.\n\t// +kubebuilder:validation:MaxItems:=2\n\t// +optional\n\tIPFamilies []v1.IPFamily `json:\"ipFamilies,omitempty\"`\n\n\t// InternalTrafficPolicy describes how nodes distribute service traffic they\n\t// receive on the ClusterIP. If set to \"Local\", the proxy will assume that pods\n\t// only want to talk to endpoints of the service on the same node as the pod,\n\t// dropping the traffic if there are no local endpoints. The default value,\n\t// \"Cluster\", uses the standard behavior of routing to all endpoints evenly\n\t// (possibly modified by topology and other features).\n\t// +optional\n\tInternalTrafficPolicy *v1.ServiceInternalTrafficPolicy `json:\"internalTrafficPolicy,omitempty\"`\n\n\t// TrafficDistribution offers a way to express preferences for how traffic\n\t// is distributed to Service endpoints. Implementations can use this field\n\t// as a hint, but are not required to guarantee strict adherence. If the\n\t// field is not set, the implementation will apply its default routing\n\t// strategy. If set to \"PreferClose\", implementations should prioritize\n\t// endpoints that are in the same zone.\n\t// +optional\n\tTrafficDistribution *string `json:\"trafficDistribution,omitempty\"`\n}\n\n// ServicePort represents the port on which the service is exposed\ntype ServicePort struct {\n\t// The name of this port within the service. This must be a DNS_LABEL.\n\t// All ports within a ServiceSpec must have unique names. When considering\n\t// the endpoints for a Service, this must match the 'name' field in the\n\t// EndpointPort.\n\t// Optional if only one ServicePort is defined on this service.\n\t// +optional\n\tName string `json:\"name,omitempty\"`\n\n\t// The IP protocol for this port. Supports \"TCP\", \"UDP\", and \"SCTP\".\n\t// Default is TCP.\n\t// +optional\n\tProtocol v1.Protocol `json:\"protocol,omitempty\"`\n\n\t// The application protocol for this port.\n\t// This is used as a hint for implementations to offer richer behavior for protocols that they understand.\n\t// This field follows standard Kubernetes label syntax.\n\t// Valid values are either:\n\t//\n\t// * Un-prefixed protocol names - reserved for IANA standard service names (as per\n\t// RFC-6335 and https://www.iana.org/assignments/service-names).\n\t//\n\t// * Kubernetes-defined prefixed names:\n\t//   * 'kubernetes.io/h2c' - HTTP/2 over cleartext as described in https://www.rfc-editor.org/rfc/rfc7540\n\t//\n\t// * Other protocols should use implementation-defined prefixed names such as\n\t// mycompany.com/my-custom-protocol.\n\t// Field can be enabled with ServiceAppProtocol feature gate.\n\t// +optional\n\tAppProtocol *string `json:\"appProtocol,omitempty\"`\n\n\t// The port that will be exposed by this service.\n\tPort int32 `json:\"port\"`\n}\n\n// ServiceImportStatus describes derived state of an imported service.\ntype ServiceImportStatus struct {\n\t// clusters is the list of exporting clusters from which this service\n\t// was derived.\n\t// +optional\n\t// +patchStrategy=merge\n\t// +patchMergeKey=cluster\n\t// +listType=map\n\t// +listMapKey=cluster\n\tClusters []ClusterStatus `json:\"clusters,omitempty\"`\n\t// +optional\n\t// +patchStrategy=merge\n\t// +patchMergeKey=type\n\t// +listType=map\n\t// +listMapKey=type\n\tConditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"`\n}\n\n// ClusterStatus contains service configuration mapped to a specific source cluster\ntype ClusterStatus struct {\n\t// cluster is the name of the exporting cluster. Must be a valid RFC-1123 DNS\n\t// label.\n\tCluster string `json:\"cluster\"`\n}\n\n// +kubebuilder:object:root=true\n\n// ServiceImportList represents a list of endpoint slices\ntype ServiceImportList struct {\n\tmetav1.TypeMeta `json:\",inline\"`\n\t// Standard list metadata.\n\t// +optional\n\tmetav1.ListMeta `json:\"metadata,omitempty\"`\n\t// List of endpoint slices\n\t// +listType=set\n\tItems []ServiceImport `json:\"items\"`\n}\n\n// ServiceImportConditionType is a type of condition associated with a\n// ServiceImport. This type should be used with the ServiceImportStatus.Conditions\n// field.\ntype ServiceImportConditionType string\n\n// ServiceImportConditionReason defines the set of reasons that explain why a\n// particular ServiceImport condition type has been raised.\ntype ServiceImportConditionReason string\n\n// NewServiceImportCondition creates a new ServiceImport condition\nfunc NewServiceImportCondition(t ServiceImportConditionType, status metav1.ConditionStatus, reason ServiceImportConditionReason, msg string) metav1.Condition {\n\treturn metav1.Condition{\n\t\tType:               string(t),\n\t\tStatus:             status,\n\t\tReason:             string(reason),\n\t\tMessage:            msg,\n\t\tLastTransitionTime: metav1.Now(),\n\t}\n}\n\nconst (\n\t// ServiceImportConditionReady is true when the Service Import is ready.\n\t//\n\t//\n\t// Possible reasons for this condition to be true are:\n\t//\n\t// * \"Ready\"\n\t//\n\t// Possible reasons for this condition to be False are:\n\t//\n\t// * \"Pending\"\n\t// * \"IPFamilyNotSupported\"\n\t//\n\t// Possible reasons for this condition to be Unknown are:\n\t//\n\t// * \"Pending\"\n\t//\n\t// Controllers may raise this condition with other reasons,\n\t// but should prefer to use the reasons listed above to improve\n\t// interoperability.\n\tServiceImportConditionReady ServiceImportConditionType = \"Ready\"\n\n\t// ServiceImportReasonReady is used with the \"Ready\" condition when the\n\t// condition is True.\n\tServiceImportReasonReady ServiceImportConditionReason = \"Ready\"\n\n\t// ServiceImportReasonPending is used with the \"Ready\" condition when\n\t// the ServiceImport is in the process of being created or updated.\n\tServiceImportReasonPending ServiceImportConditionReason = \"Pending\"\n\n\t// ServiceImportReasonIPFamilyNotSupported is used with the \"Ready\"\n\t// condition when the service can not be imported due to IP families\n\t// mismatch.\n\tServiceImportReasonIPFamilyNotSupported ServiceImportConditionReason = \"IPFamilyNotSupported\"\n)\n"
  },
  {
    "path": "pkg/apis/v1alpha1/well_known_labels.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage v1alpha1\n\nconst (\n\t// LabelServiceName is used to indicate the name of multi-cluster service\n\t// that an EndpointSlice belongs to.\n\tLabelServiceName = \"multicluster.kubernetes.io/service-name\"\n\n\t// LabelSourceCluster is used to indicate the name of the cluster in which an exported resource exists.\n\tLabelSourceCluster = \"multicluster.kubernetes.io/source-cluster\"\n)\n"
  },
  {
    "path": "pkg/apis/v1alpha1/zz_generated.deepcopy.go",
    "content": "//go:build !ignore_autogenerated\n\n/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by controller-gen. DO NOT EDIT.\n\npackage v1alpha1\n\nimport (\n\tcorev1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1\"\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 *ClusterStatus) DeepCopyInto(out *ClusterStatus) {\n\t*out = *in\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterStatus.\nfunc (in *ClusterStatus) DeepCopy() *ClusterStatus {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ClusterStatus)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceExport) DeepCopyInto(out *ServiceExport) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tin.ObjectMeta.DeepCopyInto(&out.ObjectMeta)\n\tin.Spec.DeepCopyInto(&out.Spec)\n\tin.Status.DeepCopyInto(&out.Status)\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceExport.\nfunc (in *ServiceExport) DeepCopy() *ServiceExport {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceExport)\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 *ServiceExport) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceExportList) DeepCopyInto(out *ServiceExportList) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tin.ListMeta.DeepCopyInto(&out.ListMeta)\n\tif in.Items != nil {\n\t\tin, out := &in.Items, &out.Items\n\t\t*out = make([]ServiceExport, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceExportList.\nfunc (in *ServiceExportList) DeepCopy() *ServiceExportList {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceExportList)\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 *ServiceExportList) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceExportSpec) DeepCopyInto(out *ServiceExportSpec) {\n\t*out = *in\n\tif in.ExportedLabels != nil {\n\t\tin, out := &in.ExportedLabels, &out.ExportedLabels\n\t\t*out = make(map[string]string, len(*in))\n\t\tfor key, val := range *in {\n\t\t\t(*out)[key] = val\n\t\t}\n\t}\n\tif in.ExportedAnnotations != nil {\n\t\tin, out := &in.ExportedAnnotations, &out.ExportedAnnotations\n\t\t*out = make(map[string]string, len(*in))\n\t\tfor key, val := range *in {\n\t\t\t(*out)[key] = val\n\t\t}\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceExportSpec.\nfunc (in *ServiceExportSpec) DeepCopy() *ServiceExportSpec {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceExportSpec)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceExportStatus) DeepCopyInto(out *ServiceExportStatus) {\n\t*out = *in\n\tif in.Conditions != nil {\n\t\tin, out := &in.Conditions, &out.Conditions\n\t\t*out = make([]v1.Condition, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceExportStatus.\nfunc (in *ServiceExportStatus) DeepCopy() *ServiceExportStatus {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceExportStatus)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceImport) DeepCopyInto(out *ServiceImport) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tin.ObjectMeta.DeepCopyInto(&out.ObjectMeta)\n\tin.Spec.DeepCopyInto(&out.Spec)\n\tin.Status.DeepCopyInto(&out.Status)\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceImport.\nfunc (in *ServiceImport) DeepCopy() *ServiceImport {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceImport)\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 *ServiceImport) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceImportList) DeepCopyInto(out *ServiceImportList) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tin.ListMeta.DeepCopyInto(&out.ListMeta)\n\tif in.Items != nil {\n\t\tin, out := &in.Items, &out.Items\n\t\t*out = make([]ServiceImport, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceImportList.\nfunc (in *ServiceImportList) DeepCopy() *ServiceImportList {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceImportList)\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 *ServiceImportList) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceImportSpec) DeepCopyInto(out *ServiceImportSpec) {\n\t*out = *in\n\tif in.Ports != nil {\n\t\tin, out := &in.Ports, &out.Ports\n\t\t*out = make([]ServicePort, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n\tif in.IPs != nil {\n\t\tin, out := &in.IPs, &out.IPs\n\t\t*out = make([]string, len(*in))\n\t\tcopy(*out, *in)\n\t}\n\tif in.SessionAffinityConfig != nil {\n\t\tin, out := &in.SessionAffinityConfig, &out.SessionAffinityConfig\n\t\t*out = new(corev1.SessionAffinityConfig)\n\t\t(*in).DeepCopyInto(*out)\n\t}\n\tif in.IPFamilies != nil {\n\t\tin, out := &in.IPFamilies, &out.IPFamilies\n\t\t*out = make([]corev1.IPFamily, len(*in))\n\t\tcopy(*out, *in)\n\t}\n\tif in.InternalTrafficPolicy != nil {\n\t\tin, out := &in.InternalTrafficPolicy, &out.InternalTrafficPolicy\n\t\t*out = new(corev1.ServiceInternalTrafficPolicy)\n\t\t**out = **in\n\t}\n\tif in.TrafficDistribution != nil {\n\t\tin, out := &in.TrafficDistribution, &out.TrafficDistribution\n\t\t*out = new(string)\n\t\t**out = **in\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceImportSpec.\nfunc (in *ServiceImportSpec) DeepCopy() *ServiceImportSpec {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceImportSpec)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceImportStatus) DeepCopyInto(out *ServiceImportStatus) {\n\t*out = *in\n\tif in.Clusters != nil {\n\t\tin, out := &in.Clusters, &out.Clusters\n\t\t*out = make([]ClusterStatus, len(*in))\n\t\tcopy(*out, *in)\n\t}\n\tif in.Conditions != nil {\n\t\tin, out := &in.Conditions, &out.Conditions\n\t\t*out = make([]v1.Condition, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceImportStatus.\nfunc (in *ServiceImportStatus) DeepCopy() *ServiceImportStatus {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceImportStatus)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServicePort) DeepCopyInto(out *ServicePort) {\n\t*out = *in\n\tif in.AppProtocol != nil {\n\t\tin, out := &in.AppProtocol, &out.AppProtocol\n\t\t*out = new(string)\n\t\t**out = **in\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePort.\nfunc (in *ServicePort) DeepCopy() *ServicePort {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServicePort)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n"
  },
  {
    "path": "pkg/apis/v1alpha1/zz_generated.register.go",
    "content": "//go:build !ignore_autogenerated\n// +build !ignore_autogenerated\n\n/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by register-gen. DO NOT EDIT.\n\npackage v1alpha1\n\nimport (\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n\tschema \"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// GroupName specifies the group name used to register the objects.\nconst GroupName = \"multicluster.x-k8s.io\"\n\n// GroupVersion specifies the group and the version used to register the objects.\nvar GroupVersion = v1.GroupVersion{Group: GroupName, Version: \"v1alpha1\"}\n\n// SchemeGroupVersion is group version used to register these objects\n// Deprecated: use GroupVersion instead.\nvar SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: \"v1alpha1\"}\n\n// Resource takes an unqualified resource and returns a Group qualified GroupResource\nfunc Resource(resource string) schema.GroupResource {\n\treturn SchemeGroupVersion.WithResource(resource).GroupResource()\n}\n\nvar (\n\t// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.\n\tSchemeBuilder      runtime.SchemeBuilder\n\tlocalSchemeBuilder = &SchemeBuilder\n\t// Deprecated: use Install instead\n\tAddToScheme = localSchemeBuilder.AddToScheme\n\tInstall     = localSchemeBuilder.AddToScheme\n)\n\nfunc init() {\n\t// We only register manually written functions here. The registration of the\n\t// generated functions takes place in the generated files. The separation\n\t// makes the code compile even when the generated files are missing.\n\tlocalSchemeBuilder.Register(addKnownTypes)\n}\n\n// Adds the list of known types to Scheme.\nfunc addKnownTypes(scheme *runtime.Scheme) error {\n\tscheme.AddKnownTypes(SchemeGroupVersion,\n\t\t&ServiceExport{},\n\t\t&ServiceExportList{},\n\t\t&ServiceImport{},\n\t\t&ServiceImportList{},\n\t)\n\t// AddToGroupVersion allows the serialization of client types like ListOptions.\n\tv1.AddToGroupVersion(scheme, SchemeGroupVersion)\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/apis/v1beta1/BUILD",
    "content": "load(\"@io_bazel_rules_go//go:def.bzl\", \"go_library\")\n\ngo_library(\n    name = \"go_default_library\",\n    srcs = [\n        \"doc.go\",\n        \"register.go\",\n        \"types.go\",\n        \"well_known_labels.go\",\n        \"zz_generated.deepcopy.go\",\n    ],\n    importmap = \"k8s.io/kubernetes/vendor/k8s.io/mcs-api/pkg/apis/v1beta1\",\n    importpath = \"k8s.io/mcs-api/pkg/apis/v1beta1\",\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"//staging/src/k8s.io/api/core/v1:go_default_library\",\n        \"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library\",\n        \"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library\",\n        \"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library\",\n    ],\n)\n\nfilegroup(\n    name = \"package-srcs\",\n    srcs = glob([\"**\"]),\n    tags = [\"automanaged\"],\n    visibility = [\"//visibility:private\"],\n)\n\nfilegroup(\n    name = \"all-srcs\",\n    srcs = [\":package-srcs\"],\n    tags = [\"automanaged\"],\n    visibility = [\"//visibility:public\"],\n)\n"
  },
  {
    "path": "pkg/apis/v1beta1/doc.go",
    "content": "/*\nCopyright 2025 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Package v1beta1 contains API schema definitions for the Multi-Cluster\n// Services v1beta1 API group.\n// +kubebuilder:object:generate=true\n// +groupName=multicluster.x-k8s.io\npackage v1beta1\n"
  },
  {
    "path": "pkg/apis/v1beta1/serviceexport.go",
    "content": "/*\nCopyright 2025 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage v1beta1\n\nimport (\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nconst (\n\t// ServiceExportPluralName is the plural name of ServiceExport\n\tServiceExportPluralName = \"serviceexports\"\n\t// ServiceExportKindName is the kind name of ServiceExport\n\tServiceExportKindName = \"ServiceExport\"\n\t// ServiceExportFullName is the full name of ServiceExport\n\tServiceExportFullName = ServiceExportPluralName + \".\" + GroupName\n)\n\n// ServiceExportVersionedName is the versioned name of ServiceExport\nvar ServiceExportVersionedName = ServiceExportKindName + \"/\" + GroupVersion.Version\n\n// +genclient\n// +kubebuilder:object:root=true\n// +kubebuilder:resource:shortName={svcex,svcexport}\n// +kubebuilder:storageversion\n\n// ServiceExport declares that the Service with the same name and namespace\n// as this export should be consumable from other clusters.\ntype ServiceExport struct {\n\tmetav1.TypeMeta `json:\",inline\"`\n\t// +optional\n\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\t// spec defines the behavior of a ServiceExport.\n\t// +optional\n\tSpec ServiceExportSpec `json:\"spec,omitempty\"`\n\t// status describes the current state of an exported service.\n\t// Service configuration comes from the Service that had the same\n\t// name and namespace as this ServiceExport.\n\t// Populated by the multi-cluster service implementation's controller.\n\t// +optional\n\tStatus ServiceExportStatus `json:\"status,omitempty\"`\n}\n\n// ServiceExportSpec describes an exported service extra information\ntype ServiceExportSpec struct {\n\t// exportedLabels describes the labels exported. It is optional for implementation.\n\t// +optional\n\tExportedLabels map[string]string `json:\"exportedLabels,omitempty\"`\n\t// exportedAnnotations describes the annotations exported. It is optional for implementation.\n\t// +optional\n\tExportedAnnotations map[string]string `json:\"exportedAnnotations,omitempty\"`\n}\n\n// ServiceExportStatus contains the current status of an export.\ntype ServiceExportStatus struct {\n\t// +optional\n\t// +patchStrategy=merge\n\t// +patchMergeKey=type\n\t// +listType=map\n\t// +listMapKey=type\n\tConditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"`\n}\n\n// +kubebuilder:object:root=true\n\n// ServiceExportList represents a list of endpoint slices\ntype ServiceExportList struct {\n\tmetav1.TypeMeta `json:\",inline\"`\n\t// Standard list metadata.\n\t// +optional\n\tmetav1.ListMeta `json:\"metadata,omitempty\"`\n\t// List of endpoint slices\n\t// +listType=set\n\tItems []ServiceExport `json:\"items\"`\n}\n\n// ServiceExportConditionType is a type of condition associated with a\n// ServiceExport. This type should be used with the ServiceExportStatus.Conditions\n// field.\ntype ServiceExportConditionType string\n\n// ServiceExportConditionReason defines the set of reasons that explain why a\n// particular ServiceExport condition type has been raised.\ntype ServiceExportConditionReason string\n\n// NewServiceExportCondition creates a new ServiceExport condition\nfunc NewServiceExportCondition(t ServiceExportConditionType, status metav1.ConditionStatus, reason ServiceExportConditionReason, msg string) metav1.Condition {\n\treturn metav1.Condition{\n\t\tType:               string(t),\n\t\tStatus:             status,\n\t\tReason:             string(reason),\n\t\tMessage:            msg,\n\t\tLastTransitionTime: metav1.Now(),\n\t}\n}\n\nconst (\n\t// ServiceExportConditionValid is true when the Service Export is valid.\n\t// This does not indicate whether or not the configuration has been exported\n\t// to a control plane / data plane.\n\t//\n\t//\n\t// Possible reasons for this condition to be true are:\n\t//\n\t// * \"Valid\"\n\t//\n\t// Possible reasons for this condition to be False are:\n\t//\n\t// * \"NoService\"\n\t// * \"InvalidServiceType\"\n\t//\n\t// Controllers may raise this condition with other reasons,\n\t// but should prefer to use the reasons listed above to improve\n\t// interoperability.\n\tServiceExportConditionValid ServiceExportConditionType = \"Valid\"\n\n\t// ServiceExportReasonValid is used with the \"Valid\" condition when the\n\t// condition is True.\n\tServiceExportReasonValid ServiceExportConditionReason = \"Valid\"\n\n\t// ServiceExportReasonNoService is used with the \"Valid\" condition when\n\t// the associated Service does not exist.\n\tServiceExportReasonNoService ServiceExportConditionReason = \"NoService\"\n\n\t// ServiceExportReasonInvalidServiceType is used with the \"Valid\"\n\t// condition when the associated Service has an invalid type\n\t// (per the KEP at least the ExternalName type).\n\tServiceExportReasonInvalidServiceType ServiceExportConditionReason = \"InvalidServiceType\"\n)\n\nconst (\n\t// ServiceExportConditionReady is true when the service is exported\n\t// to some control plane or data plane or ready to be pulled.\n\t//\n\t//\n\t// Possible reasons for this condition to be true are:\n\t//\n\t// * \"Exported\"\n\t// * \"Ready\"\n\t//\n\t// Possible reasons for this condition to be False are:\n\t//\n\t// * \"Pending\"\n\t// * \"Failed\"\n\t//\n\t// Possible reasons for this condition to be Unknown are:\n\t//\n\t// * \"Pending\"\n\t//\n\t// Controllers may raise this condition with other reasons,\n\t// but should prefer to use the reasons listed above to improve\n\t// interoperability.\n\tServiceExportConditionReady ServiceExportConditionType = \"Ready\"\n\n\t// ServiceExportReasonExported is used with the \"Ready\" condition\n\t// when the condition is True and the service has been exported.\n\t// This would be used when an implementation exports a service\n\t// to a control plane or data plane.\n\tServiceExportReasonExported ServiceExportConditionReason = \"Exported\"\n\n\t// ServiceExportReasonReady is used with the \"Ready\" condition\n\t// when the condition is True and the service has been exported.\n\t// This would typically be used in an implementation that uses a\n\t// pull model.\n\tServiceExportReasonReady ServiceExportConditionReason = \"Ready\"\n\n\t// ServiceExportReasonPending is used with the \"Ready\" condition\n\t// when the service is in the process of being exported.\n\tServiceExportReasonPending ServiceExportConditionReason = \"Pending\"\n\n\t// ServiceExportReasonFailed is used with the \"Ready\" condition\n\t// when the service failed to be exported with the message providing\n\t// the specific reason.\n\tServiceExportReasonFailed ServiceExportConditionReason = \"Failed\"\n)\n\nconst (\n\t// ServiceExportConditionConflict indicates that some property of an\n\t// exported service has conflicting values across the constituent\n\t// ServiceExports. This condition must be at least raised on the\n\t// conflicting ServiceExport and is recommended to be raised on all on\n\t// all the constituent ServiceExports if feasible.\n\t//\n\t//\n\t// Possible reasons for this condition to be true are:\n\t//\n\t// * \"PortConflict\"\n\t// * \"TypeConflict\"\n\t// * \"SessionAffinityConflict\"\n\t// * \"SessionAffinityConfigConflict\"\n\t// * \"AnnotationsConflict\"\n\t// * \"LabelsConflict\"\n\t//\n\t// When multiple conflicts occurs the above reasons may be combined\n\t// using commas.\n\t//\n\t// Possible reasons for this condition to be False are:\n\t//\n\t// * \"NoConflicts\"\n\t//\n\t// Controllers may raise this condition with other reasons,\n\t// but should prefer to use the reasons listed above to improve\n\t// interoperability.\n\tServiceExportConditionConflict ServiceExportConditionType = \"Conflict\"\n\n\t// ServiceExportReasonPortConflict is used with the \"Conflict\" condition\n\t// when the exported service has a conflict related to port configuration\n\t// if the ports are not identical in all the constituent Services.\n\tServiceExportReasonPortConflict ServiceExportConditionReason = \"PortConflict\"\n\n\t// ServiceExportReasonTypeConflict is used with the \"Conflict\" condition\n\t// when the exported service has a conflict related to the service type\n\t// (eg headless vs non-headless).\n\tServiceExportReasonTypeConflict ServiceExportConditionReason = \"TypeConflict\"\n\n\t// ServiceExportReasonSessionAffinityConflict is used with the \"Conflict\"\n\t// condition when the exported service has a conflict related to session affinity.\n\tServiceExportReasonSessionAffinityConflict ServiceExportConditionReason = \"SessionAffinityConflict\"\n\n\t// ServiceExportReasonSessionAffinityConfigConflict is used with the\n\t// \"Conflict\" condition when the exported service has a conflict related\n\t// to session affinity config.\n\tServiceExportReasonSessionAffinityConfigConflict ServiceExportConditionReason = \"SessionAffinityConfigConflict\"\n\n\t// ServiceExportReasonLabelsConflict is used with the \"Conflict\"\n\t// condition when the ServiceExport has a conflict related to exported\n\t// labels.\n\tServiceExportReasonLabelsConflict ServiceExportConditionReason = \"LabelsConflict\"\n\n\t// ServiceExportReasonAnnotationsConflict is used with the \"Conflict\"\n\t// condition when the ServiceExport has a conflict related to exported\n\t// annotations.\n\tServiceExportReasonAnnotationsConflict ServiceExportConditionReason = \"AnnotationsConflict\"\n\n\t// ServiceExportReasonInternalTrafficPolicyConflict is used with the \"Conflict\"\n\t// condition when the exported service has a conflict related to internal traffic policy.\n\tServiceExportReasonInternalTrafficPolicyConflict ServiceExportConditionReason = \"InternalTrafficPolicyConflict\"\n\n\t// ServiceExportReasonTrafficDistributionConflict is used with the \"Conflict\"\n\t// condition when the exported service has a conflict related to traffic distribution.\n\tServiceExportReasonTrafficDistributionConflict ServiceExportConditionReason = \"TrafficDistributionConflict\"\n\n\t// ServiceExportReasonIPFamilyConflict is used with the \"Conflict\" condition\n\t// when the exported service has a conflict related to IPFamilies.\n\t// The handling of IP families is implementation-specific but this condition\n\t// must be used if a conflicting IP family may result in network traffic reaching\n\t// only a subset of the backends depending on the IP protocol used.\n\tServiceExportReasonIPFamilyConflict ServiceExportConditionReason = \"IPFamilyConflict\"\n\n\t// ServiceExportReasonNoConflicts is used with the \"Conflict\" condition\n\t// when the condition is False.\n\tServiceExportReasonNoConflicts ServiceExportConditionReason = \"NoConflicts\"\n)\n"
  },
  {
    "path": "pkg/apis/v1beta1/serviceimport.go",
    "content": "/*\nCopyright 2025 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage v1beta1\n\nimport (\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nconst (\n\t// ServiceImportPluralName is the plural name of ServiceImport\n\tServiceImportPluralName = \"serviceimports\"\n\t// ServiceImportKindName is the kind name of ServiceImport\n\tServiceImportKindName = \"ServiceImport\"\n\t// ServiceImportFullName is the full name of ServiceImport\n\tServiceImportFullName = ServiceImportPluralName + \".\" + GroupName\n)\n\n// ServiceImportVersionedName is the versioned name of ServiceImport\nvar ServiceImportVersionedName = ServiceImportKindName + \"/\" + GroupVersion.Version\n\n// +genclient\n// +kubebuilder:object:root=true\n// +kubebuilder:resource:shortName={svcim,svcimport}\n// +kubebuilder:storageversion\n\n// ServiceImport describes a service imported from clusters in a ClusterSet.\ntype ServiceImport struct {\n\tmetav1.TypeMeta `json:\",inline\"`\n\t// +optional\n\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\t// spec defines the behavior of a ServiceImport.\n\t// +optional\n\tSpec ServiceImportSpec `json:\"spec,omitempty\"`\n\t// status contains information about the exported services that form\n\t// the multi-cluster service referenced by this ServiceImport.\n\t// +optional\n\tStatus ServiceImportStatus `json:\"status,omitempty\"`\n}\n\n// ServiceImportType designates the type of a ServiceImport\ntype ServiceImportType string\n\nconst (\n\t// ClusterSetIP are only accessible via the ClusterSet IP.\n\tClusterSetIP ServiceImportType = \"ClusterSetIP\"\n\t// Headless services allow backend pods to be addressed directly.\n\tHeadless ServiceImportType = \"Headless\"\n)\n\n// ServiceImportSpec describes an imported service and the information necessary to consume it.\ntype ServiceImportSpec struct {\n\t// +listType=atomic\n\tPorts []ServicePort `json:\"ports\"`\n\t// ip will be used as the VIP for this service when type is ClusterSetIP.\n\t// +kubebuilder:validation:MaxItems:=2\n\t// +optional\n\tIPs []string `json:\"ips,omitempty\"`\n\t// type defines the type of this service.\n\t// Must be ClusterSetIP or Headless.\n\t// +kubebuilder:validation:Enum=ClusterSetIP;Headless\n\tType ServiceImportType `json:\"type\"`\n\t// Supports \"ClientIP\" and \"None\". Used to maintain session affinity.\n\t// Enable client IP based session affinity.\n\t// Must be ClientIP or None.\n\t// Defaults to None.\n\t// Ignored when type is Headless\n\t// More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\n\t// +optional\n\tSessionAffinity v1.ServiceAffinity `json:\"sessionAffinity,omitempty\"`\n\t// sessionAffinityConfig contains session affinity configuration.\n\t// +optional\n\tSessionAffinityConfig *v1.SessionAffinityConfig `json:\"sessionAffinityConfig,omitempty\"`\n\t// IPFamilies identifies all the IPFamilies assigned for this ServiceImport.\n\t// +kubebuilder:validation:MaxItems:=2\n\t// +optional\n\tIPFamilies []v1.IPFamily `json:\"ipFamilies,omitempty\"`\n\n\t// InternalTrafficPolicy describes how nodes distribute service traffic they\n\t// receive on the ClusterIP. If set to \"Local\", the proxy will assume that pods\n\t// only want to talk to endpoints of the service on the same node as the pod,\n\t// dropping the traffic if there are no local endpoints. The default value,\n\t// \"Cluster\", uses the standard behavior of routing to all endpoints evenly\n\t// (possibly modified by topology and other features).\n\t// +optional\n\tInternalTrafficPolicy *v1.ServiceInternalTrafficPolicy `json:\"internalTrafficPolicy,omitempty\"`\n\n\t// TrafficDistribution offers a way to express preferences for how traffic\n\t// is distributed to Service endpoints. Implementations can use this field\n\t// as a hint, but are not required to guarantee strict adherence. If the\n\t// field is not set, the implementation will apply its default routing\n\t// strategy. If set to \"PreferClose\", implementations should prioritize\n\t// endpoints that are in the same zone.\n\t// +optional\n\tTrafficDistribution *string `json:\"trafficDistribution,omitempty\"`\n}\n\n// ServicePort represents the port on which the service is exposed\ntype ServicePort struct {\n\t// The name of this port within the service. This must be a DNS_LABEL.\n\t// All ports within a ServiceSpec must have unique names. When considering\n\t// the endpoints for a Service, this must match the 'name' field in the\n\t// EndpointPort.\n\t// Optional if only one ServicePort is defined on this service.\n\t// +optional\n\tName string `json:\"name,omitempty\"`\n\n\t// The IP protocol for this port. Supports \"TCP\", \"UDP\", and \"SCTP\".\n\t// Default is TCP.\n\t// +optional\n\tProtocol v1.Protocol `json:\"protocol,omitempty\"`\n\n\t// The application protocol for this port.\n\t// This is used as a hint for implementations to offer richer behavior for protocols that they understand.\n\t// This field follows standard Kubernetes label syntax.\n\t// Valid values are either:\n\t//\n\t// * Un-prefixed protocol names - reserved for IANA standard service names (as per\n\t// RFC-6335 and https://www.iana.org/assignments/service-names).\n\t//\n\t// * Kubernetes-defined prefixed names:\n\t//   * 'kubernetes.io/h2c' - HTTP/2 over cleartext as described in https://www.rfc-editor.org/rfc/rfc7540\n\t//\n\t// * Other protocols should use implementation-defined prefixed names such as\n\t// mycompany.com/my-custom-protocol.\n\t// Field can be enabled with ServiceAppProtocol feature gate.\n\t// +optional\n\tAppProtocol *string `json:\"appProtocol,omitempty\"`\n\n\t// The port that will be exposed by this service.\n\tPort int32 `json:\"port\"`\n}\n\n// ServiceImportStatus describes derived state of an imported service.\ntype ServiceImportStatus struct {\n\t// clusters is the list of exporting clusters from which this service\n\t// was derived.\n\t// +optional\n\t// +patchStrategy=merge\n\t// +patchMergeKey=cluster\n\t// +listType=map\n\t// +listMapKey=cluster\n\tClusters []ClusterStatus `json:\"clusters,omitempty\"`\n\t// +optional\n\t// +patchStrategy=merge\n\t// +patchMergeKey=type\n\t// +listType=map\n\t// +listMapKey=type\n\tConditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"`\n}\n\n// ClusterStatus contains service configuration mapped to a specific source cluster\ntype ClusterStatus struct {\n\t// cluster is the name of the exporting cluster. Must be a valid RFC-1123 DNS\n\t// label.\n\tCluster string `json:\"cluster\"`\n}\n\n// +kubebuilder:object:root=true\n\n// ServiceImportList represents a list of endpoint slices\ntype ServiceImportList struct {\n\tmetav1.TypeMeta `json:\",inline\"`\n\t// Standard list metadata.\n\t// +optional\n\tmetav1.ListMeta `json:\"metadata,omitempty\"`\n\t// List of endpoint slices\n\t// +listType=set\n\tItems []ServiceImport `json:\"items\"`\n}\n\n// ServiceImportConditionType is a type of condition associated with a\n// ServiceImport. This type should be used with the ServiceImportStatus.Conditions\n// field.\ntype ServiceImportConditionType string\n\n// ServiceImportConditionReason defines the set of reasons that explain why a\n// particular ServiceImport condition type has been raised.\ntype ServiceImportConditionReason string\n\n// NewServiceImportCondition creates a new ServiceImport condition\nfunc NewServiceImportCondition(t ServiceImportConditionType, status metav1.ConditionStatus, reason ServiceImportConditionReason, msg string) metav1.Condition {\n\treturn metav1.Condition{\n\t\tType:               string(t),\n\t\tStatus:             status,\n\t\tReason:             string(reason),\n\t\tMessage:            msg,\n\t\tLastTransitionTime: metav1.Now(),\n\t}\n}\n\nconst (\n\t// ServiceImportConditionReady is true when the Service Import is ready.\n\t//\n\t//\n\t// Possible reasons for this condition to be true are:\n\t//\n\t// * \"Ready\"\n\t//\n\t// Possible reasons for this condition to be False are:\n\t//\n\t// * \"Pending\"\n\t// * \"IPFamilyNotSupported\"\n\t//\n\t// Possible reasons for this condition to be Unknown are:\n\t//\n\t// * \"Pending\"\n\t//\n\t// Controllers may raise this condition with other reasons,\n\t// but should prefer to use the reasons listed above to improve\n\t// interoperability.\n\tServiceImportConditionReady ServiceImportConditionType = \"Ready\"\n\n\t// ServiceImportReasonReady is used with the \"Ready\" condition when the\n\t// condition is True.\n\tServiceImportReasonReady ServiceImportConditionReason = \"Ready\"\n\n\t// ServiceImportReasonPending is used with the \"Ready\" condition when\n\t// the ServiceImport is in the process of being created or updated.\n\tServiceImportReasonPending ServiceImportConditionReason = \"Pending\"\n\n\t// ServiceImportReasonIPFamilyNotSupported is used with the \"Ready\"\n\t// condition when the service can not be imported due to IP families\n\t// mismatch.\n\tServiceImportReasonIPFamilyNotSupported ServiceImportConditionReason = \"IPFamilyNotSupported\"\n)\n"
  },
  {
    "path": "pkg/apis/v1beta1/well_known_labels.go",
    "content": "/*\nCopyright 2025 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage v1beta1\n\nconst (\n\t// LabelServiceName is used to indicate the name of multi-cluster service\n\t// that an EndpointSlice belongs to.\n\tLabelServiceName = \"multicluster.kubernetes.io/service-name\"\n\n\t// LabelSourceCluster is used to indicate the name of the cluster in which an exported resource exists.\n\tLabelSourceCluster = \"multicluster.kubernetes.io/source-cluster\"\n)\n"
  },
  {
    "path": "pkg/apis/v1beta1/zz_generated.deepcopy.go",
    "content": "//go:build !ignore_autogenerated\n\n/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by controller-gen. DO NOT EDIT.\n\npackage v1beta1\n\nimport (\n\tcorev1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1\"\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 *ClusterStatus) DeepCopyInto(out *ClusterStatus) {\n\t*out = *in\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterStatus.\nfunc (in *ClusterStatus) DeepCopy() *ClusterStatus {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ClusterStatus)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceExport) DeepCopyInto(out *ServiceExport) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tin.ObjectMeta.DeepCopyInto(&out.ObjectMeta)\n\tin.Spec.DeepCopyInto(&out.Spec)\n\tin.Status.DeepCopyInto(&out.Status)\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceExport.\nfunc (in *ServiceExport) DeepCopy() *ServiceExport {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceExport)\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 *ServiceExport) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceExportList) DeepCopyInto(out *ServiceExportList) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tin.ListMeta.DeepCopyInto(&out.ListMeta)\n\tif in.Items != nil {\n\t\tin, out := &in.Items, &out.Items\n\t\t*out = make([]ServiceExport, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceExportList.\nfunc (in *ServiceExportList) DeepCopy() *ServiceExportList {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceExportList)\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 *ServiceExportList) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceExportSpec) DeepCopyInto(out *ServiceExportSpec) {\n\t*out = *in\n\tif in.ExportedLabels != nil {\n\t\tin, out := &in.ExportedLabels, &out.ExportedLabels\n\t\t*out = make(map[string]string, len(*in))\n\t\tfor key, val := range *in {\n\t\t\t(*out)[key] = val\n\t\t}\n\t}\n\tif in.ExportedAnnotations != nil {\n\t\tin, out := &in.ExportedAnnotations, &out.ExportedAnnotations\n\t\t*out = make(map[string]string, len(*in))\n\t\tfor key, val := range *in {\n\t\t\t(*out)[key] = val\n\t\t}\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceExportSpec.\nfunc (in *ServiceExportSpec) DeepCopy() *ServiceExportSpec {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceExportSpec)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceExportStatus) DeepCopyInto(out *ServiceExportStatus) {\n\t*out = *in\n\tif in.Conditions != nil {\n\t\tin, out := &in.Conditions, &out.Conditions\n\t\t*out = make([]v1.Condition, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceExportStatus.\nfunc (in *ServiceExportStatus) DeepCopy() *ServiceExportStatus {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceExportStatus)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceImport) DeepCopyInto(out *ServiceImport) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tin.ObjectMeta.DeepCopyInto(&out.ObjectMeta)\n\tin.Spec.DeepCopyInto(&out.Spec)\n\tin.Status.DeepCopyInto(&out.Status)\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceImport.\nfunc (in *ServiceImport) DeepCopy() *ServiceImport {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceImport)\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 *ServiceImport) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceImportList) DeepCopyInto(out *ServiceImportList) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tin.ListMeta.DeepCopyInto(&out.ListMeta)\n\tif in.Items != nil {\n\t\tin, out := &in.Items, &out.Items\n\t\t*out = make([]ServiceImport, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceImportList.\nfunc (in *ServiceImportList) DeepCopy() *ServiceImportList {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceImportList)\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 *ServiceImportList) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceImportSpec) DeepCopyInto(out *ServiceImportSpec) {\n\t*out = *in\n\tif in.Ports != nil {\n\t\tin, out := &in.Ports, &out.Ports\n\t\t*out = make([]ServicePort, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n\tif in.IPs != nil {\n\t\tin, out := &in.IPs, &out.IPs\n\t\t*out = make([]string, len(*in))\n\t\tcopy(*out, *in)\n\t}\n\tif in.SessionAffinityConfig != nil {\n\t\tin, out := &in.SessionAffinityConfig, &out.SessionAffinityConfig\n\t\t*out = new(corev1.SessionAffinityConfig)\n\t\t(*in).DeepCopyInto(*out)\n\t}\n\tif in.IPFamilies != nil {\n\t\tin, out := &in.IPFamilies, &out.IPFamilies\n\t\t*out = make([]corev1.IPFamily, len(*in))\n\t\tcopy(*out, *in)\n\t}\n\tif in.InternalTrafficPolicy != nil {\n\t\tin, out := &in.InternalTrafficPolicy, &out.InternalTrafficPolicy\n\t\t*out = new(corev1.ServiceInternalTrafficPolicy)\n\t\t**out = **in\n\t}\n\tif in.TrafficDistribution != nil {\n\t\tin, out := &in.TrafficDistribution, &out.TrafficDistribution\n\t\t*out = new(string)\n\t\t**out = **in\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceImportSpec.\nfunc (in *ServiceImportSpec) DeepCopy() *ServiceImportSpec {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceImportSpec)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceImportStatus) DeepCopyInto(out *ServiceImportStatus) {\n\t*out = *in\n\tif in.Clusters != nil {\n\t\tin, out := &in.Clusters, &out.Clusters\n\t\t*out = make([]ClusterStatus, len(*in))\n\t\tcopy(*out, *in)\n\t}\n\tif in.Conditions != nil {\n\t\tin, out := &in.Conditions, &out.Conditions\n\t\t*out = make([]v1.Condition, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceImportStatus.\nfunc (in *ServiceImportStatus) DeepCopy() *ServiceImportStatus {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceImportStatus)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServicePort) DeepCopyInto(out *ServicePort) {\n\t*out = *in\n\tif in.AppProtocol != nil {\n\t\tin, out := &in.AppProtocol, &out.AppProtocol\n\t\t*out = new(string)\n\t\t**out = **in\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePort.\nfunc (in *ServicePort) DeepCopy() *ServicePort {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServicePort)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n"
  },
  {
    "path": "pkg/apis/v1beta1/zz_generated.register.go",
    "content": "//go:build !ignore_autogenerated\n// +build !ignore_autogenerated\n\n/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by register-gen. DO NOT EDIT.\n\npackage v1beta1\n\nimport (\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n\tschema \"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\n// GroupName specifies the group name used to register the objects.\nconst GroupName = \"multicluster.x-k8s.io\"\n\n// GroupVersion specifies the group and the version used to register the objects.\nvar GroupVersion = v1.GroupVersion{Group: GroupName, Version: \"v1beta1\"}\n\n// SchemeGroupVersion is group version used to register these objects\n// Deprecated: use GroupVersion instead.\nvar SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: \"v1beta1\"}\n\n// Resource takes an unqualified resource and returns a Group qualified GroupResource\nfunc Resource(resource string) schema.GroupResource {\n\treturn SchemeGroupVersion.WithResource(resource).GroupResource()\n}\n\nvar (\n\t// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.\n\tSchemeBuilder      runtime.SchemeBuilder\n\tlocalSchemeBuilder = &SchemeBuilder\n\t// Deprecated: use Install instead\n\tAddToScheme = localSchemeBuilder.AddToScheme\n\tInstall     = localSchemeBuilder.AddToScheme\n)\n\nfunc init() {\n\t// We only register manually written functions here. The registration of the\n\t// generated functions takes place in the generated files. The separation\n\t// makes the code compile even when the generated files are missing.\n\tlocalSchemeBuilder.Register(addKnownTypes)\n}\n\n// Adds the list of known types to Scheme.\nfunc addKnownTypes(scheme *runtime.Scheme) error {\n\tscheme.AddKnownTypes(SchemeGroupVersion,\n\t\t&ServiceExport{},\n\t\t&ServiceExportList{},\n\t\t&ServiceImport{},\n\t\t&ServiceImportList{},\n\t)\n\t// AddToGroupVersion allows the serialization of client types like ListOptions.\n\tv1.AddToGroupVersion(scheme, SchemeGroupVersion)\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/clientset.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage versioned\n\nimport (\n\tfmt \"fmt\"\n\thttp \"net/http\"\n\n\tdiscovery \"k8s.io/client-go/discovery\"\n\trest \"k8s.io/client-go/rest\"\n\tflowcontrol \"k8s.io/client-go/util/flowcontrol\"\n\tmulticlusterv1alpha1 \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1alpha1\"\n\tmulticlusterv1beta1 \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1beta1\"\n)\n\ntype Interface interface {\n\tDiscovery() discovery.DiscoveryInterface\n\tMulticlusterV1alpha1() multiclusterv1alpha1.MulticlusterV1alpha1Interface\n\tMulticlusterV1beta1() multiclusterv1beta1.MulticlusterV1beta1Interface\n}\n\n// Clientset contains the clients for groups.\ntype Clientset struct {\n\t*discovery.DiscoveryClient\n\tmulticlusterV1alpha1 *multiclusterv1alpha1.MulticlusterV1alpha1Client\n\tmulticlusterV1beta1  *multiclusterv1beta1.MulticlusterV1beta1Client\n}\n\n// MulticlusterV1alpha1 retrieves the MulticlusterV1alpha1Client\nfunc (c *Clientset) MulticlusterV1alpha1() multiclusterv1alpha1.MulticlusterV1alpha1Interface {\n\treturn c.multiclusterV1alpha1\n}\n\n// MulticlusterV1beta1 retrieves the MulticlusterV1beta1Client\nfunc (c *Clientset) MulticlusterV1beta1() multiclusterv1beta1.MulticlusterV1beta1Interface {\n\treturn c.multiclusterV1beta1\n}\n\n// Discovery retrieves the DiscoveryClient\nfunc (c *Clientset) Discovery() discovery.DiscoveryInterface {\n\tif c == nil {\n\t\treturn nil\n\t}\n\treturn c.DiscoveryClient\n}\n\n// NewForConfig creates a new Clientset for the given config.\n// If config's RateLimiter is not set and QPS and Burst are acceptable,\n// NewForConfig will generate a rate-limiter in configShallowCopy.\n// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),\n// where httpClient was generated with rest.HTTPClientFor(c).\nfunc NewForConfig(c *rest.Config) (*Clientset, error) {\n\tconfigShallowCopy := *c\n\n\tif configShallowCopy.UserAgent == \"\" {\n\t\tconfigShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent()\n\t}\n\n\t// share the transport between all clients\n\thttpClient, err := rest.HTTPClientFor(&configShallowCopy)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewForConfigAndClient(&configShallowCopy, httpClient)\n}\n\n// NewForConfigAndClient creates a new Clientset for the given config and http client.\n// Note the http client provided takes precedence over the configured transport values.\n// If config's RateLimiter is not set and QPS and Burst are acceptable,\n// NewForConfigAndClient will generate a rate-limiter in configShallowCopy.\nfunc NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) {\n\tconfigShallowCopy := *c\n\tif configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {\n\t\tif configShallowCopy.Burst <= 0 {\n\t\t\treturn nil, fmt.Errorf(\"burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0\")\n\t\t}\n\t\tconfigShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)\n\t}\n\n\tvar cs Clientset\n\tvar err error\n\tcs.multiclusterV1alpha1, err = multiclusterv1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcs.multiclusterV1beta1, err = multiclusterv1beta1.NewForConfigAndClient(&configShallowCopy, httpClient)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &cs, nil\n}\n\n// NewForConfigOrDie creates a new Clientset for the given config and\n// panics if there is an error in the config.\nfunc NewForConfigOrDie(c *rest.Config) *Clientset {\n\tcs, err := NewForConfig(c)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn cs\n}\n\n// New creates a new Clientset for the given RESTClient.\nfunc New(c rest.Interface) *Clientset {\n\tvar cs Clientset\n\tcs.multiclusterV1alpha1 = multiclusterv1alpha1.New(c)\n\tcs.multiclusterV1beta1 = multiclusterv1beta1.New(c)\n\n\tcs.DiscoveryClient = discovery.NewDiscoveryClient(c)\n\treturn &cs\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/fake/clientset_generated.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage fake\n\nimport (\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/watch\"\n\t\"k8s.io/client-go/discovery\"\n\tfakediscovery \"k8s.io/client-go/discovery/fake\"\n\t\"k8s.io/client-go/testing\"\n\tclientset \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned\"\n\tmulticlusterv1alpha1 \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1alpha1\"\n\tfakemulticlusterv1alpha1 \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1alpha1/fake\"\n\tmulticlusterv1beta1 \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1beta1\"\n\tfakemulticlusterv1beta1 \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1beta1/fake\"\n)\n\n// NewSimpleClientset returns a clientset that will respond with the provided objects.\n// It's backed by a very simple object tracker that processes creates, updates and deletions as-is,\n// without applying any field management, validations and/or defaults. It shouldn't be considered a replacement\n// for a real clientset and is mostly useful in simple unit tests.\n//\n// DEPRECATED: NewClientset replaces this with support for field management, which significantly improves\n// server side apply testing. NewClientset is only available when apply configurations are generated (e.g.\n// via --with-applyconfig).\nfunc NewSimpleClientset(objects ...runtime.Object) *Clientset {\n\to := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())\n\tfor _, obj := range objects {\n\t\tif err := o.Add(obj); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\tcs := &Clientset{tracker: o}\n\tcs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}\n\tcs.AddReactor(\"*\", \"*\", testing.ObjectReaction(o))\n\tcs.AddWatchReactor(\"*\", func(action testing.Action) (handled bool, ret watch.Interface, err error) {\n\t\tgvr := action.GetResource()\n\t\tns := action.GetNamespace()\n\t\twatch, err := o.Watch(gvr, ns)\n\t\tif err != nil {\n\t\t\treturn false, nil, err\n\t\t}\n\t\treturn true, watch, nil\n\t})\n\n\treturn cs\n}\n\n// Clientset implements clientset.Interface. Meant to be embedded into a\n// struct to get a default implementation. This makes faking out just the method\n// you want to test easier.\ntype Clientset struct {\n\ttesting.Fake\n\tdiscovery *fakediscovery.FakeDiscovery\n\ttracker   testing.ObjectTracker\n}\n\nfunc (c *Clientset) Discovery() discovery.DiscoveryInterface {\n\treturn c.discovery\n}\n\nfunc (c *Clientset) Tracker() testing.ObjectTracker {\n\treturn c.tracker\n}\n\nvar (\n\t_ clientset.Interface = &Clientset{}\n\t_ testing.FakeClient  = &Clientset{}\n)\n\n// MulticlusterV1alpha1 retrieves the MulticlusterV1alpha1Client\nfunc (c *Clientset) MulticlusterV1alpha1() multiclusterv1alpha1.MulticlusterV1alpha1Interface {\n\treturn &fakemulticlusterv1alpha1.FakeMulticlusterV1alpha1{Fake: &c.Fake}\n}\n\n// MulticlusterV1beta1 retrieves the MulticlusterV1beta1Client\nfunc (c *Clientset) MulticlusterV1beta1() multiclusterv1beta1.MulticlusterV1beta1Interface {\n\treturn &fakemulticlusterv1beta1.FakeMulticlusterV1beta1{Fake: &c.Fake}\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/fake/doc.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\n// This package has the automatically generated fake clientset.\npackage fake\n"
  },
  {
    "path": "pkg/client/clientset/versioned/fake/register.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage fake\n\nimport (\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n\tschema \"k8s.io/apimachinery/pkg/runtime/schema\"\n\tserializer \"k8s.io/apimachinery/pkg/runtime/serializer\"\n\tutilruntime \"k8s.io/apimachinery/pkg/util/runtime\"\n\tmulticlusterv1alpha1 \"sigs.k8s.io/mcs-api/pkg/apis/v1alpha1\"\n\tmulticlusterv1beta1 \"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\nvar scheme = runtime.NewScheme()\nvar codecs = serializer.NewCodecFactory(scheme)\n\nvar localSchemeBuilder = runtime.SchemeBuilder{\n\tmulticlusterv1alpha1.AddToScheme,\n\tmulticlusterv1beta1.AddToScheme,\n}\n\n// AddToScheme adds all types of this clientset into the given scheme. This allows composition\n// of clientsets, like in:\n//\n//\timport (\n//\t  \"k8s.io/client-go/kubernetes\"\n//\t  clientsetscheme \"k8s.io/client-go/kubernetes/scheme\"\n//\t  aggregatorclientsetscheme \"k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme\"\n//\t)\n//\n//\tkclientset, _ := kubernetes.NewForConfig(c)\n//\t_ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)\n//\n// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types\n// correctly.\nvar AddToScheme = localSchemeBuilder.AddToScheme\n\nfunc init() {\n\tv1.AddToGroupVersion(scheme, schema.GroupVersion{Version: \"v1\"})\n\tutilruntime.Must(AddToScheme(scheme))\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/scheme/doc.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\n// This package contains the scheme of the automatically generated clientset.\npackage scheme\n"
  },
  {
    "path": "pkg/client/clientset/versioned/scheme/register.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage scheme\n\nimport (\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n\tschema \"k8s.io/apimachinery/pkg/runtime/schema\"\n\tserializer \"k8s.io/apimachinery/pkg/runtime/serializer\"\n\tutilruntime \"k8s.io/apimachinery/pkg/util/runtime\"\n\tmulticlusterv1alpha1 \"sigs.k8s.io/mcs-api/pkg/apis/v1alpha1\"\n\tmulticlusterv1beta1 \"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\nvar Scheme = runtime.NewScheme()\nvar Codecs = serializer.NewCodecFactory(Scheme)\nvar ParameterCodec = runtime.NewParameterCodec(Scheme)\nvar localSchemeBuilder = runtime.SchemeBuilder{\n\tmulticlusterv1alpha1.AddToScheme,\n\tmulticlusterv1beta1.AddToScheme,\n}\n\n// AddToScheme adds all types of this clientset into the given scheme. This allows composition\n// of clientsets, like in:\n//\n//\timport (\n//\t  \"k8s.io/client-go/kubernetes\"\n//\t  clientsetscheme \"k8s.io/client-go/kubernetes/scheme\"\n//\t  aggregatorclientsetscheme \"k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme\"\n//\t)\n//\n//\tkclientset, _ := kubernetes.NewForConfig(c)\n//\t_ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)\n//\n// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types\n// correctly.\nvar AddToScheme = localSchemeBuilder.AddToScheme\n\nfunc init() {\n\tv1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: \"v1\"})\n\tutilruntime.Must(AddToScheme(Scheme))\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1alpha1/apis_client.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage v1alpha1\n\nimport (\n\thttp \"net/http\"\n\n\trest \"k8s.io/client-go/rest\"\n\tapisv1alpha1 \"sigs.k8s.io/mcs-api/pkg/apis/v1alpha1\"\n\tscheme \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/scheme\"\n)\n\ntype MulticlusterV1alpha1Interface interface {\n\tRESTClient() rest.Interface\n\tServiceExportsGetter\n\tServiceImportsGetter\n}\n\n// MulticlusterV1alpha1Client is used to interact with features provided by the multicluster.x-k8s.io group.\ntype MulticlusterV1alpha1Client struct {\n\trestClient rest.Interface\n}\n\nfunc (c *MulticlusterV1alpha1Client) ServiceExports(namespace string) ServiceExportInterface {\n\treturn newServiceExports(c, namespace)\n}\n\nfunc (c *MulticlusterV1alpha1Client) ServiceImports(namespace string) ServiceImportInterface {\n\treturn newServiceImports(c, namespace)\n}\n\n// NewForConfig creates a new MulticlusterV1alpha1Client for the given config.\n// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),\n// where httpClient was generated with rest.HTTPClientFor(c).\nfunc NewForConfig(c *rest.Config) (*MulticlusterV1alpha1Client, error) {\n\tconfig := *c\n\tif err := setConfigDefaults(&config); err != nil {\n\t\treturn nil, err\n\t}\n\thttpClient, err := rest.HTTPClientFor(&config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewForConfigAndClient(&config, httpClient)\n}\n\n// NewForConfigAndClient creates a new MulticlusterV1alpha1Client for the given config and http client.\n// Note the http client provided takes precedence over the configured transport values.\nfunc NewForConfigAndClient(c *rest.Config, h *http.Client) (*MulticlusterV1alpha1Client, error) {\n\tconfig := *c\n\tif err := setConfigDefaults(&config); err != nil {\n\t\treturn nil, err\n\t}\n\tclient, err := rest.RESTClientForConfigAndClient(&config, h)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &MulticlusterV1alpha1Client{client}, nil\n}\n\n// NewForConfigOrDie creates a new MulticlusterV1alpha1Client for the given config and\n// panics if there is an error in the config.\nfunc NewForConfigOrDie(c *rest.Config) *MulticlusterV1alpha1Client {\n\tclient, err := NewForConfig(c)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn client\n}\n\n// New creates a new MulticlusterV1alpha1Client for the given RESTClient.\nfunc New(c rest.Interface) *MulticlusterV1alpha1Client {\n\treturn &MulticlusterV1alpha1Client{c}\n}\n\nfunc setConfigDefaults(config *rest.Config) error {\n\tgv := apisv1alpha1.SchemeGroupVersion\n\tconfig.GroupVersion = &gv\n\tconfig.APIPath = \"/apis\"\n\tconfig.NegotiatedSerializer = rest.CodecFactoryForGeneratedClient(scheme.Scheme, scheme.Codecs).WithoutConversion()\n\n\tif config.UserAgent == \"\" {\n\t\tconfig.UserAgent = rest.DefaultKubernetesUserAgent()\n\t}\n\n\treturn nil\n}\n\n// RESTClient returns a RESTClient that is used to communicate\n// with API server by this client implementation.\nfunc (c *MulticlusterV1alpha1Client) RESTClient() rest.Interface {\n\tif c == nil {\n\t\treturn nil\n\t}\n\treturn c.restClient\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1alpha1/doc.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\n// This package has the automatically generated typed clients.\npackage v1alpha1\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/doc.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\n// Package fake has the automatically generated clients.\npackage fake\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_apis_client.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage fake\n\nimport (\n\trest \"k8s.io/client-go/rest\"\n\ttesting \"k8s.io/client-go/testing\"\n\tv1alpha1 \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1alpha1\"\n)\n\ntype FakeMulticlusterV1alpha1 struct {\n\t*testing.Fake\n}\n\nfunc (c *FakeMulticlusterV1alpha1) ServiceExports(namespace string) v1alpha1.ServiceExportInterface {\n\treturn newFakeServiceExports(c, namespace)\n}\n\nfunc (c *FakeMulticlusterV1alpha1) ServiceImports(namespace string) v1alpha1.ServiceImportInterface {\n\treturn newFakeServiceImports(c, namespace)\n}\n\n// RESTClient returns a RESTClient that is used to communicate\n// with API server by this client implementation.\nfunc (c *FakeMulticlusterV1alpha1) RESTClient() rest.Interface {\n\tvar ret *rest.RESTClient\n\treturn ret\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_serviceexport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage fake\n\nimport (\n\tgentype \"k8s.io/client-go/gentype\"\n\tv1alpha1 \"sigs.k8s.io/mcs-api/pkg/apis/v1alpha1\"\n\tapisv1alpha1 \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1alpha1\"\n)\n\n// fakeServiceExports implements ServiceExportInterface\ntype fakeServiceExports struct {\n\t*gentype.FakeClientWithList[*v1alpha1.ServiceExport, *v1alpha1.ServiceExportList]\n\tFake *FakeMulticlusterV1alpha1\n}\n\nfunc newFakeServiceExports(fake *FakeMulticlusterV1alpha1, namespace string) apisv1alpha1.ServiceExportInterface {\n\treturn &fakeServiceExports{\n\t\tgentype.NewFakeClientWithList[*v1alpha1.ServiceExport, *v1alpha1.ServiceExportList](\n\t\t\tfake.Fake,\n\t\t\tnamespace,\n\t\t\tv1alpha1.SchemeGroupVersion.WithResource(\"serviceexports\"),\n\t\t\tv1alpha1.SchemeGroupVersion.WithKind(\"ServiceExport\"),\n\t\t\tfunc() *v1alpha1.ServiceExport { return &v1alpha1.ServiceExport{} },\n\t\t\tfunc() *v1alpha1.ServiceExportList { return &v1alpha1.ServiceExportList{} },\n\t\t\tfunc(dst, src *v1alpha1.ServiceExportList) { dst.ListMeta = src.ListMeta },\n\t\t\tfunc(list *v1alpha1.ServiceExportList) []*v1alpha1.ServiceExport {\n\t\t\t\treturn gentype.ToPointerSlice(list.Items)\n\t\t\t},\n\t\t\tfunc(list *v1alpha1.ServiceExportList, items []*v1alpha1.ServiceExport) {\n\t\t\t\tlist.Items = gentype.FromPointerSlice(items)\n\t\t\t},\n\t\t),\n\t\tfake,\n\t}\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1alpha1/fake/fake_serviceimport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage fake\n\nimport (\n\tgentype \"k8s.io/client-go/gentype\"\n\tv1alpha1 \"sigs.k8s.io/mcs-api/pkg/apis/v1alpha1\"\n\tapisv1alpha1 \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1alpha1\"\n)\n\n// fakeServiceImports implements ServiceImportInterface\ntype fakeServiceImports struct {\n\t*gentype.FakeClientWithList[*v1alpha1.ServiceImport, *v1alpha1.ServiceImportList]\n\tFake *FakeMulticlusterV1alpha1\n}\n\nfunc newFakeServiceImports(fake *FakeMulticlusterV1alpha1, namespace string) apisv1alpha1.ServiceImportInterface {\n\treturn &fakeServiceImports{\n\t\tgentype.NewFakeClientWithList[*v1alpha1.ServiceImport, *v1alpha1.ServiceImportList](\n\t\t\tfake.Fake,\n\t\t\tnamespace,\n\t\t\tv1alpha1.SchemeGroupVersion.WithResource(\"serviceimports\"),\n\t\t\tv1alpha1.SchemeGroupVersion.WithKind(\"ServiceImport\"),\n\t\t\tfunc() *v1alpha1.ServiceImport { return &v1alpha1.ServiceImport{} },\n\t\t\tfunc() *v1alpha1.ServiceImportList { return &v1alpha1.ServiceImportList{} },\n\t\t\tfunc(dst, src *v1alpha1.ServiceImportList) { dst.ListMeta = src.ListMeta },\n\t\t\tfunc(list *v1alpha1.ServiceImportList) []*v1alpha1.ServiceImport {\n\t\t\t\treturn gentype.ToPointerSlice(list.Items)\n\t\t\t},\n\t\t\tfunc(list *v1alpha1.ServiceImportList, items []*v1alpha1.ServiceImport) {\n\t\t\t\tlist.Items = gentype.FromPointerSlice(items)\n\t\t\t},\n\t\t),\n\t\tfake,\n\t}\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1alpha1/generated_expansion.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage v1alpha1\n\ntype ServiceExportExpansion interface{}\n\ntype ServiceImportExpansion interface{}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1alpha1/serviceexport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage v1alpha1\n\nimport (\n\tcontext \"context\"\n\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\ttypes \"k8s.io/apimachinery/pkg/types\"\n\twatch \"k8s.io/apimachinery/pkg/watch\"\n\tgentype \"k8s.io/client-go/gentype\"\n\tapisv1alpha1 \"sigs.k8s.io/mcs-api/pkg/apis/v1alpha1\"\n\tscheme \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/scheme\"\n)\n\n// ServiceExportsGetter has a method to return a ServiceExportInterface.\n// A group's client should implement this interface.\ntype ServiceExportsGetter interface {\n\tServiceExports(namespace string) ServiceExportInterface\n}\n\n// ServiceExportInterface has methods to work with ServiceExport resources.\ntype ServiceExportInterface interface {\n\tCreate(ctx context.Context, serviceExport *apisv1alpha1.ServiceExport, opts v1.CreateOptions) (*apisv1alpha1.ServiceExport, error)\n\tUpdate(ctx context.Context, serviceExport *apisv1alpha1.ServiceExport, opts v1.UpdateOptions) (*apisv1alpha1.ServiceExport, error)\n\t// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().\n\tUpdateStatus(ctx context.Context, serviceExport *apisv1alpha1.ServiceExport, opts v1.UpdateOptions) (*apisv1alpha1.ServiceExport, error)\n\tDelete(ctx context.Context, name string, opts v1.DeleteOptions) error\n\tDeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error\n\tGet(ctx context.Context, name string, opts v1.GetOptions) (*apisv1alpha1.ServiceExport, error)\n\tList(ctx context.Context, opts v1.ListOptions) (*apisv1alpha1.ServiceExportList, error)\n\tWatch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)\n\tPatch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *apisv1alpha1.ServiceExport, err error)\n\tServiceExportExpansion\n}\n\n// serviceExports implements ServiceExportInterface\ntype serviceExports struct {\n\t*gentype.ClientWithList[*apisv1alpha1.ServiceExport, *apisv1alpha1.ServiceExportList]\n}\n\n// newServiceExports returns a ServiceExports\nfunc newServiceExports(c *MulticlusterV1alpha1Client, namespace string) *serviceExports {\n\treturn &serviceExports{\n\t\tgentype.NewClientWithList[*apisv1alpha1.ServiceExport, *apisv1alpha1.ServiceExportList](\n\t\t\t\"serviceexports\",\n\t\t\tc.RESTClient(),\n\t\t\tscheme.ParameterCodec,\n\t\t\tnamespace,\n\t\t\tfunc() *apisv1alpha1.ServiceExport { return &apisv1alpha1.ServiceExport{} },\n\t\t\tfunc() *apisv1alpha1.ServiceExportList { return &apisv1alpha1.ServiceExportList{} },\n\t\t),\n\t}\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1alpha1/serviceimport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage v1alpha1\n\nimport (\n\tcontext \"context\"\n\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\ttypes \"k8s.io/apimachinery/pkg/types\"\n\twatch \"k8s.io/apimachinery/pkg/watch\"\n\tgentype \"k8s.io/client-go/gentype\"\n\tapisv1alpha1 \"sigs.k8s.io/mcs-api/pkg/apis/v1alpha1\"\n\tscheme \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/scheme\"\n)\n\n// ServiceImportsGetter has a method to return a ServiceImportInterface.\n// A group's client should implement this interface.\ntype ServiceImportsGetter interface {\n\tServiceImports(namespace string) ServiceImportInterface\n}\n\n// ServiceImportInterface has methods to work with ServiceImport resources.\ntype ServiceImportInterface interface {\n\tCreate(ctx context.Context, serviceImport *apisv1alpha1.ServiceImport, opts v1.CreateOptions) (*apisv1alpha1.ServiceImport, error)\n\tUpdate(ctx context.Context, serviceImport *apisv1alpha1.ServiceImport, opts v1.UpdateOptions) (*apisv1alpha1.ServiceImport, error)\n\t// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().\n\tUpdateStatus(ctx context.Context, serviceImport *apisv1alpha1.ServiceImport, opts v1.UpdateOptions) (*apisv1alpha1.ServiceImport, error)\n\tDelete(ctx context.Context, name string, opts v1.DeleteOptions) error\n\tDeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error\n\tGet(ctx context.Context, name string, opts v1.GetOptions) (*apisv1alpha1.ServiceImport, error)\n\tList(ctx context.Context, opts v1.ListOptions) (*apisv1alpha1.ServiceImportList, error)\n\tWatch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)\n\tPatch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *apisv1alpha1.ServiceImport, err error)\n\tServiceImportExpansion\n}\n\n// serviceImports implements ServiceImportInterface\ntype serviceImports struct {\n\t*gentype.ClientWithList[*apisv1alpha1.ServiceImport, *apisv1alpha1.ServiceImportList]\n}\n\n// newServiceImports returns a ServiceImports\nfunc newServiceImports(c *MulticlusterV1alpha1Client, namespace string) *serviceImports {\n\treturn &serviceImports{\n\t\tgentype.NewClientWithList[*apisv1alpha1.ServiceImport, *apisv1alpha1.ServiceImportList](\n\t\t\t\"serviceimports\",\n\t\t\tc.RESTClient(),\n\t\t\tscheme.ParameterCodec,\n\t\t\tnamespace,\n\t\t\tfunc() *apisv1alpha1.ServiceImport { return &apisv1alpha1.ServiceImport{} },\n\t\t\tfunc() *apisv1alpha1.ServiceImportList { return &apisv1alpha1.ServiceImportList{} },\n\t\t),\n\t}\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1beta1/apis_client.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage v1beta1\n\nimport (\n\thttp \"net/http\"\n\n\trest \"k8s.io/client-go/rest\"\n\tapisv1beta1 \"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n\tscheme \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/scheme\"\n)\n\ntype MulticlusterV1beta1Interface interface {\n\tRESTClient() rest.Interface\n\tServiceExportsGetter\n\tServiceImportsGetter\n}\n\n// MulticlusterV1beta1Client is used to interact with features provided by the multicluster.x-k8s.io group.\ntype MulticlusterV1beta1Client struct {\n\trestClient rest.Interface\n}\n\nfunc (c *MulticlusterV1beta1Client) ServiceExports(namespace string) ServiceExportInterface {\n\treturn newServiceExports(c, namespace)\n}\n\nfunc (c *MulticlusterV1beta1Client) ServiceImports(namespace string) ServiceImportInterface {\n\treturn newServiceImports(c, namespace)\n}\n\n// NewForConfig creates a new MulticlusterV1beta1Client for the given config.\n// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),\n// where httpClient was generated with rest.HTTPClientFor(c).\nfunc NewForConfig(c *rest.Config) (*MulticlusterV1beta1Client, error) {\n\tconfig := *c\n\tif err := setConfigDefaults(&config); err != nil {\n\t\treturn nil, err\n\t}\n\thttpClient, err := rest.HTTPClientFor(&config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewForConfigAndClient(&config, httpClient)\n}\n\n// NewForConfigAndClient creates a new MulticlusterV1beta1Client for the given config and http client.\n// Note the http client provided takes precedence over the configured transport values.\nfunc NewForConfigAndClient(c *rest.Config, h *http.Client) (*MulticlusterV1beta1Client, error) {\n\tconfig := *c\n\tif err := setConfigDefaults(&config); err != nil {\n\t\treturn nil, err\n\t}\n\tclient, err := rest.RESTClientForConfigAndClient(&config, h)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &MulticlusterV1beta1Client{client}, nil\n}\n\n// NewForConfigOrDie creates a new MulticlusterV1beta1Client for the given config and\n// panics if there is an error in the config.\nfunc NewForConfigOrDie(c *rest.Config) *MulticlusterV1beta1Client {\n\tclient, err := NewForConfig(c)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn client\n}\n\n// New creates a new MulticlusterV1beta1Client for the given RESTClient.\nfunc New(c rest.Interface) *MulticlusterV1beta1Client {\n\treturn &MulticlusterV1beta1Client{c}\n}\n\nfunc setConfigDefaults(config *rest.Config) error {\n\tgv := apisv1beta1.SchemeGroupVersion\n\tconfig.GroupVersion = &gv\n\tconfig.APIPath = \"/apis\"\n\tconfig.NegotiatedSerializer = rest.CodecFactoryForGeneratedClient(scheme.Scheme, scheme.Codecs).WithoutConversion()\n\n\tif config.UserAgent == \"\" {\n\t\tconfig.UserAgent = rest.DefaultKubernetesUserAgent()\n\t}\n\n\treturn nil\n}\n\n// RESTClient returns a RESTClient that is used to communicate\n// with API server by this client implementation.\nfunc (c *MulticlusterV1beta1Client) RESTClient() rest.Interface {\n\tif c == nil {\n\t\treturn nil\n\t}\n\treturn c.restClient\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1beta1/doc.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\n// This package has the automatically generated typed clients.\npackage v1beta1\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1beta1/fake/doc.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\n// Package fake has the automatically generated clients.\npackage fake\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1beta1/fake/fake_apis_client.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage fake\n\nimport (\n\trest \"k8s.io/client-go/rest\"\n\ttesting \"k8s.io/client-go/testing\"\n\tv1beta1 \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1beta1\"\n)\n\ntype FakeMulticlusterV1beta1 struct {\n\t*testing.Fake\n}\n\nfunc (c *FakeMulticlusterV1beta1) ServiceExports(namespace string) v1beta1.ServiceExportInterface {\n\treturn newFakeServiceExports(c, namespace)\n}\n\nfunc (c *FakeMulticlusterV1beta1) ServiceImports(namespace string) v1beta1.ServiceImportInterface {\n\treturn newFakeServiceImports(c, namespace)\n}\n\n// RESTClient returns a RESTClient that is used to communicate\n// with API server by this client implementation.\nfunc (c *FakeMulticlusterV1beta1) RESTClient() rest.Interface {\n\tvar ret *rest.RESTClient\n\treturn ret\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1beta1/fake/fake_serviceexport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage fake\n\nimport (\n\tgentype \"k8s.io/client-go/gentype\"\n\tv1beta1 \"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n\tapisv1beta1 \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1beta1\"\n)\n\n// fakeServiceExports implements ServiceExportInterface\ntype fakeServiceExports struct {\n\t*gentype.FakeClientWithList[*v1beta1.ServiceExport, *v1beta1.ServiceExportList]\n\tFake *FakeMulticlusterV1beta1\n}\n\nfunc newFakeServiceExports(fake *FakeMulticlusterV1beta1, namespace string) apisv1beta1.ServiceExportInterface {\n\treturn &fakeServiceExports{\n\t\tgentype.NewFakeClientWithList[*v1beta1.ServiceExport, *v1beta1.ServiceExportList](\n\t\t\tfake.Fake,\n\t\t\tnamespace,\n\t\t\tv1beta1.SchemeGroupVersion.WithResource(\"serviceexports\"),\n\t\t\tv1beta1.SchemeGroupVersion.WithKind(\"ServiceExport\"),\n\t\t\tfunc() *v1beta1.ServiceExport { return &v1beta1.ServiceExport{} },\n\t\t\tfunc() *v1beta1.ServiceExportList { return &v1beta1.ServiceExportList{} },\n\t\t\tfunc(dst, src *v1beta1.ServiceExportList) { dst.ListMeta = src.ListMeta },\n\t\t\tfunc(list *v1beta1.ServiceExportList) []*v1beta1.ServiceExport {\n\t\t\t\treturn gentype.ToPointerSlice(list.Items)\n\t\t\t},\n\t\t\tfunc(list *v1beta1.ServiceExportList, items []*v1beta1.ServiceExport) {\n\t\t\t\tlist.Items = gentype.FromPointerSlice(items)\n\t\t\t},\n\t\t),\n\t\tfake,\n\t}\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1beta1/fake/fake_serviceimport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage fake\n\nimport (\n\tgentype \"k8s.io/client-go/gentype\"\n\tv1beta1 \"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n\tapisv1beta1 \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1beta1\"\n)\n\n// fakeServiceImports implements ServiceImportInterface\ntype fakeServiceImports struct {\n\t*gentype.FakeClientWithList[*v1beta1.ServiceImport, *v1beta1.ServiceImportList]\n\tFake *FakeMulticlusterV1beta1\n}\n\nfunc newFakeServiceImports(fake *FakeMulticlusterV1beta1, namespace string) apisv1beta1.ServiceImportInterface {\n\treturn &fakeServiceImports{\n\t\tgentype.NewFakeClientWithList[*v1beta1.ServiceImport, *v1beta1.ServiceImportList](\n\t\t\tfake.Fake,\n\t\t\tnamespace,\n\t\t\tv1beta1.SchemeGroupVersion.WithResource(\"serviceimports\"),\n\t\t\tv1beta1.SchemeGroupVersion.WithKind(\"ServiceImport\"),\n\t\t\tfunc() *v1beta1.ServiceImport { return &v1beta1.ServiceImport{} },\n\t\t\tfunc() *v1beta1.ServiceImportList { return &v1beta1.ServiceImportList{} },\n\t\t\tfunc(dst, src *v1beta1.ServiceImportList) { dst.ListMeta = src.ListMeta },\n\t\t\tfunc(list *v1beta1.ServiceImportList) []*v1beta1.ServiceImport {\n\t\t\t\treturn gentype.ToPointerSlice(list.Items)\n\t\t\t},\n\t\t\tfunc(list *v1beta1.ServiceImportList, items []*v1beta1.ServiceImport) {\n\t\t\t\tlist.Items = gentype.FromPointerSlice(items)\n\t\t\t},\n\t\t),\n\t\tfake,\n\t}\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1beta1/generated_expansion.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage v1beta1\n\ntype ServiceExportExpansion interface{}\n\ntype ServiceImportExpansion interface{}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1beta1/serviceexport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage v1beta1\n\nimport (\n\tcontext \"context\"\n\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\ttypes \"k8s.io/apimachinery/pkg/types\"\n\twatch \"k8s.io/apimachinery/pkg/watch\"\n\tgentype \"k8s.io/client-go/gentype\"\n\tapisv1beta1 \"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n\tscheme \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/scheme\"\n)\n\n// ServiceExportsGetter has a method to return a ServiceExportInterface.\n// A group's client should implement this interface.\ntype ServiceExportsGetter interface {\n\tServiceExports(namespace string) ServiceExportInterface\n}\n\n// ServiceExportInterface has methods to work with ServiceExport resources.\ntype ServiceExportInterface interface {\n\tCreate(ctx context.Context, serviceExport *apisv1beta1.ServiceExport, opts v1.CreateOptions) (*apisv1beta1.ServiceExport, error)\n\tUpdate(ctx context.Context, serviceExport *apisv1beta1.ServiceExport, opts v1.UpdateOptions) (*apisv1beta1.ServiceExport, error)\n\t// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().\n\tUpdateStatus(ctx context.Context, serviceExport *apisv1beta1.ServiceExport, opts v1.UpdateOptions) (*apisv1beta1.ServiceExport, error)\n\tDelete(ctx context.Context, name string, opts v1.DeleteOptions) error\n\tDeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error\n\tGet(ctx context.Context, name string, opts v1.GetOptions) (*apisv1beta1.ServiceExport, error)\n\tList(ctx context.Context, opts v1.ListOptions) (*apisv1beta1.ServiceExportList, error)\n\tWatch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)\n\tPatch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *apisv1beta1.ServiceExport, err error)\n\tServiceExportExpansion\n}\n\n// serviceExports implements ServiceExportInterface\ntype serviceExports struct {\n\t*gentype.ClientWithList[*apisv1beta1.ServiceExport, *apisv1beta1.ServiceExportList]\n}\n\n// newServiceExports returns a ServiceExports\nfunc newServiceExports(c *MulticlusterV1beta1Client, namespace string) *serviceExports {\n\treturn &serviceExports{\n\t\tgentype.NewClientWithList[*apisv1beta1.ServiceExport, *apisv1beta1.ServiceExportList](\n\t\t\t\"serviceexports\",\n\t\t\tc.RESTClient(),\n\t\t\tscheme.ParameterCodec,\n\t\t\tnamespace,\n\t\t\tfunc() *apisv1beta1.ServiceExport { return &apisv1beta1.ServiceExport{} },\n\t\t\tfunc() *apisv1beta1.ServiceExportList { return &apisv1beta1.ServiceExportList{} },\n\t\t),\n\t}\n}\n"
  },
  {
    "path": "pkg/client/clientset/versioned/typed/apis/v1beta1/serviceimport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by client-gen. DO NOT EDIT.\n\npackage v1beta1\n\nimport (\n\tcontext \"context\"\n\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\ttypes \"k8s.io/apimachinery/pkg/types\"\n\twatch \"k8s.io/apimachinery/pkg/watch\"\n\tgentype \"k8s.io/client-go/gentype\"\n\tapisv1beta1 \"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n\tscheme \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/scheme\"\n)\n\n// ServiceImportsGetter has a method to return a ServiceImportInterface.\n// A group's client should implement this interface.\ntype ServiceImportsGetter interface {\n\tServiceImports(namespace string) ServiceImportInterface\n}\n\n// ServiceImportInterface has methods to work with ServiceImport resources.\ntype ServiceImportInterface interface {\n\tCreate(ctx context.Context, serviceImport *apisv1beta1.ServiceImport, opts v1.CreateOptions) (*apisv1beta1.ServiceImport, error)\n\tUpdate(ctx context.Context, serviceImport *apisv1beta1.ServiceImport, opts v1.UpdateOptions) (*apisv1beta1.ServiceImport, error)\n\t// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().\n\tUpdateStatus(ctx context.Context, serviceImport *apisv1beta1.ServiceImport, opts v1.UpdateOptions) (*apisv1beta1.ServiceImport, error)\n\tDelete(ctx context.Context, name string, opts v1.DeleteOptions) error\n\tDeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error\n\tGet(ctx context.Context, name string, opts v1.GetOptions) (*apisv1beta1.ServiceImport, error)\n\tList(ctx context.Context, opts v1.ListOptions) (*apisv1beta1.ServiceImportList, error)\n\tWatch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)\n\tPatch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *apisv1beta1.ServiceImport, err error)\n\tServiceImportExpansion\n}\n\n// serviceImports implements ServiceImportInterface\ntype serviceImports struct {\n\t*gentype.ClientWithList[*apisv1beta1.ServiceImport, *apisv1beta1.ServiceImportList]\n}\n\n// newServiceImports returns a ServiceImports\nfunc newServiceImports(c *MulticlusterV1beta1Client, namespace string) *serviceImports {\n\treturn &serviceImports{\n\t\tgentype.NewClientWithList[*apisv1beta1.ServiceImport, *apisv1beta1.ServiceImportList](\n\t\t\t\"serviceimports\",\n\t\t\tc.RESTClient(),\n\t\t\tscheme.ParameterCodec,\n\t\t\tnamespace,\n\t\t\tfunc() *apisv1beta1.ServiceImport { return &apisv1beta1.ServiceImport{} },\n\t\t\tfunc() *apisv1beta1.ServiceImportList { return &apisv1beta1.ServiceImportList{} },\n\t\t),\n\t}\n}\n"
  },
  {
    "path": "pkg/client/informers/externalversions/apis/interface.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by informer-gen. DO NOT EDIT.\n\npackage apis\n\nimport (\n\tv1alpha1 \"sigs.k8s.io/mcs-api/pkg/client/informers/externalversions/apis/v1alpha1\"\n\tv1beta1 \"sigs.k8s.io/mcs-api/pkg/client/informers/externalversions/apis/v1beta1\"\n\tinternalinterfaces \"sigs.k8s.io/mcs-api/pkg/client/informers/externalversions/internalinterfaces\"\n)\n\n// Interface provides access to each of this group's versions.\ntype Interface interface {\n\t// V1alpha1 provides access to shared informers for resources in V1alpha1.\n\tV1alpha1() v1alpha1.Interface\n\t// V1beta1 provides access to shared informers for resources in V1beta1.\n\tV1beta1() v1beta1.Interface\n}\n\ntype group struct {\n\tfactory          internalinterfaces.SharedInformerFactory\n\tnamespace        string\n\ttweakListOptions internalinterfaces.TweakListOptionsFunc\n}\n\n// New returns a new Interface.\nfunc New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {\n\treturn &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}\n}\n\n// V1alpha1 returns a new v1alpha1.Interface.\nfunc (g *group) V1alpha1() v1alpha1.Interface {\n\treturn v1alpha1.New(g.factory, g.namespace, g.tweakListOptions)\n}\n\n// V1beta1 returns a new v1beta1.Interface.\nfunc (g *group) V1beta1() v1beta1.Interface {\n\treturn v1beta1.New(g.factory, g.namespace, g.tweakListOptions)\n}\n"
  },
  {
    "path": "pkg/client/informers/externalversions/apis/v1alpha1/interface.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by informer-gen. DO NOT EDIT.\n\npackage v1alpha1\n\nimport (\n\tinternalinterfaces \"sigs.k8s.io/mcs-api/pkg/client/informers/externalversions/internalinterfaces\"\n)\n\n// Interface provides access to all the informers in this group version.\ntype Interface interface {\n\t// ServiceExports returns a ServiceExportInformer.\n\tServiceExports() ServiceExportInformer\n\t// ServiceImports returns a ServiceImportInformer.\n\tServiceImports() ServiceImportInformer\n}\n\ntype version struct {\n\tfactory          internalinterfaces.SharedInformerFactory\n\tnamespace        string\n\ttweakListOptions internalinterfaces.TweakListOptionsFunc\n}\n\n// New returns a new Interface.\nfunc New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {\n\treturn &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}\n}\n\n// ServiceExports returns a ServiceExportInformer.\nfunc (v *version) ServiceExports() ServiceExportInformer {\n\treturn &serviceExportInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}\n}\n\n// ServiceImports returns a ServiceImportInformer.\nfunc (v *version) ServiceImports() ServiceImportInformer {\n\treturn &serviceImportInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}\n}\n"
  },
  {
    "path": "pkg/client/informers/externalversions/apis/v1alpha1/serviceexport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by informer-gen. DO NOT EDIT.\n\npackage v1alpha1\n\nimport (\n\tcontext \"context\"\n\ttime \"time\"\n\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n\twatch \"k8s.io/apimachinery/pkg/watch\"\n\tcache \"k8s.io/client-go/tools/cache\"\n\tpkgapisv1alpha1 \"sigs.k8s.io/mcs-api/pkg/apis/v1alpha1\"\n\tversioned \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned\"\n\tinternalinterfaces \"sigs.k8s.io/mcs-api/pkg/client/informers/externalversions/internalinterfaces\"\n\tapisv1alpha1 \"sigs.k8s.io/mcs-api/pkg/client/listers/apis/v1alpha1\"\n)\n\n// ServiceExportInformer provides access to a shared informer and lister for\n// ServiceExports.\ntype ServiceExportInformer interface {\n\tInformer() cache.SharedIndexInformer\n\tLister() apisv1alpha1.ServiceExportLister\n}\n\ntype serviceExportInformer struct {\n\tfactory          internalinterfaces.SharedInformerFactory\n\ttweakListOptions internalinterfaces.TweakListOptionsFunc\n\tnamespace        string\n}\n\n// NewServiceExportInformer constructs a new informer for ServiceExport type.\n// Always prefer using an informer factory to get a shared informer instead of getting an independent\n// one. This reduces memory footprint and number of connections to the server.\nfunc NewServiceExportInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {\n\treturn NewFilteredServiceExportInformer(client, namespace, resyncPeriod, indexers, nil)\n}\n\n// NewFilteredServiceExportInformer constructs a new informer for ServiceExport type.\n// Always prefer using an informer factory to get a shared informer instead of getting an independent\n// one. This reduces memory footprint and number of connections to the server.\nfunc NewFilteredServiceExportInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {\n\treturn cache.NewSharedIndexInformer(\n\t\t&cache.ListWatch{\n\t\t\tListFunc: func(options v1.ListOptions) (runtime.Object, error) {\n\t\t\t\tif tweakListOptions != nil {\n\t\t\t\t\ttweakListOptions(&options)\n\t\t\t\t}\n\t\t\t\treturn client.MulticlusterV1alpha1().ServiceExports(namespace).List(context.TODO(), options)\n\t\t\t},\n\t\t\tWatchFunc: func(options v1.ListOptions) (watch.Interface, error) {\n\t\t\t\tif tweakListOptions != nil {\n\t\t\t\t\ttweakListOptions(&options)\n\t\t\t\t}\n\t\t\t\treturn client.MulticlusterV1alpha1().ServiceExports(namespace).Watch(context.TODO(), options)\n\t\t\t},\n\t\t},\n\t\t&pkgapisv1alpha1.ServiceExport{},\n\t\tresyncPeriod,\n\t\tindexers,\n\t)\n}\n\nfunc (f *serviceExportInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {\n\treturn NewFilteredServiceExportInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)\n}\n\nfunc (f *serviceExportInformer) Informer() cache.SharedIndexInformer {\n\treturn f.factory.InformerFor(&pkgapisv1alpha1.ServiceExport{}, f.defaultInformer)\n}\n\nfunc (f *serviceExportInformer) Lister() apisv1alpha1.ServiceExportLister {\n\treturn apisv1alpha1.NewServiceExportLister(f.Informer().GetIndexer())\n}\n"
  },
  {
    "path": "pkg/client/informers/externalversions/apis/v1alpha1/serviceimport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by informer-gen. DO NOT EDIT.\n\npackage v1alpha1\n\nimport (\n\tcontext \"context\"\n\ttime \"time\"\n\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n\twatch \"k8s.io/apimachinery/pkg/watch\"\n\tcache \"k8s.io/client-go/tools/cache\"\n\tpkgapisv1alpha1 \"sigs.k8s.io/mcs-api/pkg/apis/v1alpha1\"\n\tversioned \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned\"\n\tinternalinterfaces \"sigs.k8s.io/mcs-api/pkg/client/informers/externalversions/internalinterfaces\"\n\tapisv1alpha1 \"sigs.k8s.io/mcs-api/pkg/client/listers/apis/v1alpha1\"\n)\n\n// ServiceImportInformer provides access to a shared informer and lister for\n// ServiceImports.\ntype ServiceImportInformer interface {\n\tInformer() cache.SharedIndexInformer\n\tLister() apisv1alpha1.ServiceImportLister\n}\n\ntype serviceImportInformer struct {\n\tfactory          internalinterfaces.SharedInformerFactory\n\ttweakListOptions internalinterfaces.TweakListOptionsFunc\n\tnamespace        string\n}\n\n// NewServiceImportInformer constructs a new informer for ServiceImport type.\n// Always prefer using an informer factory to get a shared informer instead of getting an independent\n// one. This reduces memory footprint and number of connections to the server.\nfunc NewServiceImportInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {\n\treturn NewFilteredServiceImportInformer(client, namespace, resyncPeriod, indexers, nil)\n}\n\n// NewFilteredServiceImportInformer constructs a new informer for ServiceImport type.\n// Always prefer using an informer factory to get a shared informer instead of getting an independent\n// one. This reduces memory footprint and number of connections to the server.\nfunc NewFilteredServiceImportInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {\n\treturn cache.NewSharedIndexInformer(\n\t\t&cache.ListWatch{\n\t\t\tListFunc: func(options v1.ListOptions) (runtime.Object, error) {\n\t\t\t\tif tweakListOptions != nil {\n\t\t\t\t\ttweakListOptions(&options)\n\t\t\t\t}\n\t\t\t\treturn client.MulticlusterV1alpha1().ServiceImports(namespace).List(context.TODO(), options)\n\t\t\t},\n\t\t\tWatchFunc: func(options v1.ListOptions) (watch.Interface, error) {\n\t\t\t\tif tweakListOptions != nil {\n\t\t\t\t\ttweakListOptions(&options)\n\t\t\t\t}\n\t\t\t\treturn client.MulticlusterV1alpha1().ServiceImports(namespace).Watch(context.TODO(), options)\n\t\t\t},\n\t\t},\n\t\t&pkgapisv1alpha1.ServiceImport{},\n\t\tresyncPeriod,\n\t\tindexers,\n\t)\n}\n\nfunc (f *serviceImportInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {\n\treturn NewFilteredServiceImportInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)\n}\n\nfunc (f *serviceImportInformer) Informer() cache.SharedIndexInformer {\n\treturn f.factory.InformerFor(&pkgapisv1alpha1.ServiceImport{}, f.defaultInformer)\n}\n\nfunc (f *serviceImportInformer) Lister() apisv1alpha1.ServiceImportLister {\n\treturn apisv1alpha1.NewServiceImportLister(f.Informer().GetIndexer())\n}\n"
  },
  {
    "path": "pkg/client/informers/externalversions/apis/v1beta1/interface.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by informer-gen. DO NOT EDIT.\n\npackage v1beta1\n\nimport (\n\tinternalinterfaces \"sigs.k8s.io/mcs-api/pkg/client/informers/externalversions/internalinterfaces\"\n)\n\n// Interface provides access to all the informers in this group version.\ntype Interface interface {\n\t// ServiceExports returns a ServiceExportInformer.\n\tServiceExports() ServiceExportInformer\n\t// ServiceImports returns a ServiceImportInformer.\n\tServiceImports() ServiceImportInformer\n}\n\ntype version struct {\n\tfactory          internalinterfaces.SharedInformerFactory\n\tnamespace        string\n\ttweakListOptions internalinterfaces.TweakListOptionsFunc\n}\n\n// New returns a new Interface.\nfunc New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {\n\treturn &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}\n}\n\n// ServiceExports returns a ServiceExportInformer.\nfunc (v *version) ServiceExports() ServiceExportInformer {\n\treturn &serviceExportInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}\n}\n\n// ServiceImports returns a ServiceImportInformer.\nfunc (v *version) ServiceImports() ServiceImportInformer {\n\treturn &serviceImportInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}\n}\n"
  },
  {
    "path": "pkg/client/informers/externalversions/apis/v1beta1/serviceexport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by informer-gen. DO NOT EDIT.\n\npackage v1beta1\n\nimport (\n\tcontext \"context\"\n\ttime \"time\"\n\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n\twatch \"k8s.io/apimachinery/pkg/watch\"\n\tcache \"k8s.io/client-go/tools/cache\"\n\tpkgapisv1beta1 \"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n\tversioned \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned\"\n\tinternalinterfaces \"sigs.k8s.io/mcs-api/pkg/client/informers/externalversions/internalinterfaces\"\n\tapisv1beta1 \"sigs.k8s.io/mcs-api/pkg/client/listers/apis/v1beta1\"\n)\n\n// ServiceExportInformer provides access to a shared informer and lister for\n// ServiceExports.\ntype ServiceExportInformer interface {\n\tInformer() cache.SharedIndexInformer\n\tLister() apisv1beta1.ServiceExportLister\n}\n\ntype serviceExportInformer struct {\n\tfactory          internalinterfaces.SharedInformerFactory\n\ttweakListOptions internalinterfaces.TweakListOptionsFunc\n\tnamespace        string\n}\n\n// NewServiceExportInformer constructs a new informer for ServiceExport type.\n// Always prefer using an informer factory to get a shared informer instead of getting an independent\n// one. This reduces memory footprint and number of connections to the server.\nfunc NewServiceExportInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {\n\treturn NewFilteredServiceExportInformer(client, namespace, resyncPeriod, indexers, nil)\n}\n\n// NewFilteredServiceExportInformer constructs a new informer for ServiceExport type.\n// Always prefer using an informer factory to get a shared informer instead of getting an independent\n// one. This reduces memory footprint and number of connections to the server.\nfunc NewFilteredServiceExportInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {\n\treturn cache.NewSharedIndexInformer(\n\t\t&cache.ListWatch{\n\t\t\tListFunc: func(options v1.ListOptions) (runtime.Object, error) {\n\t\t\t\tif tweakListOptions != nil {\n\t\t\t\t\ttweakListOptions(&options)\n\t\t\t\t}\n\t\t\t\treturn client.MulticlusterV1beta1().ServiceExports(namespace).List(context.TODO(), options)\n\t\t\t},\n\t\t\tWatchFunc: func(options v1.ListOptions) (watch.Interface, error) {\n\t\t\t\tif tweakListOptions != nil {\n\t\t\t\t\ttweakListOptions(&options)\n\t\t\t\t}\n\t\t\t\treturn client.MulticlusterV1beta1().ServiceExports(namespace).Watch(context.TODO(), options)\n\t\t\t},\n\t\t},\n\t\t&pkgapisv1beta1.ServiceExport{},\n\t\tresyncPeriod,\n\t\tindexers,\n\t)\n}\n\nfunc (f *serviceExportInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {\n\treturn NewFilteredServiceExportInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)\n}\n\nfunc (f *serviceExportInformer) Informer() cache.SharedIndexInformer {\n\treturn f.factory.InformerFor(&pkgapisv1beta1.ServiceExport{}, f.defaultInformer)\n}\n\nfunc (f *serviceExportInformer) Lister() apisv1beta1.ServiceExportLister {\n\treturn apisv1beta1.NewServiceExportLister(f.Informer().GetIndexer())\n}\n"
  },
  {
    "path": "pkg/client/informers/externalversions/apis/v1beta1/serviceimport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by informer-gen. DO NOT EDIT.\n\npackage v1beta1\n\nimport (\n\tcontext \"context\"\n\ttime \"time\"\n\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n\twatch \"k8s.io/apimachinery/pkg/watch\"\n\tcache \"k8s.io/client-go/tools/cache\"\n\tpkgapisv1beta1 \"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n\tversioned \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned\"\n\tinternalinterfaces \"sigs.k8s.io/mcs-api/pkg/client/informers/externalversions/internalinterfaces\"\n\tapisv1beta1 \"sigs.k8s.io/mcs-api/pkg/client/listers/apis/v1beta1\"\n)\n\n// ServiceImportInformer provides access to a shared informer and lister for\n// ServiceImports.\ntype ServiceImportInformer interface {\n\tInformer() cache.SharedIndexInformer\n\tLister() apisv1beta1.ServiceImportLister\n}\n\ntype serviceImportInformer struct {\n\tfactory          internalinterfaces.SharedInformerFactory\n\ttweakListOptions internalinterfaces.TweakListOptionsFunc\n\tnamespace        string\n}\n\n// NewServiceImportInformer constructs a new informer for ServiceImport type.\n// Always prefer using an informer factory to get a shared informer instead of getting an independent\n// one. This reduces memory footprint and number of connections to the server.\nfunc NewServiceImportInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {\n\treturn NewFilteredServiceImportInformer(client, namespace, resyncPeriod, indexers, nil)\n}\n\n// NewFilteredServiceImportInformer constructs a new informer for ServiceImport type.\n// Always prefer using an informer factory to get a shared informer instead of getting an independent\n// one. This reduces memory footprint and number of connections to the server.\nfunc NewFilteredServiceImportInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {\n\treturn cache.NewSharedIndexInformer(\n\t\t&cache.ListWatch{\n\t\t\tListFunc: func(options v1.ListOptions) (runtime.Object, error) {\n\t\t\t\tif tweakListOptions != nil {\n\t\t\t\t\ttweakListOptions(&options)\n\t\t\t\t}\n\t\t\t\treturn client.MulticlusterV1beta1().ServiceImports(namespace).List(context.TODO(), options)\n\t\t\t},\n\t\t\tWatchFunc: func(options v1.ListOptions) (watch.Interface, error) {\n\t\t\t\tif tweakListOptions != nil {\n\t\t\t\t\ttweakListOptions(&options)\n\t\t\t\t}\n\t\t\t\treturn client.MulticlusterV1beta1().ServiceImports(namespace).Watch(context.TODO(), options)\n\t\t\t},\n\t\t},\n\t\t&pkgapisv1beta1.ServiceImport{},\n\t\tresyncPeriod,\n\t\tindexers,\n\t)\n}\n\nfunc (f *serviceImportInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {\n\treturn NewFilteredServiceImportInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)\n}\n\nfunc (f *serviceImportInformer) Informer() cache.SharedIndexInformer {\n\treturn f.factory.InformerFor(&pkgapisv1beta1.ServiceImport{}, f.defaultInformer)\n}\n\nfunc (f *serviceImportInformer) Lister() apisv1beta1.ServiceImportLister {\n\treturn apisv1beta1.NewServiceImportLister(f.Informer().GetIndexer())\n}\n"
  },
  {
    "path": "pkg/client/informers/externalversions/factory.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by informer-gen. DO NOT EDIT.\n\npackage externalversions\n\nimport (\n\treflect \"reflect\"\n\tsync \"sync\"\n\ttime \"time\"\n\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n\tschema \"k8s.io/apimachinery/pkg/runtime/schema\"\n\tcache \"k8s.io/client-go/tools/cache\"\n\tversioned \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned\"\n\tapis \"sigs.k8s.io/mcs-api/pkg/client/informers/externalversions/apis\"\n\tinternalinterfaces \"sigs.k8s.io/mcs-api/pkg/client/informers/externalversions/internalinterfaces\"\n)\n\n// SharedInformerOption defines the functional option type for SharedInformerFactory.\ntype SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory\n\ntype sharedInformerFactory struct {\n\tclient           versioned.Interface\n\tnamespace        string\n\ttweakListOptions internalinterfaces.TweakListOptionsFunc\n\tlock             sync.Mutex\n\tdefaultResync    time.Duration\n\tcustomResync     map[reflect.Type]time.Duration\n\ttransform        cache.TransformFunc\n\n\tinformers map[reflect.Type]cache.SharedIndexInformer\n\t// startedInformers is used for tracking which informers have been started.\n\t// This allows Start() to be called multiple times safely.\n\tstartedInformers map[reflect.Type]bool\n\t// wg tracks how many goroutines were started.\n\twg sync.WaitGroup\n\t// shuttingDown is true when Shutdown has been called. It may still be running\n\t// because it needs to wait for goroutines.\n\tshuttingDown bool\n}\n\n// WithCustomResyncConfig sets a custom resync period for the specified informer types.\nfunc WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption {\n\treturn func(factory *sharedInformerFactory) *sharedInformerFactory {\n\t\tfor k, v := range resyncConfig {\n\t\t\tfactory.customResync[reflect.TypeOf(k)] = v\n\t\t}\n\t\treturn factory\n\t}\n}\n\n// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory.\nfunc WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption {\n\treturn func(factory *sharedInformerFactory) *sharedInformerFactory {\n\t\tfactory.tweakListOptions = tweakListOptions\n\t\treturn factory\n\t}\n}\n\n// WithNamespace limits the SharedInformerFactory to the specified namespace.\nfunc WithNamespace(namespace string) SharedInformerOption {\n\treturn func(factory *sharedInformerFactory) *sharedInformerFactory {\n\t\tfactory.namespace = namespace\n\t\treturn factory\n\t}\n}\n\n// WithTransform sets a transform on all informers.\nfunc WithTransform(transform cache.TransformFunc) SharedInformerOption {\n\treturn func(factory *sharedInformerFactory) *sharedInformerFactory {\n\t\tfactory.transform = transform\n\t\treturn factory\n\t}\n}\n\n// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces.\nfunc NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory {\n\treturn NewSharedInformerFactoryWithOptions(client, defaultResync)\n}\n\n// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory.\n// Listers obtained via this SharedInformerFactory will be subject to the same filters\n// as specified here.\n// Deprecated: Please use NewSharedInformerFactoryWithOptions instead\nfunc NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory {\n\treturn NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions))\n}\n\n// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options.\nfunc NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory {\n\tfactory := &sharedInformerFactory{\n\t\tclient:           client,\n\t\tnamespace:        v1.NamespaceAll,\n\t\tdefaultResync:    defaultResync,\n\t\tinformers:        make(map[reflect.Type]cache.SharedIndexInformer),\n\t\tstartedInformers: make(map[reflect.Type]bool),\n\t\tcustomResync:     make(map[reflect.Type]time.Duration),\n\t}\n\n\t// Apply all options\n\tfor _, opt := range options {\n\t\tfactory = opt(factory)\n\t}\n\n\treturn factory\n}\n\nfunc (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {\n\tf.lock.Lock()\n\tdefer f.lock.Unlock()\n\n\tif f.shuttingDown {\n\t\treturn\n\t}\n\n\tfor informerType, informer := range f.informers {\n\t\tif !f.startedInformers[informerType] {\n\t\t\tf.wg.Add(1)\n\t\t\t// We need a new variable in each loop iteration,\n\t\t\t// otherwise the goroutine would use the loop variable\n\t\t\t// and that keeps changing.\n\t\t\tinformer := informer\n\t\t\tgo func() {\n\t\t\t\tdefer f.wg.Done()\n\t\t\t\tinformer.Run(stopCh)\n\t\t\t}()\n\t\t\tf.startedInformers[informerType] = true\n\t\t}\n\t}\n}\n\nfunc (f *sharedInformerFactory) Shutdown() {\n\tf.lock.Lock()\n\tf.shuttingDown = true\n\tf.lock.Unlock()\n\n\t// Will return immediately if there is nothing to wait for.\n\tf.wg.Wait()\n}\n\nfunc (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool {\n\tinformers := func() map[reflect.Type]cache.SharedIndexInformer {\n\t\tf.lock.Lock()\n\t\tdefer f.lock.Unlock()\n\n\t\tinformers := map[reflect.Type]cache.SharedIndexInformer{}\n\t\tfor informerType, informer := range f.informers {\n\t\t\tif f.startedInformers[informerType] {\n\t\t\t\tinformers[informerType] = informer\n\t\t\t}\n\t\t}\n\t\treturn informers\n\t}()\n\n\tres := map[reflect.Type]bool{}\n\tfor informType, informer := range informers {\n\t\tres[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced)\n\t}\n\treturn res\n}\n\n// InformerFor returns the SharedIndexInformer for obj using an internal\n// client.\nfunc (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer {\n\tf.lock.Lock()\n\tdefer f.lock.Unlock()\n\n\tinformerType := reflect.TypeOf(obj)\n\tinformer, exists := f.informers[informerType]\n\tif exists {\n\t\treturn informer\n\t}\n\n\tresyncPeriod, exists := f.customResync[informerType]\n\tif !exists {\n\t\tresyncPeriod = f.defaultResync\n\t}\n\n\tinformer = newFunc(f.client, resyncPeriod)\n\tinformer.SetTransform(f.transform)\n\tf.informers[informerType] = informer\n\n\treturn informer\n}\n\n// SharedInformerFactory provides shared informers for resources in all known\n// API group versions.\n//\n// It is typically used like this:\n//\n//\tctx, cancel := context.Background()\n//\tdefer cancel()\n//\tfactory := NewSharedInformerFactory(client, resyncPeriod)\n//\tdefer factory.WaitForStop()    // Returns immediately if nothing was started.\n//\tgenericInformer := factory.ForResource(resource)\n//\ttypedInformer := factory.SomeAPIGroup().V1().SomeType()\n//\tfactory.Start(ctx.Done())          // Start processing these informers.\n//\tsynced := factory.WaitForCacheSync(ctx.Done())\n//\tfor v, ok := range synced {\n//\t    if !ok {\n//\t        fmt.Fprintf(os.Stderr, \"caches failed to sync: %v\", v)\n//\t        return\n//\t    }\n//\t}\n//\n//\t// Creating informers can also be created after Start, but then\n//\t// Start must be called again:\n//\tanotherGenericInformer := factory.ForResource(resource)\n//\tfactory.Start(ctx.Done())\ntype SharedInformerFactory interface {\n\tinternalinterfaces.SharedInformerFactory\n\n\t// Start initializes all requested informers. They are handled in goroutines\n\t// which run until the stop channel gets closed.\n\t// Warning: Start does not block. When run in a go-routine, it will race with a later WaitForCacheSync.\n\tStart(stopCh <-chan struct{})\n\n\t// Shutdown marks a factory as shutting down. At that point no new\n\t// informers can be started anymore and Start will return without\n\t// doing anything.\n\t//\n\t// In addition, Shutdown blocks until all goroutines have terminated. For that\n\t// to happen, the close channel(s) that they were started with must be closed,\n\t// either before Shutdown gets called or while it is waiting.\n\t//\n\t// Shutdown may be called multiple times, even concurrently. All such calls will\n\t// block until all goroutines have terminated.\n\tShutdown()\n\n\t// WaitForCacheSync blocks until all started informers' caches were synced\n\t// or the stop channel gets closed.\n\tWaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool\n\n\t// ForResource gives generic access to a shared informer of the matching type.\n\tForResource(resource schema.GroupVersionResource) (GenericInformer, error)\n\n\t// InformerFor returns the SharedIndexInformer for obj using an internal\n\t// client.\n\tInformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer\n\n\tMulticluster() apis.Interface\n}\n\nfunc (f *sharedInformerFactory) Multicluster() apis.Interface {\n\treturn apis.New(f, f.namespace, f.tweakListOptions)\n}\n"
  },
  {
    "path": "pkg/client/informers/externalversions/generic.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by informer-gen. DO NOT EDIT.\n\npackage externalversions\n\nimport (\n\tfmt \"fmt\"\n\n\tschema \"k8s.io/apimachinery/pkg/runtime/schema\"\n\tcache \"k8s.io/client-go/tools/cache\"\n\tv1alpha1 \"sigs.k8s.io/mcs-api/pkg/apis/v1alpha1\"\n\tv1beta1 \"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\n// GenericInformer is type of SharedIndexInformer which will locate and delegate to other\n// sharedInformers based on type\ntype GenericInformer interface {\n\tInformer() cache.SharedIndexInformer\n\tLister() cache.GenericLister\n}\n\ntype genericInformer struct {\n\tinformer cache.SharedIndexInformer\n\tresource schema.GroupResource\n}\n\n// Informer returns the SharedIndexInformer.\nfunc (f *genericInformer) Informer() cache.SharedIndexInformer {\n\treturn f.informer\n}\n\n// Lister returns the GenericLister.\nfunc (f *genericInformer) Lister() cache.GenericLister {\n\treturn cache.NewGenericLister(f.Informer().GetIndexer(), f.resource)\n}\n\n// ForResource gives generic access to a shared informer of the matching type\n// TODO extend this to unknown resources with a client pool\nfunc (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {\n\tswitch resource {\n\t// Group=multicluster.x-k8s.io, Version=v1alpha1\n\tcase v1alpha1.SchemeGroupVersion.WithResource(\"serviceexports\"):\n\t\treturn &genericInformer{resource: resource.GroupResource(), informer: f.Multicluster().V1alpha1().ServiceExports().Informer()}, nil\n\tcase v1alpha1.SchemeGroupVersion.WithResource(\"serviceimports\"):\n\t\treturn &genericInformer{resource: resource.GroupResource(), informer: f.Multicluster().V1alpha1().ServiceImports().Informer()}, nil\n\n\t\t// Group=multicluster.x-k8s.io, Version=v1beta1\n\tcase v1beta1.SchemeGroupVersion.WithResource(\"serviceexports\"):\n\t\treturn &genericInformer{resource: resource.GroupResource(), informer: f.Multicluster().V1beta1().ServiceExports().Informer()}, nil\n\tcase v1beta1.SchemeGroupVersion.WithResource(\"serviceimports\"):\n\t\treturn &genericInformer{resource: resource.GroupResource(), informer: f.Multicluster().V1beta1().ServiceImports().Informer()}, nil\n\n\t}\n\n\treturn nil, fmt.Errorf(\"no informer found for %v\", resource)\n}\n"
  },
  {
    "path": "pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by informer-gen. DO NOT EDIT.\n\npackage internalinterfaces\n\nimport (\n\ttime \"time\"\n\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n\tcache \"k8s.io/client-go/tools/cache\"\n\tversioned \"sigs.k8s.io/mcs-api/pkg/client/clientset/versioned\"\n)\n\n// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer.\ntype NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer\n\n// SharedInformerFactory a small interface to allow for adding an informer without an import cycle\ntype SharedInformerFactory interface {\n\tStart(stopCh <-chan struct{})\n\tInformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer\n}\n\n// TweakListOptionsFunc is a function that transforms a v1.ListOptions.\ntype TweakListOptionsFunc func(*v1.ListOptions)\n"
  },
  {
    "path": "pkg/client/listers/apis/v1alpha1/expansion_generated.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by lister-gen. DO NOT EDIT.\n\npackage v1alpha1\n\n// ServiceExportListerExpansion allows custom methods to be added to\n// ServiceExportLister.\ntype ServiceExportListerExpansion interface{}\n\n// ServiceExportNamespaceListerExpansion allows custom methods to be added to\n// ServiceExportNamespaceLister.\ntype ServiceExportNamespaceListerExpansion interface{}\n\n// ServiceImportListerExpansion allows custom methods to be added to\n// ServiceImportLister.\ntype ServiceImportListerExpansion interface{}\n\n// ServiceImportNamespaceListerExpansion allows custom methods to be added to\n// ServiceImportNamespaceLister.\ntype ServiceImportNamespaceListerExpansion interface{}\n"
  },
  {
    "path": "pkg/client/listers/apis/v1alpha1/serviceexport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by lister-gen. DO NOT EDIT.\n\npackage v1alpha1\n\nimport (\n\tlabels \"k8s.io/apimachinery/pkg/labels\"\n\tlisters \"k8s.io/client-go/listers\"\n\tcache \"k8s.io/client-go/tools/cache\"\n\tapisv1alpha1 \"sigs.k8s.io/mcs-api/pkg/apis/v1alpha1\"\n)\n\n// ServiceExportLister helps list ServiceExports.\n// All objects returned here must be treated as read-only.\ntype ServiceExportLister interface {\n\t// List lists all ServiceExports in the indexer.\n\t// Objects returned here must be treated as read-only.\n\tList(selector labels.Selector) (ret []*apisv1alpha1.ServiceExport, err error)\n\t// ServiceExports returns an object that can list and get ServiceExports.\n\tServiceExports(namespace string) ServiceExportNamespaceLister\n\tServiceExportListerExpansion\n}\n\n// serviceExportLister implements the ServiceExportLister interface.\ntype serviceExportLister struct {\n\tlisters.ResourceIndexer[*apisv1alpha1.ServiceExport]\n}\n\n// NewServiceExportLister returns a new ServiceExportLister.\nfunc NewServiceExportLister(indexer cache.Indexer) ServiceExportLister {\n\treturn &serviceExportLister{listers.New[*apisv1alpha1.ServiceExport](indexer, apisv1alpha1.Resource(\"serviceexport\"))}\n}\n\n// ServiceExports returns an object that can list and get ServiceExports.\nfunc (s *serviceExportLister) ServiceExports(namespace string) ServiceExportNamespaceLister {\n\treturn serviceExportNamespaceLister{listers.NewNamespaced[*apisv1alpha1.ServiceExport](s.ResourceIndexer, namespace)}\n}\n\n// ServiceExportNamespaceLister helps list and get ServiceExports.\n// All objects returned here must be treated as read-only.\ntype ServiceExportNamespaceLister interface {\n\t// List lists all ServiceExports in the indexer for a given namespace.\n\t// Objects returned here must be treated as read-only.\n\tList(selector labels.Selector) (ret []*apisv1alpha1.ServiceExport, err error)\n\t// Get retrieves the ServiceExport from the indexer for a given namespace and name.\n\t// Objects returned here must be treated as read-only.\n\tGet(name string) (*apisv1alpha1.ServiceExport, error)\n\tServiceExportNamespaceListerExpansion\n}\n\n// serviceExportNamespaceLister implements the ServiceExportNamespaceLister\n// interface.\ntype serviceExportNamespaceLister struct {\n\tlisters.ResourceIndexer[*apisv1alpha1.ServiceExport]\n}\n"
  },
  {
    "path": "pkg/client/listers/apis/v1alpha1/serviceimport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by lister-gen. DO NOT EDIT.\n\npackage v1alpha1\n\nimport (\n\tlabels \"k8s.io/apimachinery/pkg/labels\"\n\tlisters \"k8s.io/client-go/listers\"\n\tcache \"k8s.io/client-go/tools/cache\"\n\tapisv1alpha1 \"sigs.k8s.io/mcs-api/pkg/apis/v1alpha1\"\n)\n\n// ServiceImportLister helps list ServiceImports.\n// All objects returned here must be treated as read-only.\ntype ServiceImportLister interface {\n\t// List lists all ServiceImports in the indexer.\n\t// Objects returned here must be treated as read-only.\n\tList(selector labels.Selector) (ret []*apisv1alpha1.ServiceImport, err error)\n\t// ServiceImports returns an object that can list and get ServiceImports.\n\tServiceImports(namespace string) ServiceImportNamespaceLister\n\tServiceImportListerExpansion\n}\n\n// serviceImportLister implements the ServiceImportLister interface.\ntype serviceImportLister struct {\n\tlisters.ResourceIndexer[*apisv1alpha1.ServiceImport]\n}\n\n// NewServiceImportLister returns a new ServiceImportLister.\nfunc NewServiceImportLister(indexer cache.Indexer) ServiceImportLister {\n\treturn &serviceImportLister{listers.New[*apisv1alpha1.ServiceImport](indexer, apisv1alpha1.Resource(\"serviceimport\"))}\n}\n\n// ServiceImports returns an object that can list and get ServiceImports.\nfunc (s *serviceImportLister) ServiceImports(namespace string) ServiceImportNamespaceLister {\n\treturn serviceImportNamespaceLister{listers.NewNamespaced[*apisv1alpha1.ServiceImport](s.ResourceIndexer, namespace)}\n}\n\n// ServiceImportNamespaceLister helps list and get ServiceImports.\n// All objects returned here must be treated as read-only.\ntype ServiceImportNamespaceLister interface {\n\t// List lists all ServiceImports in the indexer for a given namespace.\n\t// Objects returned here must be treated as read-only.\n\tList(selector labels.Selector) (ret []*apisv1alpha1.ServiceImport, err error)\n\t// Get retrieves the ServiceImport from the indexer for a given namespace and name.\n\t// Objects returned here must be treated as read-only.\n\tGet(name string) (*apisv1alpha1.ServiceImport, error)\n\tServiceImportNamespaceListerExpansion\n}\n\n// serviceImportNamespaceLister implements the ServiceImportNamespaceLister\n// interface.\ntype serviceImportNamespaceLister struct {\n\tlisters.ResourceIndexer[*apisv1alpha1.ServiceImport]\n}\n"
  },
  {
    "path": "pkg/client/listers/apis/v1beta1/expansion_generated.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by lister-gen. DO NOT EDIT.\n\npackage v1beta1\n\n// ServiceExportListerExpansion allows custom methods to be added to\n// ServiceExportLister.\ntype ServiceExportListerExpansion interface{}\n\n// ServiceExportNamespaceListerExpansion allows custom methods to be added to\n// ServiceExportNamespaceLister.\ntype ServiceExportNamespaceListerExpansion interface{}\n\n// ServiceImportListerExpansion allows custom methods to be added to\n// ServiceImportLister.\ntype ServiceImportListerExpansion interface{}\n\n// ServiceImportNamespaceListerExpansion allows custom methods to be added to\n// ServiceImportNamespaceLister.\ntype ServiceImportNamespaceListerExpansion interface{}\n"
  },
  {
    "path": "pkg/client/listers/apis/v1beta1/serviceexport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by lister-gen. DO NOT EDIT.\n\npackage v1beta1\n\nimport (\n\tlabels \"k8s.io/apimachinery/pkg/labels\"\n\tlisters \"k8s.io/client-go/listers\"\n\tcache \"k8s.io/client-go/tools/cache\"\n\tapisv1beta1 \"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\n// ServiceExportLister helps list ServiceExports.\n// All objects returned here must be treated as read-only.\ntype ServiceExportLister interface {\n\t// List lists all ServiceExports in the indexer.\n\t// Objects returned here must be treated as read-only.\n\tList(selector labels.Selector) (ret []*apisv1beta1.ServiceExport, err error)\n\t// ServiceExports returns an object that can list and get ServiceExports.\n\tServiceExports(namespace string) ServiceExportNamespaceLister\n\tServiceExportListerExpansion\n}\n\n// serviceExportLister implements the ServiceExportLister interface.\ntype serviceExportLister struct {\n\tlisters.ResourceIndexer[*apisv1beta1.ServiceExport]\n}\n\n// NewServiceExportLister returns a new ServiceExportLister.\nfunc NewServiceExportLister(indexer cache.Indexer) ServiceExportLister {\n\treturn &serviceExportLister{listers.New[*apisv1beta1.ServiceExport](indexer, apisv1beta1.Resource(\"serviceexport\"))}\n}\n\n// ServiceExports returns an object that can list and get ServiceExports.\nfunc (s *serviceExportLister) ServiceExports(namespace string) ServiceExportNamespaceLister {\n\treturn serviceExportNamespaceLister{listers.NewNamespaced[*apisv1beta1.ServiceExport](s.ResourceIndexer, namespace)}\n}\n\n// ServiceExportNamespaceLister helps list and get ServiceExports.\n// All objects returned here must be treated as read-only.\ntype ServiceExportNamespaceLister interface {\n\t// List lists all ServiceExports in the indexer for a given namespace.\n\t// Objects returned here must be treated as read-only.\n\tList(selector labels.Selector) (ret []*apisv1beta1.ServiceExport, err error)\n\t// Get retrieves the ServiceExport from the indexer for a given namespace and name.\n\t// Objects returned here must be treated as read-only.\n\tGet(name string) (*apisv1beta1.ServiceExport, error)\n\tServiceExportNamespaceListerExpansion\n}\n\n// serviceExportNamespaceLister implements the ServiceExportNamespaceLister\n// interface.\ntype serviceExportNamespaceLister struct {\n\tlisters.ResourceIndexer[*apisv1beta1.ServiceExport]\n}\n"
  },
  {
    "path": "pkg/client/listers/apis/v1beta1/serviceimport.go",
    "content": "/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by lister-gen. DO NOT EDIT.\n\npackage v1beta1\n\nimport (\n\tlabels \"k8s.io/apimachinery/pkg/labels\"\n\tlisters \"k8s.io/client-go/listers\"\n\tcache \"k8s.io/client-go/tools/cache\"\n\tapisv1beta1 \"sigs.k8s.io/mcs-api/pkg/apis/v1beta1\"\n)\n\n// ServiceImportLister helps list ServiceImports.\n// All objects returned here must be treated as read-only.\ntype ServiceImportLister interface {\n\t// List lists all ServiceImports in the indexer.\n\t// Objects returned here must be treated as read-only.\n\tList(selector labels.Selector) (ret []*apisv1beta1.ServiceImport, err error)\n\t// ServiceImports returns an object that can list and get ServiceImports.\n\tServiceImports(namespace string) ServiceImportNamespaceLister\n\tServiceImportListerExpansion\n}\n\n// serviceImportLister implements the ServiceImportLister interface.\ntype serviceImportLister struct {\n\tlisters.ResourceIndexer[*apisv1beta1.ServiceImport]\n}\n\n// NewServiceImportLister returns a new ServiceImportLister.\nfunc NewServiceImportLister(indexer cache.Indexer) ServiceImportLister {\n\treturn &serviceImportLister{listers.New[*apisv1beta1.ServiceImport](indexer, apisv1beta1.Resource(\"serviceimport\"))}\n}\n\n// ServiceImports returns an object that can list and get ServiceImports.\nfunc (s *serviceImportLister) ServiceImports(namespace string) ServiceImportNamespaceLister {\n\treturn serviceImportNamespaceLister{listers.NewNamespaced[*apisv1beta1.ServiceImport](s.ResourceIndexer, namespace)}\n}\n\n// ServiceImportNamespaceLister helps list and get ServiceImports.\n// All objects returned here must be treated as read-only.\ntype ServiceImportNamespaceLister interface {\n\t// List lists all ServiceImports in the indexer for a given namespace.\n\t// Objects returned here must be treated as read-only.\n\tList(selector labels.Selector) (ret []*apisv1beta1.ServiceImport, err error)\n\t// Get retrieves the ServiceImport from the indexer for a given namespace and name.\n\t// Objects returned here must be treated as read-only.\n\tGet(name string) (*apisv1beta1.ServiceImport, error)\n\tServiceImportNamespaceListerExpansion\n}\n\n// serviceImportNamespaceLister implements the ServiceImportNamespaceLister\n// interface.\ntype serviceImportNamespaceLister struct {\n\tlisters.ResourceIndexer[*apisv1beta1.ServiceImport]\n}\n"
  },
  {
    "path": "scripts/.gitignore",
    "content": "*.kubeconfig\n*.tmp"
  },
  {
    "path": "scripts/c1.yaml",
    "content": "kind: Cluster\napiVersion: \"kind.x-k8s.io/v1alpha4\"\nnetworking:\n  podSubnet: \"10.10.0.0/16\"\n  serviceSubnet: \"10.11.0.0/16\"\nnodes:\n- role: control-plane\n  kubeadmConfigPatches:\n    - |\n      kind: ClusterConfiguration\n      dns:\n        # TODO: Remove this after Kubernetes 1.35.\n        # Reference: https://github.com/kubernetes/kubernetes/pull/132288\n        imageTag: v1.12.2\n"
  },
  {
    "path": "scripts/c2.yaml",
    "content": "kind: Cluster\napiVersion: \"kind.x-k8s.io/v1alpha4\"\nnetworking:\n  podSubnet: \"10.12.0.0/16\"\n  serviceSubnet: \"10.13.0.0/16\"\nnodes:\n- role: control-plane\n  kubeadmConfigPatches:\n    - |\n      kind: ClusterConfiguration\n      dns:\n        # TODO: Remove this after Kubernetes 1.35.\n        # Reference: https://github.com/kubernetes/kubernetes/pull/132288\n        imageTag: v1.12.2\n"
  },
  {
    "path": "scripts/coredns-rbac.json",
    "content": "[\n  {\n    \"op\": \"add\",\n    \"path\": \"/rules/-\",\n    \"value\": {\n      \"apiGroups\": [\n        \"multicluster.x-k8s.io\"\n      ],\n      \"resources\": [\n        \"serviceimports\"\n      ],\n      \"verbs\": [\n        \"list\",\n        \"watch\"\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "scripts/down.sh",
    "content": "#!/bin/bash\n\n# Copyright 2020 The Kubernetes 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\nset -e\n\ncd $(dirname ${BASH_SOURCE})\n\nkind() {\n  go -C ../controllers run sigs.k8s.io/kind \"$@\"\n}\n\nc1=${CLUSTER1:-c1}\nc2=${CLUSTER2:-c2}\n\nkind delete cluster --name \"${c1}\"\nkind delete cluster --name \"${c2}\"\n"
  },
  {
    "path": "scripts/e2e-test.sh",
    "content": "#!/bin/bash\n\n# Copyright 2020 The Kubernetes 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\ncd $(dirname ${BASH_SOURCE})\n\nset -e\n\nexport KUBECONFIG1=$(mktemp --suffix=\".kubeconfig\")\nexport KUBECONFIG2=$(mktemp --suffix=\".kubeconfig\")\n\nfunction cleanup() {\n    if [ -z \"${NO_TEAR_DOWN}\" ]; then\n        ./down.sh\n        rm ${KUBECONFIG1}\n        rm ${KUBECONFIG2}\n    else\n        echo \"KUBECONFIG1=${KUBECONFIG1}\"\n        echo \"KUBECONFIG2=${KUBECONFIG2}\"\n    fi\n}\n\ntrap cleanup EXIT\n\n./up.sh\ngo -C ../e2e run github.com/onsi/ginkgo/v2/ginkgo .\n"
  },
  {
    "path": "scripts/up.sh",
    "content": "#!/bin/bash\n\n# Copyright 2020 The Kubernetes 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\ncd $(dirname ${BASH_SOURCE})\n\n. ./util.sh\n\nset -e\n\nkind() {\n  go -C ../controllers run sigs.k8s.io/kind \"$@\"\n}\n\nc1=${CLUSTER1:-c1}\nc2=${CLUSTER2:-c2}\nkubeconfig1=${KUBECONFIG1:-\"$PWD/${c1}.kubeconfig\"}\nkubeconfig2=${KUBECONFIG2:-\"$PWD/${c2}.kubeconfig\"}\ncontroller_image=${MCS_CONTROLLER_IMAGE:-\"mcs-api-controller\"}\n\nk1=\"kubectl --kubeconfig ${kubeconfig1}\"\nk2=\"kubectl --kubeconfig ${kubeconfig2}\"\n\nif [ ! -z \"${BUILD_CONTROLLER}\" ] || [ -z \"$(docker images mcs-api-controller -q)\" ]; then\n  pushd ../\n  make docker-build\n  popd\nfi\n\nkind create cluster --name \"${c1}\" --config \"$PWD/${c1}.yaml\"\nkind create cluster --name \"${c2}\" --config \"$PWD/${c2}.yaml\"\n\nkind get kubeconfig --name \"${c1}\" > \"${kubeconfig1}\"\nkind get kubeconfig --name \"${c2}\" > \"${kubeconfig2}\"\n\nkind load docker-image \"${controller_image}\" --name \"${c1}\"\nkind load docker-image \"${controller_image}\" --name \"${c2}\"\n\necho \"Configuring CoreDNS\"\nfunction update_coredns() {\n  kubectl --kubeconfig ${1} apply -f ../config/crd\n  kubectl --kubeconfig ${1} patch clusterrole system:coredns --type json --patch-file coredns-rbac.json\n  # Patching Corefile based on Cilium documentation: https://docs.cilium.io/en/latest/network/clustermesh/mcsapi/#prerequisites\n  kubectl --kubeconfig ${1} get configmap -n kube-system coredns -o yaml | \\\n    sed -e 's/cluster\\.local/cluster.local clusterset.local/g' | \\\n    sed -E 's/^(.*)kubernetes(.*)\\{/\\1kubernetes\\2{\\n\\1   multicluster clusterset.local/' | \\\n    kubectl --kubeconfig ${1} replace -f-\n  kubectl --kubeconfig ${1} rollout restart deploy -n kube-system coredns\n}\nupdate_coredns ${kubeconfig1}\nupdate_coredns ${kubeconfig2}\n\nfunction pod_cidrs() {\n  kubectl --kubeconfig \"${1}\" get nodes -o jsonpath='{range .items[*]}{.spec.podCIDR}{\"\\n\"}'\n}\n\nfunction add_routes() {\n  unset IFS\n  routes=$(kubectl --kubeconfig ${3} get node ${2} -o jsonpath='ip route add {.spec.podCIDR} via {.status.addresses[?(.type==\"InternalIP\")].address}')\n  echo \"Connecting cluster ${1} to ${2}\"\n\n  IFS=$'\\n'\n  for n in $(kind get nodes --name \"${1}\"); do\n    for r in $routes; do\n      eval \"docker exec $n $r\"\n    done\n  done\n  unset IFS\n}\n\nwaitfor pod_cidrs ${kubeconfig1}\nwaitfor pod_cidrs ${kubeconfig2}\n\necho \"Connecting cluster networks...\"\nadd_routes \"${c1}\" \"${c2}-control-plane\" \"${kubeconfig2}\"\nadd_routes \"${c2}\" \"${c1}-control-plane\" \"${kubeconfig1}\"\necho \"Cluster networks connected\"\n\n${k1} apply -f ../config/rbac\n${k2} apply -f ../config/rbac\n\n${k1} create sa mcs-api-controller\n${k1} create clusterrolebinding mcs-api-binding --clusterrole=mcs-derived-service-manager --serviceaccount=default:mcs-api-controller\n${k1} run --image \"${controller_image}\" --image-pull-policy=Never mcs-api-controller --overrides='{ \"spec\": { \"serviceAccount\": \"mcs-api-controller\" }  }'\n\n${k2} create sa mcs-api-controller\n${k2} create clusterrolebinding mcs-api-binding --clusterrole=mcs-derived-service-manager --serviceaccount=default:mcs-api-controller\n${k2} run --image \"${controller_image}\" --image-pull-policy=Never mcs-api-controller --overrides='{ \"spec\": { \"serviceAccount\": \"mcs-api-controller\" }  }'\n"
  },
  {
    "path": "scripts/util.sh",
    "content": "#!/bin/bash\n\n# Copyright 2020 The Kubernetes 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\nfunction waitfor() {\n  for i in {1..30}; do\n    if [ ! -z \"$(${@})\" ]; then\n      break\n    fi\n    sleep 1\n  done\n  if [ -z \"$(${@})\" ]; then\n    echo \"No results for '${1}' after 30 attempts\"\n  fi\n}"
  },
  {
    "path": "tools/go.mod",
    "content": "module github.com/kubernetes-sigs/mcs-api/tools\n\ngo 1.23.0\n\nrequire (\n\tgolang.org/x/lint v0.0.0-20210508222113-6edffad5e616\n\tk8s.io/code-generator v0.32.5\n\tsigs.k8s.io/controller-tools v0.17.3\n)\n\nrequire (\n\tgithub.com/fatih/color v1.18.0 // indirect\n\tgithub.com/fxamacker/cbor/v2 v2.7.0 // indirect\n\tgithub.com/go-logr/logr v1.4.2 // indirect\n\tgithub.com/gobuffalo/flect v1.0.3 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/google/gofuzz v1.2.0 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/spf13/cobra v1.9.1 // indirect\n\tgithub.com/spf13/pflag v1.0.6 // indirect\n\tgithub.com/x448/float16 v0.8.4 // indirect\n\tgolang.org/x/mod v0.23.0 // indirect\n\tgolang.org/x/net v0.35.0 // indirect\n\tgolang.org/x/sync v0.11.0 // indirect\n\tgolang.org/x/sys v0.30.0 // indirect\n\tgolang.org/x/text v0.22.0 // indirect\n\tgolang.org/x/tools v0.30.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/api v0.32.2 // indirect\n\tk8s.io/apiextensions-apiserver v0.32.2 // indirect\n\tk8s.io/apimachinery v0.32.5 // indirect\n\tk8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 // indirect\n\tk8s.io/klog/v2 v2.130.1 // indirect\n\tk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect\n\tsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect\n\tsigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect\n\tsigs.k8s.io/yaml v1.4.0 // indirect\n)\n"
  },
  {
    "path": "tools/go.sum",
    "content": "github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=\ngithub.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=\ngithub.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=\ngithub.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=\ngithub.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=\ngithub.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=\ngithub.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=\ngithub.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4=\ngithub.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=\ngithub.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\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/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\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/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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=\ngithub.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=\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/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=\ngithub.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=\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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=\ngithub.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=\ngithub.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=\ngithub.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=\ngithub.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=\ngithub.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\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/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=\ngolang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=\ngolang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=\ngolang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=\ngolang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=\ngolang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\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.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=\ngolang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=\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-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=\ngolang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=\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=\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/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.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nk8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw=\nk8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y=\nk8s.io/apiextensions-apiserver v0.32.2 h1:2YMk285jWMk2188V2AERy5yDwBYrjgWYggscghPCvV4=\nk8s.io/apiextensions-apiserver v0.32.2/go.mod h1:GPwf8sph7YlJT3H6aKUWtd0E+oyShk/YHWQHf/OOgCA=\nk8s.io/apimachinery v0.32.5 h1:6We3aJ6crC0ap8EhsEXcgX3LpI6SEjubpiOMXLROwPM=\nk8s.io/apimachinery v0.32.5/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=\nk8s.io/code-generator v0.32.5 h1:dvoXgaWTDPLsg0txUzWj5xPV8UwHOsBhmm4JC9Gd1Qo=\nk8s.io/code-generator v0.32.5/go.mod h1:7S6jUv4ZAnI2yDUJUQUEuc3gv6+qFhnkB5Fhs9Eb0d8=\nk8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 h1:si3PfKm8dDYxgfbeA6orqrtLkvvIeH8UqffFJDl0bz4=\nk8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU=\nk8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=\nk8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=\nk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=\nk8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=\nsigs.k8s.io/controller-tools v0.17.3 h1:lwFPLicpBKLgIepah+c8ikRBubFW5kOQyT88r3EwfNw=\nsigs.k8s.io/controller-tools v0.17.3/go.mod h1:1ii+oXcYZkxcBXzwv3YZBlzjt1fvkrCGjVF73blosJI=\nsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=\nsigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=\nsigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=\nsigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=\nsigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=\nsigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=\n"
  },
  {
    "path": "tools/tools.go",
    "content": "//go:build tools\n// +build tools\n\n/*\nCopyright 2020 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// This package contains import references to packages required only for the\n// build process.\n// https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module\npackage tools\n\nimport (\n\t_ \"golang.org/x/lint/golint\"\n\t_ \"k8s.io/code-generator/cmd/client-gen\"\n\t_ \"k8s.io/code-generator/cmd/deepcopy-gen\"\n\t_ \"k8s.io/code-generator/cmd/informer-gen\"\n\t_ \"k8s.io/code-generator/cmd/lister-gen\"\n\t_ \"k8s.io/code-generator/cmd/register-gen\"\n\t_ \"sigs.k8s.io/controller-tools/cmd/controller-gen\"\n)\n"
  }
]