main dbb1ed65c024 cached
138 files
2.7 MB
712.3k tokens
294 symbols
1 requests
Download .txt
Showing preview only (2,849K chars total). Download the full file or copy to clipboard to get everything.
Repository: kubernetes-sigs/cloud-provider-kind
Branch: main
Commit: dbb1ed65c024
Files: 138
Total size: 2.7 MB

Directory structure:
gitextract_ix7p8aoy/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── bats.yml
│       ├── gateway.yml
│       ├── ingress.yml
│       ├── k8s.yml
│       ├── release.yml
│       └── test.yaml
├── .gitignore
├── .golangci.yaml
├── .goreleaser.yaml
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── Makefile
├── OWNERS
├── README.md
├── SECURITY.md
├── SECURITY_CONTACTS
├── cloudbuild.yaml
├── cmd/
│   └── app.go
├── code-of-conduct.md
├── compose.yml
├── docs/
│   ├── .nojekyll
│   ├── HOWTO.md
│   ├── README.md
│   ├── _coverpage.md
│   ├── _sidebar.md
│   ├── code-of-conduct.md
│   ├── contributing/
│   │   └── CONTRIBUTING.md
│   ├── index.html
│   └── user/
│       ├── example/
│       │   ├── creating_gateway_http_route.md
│       │   ├── enable_lb_port_mapping.md
│       │   └── service_expose_via_loadbalancer.md
│       ├── gateway/
│       │   ├── allow_load_balancer_access_control_plane.md
│       │   ├── configure_proxy_image_registry.md
│       │   ├── gatewayapi.md
│       │   └── running_the_provider.md
│       ├── howto.md
│       ├── ingress/
│       │   └── ingress.md
│       ├── install/
│       │   ├── install_docker.md
│       │   └── install_go.md
│       ├── os_support.md
│       └── support/
│           └── os_support.md
├── examples/
│   ├── gateway_httproute_simple.yaml
│   ├── ingress_foo_bar.yaml
│   ├── loadbalancer_affinity.yaml
│   ├── loadbalancer_deployment.yaml
│   ├── loadbalancer_etp_cluster.yaml
│   ├── loadbalancer_etp_local.yaml
│   ├── loadbalancer_multiport.yaml
│   ├── loadbalancer_static_ip.yaml
│   ├── loadbalancer_stts.yaml
│   └── loadbalancer_udp_tcp.yaml
├── go.mod
├── go.sum
├── hack/
│   ├── build-route-adder.sh
│   ├── ci/
│   │   ├── add-kubernetes-to-workspace.sh
│   │   └── e2e.sh
│   ├── download-gateway-crds.sh
│   ├── init-buildx.sh
│   └── lint.sh
├── internal/
│   └── routeadder/
│       └── main.go
├── kind.yaml
├── main.go
├── pkg/
│   ├── config/
│   │   └── config.go
│   ├── constants/
│   │   └── constants.go
│   ├── container/
│   │   └── container.go
│   ├── controller/
│   │   ├── controller.go
│   │   ├── http.go
│   │   └── http_test.go
│   ├── gateway/
│   │   ├── backendref.go
│   │   ├── backendref_test.go
│   │   ├── controller.go
│   │   ├── controller_test.go
│   │   ├── crd_manager.go
│   │   ├── crd_manager_test.go
│   │   ├── crds/
│   │   │   ├── README.md
│   │   │   ├── experimental/
│   │   │   │   ├── gateway.networking.k8s.io_backendtlspolicies.yaml
│   │   │   │   ├── gateway.networking.k8s.io_gatewayclasses.yaml
│   │   │   │   ├── gateway.networking.k8s.io_gateways.yaml
│   │   │   │   ├── gateway.networking.k8s.io_grpcroutes.yaml
│   │   │   │   ├── gateway.networking.k8s.io_httproutes.yaml
│   │   │   │   ├── gateway.networking.k8s.io_listenersets.yaml
│   │   │   │   ├── gateway.networking.k8s.io_referencegrants.yaml
│   │   │   │   ├── gateway.networking.k8s.io_tcproutes.yaml
│   │   │   │   ├── gateway.networking.k8s.io_tlsroutes.yaml
│   │   │   │   ├── gateway.networking.k8s.io_udproutes.yaml
│   │   │   │   ├── gateway.networking.k8s.io_vap_safeupgrades.yaml
│   │   │   │   ├── gateway.networking.x-k8s.io_xbackendtrafficpolicies.yaml
│   │   │   │   ├── gateway.networking.x-k8s.io_xlistenersets.yaml
│   │   │   │   ├── gateway.networking.x-k8s.io_xmeshes.yaml
│   │   │   │   └── kustomization.yaml
│   │   │   ├── kustomization.yaml
│   │   │   └── standard/
│   │   │       ├── gateway.networking.k8s.io_backendtlspolicies.yaml
│   │   │       ├── gateway.networking.k8s.io_gatewayclasses.yaml
│   │   │       ├── gateway.networking.k8s.io_gateways.yaml
│   │   │       ├── gateway.networking.k8s.io_grpcroutes.yaml
│   │   │       ├── gateway.networking.k8s.io_httproutes.yaml
│   │   │       ├── gateway.networking.k8s.io_listenersets.yaml
│   │   │       ├── gateway.networking.k8s.io_referencegrants.yaml
│   │   │       ├── gateway.networking.k8s.io_tlsroutes.yaml
│   │   │       └── gateway.networking.k8s.io_vap_safeupgrades.yaml
│   │   ├── envoy.go
│   │   ├── envoy_test.go
│   │   ├── gateway.go
│   │   ├── gateway_test.go
│   │   ├── grpcroute.go
│   │   ├── httproute.go
│   │   ├── kindcluster.go
│   │   ├── listener.go
│   │   ├── referencegrant.go
│   │   ├── referencegrant_test.go
│   │   ├── routeadder/
│   │   │   ├── README.md
│   │   │   ├── route-adder-amd64
│   │   │   └── route-adder-arm64
│   │   ├── routes.go
│   │   └── routes_test.go
│   ├── ingress/
│   │   ├── README.md
│   │   ├── controller.go
│   │   └── controller_test.go
│   ├── loadbalancer/
│   │   ├── proxy.go
│   │   ├── proxy_test.go
│   │   ├── server.go
│   │   └── server_test.go
│   ├── provider/
│   │   ├── cloud.go
│   │   ├── clusters.go
│   │   ├── instances.go
│   │   └── loadbalancer.go
│   └── tunnels/
│       ├── address_darwin.go
│       ├── address_other.go
│       ├── address_windows.go
│       └── tunnel.go
└── tests/
    ├── README.md
    ├── custom-network/
    │   ├── setup_suite.bash
    │   └── tests.bats
    ├── kind.yaml
    ├── setup_suite.bash
    └── tests.bats

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:
  - package-ecosystem: "gomod" # See documentation for possible values
    directory: "/" # Location of package manifests
    schedule:
      interval: "weekly"
    groups:
      k8s-deps:
        patterns:
          - "*k8s.io*"
      envoy-deps:
        patterns:
          - "*envoyproxy*"
    labels:
      - "area/dependency"
      - "ok-to-test"

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
    labels:
      - "area/dependency"
      - "ok-to-test"

  - package-ecosystem: "docker"
    directories:
      - "**/*"
    schedule:
      interval: "weekly"
    labels:
      - "area/dependency"
      - "ok-to-test"


================================================
FILE: .github/workflows/bats.yml
================================================
name: bats

on:
  push:
    branches:
      - 'main'
    tags:
      - 'v*'
  pull_request:
    branches: [ main ]
  workflow_dispatch:

env:
  GO_VERSION: "1.25"
  K8S_VERSION: "v1.35.0"
  KIND_VERSION: "v0.31.0"

jobs:
  bats_tests:
    runs-on: ubuntu-22.04
    name: Bats e2e tests
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Setup Bats and bats libs
        id: setup-bats
        uses: bats-core/bats-action@77d6fb60505b4d0d1d73e48bd035b55074bbfb43 # 4.0.0

      - name: Bats tests
        shell: bash
        env:
         BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }}
         TERM: xterm
        run: |
          bats -o _artifacts tests/

      - name: Bats tests (custom network)
        shell: bash
        env:
          BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }}
          TERM: xterm
        run: |
          bats -o _artifacts-custom-network tests/custom-network/

      - name: Upload logs
        if: always()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v5
        with:
          name: kind-logs-${{ env.JOB_NAME }}-${{ github.run_id }}
          path: |
            ./_artifacts
            ./_artifacts-custom-network
 
  bats_mac_tests:
    runs-on: macos-15-intel
    name: Bats e2e tests on Mac
    # Skip until runner does not timeout on kind create cluster
    if: false
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      # c.f. https://github.com/actions/runner-images/issues/13358
      - name: Work around x86 macOS bug for multiple CPU cores
        run: |
          sudo defaults -currentHost write /Library/Preferences/com.apple.powerlogd SMCMonitorCadence 0
          sudo killall PerfPowerServices || true
      - name: Set up environment (download dependencies)
        run: |
          brew install docker colima
          brew install kind
          brew install golang
          brew install bats-core
          brew install kubectl
          LIMA_SSH_PORT_FORWARDER=true colima start
      - name: Bats tests
        env:
          TERM: linux
        run: |
          bash -c "time bats -o _artifacts --trace --formatter tap tests/"
      - name: Debug info
        if: always()
        run: |
          docker ps
          kubectl get pods -v7
          kubectl get services -v7

      - name: Upload logs
        if: always()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v5
        with:
          name: kind-logs-mac-${{ github.run_id }}
          path: ./_artifacts


================================================
FILE: .github/workflows/gateway.yml
================================================
name: gateway

on:
  push:
  pull_request:
  workflow_dispatch:

env:
  GO_VERSION: "1.25"
  GATEWAY_VERSION: "v1.4.1"
  K8S_VERSION: "v1.35.0"
  KIND_VERSION: "v0.31.0"
  KIND_CLUSTER_NAME: "kind-cloud"

jobs:
  gateway:
    name: gateway
    runs-on: ubuntu-latest
    timeout-minutes: 100
    env:
      JOB_NAME: "cloud-provider-kind-e2e-gateway"
    steps:
    - name: Set up Go
      uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
      with:
        go-version: ${{ env.GO_VERSION }}
      id: go

    - name: Check out code
      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

    - name: Enable ipv4 and ipv6 forwarding
      run: |
        sudo sysctl -w net.ipv6.conf.all.forwarding=1
        sudo sysctl -w net.ipv4.ip_forward=1

    - name: Set up environment (download dependencies)
      run: |
        TMP_DIR=$(mktemp -d)
        # Test binaries
        git clone --branch ${{ env.GATEWAY_VERSION }} --depth 1 https://github.com/kubernetes-sigs/gateway-api.git ${TMP_DIR}
        cd ${TMP_DIR}
        go test ./conformance/ -c
        cd -
        sudo cp ${TMP_DIR}/conformance.test /usr/local/bin/conformance.test
        sudo chmod +x /usr/local/bin/conformance.test
        # kubectl
        curl -L https://dl.k8s.io/${{ env.K8S_VERSION }}/bin/linux/amd64/kubectl -o ${TMP_DIR}/kubectl
        # kind
        curl -Lo ${TMP_DIR}/kind https://kind.sigs.k8s.io/dl/${{ env.KIND_VERSION }}/kind-linux-amd64
        # Install
        sudo cp ${TMP_DIR}/kubectl /usr/local/bin/kubectl
        sudo cp ${TMP_DIR}/kind /usr/local/bin/kind
        sudo chmod +x /usr/local/bin/kubectl
        sudo chmod +x /usr/local/bin/kind
        # Create folder to store artifacts
        mkdir -p _artifacts

    - name: Run cloud-provider-kind
      run: |
        make
        nohup bin/cloud-provider-kind -v 2 --enable-log-dumping --logs-dir ./_artifacts/loadbalancers > ./_artifacts/ccm-kind.log 2>&1 &

    - name: Create multi node cluster
      run: |
        # create cluster
        cat <<EOF | /usr/local/bin/kind create cluster \
          --name ${{ env.KIND_CLUSTER_NAME}}           \
          --image kindest/node:${{ env.K8S_VERSION }}  \
          -v7 --wait 1m --retain --config=-
        kind: Cluster
        apiVersion: kind.x-k8s.io/v1alpha4
        nodes:
        - role: control-plane
        - role: worker
        - role: worker
        kubeadmConfigPatches:
          - |
            kind: ClusterConfiguration
            apiServer:
              extraArgs:
                v: "5"
            controllerManager:
              extraArgs:
                cloud-provider: "external"
                v: "5"
            ---
            kind: InitConfiguration
            nodeRegistration:
              kubeletExtraArgs:
                cloud-provider: "external"
                v: "5"
            ---
            kind: JoinConfiguration
            nodeRegistration:
              kubeletExtraArgs:
                cloud-provider: "external"
                v: "5"
        EOF
        /usr/local/bin/kind get kubeconfig --name ${{ env.KIND_CLUSTER_NAME}} > _artifacts/kubeconfig.conf

    - name: Get Cluster status
      run: |
        /usr/local/bin/kubectl get nodes -o yaml
        /usr/local/bin/kubectl get pods -A -o wide
        # wait network is ready
        /usr/local/bin/kubectl wait --for=condition=ready pods --namespace=kube-system -l k8s-app=kube-dns --timeout=3m
        /usr/local/bin/kubectl get nodes -o wide
        /usr/local/bin/kubectl get pods -A

    - name: Run tests
      run: |
        /usr/local/bin/conformance.test -test.run TestConformance \
            --debug=true \
            --kubeconfig=_artifacts/kubeconfig.conf \
            --organization=sigs.k8s.io \
            --project=cloud-provider-kind \
            --url=https://github.com/kubernetes-sigs/cloud-provider-kind \
            --version=${{ github.sha }} \
            --contact=https://github.com/kubernetes-sigs/cloud-provider-kind/issues/new \
            --gateway-class=cloud-provider-kind \
            --conformance-profiles=GATEWAY-HTTP \
            --supported-features=Gateway,HTTPRoute,ReferenceGrant \
            --report-output=./_artifacts/conformance.yaml

    - name: Export logs
      if: always()
      run: |
        /usr/local/bin/kind export logs --name ${{ env.KIND_CLUSTER_NAME}} ./_artifacts/logs
        cp ./_artifacts/ccm-kind.log ./_artifacts/logs

    - name: Upload logs
      if: always()
      uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v5
      with:
        name: kind-logs-${{ env.JOB_NAME }}-${{ github.run_id }}
        path: ./_artifacts


================================================
FILE: .github/workflows/ingress.yml
================================================
name: ingress

on:
  push:
  pull_request:
  workflow_dispatch:

env:
  GO_VERSION: "1.25"
  K8S_VERSION: "v1.35.0"
  KIND_VERSION: "v0.31.0"
  KIND_CLUSTER_NAME: "kind-cloud"

jobs:
  ingress:
    name: ingress
    runs-on: ubuntu-latest
    timeout-minutes: 100
    env:
      JOB_NAME: "cloud-provider-kind-e2e-ingress"
    steps:
    - name: Set up Go
      uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
      with:
        go-version: ${{ env.GO_VERSION }}
      id: go

    - name: Check out code
      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

    - name: Enable ipv4 and ipv6 forwarding
      run: |
        sudo sysctl -w net.ipv6.conf.all.forwarding=1
        sudo sysctl -w net.ipv4.ip_forward=1

    - name: Set up environment (download dependencies)
      run: |
        TMP_DIR=$(mktemp -d)
        # kubectl
        curl -L https://dl.k8s.io/${{ env.K8S_VERSION }}/bin/linux/amd64/kubectl -o ${TMP_DIR}/kubectl
        # kind
        curl -Lo ${TMP_DIR}/kind https://kind.sigs.k8s.io/dl/${{ env.KIND_VERSION }}/kind-linux-amd64
        # Install
        sudo cp ${TMP_DIR}/kubectl /usr/local/bin/kubectl
        sudo cp ${TMP_DIR}/kind /usr/local/bin/kind
        sudo chmod +x /usr/local/bin/kubectl
        sudo chmod +x /usr/local/bin/kind
        # Create folder to store artifacts
        mkdir -p _artifacts

    - name: Run cloud-provider-kind
      run: |
        make
        nohup bin/cloud-provider-kind -v 2 --enable-log-dumping --logs-dir ./_artifacts/loadbalancers > ./_artifacts/ccm-kind.log 2>&1 &

    - name: Create multi node cluster
      run: |
        # create cluster
        cat <<EOF | /usr/local/bin/kind create cluster \
          --name ${{ env.KIND_CLUSTER_NAME}}           \
          --image kindest/node:${{ env.K8S_VERSION }}  \
          -v7 --wait 1m --retain --config=-
        kind: Cluster
        apiVersion: kind.x-k8s.io/v1alpha4
        nodes:
        - role: control-plane
        - role: worker
        - role: worker
        kubeadmConfigPatches:
          - |
            kind: ClusterConfiguration
            apiServer:
              extraArgs:
                v: "5"
            controllerManager:
              extraArgs:
                cloud-provider: "external"
                v: "5"
            ---
            kind: InitConfiguration
            nodeRegistration:
              kubeletExtraArgs:
                cloud-provider: "external"
                v: "5"
            ---
            kind: JoinConfiguration
            nodeRegistration:
              kubeletExtraArgs:
                cloud-provider: "external"
                v: "5"
        EOF
        /usr/local/bin/kind get kubeconfig --name ${{ env.KIND_CLUSTER_NAME}} > _artifacts/kubeconfig.conf

    - name: Get Cluster status
      run: |
        /usr/local/bin/kubectl get nodes -o yaml
        /usr/local/bin/kubectl get pods -A -o wide
        # wait network is ready
        /usr/local/bin/kubectl wait --for=condition=ready pods --namespace=kube-system -l k8s-app=kube-dns --timeout=3m
        /usr/local/bin/kubectl get nodes -o wide
        /usr/local/bin/kubectl get pods -A

    - name: Run tests
      run: |
        TMP_DIR=$(mktemp -d)
        # Test binaries
        git clone --depth 1 https://github.com/kubernetes-sigs/ingress-controller-conformance.git ${TMP_DIR}/ingress-controller-conformance
        cd ${TMP_DIR}/ingress-controller-conformance
        # Add the @skip tag to the incompatible test
        # The Gateway spec for spec.hostnames: ["*.foo.com"] states it matches all subdomains.
        # The Ingress spec for spec.rules.host: "*.foo.com" states it only matches a single subdomain level.
        echo "Skipping incompatible test: 'An Ingress with a wildcard host rule should not route traffic matching on more than a single dns label'"
        sed -i '/Scenario: An Ingress with a wildcard host rule should not route traffic matching on more than a single dns label/i @skip' features/host_rules.feature
        echo "Skipping load balancing spread test: 'An Ingress with no rules should send all requests to the default backend'"
        sed -i '/Scenario Outline: An Ingress with no rules should send all requests to the default backend/i @skip' features/load_balancing.feature
        make build
        chmod +x ingress-controller-conformance
        ./ingress-controller-conformance           \
          --ingress-class=cloud-provider-kind      \
          --no-colors                              \
          --output-directory="${PWD}/_artifacts"   \
          --tags="~@skip"                          \
          --wait-time-for-ingress-status=65s

    - name: Export logs
      if: always()
      run: |
        /usr/local/bin/kind export logs --name ${{ env.KIND_CLUSTER_NAME}} ./_artifacts/logs
        cp ./_artifacts/ccm-kind.log ./_artifacts/logs

    - name: Upload logs
      if: always()
      uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v5
      with:
        name: kind-logs-${{ env.JOB_NAME }}-${{ github.run_id }}
        path: ./_artifacts


================================================
FILE: .github/workflows/k8s.yml
================================================
name: k8s

on:
  push:
  pull_request:
  workflow_dispatch:

env:
  K8S_VERSION: "v1.34.0"
  KIND_VERSION: "v0.30.0"
  KIND_CLUSTER_NAME: "kind-cloud"

jobs:
  k8s:
    name: k8s
    runs-on: ubuntu-latest
    timeout-minutes: 100
    strategy:
      fail-fast: false
      matrix:
        # TODO add "dual", waiting on KEP https://github.com/kubernetes/enhancements/tree/master/keps/sig-network/3705-cloud-node-ips
        ipFamily: ["ipv4", "ipv6"]
    env:
      JOB_NAME: "cloud-provider-kind-e2e-${{ matrix.ipFamily }}"
      IP_FAMILY: ${{ matrix.ipFamily }}
    steps:
    - name: Set up Go
      uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
      with:
        go-version: stable

    - name: Check out code
      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

    - name: Enable ipv4 and ipv6 forwarding
      run: |
        sudo sysctl -w net.ipv6.conf.all.forwarding=1
        sudo sysctl -w net.ipv4.ip_forward=1

    - name: Set up environment (download dependencies)
      run: |
        TMP_DIR=$(mktemp -d)
        # Test binaries
        curl -L https://dl.k8s.io/${{ env.K8S_VERSION }}/kubernetes-test-linux-amd64.tar.gz -o ${TMP_DIR}/kubernetes-test-linux-amd64.tar.gz
        tar xvzf ${TMP_DIR}/kubernetes-test-linux-amd64.tar.gz \
          --directory ${TMP_DIR} \
          --strip-components=3 kubernetes/test/bin/ginkgo kubernetes/test/bin/e2e.test
        # kubectl
        curl -L https://dl.k8s.io/${{ env.K8S_VERSION }}/bin/linux/amd64/kubectl -o ${TMP_DIR}/kubectl
        # kind
        curl -Lo ${TMP_DIR}/kind https://kind.sigs.k8s.io/dl/${{ env.KIND_VERSION }}/kind-linux-amd64
        # Install
        sudo cp ${TMP_DIR}/ginkgo /usr/local/bin/ginkgo
        sudo cp ${TMP_DIR}/e2e.test /usr/local/bin/e2e.test
        sudo cp ${TMP_DIR}/kubectl /usr/local/bin/kubectl
        sudo cp ${TMP_DIR}/kind /usr/local/bin/kind
        sudo chmod +x /usr/local/bin/ginkgo
        sudo chmod +x /usr/local/bin/e2e.test
        sudo chmod +x /usr/local/bin/kubectl
        sudo chmod +x /usr/local/bin/kind
        # Create folder to store artifacts
        mkdir -p _artifacts

    - name: Run cloud-provider-kind
      run: |
        make
        nohup bin/cloud-provider-kind -v 2 --enable-log-dumping --logs-dir ./_artifacts/loadbalancers > ./_artifacts/ccm-kind.log 2>&1 &

    - name: Create multi node cluster
      run: |
        # create cluster
        cat <<EOF | /usr/local/bin/kind create cluster \
          --name ${{ env.KIND_CLUSTER_NAME}}           \
          --image kindest/node:${{ env.K8S_VERSION }}  \
          -v7 --wait 1m --retain --config=-
        kind: Cluster
        apiVersion: kind.x-k8s.io/v1alpha4
        networking:
          ipFamily: ${IP_FAMILY}
        nodes:
        - role: control-plane
        - role: worker
        - role: worker
        kubeadmConfigPatches:
          - |
            kind: ClusterConfiguration
            apiServer:
              extraArgs:
                v: "5"
            controllerManager:
              extraArgs:
                cloud-provider: "external"
                v: "5"
            ---
            kind: InitConfiguration
            nodeRegistration:
              kubeletExtraArgs:
                cloud-provider: "external"
                v: "5"
            ---
            kind: JoinConfiguration
            nodeRegistration:
              kubeletExtraArgs:
                cloud-provider: "external"
                v: "5"
        EOF
        /usr/local/bin/kind get kubeconfig --name ${{ env.KIND_CLUSTER_NAME}} > _artifacts/kubeconfig.conf

    - name: Workaround CoreDNS for IPv6 airgapped
      if: ${{ matrix.ipFamily == 'ipv6' }}
      run: |
        # Patch CoreDNS to work in Github CI
        # 1. Github CI doesn´t offer IPv6 connectivity, so CoreDNS should be configured
        # to work in an offline environment:
        # https://github.com/coredns/coredns/issues/2494#issuecomment-457215452
        # 2. Github CI adds following domains to resolv.conf search field:
        # .net.
        # CoreDNS should handle those domains and answer with NXDOMAIN instead of SERVFAIL
        # otherwise pods stops trying to resolve the domain.
        # Get the current config
        original_coredns=$(/usr/local/bin/kubectl get -oyaml -n=kube-system configmap/coredns)
        echo "Original CoreDNS config:"
        echo "${original_coredns}"
        # Patch it
        fixed_coredns=$(
          printf '%s' "${original_coredns}" | sed \
            -e 's/^.*kubernetes cluster\.local/& net/' \
            -e '/^.*upstream$/d' \
            -e '/^.*fallthrough.*$/d' \
            -e '/^.*forward . \/etc\/resolv.conf$/d' \
            -e '/^.*loop$/d' \
        )
        echo "Patched CoreDNS config:"
        echo "${fixed_coredns}"
        printf '%s' "${fixed_coredns}" | /usr/local/bin/kubectl apply -f -

    - name: Get Cluster status
      run: |
        /usr/local/bin/kubectl get nodes -o yaml
        /usr/local/bin/kubectl get pods -A -o wide
        # wait network is ready
        /usr/local/bin/kubectl wait --for=condition=ready pods --namespace=kube-system -l k8s-app=kube-dns --timeout=3m
        /usr/local/bin/kubectl get nodes -o wide
        /usr/local/bin/kubectl get pods -A

    - name: Run tests
      run: |
        export KUBERNETES_CONFORMANCE_TEST='y'
        export E2E_REPORT_DIR=${PWD}/_artifacts

        # Run tests and use aws (creates a null e2e provider) to not skip the loadbalancer tests
        # "should be able to create LoadBalancer Service without NodePort and change it" : the LB implementation uses haproxy and NodePorts can not forward directly to the Pods
        # "should be able to change the type and ports of a TCP service" : the test expects a connection refused haproxy seems to return EOF
        # "loadbalancer source ranges" : fails on IPv6 only
        /usr/local/bin/ginkgo --nodes=25                \
          --focus="sig-network"     \
          --skip="Feature|Federation|PerformanceDNS|DualStack|Disruptive|Serial|KubeProxy|GCE|Netpol|NetworkPolicy|256.search.list.characters|LoadBalancer.Service.without.NodePort|type.and.ports.of.a.TCP.service|loadbalancer.source.ranges"   \
          /usr/local/bin/e2e.test                       \
          --                                            \
          --kubeconfig=${PWD}/_artifacts/kubeconfig.conf     \
          --provider=aws                                \
          --dump-logs-on-failure=false                  \
          --report-dir=${E2E_REPORT_DIR}                \
          --disable-log-dump=true

    - name: Upload Junit Reports
      if: always()
      uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v5
      with:
        name: kind-junit-${{ env.JOB_NAME }}-${{ github.run_id }}
        path: './_artifacts/*.xml'

    - name: Export logs
      if: always()
      run: |
        /usr/local/bin/kind export logs --name ${{ env.KIND_CLUSTER_NAME}} ./_artifacts/logs
        cp ./_artifacts/ccm-kind.log ./_artifacts/logs
        cp ./_artifacts/loadbalancers/* ./_artifacts/logs

    - name: Upload logs
      if: always()
      uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v5
      with:
        name: kind-logs-${{ env.JOB_NAME }}-${{ github.run_id }}
        path: ./_artifacts/logs

    - name: Publish Test Report
      uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # v6
      if: always()
      with:
        report_paths: './_artifacts/*.xml'


================================================
FILE: .github/workflows/release.yml
================================================
# .github/workflows/release.yml
name: goreleaser

on:
  push:
    # run only against tags
    tags:
      - "*"

permissions:
  contents: write
  packages: write
  # issues: write

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
      - name: Set up Go
        uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
        with:
          go-version: stable
      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
        with:
          distribution: goreleaser
          # 'latest', 'nightly', or a semver
          version: latest
          args: release --clean
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .github/workflows/test.yaml
================================================
name: Test

on: [push, pull_request]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: sigs.k8s.io/cloud-provider-kind

permissions: write-all

jobs:
  test:
    strategy:
      fail-fast: false
    runs-on: ubuntu-latest
    steps:
    - name: Set up Go
      uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
      with:
        go-version: stable
    - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
    - run: make test
    - run: make lint



================================================
FILE: .gitignore
================================================
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
bin/
dist/

# macOS
.DS_Store


================================================
FILE: .golangci.yaml
================================================
version: "2"
run:
  tests: false
linters:
  default: none
  enable:
    - errcheck
    - gocritic
    - govet
    - ineffassign
    - staticcheck
  exclusions:
    generated: lax
    presets:
      - comments
      - common-false-positives
      - legacy
      - std-error-handling
    paths:
      - third_party$
      - builtin$
      - examples$
formatters:
  exclusions:
    generated: lax
    paths:
      - third_party$
      - builtin$
      - examples$


================================================
FILE: .goreleaser.yaml
================================================
version: 2
project_name: cloud-provider-kind
builds:
  - env: [CGO_ENABLED=0]
    goos:
      - linux
      - windows
      - darwin
    goarch:
      - amd64
      - arm64


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing Guidelines

Welcome 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:

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

## Getting Started

We have full documentation on how to get started contributing here:

<!---
If your repo has certain guidelines for contribution, put them here ahead of the general k8s resources
-->

- [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
- [Kubernetes Contributor Guide](https://k8s.dev/guide) - Main contributor documentation, or you can just jump directly to the [contributing page](https://k8s.dev/docs/guide/contributing/)
- [Contributor Cheat Sheet](https://k8s.dev/cheatsheet) - Common resources for existing developers

## Mentorship

- [Mentoring Initiatives](https://k8s.dev/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers!

- [Slack channel](https://kubernetes.slack.com/messages/kind)
- [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-testing)


================================================
FILE: Dockerfile
================================================
FROM --platform=$BUILDPLATFORM golang:1.25
WORKDIR /go/src
# make deps fetching cacheable
COPY go.mod go.sum ./
RUN go mod download
# build
COPY . .
ARG TARGETARCH
RUN GOARCH=$TARGETARCH make build

# build real cloud-provider-kind image
FROM docker:29-cli
COPY --from=0 --chown=root:root ./go/src/bin/cloud-provider-kind /bin/cloud-provider-kind
ENTRYPOINT ["/bin/cloud-provider-kind"]


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "{}"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright {yyyy} {name of copyright owner}

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: Makefile
================================================
REPO_ROOT:=${CURDIR}
OUT_DIR=$(REPO_ROOT)/bin
KIND_CLOUD_BINARY_NAME?=cloud-provider-kind

# go1.9+ can autodetect GOROOT, but if some other tool sets it ...
GOROOT:=
# enable modules
GO111MODULE=on
# disable CGO by default for static binaries
CGO_ENABLED=0
export GOROOT GO111MODULE CGO_ENABLED


build:
	go build -v -o "$(OUT_DIR)/$(KIND_CLOUD_BINARY_NAME)" $(KIND_CLOUD_BUILD_FLAGS) main.go

clean:
	rm -rf "$(OUT_DIR)/"

test:
	CGO_ENABLED=1 go test -v -race -count 1 ./...

e2e:
	cd tests && bats tests.bats

# code linters
lint:
	hack/lint.sh

update:
	go mod tidy

# get image name from directory we're building
IMAGE_NAME?=cloud-provider-kind
# docker image registry, default to upstream
REGISTRY?=gcr.io/k8s-staging-kind
# tag based on date-sha
TAG?=$(shell echo "$$(date +v%Y%m%d)-$$(git describe --always --dirty)")
# the full image tag
CPK_IMAGE?=$(REGISTRY)/$(IMAGE_NAME):$(TAG)
PLATFORMS?=linux/amd64,linux/arm64

.PHONY: ensure-buildx
ensure-buildx:
	./hack/init-buildx.sh

image-build:
	docker buildx build . \
		--tag="${CPK_IMAGE}" \
		--load

image-push:
	docker buildx build . \
		--platform="${PLATFORMS}" \
		--tag="${CPK_IMAGE}" \
		--push

.PHONY: release # Build a multi-arch docker image
release: ensure-buildx image-push


================================================
FILE: OWNERS
================================================
# See the OWNERS docs at https://go.k8s.io/owners

approvers:
  - aojea
  - bentheelder
  - stmcginnis


================================================
FILE: README.md
================================================
# Kubernetes Cloud Provider for KIND

KIND has demonstrated to be a very versatile, efficient, cheap and very useful tool for Kubernetes testing. However, KIND doesn't offer capabilities for testing all the features that depend on cloud-providers, specifically the Load Balancers, causing a gap on testing and a bad user experience, since is not easy to connect to the applications running on the cluster.

`cloud-provider-kind` aims to fill this gap and provide an agnostic and cheap solution for all the Kubernetes features that depend on a cloud-provider using KIND.

- [Slack channel](https://kubernetes.slack.com/messages/kind)
- [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-testing)

## Talks

Kubecon EU 2024 - [Keep Calm and Load Balance on KIND - Antonio Ojea & Benjamin Elder, Google](https://sched.co/1YhhY)

[![Keep Calm and Load Balance on KIND](https://img.youtube.com/vi/U6_-y24rJnI/0.jpg)](https://www.youtube.com/watch?v=U6_-y24rJnI)

## Install

### Installing with `go install`

You can install `cloud-provider-kind` using `go install`:

```sh
go install sigs.k8s.io/cloud-provider-kind@latest
```

This will install the binary in `$GOBIN` (typically `~/go/bin`); you
can make it available elsewhere if appropriate:

```sh
sudo install ~/go/bin/cloud-provider-kind /usr/local/bin
```

### Installing With A Package Manager

The cloud-provider-kind community has enabled installation via the following package managers.

> [!NOTE]
> The following are community supported efforts. The `cloud-provider-kind` maintainers are not involved in the creation of these packages, and the upstream community makes no claims on the validity, safety, or content of them.

On macOS via Homebrew:

```sh
brew install cloud-provider-kind
```

### Running via Docker Image

Starting with v0.4.0, the docker image for cloud-provider-kind is available
at `registry.k8s.io/cloud-provider-kind/cloud-controller-manager`

You can also build it locally:

```sh
git clone https://github.com/kubernetes-sigs/cloud-provider-kind.git
Cloning into 'cloud-provider-kind'...
remote: Enumerating objects: 6779, done.
remote: Counting objects: 100% (6779/6779), done.
remote: Compressing objects: 100% (4225/4225), done.q
remote: Total 6779 (delta 2150), reused 6755 (delta 2135), pack-reused 0
Receiving objects: 100% (6779/6779), 9.05 MiB | 1.83 MiB/s, done.
Resolving deltas: 100% (2150/2150), done.

cd cloud-provider-kind && make
sudo mv ./bin/cloud-provider-kind  /usr/local/bin/cloud-provider-kind
```

Another alternative is to run it as a container, but this will require to mount
the docker socket inside the container:

```sh
docker build . -t cloud-provider-kind
# using the host network
docker run --rm --network host -v /var/run/docker.sock:/var/run/docker.sock cloud-provider-kind
# or the kind network
docker run --rm --network kind -v /var/run/docker.sock:/var/run/docker.sock cloud-provider-kind
```

Or using `compose.yaml` file:

```sh
# using the `kind` network (`host` is the default value for NET_MODE)
NET_MODE=kind docker compose up -d
```

## Gateway API support

This provider has support for the [Gateway API](https://gateway-api.sigs.k8s.io/).
It implements the `Gateway` and `HTTPRoute` functionalities and passes the community conformance tests.

The Gateway API controller is enabled by default using the standard channel,
but you can select the Gateway API release channel (standard/experimental) or just disable the feature completely
using the flag `gateway-channel`:

```sh
cloud-provider-kind --gateway-channel standard|experimental|disabled
```

## How to use it

Run a KIND cluster:

```sh
$ kind create cluster
Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.26.0) 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Set kubectl context to "kind-kind"
You can now use your cluster with:

kubectl cluster-info --context kind-kind

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂

```

### Allowing load balancers access to control plane nodes

By default, [Kubernetes expects workloads will not run on control plane nodes](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/#control-plane-node-isolation)
and labels them with [`node.kubernetes.io/exclude-from-external-load-balancers`](https://kubernetes.io/docs/reference/labels-annotations-taints/#node-kubernetes-io-exclude-from-external-load-balancers),
which stops load balancers from accessing them.

If you are running workloads on control plane nodes, as is the [default kind configuration](https://kind.sigs.k8s.io/docs/user/configuration/#nodes),
you will need to remove this label to access them using a LoadBalancer:

```sh
$ kubectl label node kind-control-plane node.kubernetes.io/exclude-from-external-load-balancers-
```

### Running the provider

Once the cluster is running, we need to run the `cloud-provider-kind` in a terminal and keep it running. The `cloud-provider-kind` will monitor all your KIND clusters and `Services` with Type `LoadBalancer` and create the corresponding LoadBalancer containers that will expose those Services.

```sh
bin/cloud-provider-kind
I0416 19:58:18.391222 2526219 controller.go:98] Creating new cloud provider for cluster kind
I0416 19:58:18.398569 2526219 controller.go:105] Starting service controller for cluster kind
I0416 19:58:18.399421 2526219 controller.go:227] Starting service controller
I0416 19:58:18.399582 2526219 shared_informer.go:273] Waiting for caches to sync for service
I0416 19:58:18.500460 2526219 shared_informer.go:280] Caches are synced for service
...
```

### Configuring Proxy Image Registry

> [!WARNING]
> The proxy image is an implementation detail of `cloud-provider-kind` and it is not guaranteed to be stable.
> Changing the image version or tag is not supported and may break the cloud provider.
> Use the following instructions only for mirroring the image to a private registry.

You can check the image used by the current version of `cloud-provider-kind` running:

```sh
bin/cloud-provider-kind list-images
```

If you need to mirror the image to a private registry, you can override the registry URL using the `CLOUD_PROVIDER_KIND_REGISTRY_URL` environment variable.
This will use the same image name and tag but with the specified registry.

Example of use mirror registry:

```sh
CLOUD_PROVIDER_KIND_REGISTRY_URL="<your-mirror-registry-url>" bin/cloud-provider-kind
```

### Creating a Service and exposing it via a LoadBalancer

Let's create an application that listens on port 8080 and expose it in the port 80 using a LoadBalancer.

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: policy-local
  labels:
    app: MyLocalApp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: MyLocalApp
  template:
    metadata:
      labels:
        app: MyLocalApp
    spec:
      containers:
        - name: agnhost
          image: registry.k8s.io/e2e-test-images/agnhost:2.40
          args:
            - netexec
            - --http-port=8080
            - --udp-port=8080
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: lb-service-local
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app: MyLocalApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
```

```sh
$ kubectl apply -f examples/loadbalancer_etp_local.yaml
deployment.apps/policy-local created
service/lb-service-local created
$ kubectl get service/lb-service-local
NAME               TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
lb-service-local   LoadBalancer   10.96.207.137   192.168.8.7   80:31215/TCP   57s
```

We can see how the `EXTERNAL-IP` field contains an IP, and we can use it to connect to our
application.

```
$ curl  192.168.8.7:80/hostname
policy-local-59854877c9-xwtfk

$  kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
policy-local-59854877c9-xwtfk   1/1     Running   0          2m38s
```

### Creating a Gateway and a HTTPRoute

Similar to Services with LoadBalancers we can use Gateway API

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: prod-web
spec:
  gatewayClassName: cloud-provider-kind
  listeners:
  - protocol: HTTP
    port: 80
    name: prod-web-gw
    allowedRoutes:
      namespaces:
        from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: foo
spec:
  parentRefs:
  - name: prod-web
  rules:
  - backendRefs:
    - name: myapp-svc
      port: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  selector:
    matchLabels:
      app: MyApp
  replicas: 1
  template:
    metadata:
      labels:
        app: MyApp
    spec:
      containers:
      - name: myapp
        image: registry.k8s.io/e2e-test-images/agnhost:2.39
        args:
          - netexec
          - --http-port=80
          - --delay-shutdown=30
        ports:
          - name: httpd
            containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: myapp-svc
spec:
  type: ClusterIP
  selector:
    app: MyApp
  ports:
    - name: httpd
      port: 8080
      targetPort: 80
```

We can get the external IP associated to the gateway:

```sh
 kubectl get gateway
NAME       CLASS                 ADDRESS       PROGRAMMED   AGE
prod-web   cloud-provider-kind   192.168.8.5   True         3d21h
```

and the HTTPRoutes

```sh
kubectl get httproutes
NAME   HOSTNAMES   AGE
foo                3d21h
```

and test that works:

```sh
$ curl 192.168.8.5/hostname
myapp-7dcffbf547-9kl2d
```

### Enabling Load Balancer Port Mapping

When running `cloud-provider-kind` in a container on Windows or macOS, accessing
`LoadBalancer` services can be challenging. Similar problems occur when running
Podman as root, since Podman does not allow binding to privileged ports (e.g.,
1-1024). The `--enable-lb-port-mapping` flag provides a solution by enabling the
necessary port mapping, allowing host access to these services. It is
automatically enabled on platforms where this is required. See [Mac, Windows and
WSL2 support](#mac-windows-and-wsl2-support) section for more details.

To connect to your service in these cases, run `cloud-provider-kind` with the
`--enable-lb-port-mapping` option. This configures the Envoy container with an
ephemeral host port that maps to the port the `LoadBalancer`'s external IP is
listening on.

```sh
bin/cloud-provider-kind --enable-lb-port-mapping
```

For example, given a `LoadBalancer` listening on port `5678`.

```sh
> kubectl get service
NAME          TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
foo-service   LoadBalancer   10.96.240.105   10.89.0.10    5678:31889/TCP   14m
```

The Envoy container will have an ephemeral port (e.g., `42381`) mapped to the
`LoadBalancer`'s port `5678`.

```sh
> podman ps
CONTAINER ID  IMAGE                                                                                           COMMAND               CREATED         STATUS         PORTS                                              NAMES
d261abc4b540  docker.io/envoyproxy/envoy:v1.30.1                                                              bash -c echo -en ...  21 seconds ago  Up 22 seconds  0.0.0.0:42381->5678/tcp, 0.0.0.0:36673->10000/tcp  kindccm-TLRDKPBWWH4DUSI7J7BNE3ABETEPCKSYA6UIWR5B
```

Use this ephemeral port to connect to the service.

```sh
curl localhost:42381
```

### Mac, Windows and WSL2 support

Mac and Windows run the containers inside a VM and, on the contrary to Linux, the KIND nodes are not reachable from the host,
so the LoadBalancer assigned IP is not working for users.

To solve this problem, cloud-provider-kind, leverages the existing docker portmap capabilities to expose the Loadbalancer IP and Ports
on the host. When you start cloud-provider-kind and create a LoadBalancer service, you will notice that a container named `kindccm-...` is launched within Docker. You can access the service by using the port exposed from the container to the host machine with `localhost`.

For WSL2, it is recommended to use the default [NAT](https://learn.microsoft.com/en-us/windows/wsl/networking) network mode with Docker Desktop on Windows. On WSL2, you can access the service via the external IP. On Windows, you can access the service by leveraging Docker's portmap capabilities.

Limitations:

- Mutation of Services, adding or removing ports to an existing Services, is not supported.
- cloud-provider-kind binary needs permissions to add IP address to interfaces and to listen on privileged ports.
- Overlapping IP between the containers and the host can break connectivity.

Mainly tested with `docker` and `Linux`, though `Windows`, `Mac` and `WSL2` are also basically supported:

- On macOS and WSL2 you must run cloud-provider-kind using `sudo`
- On Windows you must run cloud-provider-kind from a shell that uses `Run as administrator`
- Further feedback from users will be helpful to support other related platforms.

**Note**

The project is still in very alpha state, bugs are expected, please report them back opening a Github issue.

### Code of conduct

Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).

[owners]: https://git.k8s.io/community/contributors/guide/owners.md
[Creative Commons 4.0]: https://git.k8s.io/website/LICENSE


================================================
FILE: SECURITY.md
================================================
# Security Policy

## Security Announcements

Join the [kubernetes-security-announce] group for security and vulnerability announcements.

## Reporting a Vulnerability

Instructions for reporting a vulnerability can be found on the
[Kubernetes Security and Disclosure Information] page.

## Supported Versions

Information about supported Kubernetes versions can be found on the
[Kubernetes version and version skew support policy] page on the Kubernetes website.

[kubernetes-security-announce]: https://groups.google.com/forum/#!forum/kubernetes-security-announce
[Kubernetes version and version skew support policy]: https://kubernetes.io/docs/setup/release/version-skew-policy/#supported-versions
[Kubernetes Security and Disclosure Information]: https://kubernetes.io/docs/reference/issues-security/security/#report-a-vulnerability


================================================
FILE: SECURITY_CONTACTS
================================================
# Defined below are the security contacts for this repo.
#
# They are the contact point for the Security Response Committee to reach out
# to for triaging and handling of incoming issues.
#
# The below names agree to abide by the
# [Embargo Policy](https://git.k8s.io/security/private-distributors-list.md#embargo-policy)
# and will be removed and replaced if they violate that agreement.
#
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
# INSTRUCTIONS AT https://kubernetes.io/security/

aojea
bentheelder


================================================
FILE: cloudbuild.yaml
================================================
# See https://cloud.google.com/cloud-build/docs/build-config
options:
  substitution_option: ALLOW_LOOSE
  machineType: E2_HIGHCPU_32
steps:
- name: gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20240718-5ef92b5c36
  entrypoint: make
  env:
  - REGISTRY=us-central1-docker.pkg.dev/k8s-staging-images/cloud-provider-kind
  - IMAGE_NAME=cloud-controller-manager
  args: ['release']


================================================
FILE: cmd/app.go
================================================
package cmd

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"strings"
	"syscall"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"k8s.io/component-base/logs"
	"k8s.io/klog/v2"

	"sigs.k8s.io/cloud-provider-kind/pkg/config"
	"sigs.k8s.io/cloud-provider-kind/pkg/container"
	"sigs.k8s.io/cloud-provider-kind/pkg/controller"
	"sigs.k8s.io/kind/pkg/cluster"
	kindcmd "sigs.k8s.io/kind/pkg/cmd"
)

var (
	flagV                int
	enableLogDump        bool
	logDumpDir           string
	enableLBPortMapping  bool
	gatewayChannel       string
	enableDefaultIngress bool
)

func Main() {
	cmd := NewCommand()
	if err := cmd.Execute(); err != nil {
		os.Exit(1)
	}
}

func NewCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "cloud-provider-kind",
		Short: "cloud-provider-kind is a cloud provider for kind clusters",
		RunE:  runE,
		// We silence usage on error because we don't want to print usage
		// when the command fails due to a runtime error.
		SilenceUsage: true,
	}

	// Register flags
	cmd.Flags().IntVarP(&flagV, "verbosity", "v", 2, "Verbosity level")
	cmd.Flags().BoolVar(&enableLogDump, "enable-log-dumping", false, "store logs to a temporal directory or to the directory specified using the logs-dir flag")
	cmd.Flags().StringVar(&logDumpDir, "logs-dir", "", "store logs to the specified directory")
	cmd.Flags().BoolVar(&enableLBPortMapping, "enable-lb-port-mapping", false, "enable port-mapping on the load balancer ports")
	cmd.Flags().StringVar(&gatewayChannel, "gateway-channel", "standard", "define the gateway API release channel to be used (standard, experimental, disabled), by default is standard")
	cmd.Flags().BoolVar(&enableDefaultIngress, "enable-default-ingress", true, "enable default ingress for the cloud provider kind ingress")


	cmd.AddCommand(newListImagesCommand())

	return cmd
}

func newListImagesCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "list-images",
		Short: "list images used by cloud-provider-kind",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println(config.DefaultConfig.ProxyImage)
		},
	}
}

func runE(cmd *cobra.Command, args []string) error {
	// Log flags
	cmd.Flags().VisitAll(func(flag *pflag.Flag) {
		klog.Infof("FLAG: --%s=%q", flag.Name, flag.Value)
	})

	// Process on macOS must run using sudo
	if runtime.GOOS == "darwin" && syscall.Geteuid() != 0 {
		return fmt.Errorf("please run this again with `sudo`")
	}

	// trap Ctrl+C and call cancel on the context
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)

	// Enable signal handler
	signalCh := make(chan os.Signal, 2)
	defer func() {
		close(signalCh)
		cancel()
	}()

	signal.Notify(signalCh, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		select {
		case <-signalCh:
			klog.Infof("Exiting: received signal")
			cancel()
		case <-ctx.Done():
			// cleanup
		}
	}()

	// initialize loggers, kind logger and klog
	logger := kindcmd.NewLogger()
	type verboser interface {
		SetVerbosity(int)
	}
	v, ok := logger.(verboser)
	if ok {
		v.SetVerbosity(flagV)
	}

	_, err := logs.GlogSetter(strconv.Itoa(flagV))
	if err != nil {
		logger.Errorf("error setting klog verbosity to %d : %v", flagV, err)
	}

	config.DefaultConfig.IngressDefault = enableDefaultIngress

	// Validate gateway channel
	channel := config.GatewayReleaseChannel(gatewayChannel)
	if channel != config.Standard && channel != config.Experimental && channel != config.Disabled {
		return fmt.Errorf("unknown Gateway API release channel %q", gatewayChannel)
	}
	config.DefaultConfig.GatewayReleaseChannel = channel

	// initialize log directory
	if enableLogDump {
		if logDumpDir == "" {
			dir, err := os.MkdirTemp(os.TempDir(), "kind-provider-")
			if err != nil {
				return err
			}
			logDumpDir = dir
		}

		if _, err := os.Stat(logDumpDir); os.IsNotExist(err) {
			if err := os.MkdirAll(logDumpDir, 0755); err != nil {
				return fmt.Errorf("directory %s does not exist: %v", logDumpDir, err)
			}
		}
		config.DefaultConfig.EnableLogDump = true
		config.DefaultConfig.LogDir = logDumpDir
		klog.Infof("**** Dumping load balancers logs to: %s", logDumpDir)
	}

	// some platforms require to enable tunneling for the LoadBalancers
	if runtime.GOOS == "darwin" || runtime.GOOS == "windows" || isWSL2() {
		config.DefaultConfig.LoadBalancerConnectivity = config.Tunnel
	}

	// flag overrides autodetection
	if enableLBPortMapping {
		config.DefaultConfig.LoadBalancerConnectivity = config.Portmap
	}

	// default control plane connectivity to portmap, it will be
	// overriden if the first cluster added detects direct
	// connecitivity
	config.DefaultConfig.ControlPlaneConnectivity = config.Portmap

	// initialize kind provider
	var option cluster.ProviderOption
	switch p := container.Runtime(); p {
	case "podman":
		option = cluster.ProviderWithPodman()
	case "nerdctl", "finch", "nerdctl.lima":
		option = cluster.ProviderWithNerdctl(p)
	default:
		option = cluster.ProviderWithDocker()
	}
	kindProvider := cluster.NewProvider(
		option,
		cluster.ProviderWithLogger(logger),
	)
	controller.New(kindProvider).Run(ctx)
	return nil
}

func isWSL2() bool {
	if v, err := os.ReadFile("/proc/version"); err == nil {
		return strings.Contains(string(v), "WSL2")
	}

	return false
}


================================================
FILE: code-of-conduct.md
================================================
# Kubernetes Community Code of Conduct

Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)


================================================
FILE: compose.yml
================================================
services:
  cloud-provider:
    build: .
    network_mode: "${NET_MODE-host}"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock


================================================
FILE: docs/.nojekyll
================================================


================================================
FILE: docs/HOWTO.md
================================================
# Documentation Guide

This documentation site is generated and served using **Docsify**, a lightweight documentation site generator that renders Markdown files on the fly.

## Serving the Docsify Site

To run the documentation locally:

```bash
docsify serve ./docs
```

This command starts a local server and serves the documentation from the current directory.
Open the provided URL in your browser to view the site.

---

## Adding a New Link to the Sidebar

The sidebar content is managed by the `_sidebar.md` file.
To add a new link:

1. Open `_sidebar.md`
2. Insert a new list item pointing to your Markdown file, for example:

```markdown
- [Install Docker](user/install/install_docker.md)
- [Gateway API Overview](user/gateway/gatewayapi.md)
```

Docsify will automatically load the linked page when clicked.

---

## Adding a New Page

To create a new documentation page:

1. Add a new `.md` file under the appropriate folder.
   Example:

```
user/example/new_feature_guide.md
```

2. Add a link to this file in `_sidebar.md` so it appears in navigation.

---

## Folder Structure Overview

Below is the current folder hierarchy used for organizing the docs:

```
├── code-of-conduct.md               # Code of Conduct guidelines
├── contributing/
│   └── CONTRIBUTING.md              # Contribution guide
├── _coverpage.md                    # Docsify cover page configuration
├── design/
│   └── images                       # Design-related assets
├── HOWTO.md                         # General how-to guide
├── index.html                       # Docsify entry point
├── README.md                        # Project introduction
├── _sidebar.md                      # Sidebar navigation config
└── user/
    ├── example/                     # Example implementation guides
    ├── gateway/                     # Gateway-related configuration guides
    ├── howto.md                     # Additional user how-tos
    ├── images/                      # Image assets for user docs
    ├── ingress/                     # Ingress-related guides
    ├── install/                     # Installation instructions
    ├── os_support.md                # OS support documentation
    └── support/                     # Support-related docs
```

This structure helps keep the content organized by feature areas and documentation types.

---

## Further Configuration

For more advanced configuration, theming, plugins, and deployment options, please refer to the official Docsify documentation:

[https://docsify.js.org](https://docsify.js.org)



================================================
FILE: docs/README.md
================================================
# Kubernetes Cloud Provider for KIND

KIND has demonstrated to be a very versatile, efficient, cheap and very useful tool for Kubernetes testing. However, KIND doesn't offer capabilities for testing all the features that depend on cloud-providers, specifically the Load Balancers, causing a gap on testing and a bad user experience, since is not easy to connect to the applications running on the cluster.

`cloud-provider-kind` aims to fill this gap and provide an agnostic and cheap solution for all the Kubernetes features that depend on a cloud-provider using KIND.

- [Slack channel](https://kubernetes.slack.com/messages/kind)
- [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-testing)

## Talks

Kubecon EU 2024 - [Keep Calm and Load Balance on KIND - Antonio Ojea & Benjamin Elder, Google](https://sched.co/1YhhY)

[![Keep Calm and Load Balance on KIND](https://img.youtube.com/vi/U6_-y24rJnI/0.jpg)](https://www.youtube.com/watch?v=U6_-y24rJnI)



### Code of conduct

Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).

[owners]: https://git.k8s.io/community/contributors/guide/owners.md
[Creative Commons 2.0]: https://git.k8s.io/website/LICENSE


================================================
FILE: docs/_coverpage.md
================================================
# Kubernetes Cloud Provider for KIND <small></small>

> cloud-provider-kind aims to fill this gap and provide an agnostic and cheap solution for all the Kubernetes features that depend on a cloud-provider using KIND.


[GitHub](//github.com/kubernetes-sigs/cloud-provider-kind)
[Get Started](#main)

================================================
FILE: docs/_sidebar.md
================================================
- Install 

  - [Installation with go ](user/install/install_go.md)
  - [Running with docker](user/install/install_docker.md)
- Ingress
  - [Setting up Ingress](user/ingress/ingress.md)
- Gateway API 
  - [Gateway API Support (Alpha)](user/gateway/gatewayapi.md)
  - [LoadBalancer Access to ControlPlane](user/gateway/allow_load_balancer_access_control_plane.md)
  - [Running the Provider](user/gateway/running_the_provider.md)
  - [Configuring Proxy Image Registry](user/gateway/configure_proxy_image_registry.md)

- Usage Examples
  - [Creating service via a LoadBalancer](user/example/service_expose_via_loadbalancer.md)
  - [Creating a Gateway and a HTTPRoute](user/example/creating_gateway_http_route.md.md)
  - [Enabling Load Balancer Port Mapping](user/example/enable_lb_port_mapping.md)

- Support
  - [Mac, Windows and WSL2 support](user/support/os_support.md)

- Contributing
  - [Contribution Guide Lines](contributing/CONTRIBUTING.md)


================================================
FILE: docs/code-of-conduct.md
================================================
# Kubernetes Community Code of Conduct

Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)


================================================
FILE: docs/contributing/CONTRIBUTING.md
================================================
# Contributing Guidelines

Welcome 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:

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

## Getting Started

We have full documentation on how to get started contributing here:

<!---
If your repo has certain guidelines for contribution, put them here ahead of the general k8s resources
-->

- [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
- [Kubernetes Contributor Guide](https://k8s.dev/guide) - Main contributor documentation, or you can just jump directly to the [contributing page](https://k8s.dev/docs/guide/contributing/)
- [Contributor Cheat Sheet](https://k8s.dev/cheatsheet) - Common resources for existing developers

## Mentorship

- [Mentoring Initiatives](https://k8s.dev/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers!

- [Slack channel](https://kubernetes.slack.com/messages/kind)
- [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-testing)


================================================
FILE: docs/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Kubernetes Cloud Provider for KIND</title>
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <meta name="description" content="cloud-provider-kind` aims to fill this gap and provide an agnostic and cheap solution for all the Kubernetes features that depend on a cloud-provider using KIND.">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">


  <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
  <link 
    rel="stylesheet"
    href="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/style.min.css"
    title="docsify-darklight-theme"
    type="text/css"
/>
</head>
<body>
  <div id="app"></div>
  <script>
    window.$docsify = {
      name: 'Kubernetes Cloud Provider for KIND',
      
      repo: 'https://github.com/kubernetes-sigs/cloud-provider-kind',
      loadSidebar:true,
      auto2top: true,
      coverpage: true,
      search: 'auto',
        darklightTheme: {
          defaultTheme : 'light',
        }
      // autoHeader: true,
    }
  </script>
  <!-- Docsify v4 -->
  <script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
  <script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
  <script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
  <script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
  <script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-yaml.min.js"></script>

  <script 
    src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js"
    type="text/javascript">
</script>
</body>
</html>


================================================
FILE: docs/user/example/creating_gateway_http_route.md
================================================
### Creating a Gateway and a HTTPRoute

Similar to Services with LoadBalancers we can use Gateway API

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: prod-web
spec:
  gatewayClassName: cloud-provider-kind
  listeners:
  - protocol: HTTP
    port: 80
    name: prod-web-gw
    allowedRoutes:
      namespaces:
        from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: foo
spec:
  parentRefs:
  - name: prod-web
  rules:
  - backendRefs:
    - name: myapp-svc
      port: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  selector:
    matchLabels:
      app: MyApp
  replicas: 1
  template:
    metadata:
      labels:
        app: MyApp
    spec:
      containers:
      - name: myapp
        image: registry.k8s.io/e2e-test-images/agnhost:2.39
        args:
          - netexec
          - --http-port=80
          - --delay-shutdown=30
        ports:
          - name: httpd
            containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: myapp-svc
spec:
  type: ClusterIP
  selector:
    app: MyApp
  ports:
    - name: httpd
      port: 8080
      targetPort: 80
```

We can get the external IP associated to the gateway:

```sh
 kubectl get gateway
NAME       CLASS                 ADDRESS       PROGRAMMED   AGE
prod-web   cloud-provider-kind   192.168.8.5   True         3d21h
```

and the HTTPRoutes

```sh
kubectl get httproutes
NAME   HOSTNAMES   AGE
foo                3d21h
```

and test that works:

```sh
$ curl 192.168.8.5/hostname
myapp-7dcffbf547-9kl2d
```


================================================
FILE: docs/user/example/enable_lb_port_mapping.md
================================================
### Enabling Load Balancer Port Mapping

When running `cloud-provider-kind` in a container on Windows or macOS, accessing
`LoadBalancer` services can be challenging. Similar problems occur when running
Podman as root, since Podman does not allow binding to privileged ports (e.g.,
1-1024). The `-enable-lb-port-mapping` flag provides a solution by enabling the
necessary port mapping, allowing host access to these services.

To connect to your service in these cases, run `cloud-provider-kind` with the
`-enable-lb-port-mapping` option. This configures the Envoy container with an
ephemeral host port that maps to the port the `LoadBalancer`'s external IP is
listening on.

```
bin/cloud-provider-kind -enable-lb-port-mapping
```

For example, given a `LoadBalancer` listening on port `5678`.

```
> kubectl get service
NAME          TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
foo-service   LoadBalancer   10.96.240.105   10.89.0.10    5678:31889/TCP   14m
```

The Envoy container will have an ephemeral port (e.g., `42381`) mapped to the
`LoadBalancer`'s port `5678`.

```
> podman ps
CONTAINER ID  IMAGE                                                                                           COMMAND               CREATED         STATUS         PORTS                                              NAMES
d261abc4b540  docker.io/envoyproxy/envoy:v1.30.1                                                              bash -c echo -en ...  21 seconds ago  Up 22 seconds  0.0.0.0:42381->5678/tcp, 0.0.0.0:36673->10000/tcp  kindccm-TLRDKPBWWH4DUSI7J7BNE3ABETEPCKSYA6UIWR5B
```

Use this ephemeral port to connect to the service.

```
curl localhost:42381
```


================================================
FILE: docs/user/example/service_expose_via_loadbalancer.md
================================================
### Creating a Service and exposing it via a LoadBalancer

Let's create an application that listens on port 8080 and expose it in the port 80 using a LoadBalancer.

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: policy-local
  labels:
    app: MyLocalApp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: MyLocalApp
  template:
    metadata:
      labels:
        app: MyLocalApp
    spec:
      containers:
        - name: agnhost
          image: registry.k8s.io/e2e-test-images/agnhost:2.40
          args:
            - netexec
            - --http-port=8080
            - --udp-port=8080
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: lb-service-local
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app: MyLocalApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
```

```sh
$ kubectl apply -f examples/loadbalancer_etp_local.yaml
deployment.apps/policy-local created
service/lb-service-local created
$ kubectl get service/lb-service-local
NAME               TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
lb-service-local   LoadBalancer   10.96.207.137   192.168.8.7   80:31215/TCP   57s
```

We can see how the `EXTERNAL-IP` field contains an IP, and we can use it to connect to our
application.

```
$ curl  192.168.8.7:80/hostname
policy-local-59854877c9-xwtfk

$  kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
policy-local-59854877c9-xwtfk   1/1     Running   0          2m38s
```

================================================
FILE: docs/user/gateway/allow_load_balancer_access_control_plane.md
================================================
### Allowing load balancers access to control plane nodes

By default, [Kubernetes expects workloads will not run on control plane nodes](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/#control-plane-node-isolation)
and labels them with [`node.kubernetes.io/exclude-from-external-load-balancers`](https://kubernetes.io/docs/reference/labels-annotations-taints/#node-kubernetes-io-exclude-from-external-load-balancers),
which stops load balancers from accessing them.

If you are running workloads on control plane nodes, as is the [default kind configuration](https://kind.sigs.k8s.io/docs/user/configuration/#nodes),
you will need to remove this label to access them using a LoadBalancer:

```sh
$ kubectl label node kind-control-plane node.kubernetes.io/exclude-from-external-load-balancers-
```


================================================
FILE: docs/user/gateway/configure_proxy_image_registry.md
================================================
### Configuring Proxy Image Registry

> [!WARNING]
> The proxy image is an implementation detail of `cloud-provider-kind` and it is not guaranteed to be stable.
> Changing the image version or tag is not supported and may break the cloud provider.
> Use the following instructions only for mirroring the image to a private registry.

You can check the image used by the current version of `cloud-provider-kind` running:

```sh
bin/cloud-provider-kind list-images
```

If you need to mirror the image to a private registry, you can override the registry URL using the `CLOUD_PROVIDER_KIND_REGISTRY_URL` environment variable.
This will use the same image name and tag but with the specified registry.

Example of use mirror registry:

```sh
CLOUD_PROVIDER_KIND_REGISTRY_URL="<your-mirror-registry-url>" bin/cloud-provider-kind
```

================================================
FILE: docs/user/gateway/gatewayapi.md
================================================
## Gateway API support

This provider has support for the [Gateway API](https://gateway-api.sigs.k8s.io/).
It implements the `Gateway` and `HTTPRoute` functionalities and passes the community conformance tests.

The Gateway API controller is enabled by default using the standard channel,
but you can select the Gateway API release channel (standard/experimental) or just disable the feature completely
using the flag `gateway-channel`:

```sh
cloud-provider-kind --gateway-channel standard|experimental|disabled
```

================================================
FILE: docs/user/gateway/running_the_provider.md
================================================
### Running the provider

Once the cluster is running, we need to run the `cloud-provider-kind` in a terminal and keep it running. The `cloud-provider-kind` will monitor all your KIND clusters and `Services` with Type `LoadBalancer` and create the corresponding LoadBalancer containers that will expose those Services.

```sh
bin/cloud-provider-kind
I0416 19:58:18.391222 2526219 controller.go:98] Creating new cloud provider for cluster kind
I0416 19:58:18.398569 2526219 controller.go:105] Starting service controller for cluster kind
I0416 19:58:18.399421 2526219 controller.go:227] Starting service controller
I0416 19:58:18.399582 2526219 shared_informer.go:273] Waiting for caches to sync for service
I0416 19:58:18.500460 2526219 shared_informer.go:280] Caches are synced for service
...
```

================================================
FILE: docs/user/howto.md
================================================
## How to use it

Run a KIND cluster:

```sh
$ kind create cluster
Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.26.0) 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Set kubectl context to "kind-kind"
You can now use your cluster with:

kubectl cluster-info --context kind-kind

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂

```

================================================
FILE: docs/user/ingress/ingress.md
================================================
## Compatibility:
This guide applies to [cloud-provider-kind](https://github.com/kubernetes-sigs/cloud-provider-kind) v0.9.0+. For older versions, refer to historical docs.

## Setting Up Ingress

Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster.

Since cloud-provider-kind v0.9.0, it natively supports Ingress. No third-party ingress controllers are required by default.

For third-party ingress solutions (e.g., Ingress NGINX, Contour), please follow their official documentation.

> **NOTE**: Gateway API is also natively supported (along with Ingress). See the official [Ingress migration guide](https://gateway-api.sigs.k8s.io/guides/migrating-from-ingress/) for details.

## Create Cluster

> **WARNING**: If you are using a [rootless container runtime], ensure your host is
> properly configured before creating the KIND cluster. Most Ingress and Gateway controllers will
> not work if these steps are skipped.

Create a kind cluster and run [Cloud Provider KIND] that automatically enables LoadBalancer support for Ingress. Create a cluster as follows.

```bash
kind create cluster
```

## Using Ingress

The following example creates simple http-echo services and an Ingress object to route to these services.

```yaml
kind: Pod
apiVersion: v1
metadata:
  name: foo-app
  labels:
    app: foo
spec:
  containers:
  - command:
    - /agnhost
    - serve-hostname
    - --http=true
    - --port=8080
    image: registry.k8s.io/e2e-test-images/agnhost:2.39
    name: foo-app
---
kind: Service
apiVersion: v1
metadata:
  name: foo-service
spec:
  selector:
    app: foo
  ports:
  # Default port used by the image
  - port: 8080
---
kind: Pod
apiVersion: v1
metadata:
  name: bar-app
  labels:
    app: bar
spec:
  containers:
  - command:
    - /agnhost
    - serve-hostname
    - --http=true
    - --port=8080
    image: registry.k8s.io/e2e-test-images/agnhost:2.39
    name: bar-app
---
kind: Service
apiVersion: v1
metadata:
  name: bar-service
spec:
  selector:
    app: bar
  ports:
  # Default port used by the image
  - port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
spec:
  rules:
  - http:
      paths:
      - pathType: Prefix
        path: /foo
        backend:
          service:
            name: foo-service
            port:
              number: 8080
      - pathType: Prefix
        path: /bar
        backend:
          service:
            name: bar-service
            port:
              number: 8080
---
```

Apply the configuration:

```bash
kubectl apply -f https://kind.sigs.k8s.io/examples/ingress/usage.yaml
```

### Verify Ingress Works

Check the External IP assigned to the Ingress by the built-in LoadBalancer.

```bash
kubectl get ingress
NAME              CLASS     HOSTS         ADDRESS        PORTS   AGE
example-ingress   <none>    example.com   172.18.0.5     80      10m
```


# get the Ingress IP

```bash
INGRESS_IP=$(kubectl get ingress example-ingress -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

# should output "foo-app"

curl ${INGRESS_IP}/foo

# should output "bar-app"
curl ${INGRESS_IP}/bar
```

[LoadBalancer]: /docs/user/loadbalancer/
[Cloud Provider KIND]: /docs/user/loadbalancer/
[rootless container runtime]: /docs/user/rootless/

================================================
FILE: docs/user/install/install_docker.md
================================================
### Running via Docker Image

Starting with v0.4.0, the docker image for cloud-provider-kind is available
at `registry.k8s.io/cloud-provider-kind/cloud-controller-manager`

You can also build it locally:

```sh
git clone https://github.com/kubernetes-sigs/cloud-provider-kind.git
Cloning into 'cloud-provider-kind'...
remote: Enumerating objects: 6779, done.
remote: Counting objects: 100% (6779/6779), done.
remote: Compressing objects: 100% (4225/4225), done.q
remote: Total 6779 (delta 2150), reused 6755 (delta 2135), pack-reused 0
Receiving objects: 100% (6779/6779), 9.05 MiB | 1.83 MiB/s, done.
Resolving deltas: 100% (2150/2150), done.

cd cloud-provider-kind && make
sudo mv ./bin/cloud-provider-kind  /usr/local/bin/cloud-provider-kind
```

Another alternative is to run it as a container, but this will require to mount
the docker socket inside the container:

```sh
docker build . -t cloud-provider-kind
# using the host network
docker run --rm --network host -v /var/run/docker.sock:/var/run/docker.sock cloud-provider-kind
# or the kind network
docker run --rm --network kind -v /var/run/docker.sock:/var/run/docker.sock cloud-provider-kind
```

Or using `compose.yaml` file:

```sh
# using the `kind` network (`host` is the default value for NET_MODE)
NET_MODE=kind docker compose up -d
```

================================================
FILE: docs/user/install/install_go.md
================================================
### Installing with `go install`

You can install `cloud-provider-kind` using `go install`:

```sh
go install sigs.k8s.io/cloud-provider-kind@latest
```

This will install the binary in `$GOBIN` (typically `~/go/bin`); you
can make it available elsewhere if appropriate:

```sh
sudo install ~/go/bin/cloud-provider-kind /usr/local/bin
```

### Installing With A Package Manager

The cloud-provider-kind community has enabled installation via the following package managers.

> [!NOTE]
> The following are community supported efforts. The `cloud-provider-kind` maintainers are not involved in the creation of these packages, and the upstream community makes no claims on the validity, safety, or content of them.

On macOS via Homebrew:

```sh
brew install cloud-provider-kind
```

================================================
FILE: docs/user/os_support.md
================================================
### Mac, Windows and WSL2 support

Mac and Windows run the containers inside a VM and, on the contrary to Linux, the KIND nodes are not reachable from the host,
so the LoadBalancer assigned IP is not working for users.

To solve this problem, cloud-provider-kind, leverages the existing docker portmap capabilities to expose the Loadbalancer IP and Ports
on the host. When you start cloud-provider-kind and create a LoadBalancer service, you will notice that a container named `kindccm-...` is launched within Docker. You can access the service by using the port exposed from the container to the host machine with `localhost`.

For WSL2, it is recommended to use the default [NAT](https://learn.microsoft.com/en-us/windows/wsl/networking) network mode with Docker Desktop on Windows. On WSL2, you can access the service via the external IP. On Windows, you can access the service by leveraging Docker's portmap capabilities.

Limitations:

- Mutation of Services, adding or removing ports to an existing Services, is not supported.
- cloud-provider-kind binary needs permissions to add IP address to interfaces and to listen on privileged ports.
- Overlapping IP between the containers and the host can break connectivity.

Mainly tested with `docker` and `Linux`, though `Windows`, `Mac` and `WSL2` are also basically supported:

- On macOS and WSL2 you must run cloud-provider-kind using `sudo`
- On Windows you must run cloud-provider-kind from a shell that uses `Run as administrator`
- Further feedback from users will be helpful to support other related platforms.

================================================
FILE: docs/user/support/os_support.md
================================================
### Mac, Windows and WSL2 support

Mac and Windows run the containers inside a VM and, on the contrary to Linux, the KIND nodes are not reachable from the host,
so the LoadBalancer assigned IP is not working for users.

To solve this problem, cloud-provider-kind, leverages the existing docker portmap capabilities to expose the Loadbalancer IP and Ports
on the host. When you start cloud-provider-kind and create a LoadBalancer service, you will notice that a container named `kindccm-...` is launched within Docker. You can access the service by using the port exposed from the container to the host machine with `localhost`.

For WSL2, it is recommended to use the default [NAT](https://learn.microsoft.com/en-us/windows/wsl/networking) network mode with Docker Desktop on Windows. On WSL2, you can access the service via the external IP. On Windows, you can access the service by leveraging Docker's portmap capabilities.

Limitations:

- Mutation of Services, adding or removing ports to an existing Services, is not supported.
- cloud-provider-kind binary needs permissions to add IP address to interfaces and to listen on privileged ports.
- Overlapping IP between the containers and the host can break connectivity.

Mainly tested with `docker` and `Linux`, though `Windows`, `Mac` and `WSL2` are also basically supported:

- On macOS and WSL2 you must run cloud-provider-kind using `sudo`
- On Windows you must run cloud-provider-kind from a shell that uses `Run as administrator`
- Further feedback from users will be helpful to support other related platforms.

**Note**

The project is still in very alpha state, bugs are expected, please report them back opening a Github issue.


================================================
FILE: examples/gateway_httproute_simple.yaml
================================================
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: prod-web
spec:
  gatewayClassName: cloud-provider-kind
  listeners:
  - protocol: HTTP
    port: 80
    name: prod-web-gw
    allowedRoutes:
      namespaces:
        from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: foo
spec:
  parentRefs:
  - name: prod-web
  rules:
  - backendRefs:
    - name: myapp-svc
      port: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  selector:
    matchLabels:
      app: MyApp
  replicas: 1
  template:
    metadata:
      labels:
        app: MyApp
    spec:
      containers:
      - name: myapp
        image: registry.k8s.io/e2e-test-images/agnhost:2.63.0
        args:
          - netexec
          - --http-port=80
          - --delay-shutdown=30
        ports:
          - name: httpd
            containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: myapp-svc
spec:
  type: ClusterIP
  selector:
    app: MyApp
  ports:
    - name: httpd
      port: 8080
      targetPort: 80


================================================
FILE: examples/ingress_foo_bar.yaml
================================================
kind: Pod
apiVersion: v1
metadata:
  name: foo-app
  labels:
    app: foo
spec:
  containers:
  - command:
    - /agnhost
    - netexec
    - --http-port=8080
    image: registry.k8s.io/e2e-test-images/agnhost:2.63.0
    name: foo-app
---
kind: Service
apiVersion: v1
metadata:
  name: foo-service
spec:
  selector:
    app: foo
  ports:
  - port: 8080
---
kind: Pod
apiVersion: v1
metadata:
  name: bar-app
  labels:
    app: bar
spec:
  containers:
  - command:
    - /agnhost
    - netexec
    - --http-port=8080
    image: registry.k8s.io/e2e-test-images/agnhost:2.63.0
    name: bar-app
---
kind: Service
apiVersion: v1
metadata:
  name: bar-service
spec:
  selector:
    app: bar
  ports:
  - port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
spec:
  rules:
  - host: foo.example.com
    http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: foo-service
            port:
              number: 8080
  - host: bar.example.com
    http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: bar-service
            port:
              number: 8080
---
kind: Pod
apiVersion: v1
metadata:
  name: curl-pod
spec:
  containers:
  - name: curl
    image: registry.k8s.io/e2e-test-images/agnhost:2.63.0


================================================
FILE: examples/loadbalancer_affinity.yaml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: affinity
spec:
  selector:
    matchLabels:
      app: affinity
  replicas: 2
  strategy:
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: affinity
    spec:
      terminationGracePeriodSeconds: 30
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - affinity
            topologyKey: kubernetes.io/hostname
      containers:
      - name: affinity
        image: registry.k8s.io/e2e-test-images/agnhost:2.63.0
        args:
          - netexec
          - --http-port=80
          - --delay-shutdown=30
        ports:
          - name: httpd
            containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: affinity
spec:
  type: LoadBalancer
  sessionAffinity: ClientIP
  selector:
    app: affinity
  ports:
    - name: httpd
      port: 80
      targetPort: 80


================================================
FILE: examples/loadbalancer_deployment.yaml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: zeroapp
spec:
  selector:
    matchLabels:
      app: zeroapp
  replicas: 2
  strategy:
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: zeroapp
    spec:
      terminationGracePeriodSeconds: 30
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - zeroapp
            topologyKey: kubernetes.io/hostname
      containers:
      - name: zeroapp
        image: registry.k8s.io/e2e-test-images/agnhost:2.63.0
        args:
          - netexec
          - --http-port=80
          - --delay-shutdown=30
        ports:
          - name: httpd
            containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: zeroapp
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app: zeroapp
  ports:
    - name: httpd
      port: 80
      targetPort: 80


================================================
FILE: examples/loadbalancer_etp_cluster.yaml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: policy-cluster
  labels:
    app: MyClusterApp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: MyClusterApp
  template:
    metadata:
      labels:
        app: MyClusterApp
    spec:
      containers:
      - name: agnhost
        image: registry.k8s.io/e2e-test-images/agnhost:2.63.0
        args:
          - netexec
          - --http-port=8080
          - --udp-port=8080
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: lb-service-cluster
spec:
  type: LoadBalancer
  externalTrafficPolicy: Cluster
  selector:
    app: MyClusterApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

================================================
FILE: examples/loadbalancer_etp_local.yaml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: policy-local
  labels:
    app: MyLocalApp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: MyLocalApp
  template:
    metadata:
      labels:
        app: MyLocalApp
    spec:
      containers:
      - name: agnhost
        image: registry.k8s.io/e2e-test-images/agnhost:2.63.0
        args:
          - netexec
          - --http-port=8080
          - --udp-port=8080
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: lb-service-local
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app: MyLocalApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

================================================
FILE: examples/loadbalancer_multiport.yaml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: multiport
spec:
  selector:
    matchLabels:
      app: multiport
  replicas: 2
  strategy:
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: multiport
    spec:
      terminationGracePeriodSeconds: 30
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - multiport
            topologyKey: kubernetes.io/hostname
      containers:
      - name: multiport
        image: registry.k8s.io/e2e-test-images/agnhost:2.63.0
        args:
          - porter
        env:
        - name: SERVE_PORT_80
          value: "80"
        - name: SERVE_TLS_PORT_443
          value: "443"
        ports:
          - name: http
            containerPort: 80
          - name: https
            containerPort: 443
---
apiVersion: v1
kind: Service
metadata:
  name: multiport
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app: multiport
  ports:
    - name: http
      port: 80
      targetPort: 80
    - name: https
      port: 443
      targetPort: 443


================================================
FILE: examples/loadbalancer_static_ip.yaml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: static-ip
spec:
  selector:
    matchLabels:
      app: static-ip
  replicas: 1
  strategy:
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: static-ip
    spec:
      terminationGracePeriodSeconds: 30
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values:
                      - static-ip
              topologyKey: kubernetes.io/hostname
      containers:
        - name: static-ip
          image: registry.k8s.io/e2e-test-images/agnhost:2.63.0
          args:
            - netexec
            - --http-port=80
          ports:
            - name: tcp
              containerPort: 80
              protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: static-ip
spec:
  type: LoadBalancer
  loadBalancerIP: REPLACE_WITH_STATIC_IP
  selector:
    app: static-ip
  ports:
    - name: tcp
      port: 80
      targetPort: 80
      protocol: TCP

================================================
FILE: examples/loadbalancer_stts.yaml
================================================
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: apache
spec:
  selector:
    matchLabels:
      app: apache
  serviceName: apache
  replicas: 2
  template:
    metadata:
      labels:
        app: apache
    spec:
      terminationGracePeriodSeconds: 30
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx
            topologyKey: kubernetes.io/hostname
      containers:
      - name: apache
        image: registry.k8s.io/e2e-test-images/agnhost:2.63.0
        args:
          - netexec
          - --http-port=80
          - --delay-shutdown=30
        ports:
          - name: httpd
            containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: apache
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app: apache
  ports:
    - name: httpd
      port: 80
      targetPort: 80


================================================
FILE: examples/loadbalancer_udp_tcp.yaml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: multiprotocol
spec:
  selector:
    matchLabels:
      app: multiprotocol
  replicas: 1
  strategy:
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: multiprotocol
    spec:
      terminationGracePeriodSeconds: 30
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - multiprotocol
            topologyKey: kubernetes.io/hostname
      containers:
      - name: multiprotocol
        image: registry.k8s.io/e2e-test-images/agnhost:2.63.0
        args:
          - netexec
          - --http-port=80
          - --udp-port=80
        ports:
          - name: tcp
            containerPort: 80
            protocol: TCP
          - name: udp
            containerPort: 80
            protocol: UDP
---
apiVersion: v1
kind: Service
metadata:
  name: multiprotocol
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app: multiprotocol
  ports:
    - name: tcp
      port: 80
      targetPort: 80
      protocol: TCP
    - name: udp
      port: 80
      targetPort: 80
      protocol: UDP


================================================
FILE: go.mod
================================================
module sigs.k8s.io/cloud-provider-kind

go 1.25.5

require (
	github.com/envoyproxy/go-control-plane v0.14.0
	github.com/envoyproxy/go-control-plane/envoy v1.37.0
	github.com/google/go-cmp v0.7.0
	github.com/lithammer/dedent v1.1.0
	github.com/pkg/errors v0.9.1
	github.com/spf13/cobra v1.10.2
	github.com/spf13/pflag v1.0.10
	github.com/vishvananda/netlink v1.3.1
	google.golang.org/grpc v1.80.0
	google.golang.org/protobuf v1.36.11
	k8s.io/api v0.35.3
	k8s.io/apimachinery v0.35.3
	k8s.io/apiserver v0.35.3
	k8s.io/client-go v0.35.3
	k8s.io/cloud-provider v0.35.3
	k8s.io/component-base v0.35.3
	k8s.io/controller-manager v0.35.3
	k8s.io/klog/v2 v2.140.0
	k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2
	sigs.k8s.io/gateway-api v1.5.1
	sigs.k8s.io/kind v0.31.0
)

require (
	al.essio.dev/pkg/shellescape v1.6.0 // indirect
	cel.dev/expr v0.25.1 // indirect
	github.com/BurntSushi/toml v1.6.0 // indirect
	github.com/beorn7/perks v1.0.1 // indirect
	github.com/blang/semver/v4 v4.0.0 // indirect
	github.com/cespare/xxhash/v2 v2.3.0 // indirect
	github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect
	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
	github.com/emicklei/go-restful/v3 v3.13.0 // indirect
	github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 // indirect
	github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
	github.com/evanphx/json-patch/v5 v5.9.11 // indirect
	github.com/fxamacker/cbor/v2 v2.9.0 // indirect
	github.com/go-logr/logr v1.4.3 // indirect
	github.com/go-openapi/jsonpointer v0.22.4 // indirect
	github.com/go-openapi/jsonreference v0.21.4 // indirect
	github.com/go-openapi/swag v0.25.4 // indirect
	github.com/go-openapi/swag/cmdutils v0.25.4 // indirect
	github.com/go-openapi/swag/conv v0.25.4 // indirect
	github.com/go-openapi/swag/fileutils v0.25.4 // indirect
	github.com/go-openapi/swag/jsonname v0.25.4 // indirect
	github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
	github.com/go-openapi/swag/loading v0.25.4 // indirect
	github.com/go-openapi/swag/mangling v0.25.4 // indirect
	github.com/go-openapi/swag/netutils v0.25.4 // indirect
	github.com/go-openapi/swag/stringutils v0.25.4 // indirect
	github.com/go-openapi/swag/typeutils v0.25.4 // indirect
	github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
	github.com/google/gnostic-models v0.7.1 // indirect
	github.com/google/uuid v1.6.0 // indirect
	github.com/inconshreveable/mousetrap v1.1.0 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/mattn/go-isatty v0.0.20 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
	github.com/pelletier/go-toml v1.9.5 // indirect
	github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
	github.com/prometheus/client_golang v1.23.2 // indirect
	github.com/prometheus/client_model v0.6.2 // indirect
	github.com/prometheus/common v0.67.5 // indirect
	github.com/prometheus/procfs v0.19.2 // indirect
	github.com/vishvananda/netns v0.0.5 // indirect
	github.com/x448/float16 v0.8.4 // indirect
	go.opentelemetry.io/otel v1.40.0 // indirect
	go.opentelemetry.io/otel/trace v1.40.0 // indirect
	go.yaml.in/yaml/v2 v2.4.3 // indirect
	go.yaml.in/yaml/v3 v3.0.4 // indirect
	golang.org/x/net v0.50.0 // indirect
	golang.org/x/oauth2 v0.35.0 // indirect
	golang.org/x/sys v0.41.0 // indirect
	golang.org/x/term v0.40.0 // indirect
	golang.org/x/text v0.34.0 // indirect
	golang.org/x/time v0.14.0 // indirect
	google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
	google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
	gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
	gopkg.in/inf.v0 v0.9.1 // indirect
	k8s.io/component-helpers v0.35.3 // indirect
	k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 // indirect
	sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
	sigs.k8s.io/randfill v1.0.0 // indirect
	sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
	sigs.k8s.io/yaml v1.6.0 // indirect
)


================================================
FILE: go.sum
================================================
al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA=
al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ=
github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU=
github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ=
github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4=
github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y=
github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk=
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48=
github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg=
github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0=
github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg=
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c=
github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.28.0 h1:Rrf+lVLmtlBIKv6KrIGJCjyY8N36vDVcutbGJkyqjJc=
github.com/onsi/ginkgo/v2 v2.28.0/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.35.3 h1:pA2fiBc6+N9PDf7SAiluKGEBuScsTzd2uYBkA5RzNWQ=
k8s.io/api v0.35.3/go.mod h1:9Y9tkBcFwKNq2sxwZTQh1Njh9qHl81D0As56tu42GA4=
k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8=
k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
k8s.io/apiserver v0.35.3 h1:D2eIcfJ05hEAEewoSDg+05e0aSRwx8Y4Agvd/wiomUI=
k8s.io/apiserver v0.35.3/go.mod h1:JI0n9bHYzSgIxgIrfe21dbduJ9NHzKJ6RchcsmIKWKY=
k8s.io/client-go v0.35.3 h1:s1lZbpN4uI6IxeTM2cpdtrwHcSOBML1ODNTCCfsP1pg=
k8s.io/client-go v0.35.3/go.mod h1:RzoXkc0mzpWIDvBrRnD+VlfXP+lRzqQjCmKtiwZ8Q9c=
k8s.io/cloud-provider v0.35.3 h1:8gmlIXd9LHhDOXfv0fqsM9KIgVbgl3fwtNI2lDL0qWE=
k8s.io/cloud-provider v0.35.3/go.mod h1:bHkYmoCLBoyDkx8r8usg0sOo6PIzYrM5qYuDxCyuUxw=
k8s.io/component-base v0.35.3 h1:mbKbzoIMy7JDWS/wqZobYW1JDVRn/RKRaoMQHP9c4P0=
k8s.io/component-base v0.35.3/go.mod h1:IZ8LEG30kPN4Et5NeC7vjNv5aU73ku5MS15iZyvyMYk=
k8s.io/component-helpers v0.35.3 h1:Rl2p3wNMC0YU21rziLkWXavr7MwkB5Td3lNZ/+gYGm8=
k8s.io/component-helpers v0.35.3/go.mod h1:8BkyfcBA6XsCtFYxDB+mCfZqM6P39Aco12AKigNn0C8=
k8s.io/controller-manager v0.35.3 h1:BlX95jtN41/vCwuTsmfzR9UpqweX7KDWdwm/mRHez/o=
k8s.io/controller-manager v0.35.3/go.mod h1:OaG4bXsMfN5zpqowtdyfoRX20LrfwUh6V0zmpF7hw30=
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 h1:HhDfevmPS+OalTjQRKbTHppRIz01AWi8s45TMXStgYY=
k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
sigs.k8s.io/gateway-api v1.5.1 h1:RqVRIlkhLhUO8wOHKTLnTJA6o/1un4po4/6M1nRzdd0=
sigs.k8s.io/gateway-api v1.5.1/go.mod h1:GvCETiaMAlLym5CovLxGjS0NysqFk3+Yuq3/rh6QL2o=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/kind v0.31.0 h1:UcT4nzm+YM7YEbqiAKECk+b6dsvc/HRZZu9U0FolL1g=
sigs.k8s.io/kind v0.31.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=


================================================
FILE: hack/build-route-adder.sh
================================================
#!/bin/bash

set -o errexit
set -o nounset
set -o pipefail

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
REPO_ROOT=$( cd -- "$SCRIPT_DIR/.." &> /dev/null && pwd )

cd "$REPO_ROOT"

export CGO_ENABLED=0

# Compile for amd64 (x64)
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o ./pkg/gateway/routeadder/route-adder-amd64 ./internal/routeadder/

# Compile for arm64
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o ./pkg/gateway/routeadder/route-adder-arm64 ./internal/routeadder/

# Cleanup is handled by the trap
exit 0


================================================
FILE: hack/ci/add-kubernetes-to-workspace.sh
================================================
#!/usr/bin/env bash

# Copyright 2024 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -e
set -x

REPO_ROOT=$(git rev-parse --show-toplevel)
KUBERNETES_ROOT=${KUBERNETES_ROOT:-$GOPATH/src/k8s.io/kubernetes}
cd ${REPO_ROOT}

# Set up go workspace to build with this version
go work init

go work use .

# Add kubernetes to workspace
go work use ${KUBERNETES_ROOT}
for d in ${KUBERNETES_ROOT}/staging/src/k8s.io/*; do
  go work use $d
done

# Workaround for go.mod replacements
sed -i 's/^\s*k8s.io.*//g' go.mod
go work sync


================================================
FILE: hack/ci/e2e.sh
================================================
#!/bin/sh
# Copyright 2018 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# hack script for running a kind e2e
# must be run with a kubernetes checkout in $PWD (IE from the checkout)
# Usage: SKIP="ginkgo skip regex" FOCUS="ginkgo focus regex" kind-e2e.sh

set -o errexit -o nounset -o xtrace

# Settings:
# SKIP: ginkgo skip regex
# FOCUS: ginkgo focus regex

# cleanup logic for cleanup on exit
CLEANED_UP=false
cleanup() {
  if [ "$CLEANED_UP" = "true" ]; then
    return
  fi
  # KIND_CREATE_ATTEMPTED is true once we: kind create
  if [ "${KIND_CREATE_ATTEMPTED:-}" = true ]; then
    kind "export" logs "${ARTIFACTS}" || true
    kind delete cluster || true
  fi
  rm -f _output/bin/e2e.test || true
  # remove our tempdir, this needs to be last, or it will prevent kind delete
  if [ -n "${TMP_DIR:-}" ]; then
    rm -rf "${TMP_DIR:?}"
  fi
  CLEANED_UP=true
}

# setup signal handlers
# shellcheck disable=SC2317 # this is not unreachable code
signal_handler() {
  if [ -n "${GINKGO_PID:-}" ]; then
    kill -TERM "$GINKGO_PID" || true
  fi
  cleanup
}
trap signal_handler INT TERM

# build kubernetes / node image, e2e binaries
build() {
  # build the node image w/ kubernetes
  kind build node-image -v 1
  # Ginkgo v1 is used by Kubernetes 1.24 and earlier, fallback if v2 is not available.
  GINKGO_SRC_DIR="vendor/github.com/onsi/ginkgo/v2/ginkgo"
  if [ ! -d "$GINKGO_SRC_DIR" ]; then
      GINKGO_SRC_DIR="vendor/github.com/onsi/ginkgo/ginkgo"
  fi
  # make sure we have e2e requirements
  make all WHAT="cmd/kubectl test/e2e/e2e.test ${GINKGO_SRC_DIR}"
}

# up a cluster with kind
create_cluster() {
  # Grab the version of the cluster we're about to start
  KUBE_VERSION="$(docker run --rm --entrypoint=cat "kindest/node:latest" /kind/version)"

  # Default Log level for all components in test clusters
  KIND_CLUSTER_LOG_LEVEL=${KIND_CLUSTER_LOG_LEVEL:-4}


  # JSON or YAML map injected into featureGates config
  feature_gates="${FEATURE_GATES:-{\}}"
  # --runtime-config argument value passed to the API server, again as a map
  runtime_config="${RUNTIME_CONFIG:-{\}}"

  # create the config file
  cat <<EOF > "${ARTIFACTS}/kind-config.yaml"
# config for 1 control plane node and 2 workers (necessary for conformance)
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  ipFamily: ${IP_FAMILY:-ipv4}
  kubeProxyMode: ${KUBE_PROXY_MODE:-iptables}
  # don't pass through host search paths
  # TODO: possibly a reasonable default in the future for kind ...
  dnsSearch: []
nodes:
- role: control-plane
- role: worker
- role: worker
featureGates: ${feature_gates}
runtimeConfig: ${runtime_config}
kubeadmConfigPatches:
- |
  kind: ClusterConfiguration
  metadata:
    name: config
  apiServer:
    extraArgs:
      v: "${KIND_CLUSTER_LOG_LEVEL}"
  controllerManager:
    extraArgs:
      cloud-provider: "external"
      v: "${KIND_CLUSTER_LOG_LEVEL}"
  ---
  kind: InitConfiguration
  nodeRegistration:
    kubeletExtraArgs:
      cloud-provider: "external"
      v: "${KIND_CLUSTER_LOG_LEVEL}"
  ---
  kind: JoinConfiguration
  nodeRegistration:
    kubeletExtraArgs:
      cloud-provider: "external"
      v: "${KIND_CLUSTER_LOG_LEVEL}"
EOF
  # NOTE: must match the number of workers above
  NUM_NODES=2
  # actually create the cluster
  # TODO(BenTheElder): settle on verbosity for this script
  KIND_CREATE_ATTEMPTED=true
  kind create cluster \
    --image=kindest/node:latest \
    --retain \
    --wait=1m \
    -v=3 \
    "--config=${ARTIFACTS}/kind-config.yaml"

  # debug cluster version
  kubectl version

  # Patch kube-proxy to set the verbosity level
  kubectl patch -n kube-system daemonset/kube-proxy \
    --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/command/-", "value": "--v='"${KIND_CLUSTER_LOG_LEVEL}"'" }]'
}

# run e2es with ginkgo-e2e.sh
run_tests() {
  # IPv6 clusters need some CoreDNS changes in order to work in k8s CI:
  # 1. k8s CI doesn´t offer IPv6 connectivity, so CoreDNS should be configured
  # to work in an offline environment:
  # https://github.com/coredns/coredns/issues/2494#issuecomment-457215452
  # 2. k8s CI adds following domains to resolv.conf search field:
  # c.k8s-prow-builds.internal google.internal.
  # CoreDNS should handle those domains and answer with NXDOMAIN instead of SERVFAIL
  # otherwise pods stops trying to resolve the domain.
  if [ "${IP_FAMILY:-ipv4}" = "ipv6" ]; then
    # Get the current config
    original_coredns=$(kubectl get -oyaml -n=kube-system configmap/coredns)
    echo "Original CoreDNS config:"
    echo "${original_coredns}"
    # Patch it
    fixed_coredns=$(
      printf '%s' "${original_coredns}" | sed \
        -e 's/^.*kubernetes cluster\.local/& internal/' \
        -e '/^.*upstream$/d' \
        -e '/^.*fallthrough.*$/d' \
        -e '/^.*forward . \/etc\/resolv.conf$/d' \
        -e '/^.*loop$/d' \
    )
    echo "Patched CoreDNS config:"
    echo "${fixed_coredns}"
    printf '%s' "${fixed_coredns}" | kubectl apply -f -
  fi

  # ginkgo regexes
  SKIP="${SKIP:-"Feature|Federation|PerformanceDNS|DualStack|Disruptive|Serial|KubeProxy|GCE|Netpol|NetworkPolicy|256.search.list.characters|LoadBalancer.Service.without.NodePort|type.and.ports.of.a.TCP.service|loadbalancer.source.ranges"}"
  FOCUS="${FOCUS:-"\\[sig-network\\]"}"
  # if we set PARALLEL=true, skip serial tests set --ginkgo-parallel
  if [ "${PARALLEL:-false}" = "true" ]; then
    export GINKGO_PARALLEL=y
    if [ -z "${SKIP}" ]; then
      SKIP="\\[Serial\\]"
    else
      SKIP="\\[Serial\\]|${SKIP}"
    fi
  fi

  # setting this env prevents ginkgo e2e from trying to run provider setup
  export KUBERNETES_CONFORMANCE_TEST='y'
  # setting these is required to make RuntimeClass tests work ... :/
  export KUBE_CONTAINER_RUNTIME=remote
  export KUBE_CONTAINER_RUNTIME_ENDPOINT=unix:///run/containerd/containerd.sock
  export KUBE_CONTAINER_RUNTIME_NAME=containerd
  # ginkgo can take forever to exit, so we run it in the background and save the
  # PID, bash will not run traps while waiting on a process, but it will while
  # running a builtin like `wait`, saving the PID also allows us to forward the
  # interrupt
  # use aws provider to enable cloud-provider tests, aws is just a nullprovider
  # without any custom logic
  ./hack/ginkgo-e2e.sh \
    "--num-nodes=${NUM_NODES}" \
    "--ginkgo.focus=${FOCUS}" "--ginkgo.skip=${SKIP}" \
    "--report-dir=${ARTIFACTS}" '--disable-log-dump=true' &
  GINKGO_PID=$!
  wait "$GINKGO_PID"
}

main() {
  # create temp dir and setup cleanup
  TMP_DIR=$(mktemp -d)

  # ensure artifacts (results) directory exists when not in CI
  export ARTIFACTS="${ARTIFACTS:-${PWD}/_artifacts}"
  mkdir -p "${ARTIFACTS}"

  # export the KUBECONFIG to a unique path for testing
  KUBECONFIG="${HOME}/.kube/kind-test-config"
  export KUBECONFIG
  echo "exported KUBECONFIG=${KUBECONFIG}"

  # debug kind version
  kind version

  # build cloud-provider-kind
  make
  nohup bin/cloud-provider-kind --enable-log-dumping --logs-dir ${ARTIFACTS}/loadbalancers > ${ARTIFACTS}/ccm-kind.log 2>&1 &

  # build kubernetes
  K8S_PATH=$(find ${GOPATH} -path '*/k8s.io/kubernetes/go.mod' -print -quit)
  if [ -z "${K8S_PATH}" ]; then
    K8S_PATH=$(find / -path '*/kubernetes/go.mod' -print -quit)
  fi
  cd $(dirname ${K8S_PATH})

  build
  # in CI attempt to release some memory after building
  if [ -n "${KUBETEST_IN_DOCKER:-}" ]; then
    sync || true
    echo 1 > /proc/sys/vm/drop_caches || true
  fi

  # create the cluster and run tests
  res=0
  create_cluster || res=$?
  run_tests || res=$?
  cleanup || res=$?
  exit $res
}

main


================================================
FILE: hack/download-gateway-crds.sh
================================================
#!/bin/bash

set -o errexit
set -o nounset
set -o pipefail

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
REPO_ROOT=$( cd -- "$SCRIPT_DIR/.." &> /dev/null && pwd )

repo_url="https://github.com/kubernetes-sigs/gateway-api"

# Define the desired git version (tag or branch). Default to 'main'.
# Can be overridden by setting the GIT_VERSION environment variable.
GIT_VERSION="${GIT_VERSION:-main}"

# Define the path within the repo where CRDs are located
# Using 'standard' as it's common, adjust if needed for different versions
crd_repo_subdir="config/crd"

# Define the output directory relative to the REPO_ROOT
output_dir_rel="pkg/gateway/crds"
output_dir_abs="$REPO_ROOT/$output_dir_rel"

# --- Temporary Directory and Cleanup ---
TEMP_CLONE_DIR="" # Will hold the path to the temporary directory

cleanup() {
  local exit_code=$? # Capture exit code
  echo "Cleaning up..."
  if [ -n "$TEMP_CLONE_DIR" ] && [ -d "$TEMP_CLONE_DIR" ]; then
    echo "Removing temporary clone directory $TEMP_CLONE_DIR..."
    rm -rf "$TEMP_CLONE_DIR"
  else
      echo "No temporary directory to remove."
  fi
  # Ensure we exit with the original script's exit code
  exit $exit_code
}

# Set trap for cleanup on EXIT, ERR, SIGINT, SIGTERM
trap cleanup EXIT ERR SIGINT SIGTERM

# Create a temporary directory for the clone
TEMP_CLONE_DIR=$(mktemp -d -t gateway-api-clone-XXXXXX)
# Define the actual path where the repo will be cloned inside the temp dir
repo_local_path="$TEMP_CLONE_DIR/gateway-api"

echo "Cloning $repo_url (version: $GIT_VERSION) into temporary directory $repo_local_path..."
# Use --depth 1 for a shallow clone.
# --branch works for tags and branches.
# Use --no-tags to potentially speed up if tags aren't needed beyond the specific version tag.
# Add --filter=blob:none if supported by git version and remote to further reduce clone size.
git clone --branch "$GIT_VERSION" --depth 1 "$repo_url" "$repo_local_path"
if [ $? -ne 0 ]; then
    echo "Error: Failed to clone repository for version '$GIT_VERSION'."
    # Cleanup function will handle removing TEMP_CLONE_DIR via trap
    exit 1
fi
echo "Successfully cloned version '$GIT_VERSION'."

# Create the output directory if it doesn't exist
mkdir -p "$output_dir_abs"

# Define the full path to the CRD source directory within the clone
crd_source_dir="$repo_local_path/$crd_repo_subdir"

# Check if the source directory exists
if [ ! -d "$crd_source_dir" ]; then
    echo "Error: CRD source directory not found: $crd_source_dir (for version: $GIT_VERSION)"
    exit 1
fi

echo "Copying files from: $crd_source_dir (Version: $GIT_VERSION)"

cp -rf "$crd_source_dir"/* "$output_dir_abs"

echo "Successfully copied files to the '$output_dir_abs' directory from version $GIT_VERSION."

# Cleanup is handled by the trap
exit 0


================================================
FILE: hack/init-buildx.sh
================================================
#!/usr/bin/env bash
# Copyright 2020 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -o errexit -o nounset -o pipefail

# We can skip setup if the current builder already has multi-arch
# AND if it isn't the docker driver, which doesn't work
current_builder="$(docker buildx inspect)"
# linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
if ! grep -q "^Driver: docker$" <<<"${current_builder}" && \
     grep -q "linux/amd64" <<<"${current_builder}" && \
     grep -q "linux/arm64" <<<"${current_builder}"; then
  exit 0
fi

# Ensure qemu is in binfmt_misc
# Docker desktop already has these in versions recent enough to have buildx
# We only need to do this setup on linux hosts
if [ "$(uname)" == 'Linux' ]; then
  # NOTE: this is pinned to a digest for a reason!
  docker run --rm --privileged tonistiigi/binfmt:qemu-v7.0.0-28@sha256:66e11bea77a5ea9d6f0fe79b57cd2b189b5d15b93a2bdb925be22949232e4e55 --install all
fi

# Ensure we use a builder that can leverage it (the default on linux will not)
docker buildx rm knp-builder || true
docker buildx create --use --name=knp-builder


================================================
FILE: hack/lint.sh
================================================
#!/bin/bash

set -o errexit
set -o nounset
set -o pipefail

REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")/..

cd $REPO_ROOT
docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint:v2.7.2 golangci-lint run -v


================================================
FILE: internal/routeadder/main.go
================================================
// main.go
package main

import (
	"fmt"
	"net"
	"os"

	"github.com/vishvananda/netlink"
)

// usage: ./route-adder <cidr> <gateway_ip>
func main() {
	if len(os.Args) != 3 {
		fmt.Fprintf(os.Stderr, "Usage: %s <destination_cidr> <gateway_ip>\n", os.Args[0])
		os.Exit(1)
	}

	cidr := os.Args[1]
	gatewayIP := os.Args[2]

	// Parse the destination CIDR. This gives us the destination network.
	_, dstNet, err := net.ParseCIDR(cidr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error parsing destination CIDR %s: %v\n", cidr, err)
		os.Exit(1)
	}

	// Parse the gateway IP address.
	gw := net.ParseIP(gatewayIP)
	if gw == nil {
		fmt.Fprintf(os.Stderr, "Error parsing gateway IP %s\n", gatewayIP)
		os.Exit(1)
	}

	// Create the route object
	route := &netlink.Route{
		Dst: dstNet,
		Gw:  gw,
	}

	// Use RouteReplace, which is equivalent to `ip route replace`.
	// It will add the route if it doesn't exist or update it if it does.
	if err := netlink.RouteReplace(route); err != nil {
		fmt.Fprintf(os.Stderr, "Error replacing route: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("Successfully replaced route: %s via %s\n", cidr, gatewayIP)
}


================================================
FILE: kind.yaml
================================================
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
kubeadmConfigPatches:
- |
  kind: ClusterConfiguration
  apiServer:
    extraArgs:
      v: "5"
  controllerManager:
    extraArgs:
      cloud-provider: "external"
      v: "5"
  ---
  kind: InitConfiguration
  nodeRegistration:
    kubeletExtraArgs:
      cloud-provider: "external"
  ---
  kind: JoinConfiguration
  nodeRegistration:
    kubeletExtraArgs:
      cloud-provider: "external"
nodes:
- role: control-plane
- role: worker
- role: worker


================================================
FILE: main.go
================================================
package main

import (
	"sigs.k8s.io/cloud-provider-kind/cmd"
)

func main() {
	cmd.Main()
}


================================================
FILE: pkg/config/config.go
================================================
package config

import (
	"os"

	"sigs.k8s.io/cloud-provider-kind/pkg/constants"
)

// DefaultConfig is a global variable that is initialized at startup.
// Note: Its fields are modified by command-line flags in cmd/app.go.
var DefaultConfig = newDefaultConfig()

func newDefaultConfig() *Config {
	c := &Config{
		GatewayReleaseChannel: Standard,
		IngressDefault:        true,
		ProxyImage:            constants.DefaultProxyImageRegistry + "/" + constants.DefaultProxyImageName,
	}

	if registry := os.Getenv("CLOUD_PROVIDER_KIND_REGISTRY_URL"); registry != "" {
		c.ProxyImage = registry + "/" + constants.DefaultProxyImageName
	}

	return c
}



type Config struct {
	EnableLogDump bool
	LogDir        string
	// Platforms like Mac or Windows can not access the containers directly
	// so we do a double hop, enable container portmapping for the LoadBalancer containter
	// and do userspace proxying from the original port to the portmaps.
	// If the cloud-provider-kind runs in a container on these platforms only enables portmapping.
	LoadBalancerConnectivity Connectivity
	// Type of connectivity between the cloud-provider-kind and the clusters
	ControlPlaneConnectivity Connectivity
	// Gateway API Release channel (default stable)
	// https://gateway-api.sigs.k8s.io/concepts/versioning/
	GatewayReleaseChannel GatewayReleaseChannel
	IngressDefault        bool
	ProxyImage            string
}

type Connectivity int

const (
	Unknown Connectivity = iota
	Direct
	Portmap
	Tunnel
)

type GatewayReleaseChannel string

const (
	Standard     GatewayReleaseChannel = "standard"
	Experimental GatewayReleaseChannel = "experimental"
	Disabled     GatewayReleaseChannel = "disabled"
)


================================================
FILE: pkg/constants/constants.go
================================================
package constants

const (
	ProviderName = "kind"
	// cloud-provider-kind
	ContainerPrefix = "kindccm"
	// DefaultProxyImageRegistry is the default registry for the Envoy proxy image
	DefaultProxyImageRegistry = "docker.io"
	// DefaultProxyImageName is the default Envoy proxy image
	DefaultProxyImageName = "envoyproxy/envoy:v1.33.2"
	// KIND constants
	FixedNetworkName = "kind"
	// NodeCCMLabelKey
	NodeCCMLabelKey = "io.x-k8s.cloud-provider-kind.cluster"
	// LoadBalancerNameLabelKey clustername/serviceNamespace/serviceName
	LoadBalancerNameLabelKey = "io.x-k8s.cloud-provider-kind.loadbalancer.name"
	// GatewayNameLabelKey clustername/gatewayNamespace/gatewayName
	GatewayNameLabelKey = "io.x-k8s.cloud-provider-kind.gateway.name"
)


================================================
FILE: pkg/container/container.go
================================================
package container

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"

	"k8s.io/klog/v2"
	kindexec "sigs.k8s.io/kind/pkg/exec"
)

// TODO we can do it as in KIND
var containerRuntime = "docker"

// dockerIsAvailable checks if docker is available in the system
func dockerIsAvailable() bool {
	cmd := kindexec.Command("docker", "-v")
	lines, err := kindexec.OutputLines(cmd)
	if err != nil || len(lines) != 1 {
		return false
	}
	return strings.HasPrefix(lines[0], "Docker version")
}

func podmanIsAvailable() bool {
	cmd := kindexec.Command("podman", "-v")
	lines, err := kindexec.OutputLines(cmd)
	if err != nil || len(lines) != 1 {
		return false
	}
	return strings.HasPrefix(lines[0], "podman version")
}

func nerdctlIsAvailable() bool {
	cmd := kindexec.Command("nerdctl", "-v")
	lines, err := kindexec.OutputLines(cmd)
	if err != nil || len(lines) != 1 {
		// check finch
		cmd = kindexec.Command("finch", "-v")
		lines, err = kindexec.OutputLines(cmd)
		if err != nil || len(lines) != 1 {
			return false
		}
		return strings.HasPrefix(lines[0], "finch version")
	}
	return strings.HasPrefix(lines[0], "nerdctl version")
}

// Runtime returns the detected container runtime name.
func Runtime() string {
	return containerRuntime
}

func init() {
	// allow to override the container provider as we do in KIND
	if p := os.Getenv("KIND_EXPERIMENTAL_PROVIDER"); p != "" {
		containerRuntime = p
		return
	}

	if dockerIsAvailable() {
		return
	}
	if podmanIsAvailable() {
		containerRuntime = "podman"
		return
	}
	if nerdctlIsAvailable() {
		containerRuntime = "nerdctl"
		if _, err := exec.LookPath("nerdctl"); err != nil {
			if _, err := exec.LookPath("finch"); err == nil {
				containerRuntime = "finch"
			}
		}
	}
}

func Logs(name string, w io.Writer) error {
	cmd := exec.Command(containerRuntime, []string{"logs", name}...)
	cmd.Stderr = w
	cmd.Stdout = w
	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("failed to get container logs: %w", err)
	}
	return nil
}

func LogDump(containerName string, fileName string) error {
	f, err := os.Create(fileName)
	if err != nil {
		return err
	}
	defer f.Close()

	err = Logs(containerName, f)
	if err != nil {
		return err
	}
	return nil
}

func Create(name string, args []string) error {
	if err := exec.Command(containerRuntime, append([]string{"run", "--name", name}, args...)...).Run(); err != nil {
		return err
	}
	return nil
}

func Restart(name string) error {
	if err := exec.Command(containerRuntime, []string{"restart", name}...).Run(); err != nil {
		return err
	}
	return nil
}

func Delete(name string) error {
	if err := exec.Command(containerRuntime, []string{"rm", "-f", name}...).Run(); err != nil {
		return err
	}
	return nil
}

func IsRunning(name string) bool {
	cmd := exec.Command(containerRuntime, []string{"ps", "-q", "-f", "name=" + name}...)
	output, err := cmd.Output()
	if err != nil || len(output) == 0 {
		return false
	}
	return true
}

func Exist(name string) bool {
	err := exec.Command(containerRuntime, []string{"inspect", name}...).Run()
	return err == nil
}

func Signal(name string, signal string) error {
	err := exec.Command(containerRuntime, []string{"kill", "-s", signal, name}...).Run()
	return err
}

func Exec(name string, command []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
	args := []string{"exec", "--privileged"}
	if stdin != nil {
		args = append(args, "-i")
	}
	args = append(args, name)
	args = append(args, command...)
	cmd := exec.Command(containerRuntime, args...)
	if stdin != nil {
		cmd.Stdin = stdin
	}
	if stdout != nil {
		cmd.Stdout = stdout
	}
	if stderr != nil {
		cmd.Stderr = stderr
	}
	return cmd.Run()
}

func IPs(name string) (ipv4 string, ipv6 string, err error) {
	// retrieve the IP address of the node using docker inspect
	cmd := kindexec.Command(containerRuntime, "inspect",
		"-f", "{{range .NetworkSettings.Networks}}{{.IPAddress}},{{.GlobalIPv6Address}}{{end}}",
		name, // ... against the "node" container
	)
	lines, err := kindexec.OutputLines(cmd)
	if err != nil {
		return "", "", fmt.Errorf("failed to get container details: %w", err)
	}
	if len(lines) != 1 {
		return "", "", fmt.Errorf("file should only be one line, got %d lines: %w", len(lines), err)
	}
	ips := strings.Split(lines[0], ",")
	if len(ips) != 2 {
		return "", "", fmt.Errorf("container addresses should have 2 values, got %d values", len(ips))
	}
	return ips[0], ips[1], nil
}

// return a list with the map of the internal port to the external port
func PortMaps(name string) (map[string]string, error) {
	// retrieve the IP address of the node using docker inspect
	cmd := kindexec.Command(containerRuntime, "inspect",
		"-f", "{{ json .NetworkSettings.Ports }}",
		name, // ... against the "node" container
	)

	lines, err := kindexec.OutputLines(cmd)
	if err != nil {
		return nil, fmt.Errorf("failed to get container details: %w", err)
	}
	if len(lines) != 1 {
		return nil, fmt.Errorf("file should only be one line, got %d lines: %w", len(lines), err)
	}

	type portMapping struct {
		HostPort string `json:"HostPort"`
		HostIP   string `json:"HostIp"`
	}

	portMappings := make(map[string][]portMapping)
	err = json.Unmarshal([]byte(lines[0]), &portMappings)
	if err != nil {
		return nil, err
	}

	result := map[string]string{}
	for k, v := range portMappings {
		protocol := "tcp"
		parts := strings.Split(k, "/")
		if len(parts) == 2 {
			protocol = strings.ToLower(parts[1])
		}
		if protocol != "tcp" && protocol != "udp" {
			klog.Infof("skipping protocol %s not supported, only UDP and TCP", protocol)
			continue
		}

		// TODO we just can get the first entry or look for ip families
		for _, pm := range v {
			if pm.HostPort != "" {
				result[parts[0]+"/"+protocol] = pm.HostPort
				break
			}
		}
	}
	return result, nil
}

func ListByLabel(label string) ([]string, error) {
	cmd := kindexec.Command(containerRuntime,
		"ps",
		"-a", // show stopped nodes
		// filter for nodes with the cluster label
		"--filter", "label="+label,
		// format to include the cluster name
		"--format", `{{.ID }}`,
	)
	lines, err := kindexec.OutputLines(cmd)
	return lines, err
}

// GetLabelValue return the value of the associated label
// It returns an error if the label value does not exist
func GetLabelValue(name string, label string) (string, error) {
	cmd := kindexec.Command(containerRuntime,
		"inspect",
		"--format", fmt.Sprintf(`{{ index .Config.Labels "%s"}}`, label),
		name,
	)
	lines, err := kindexec.OutputLines(cmd)
	if err != nil {
		return "", err
	}
	if len(lines) != 1 {
		return "", fmt.Errorf("expected 1 line, got %d", len(lines))
	}
	return lines[0], nil
}


================================================
FILE: pkg/controller/controller.go
================================================
package controller

import (
	"context"
	"fmt"
	"net/http"
	"sync"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/wait"
	utilfeature "k8s.io/apiserver/pkg/util/feature"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	cloudprovider "k8s.io/cloud-provider"
	cloudproviderapi "k8s.io/cloud-provider/api"

	nodecontroller "k8s.io/cloud-provider/controllers/node"
	servicecontroller "k8s.io/cloud-provider/controllers/service"
	controllersmetrics "k8s.io/component-base/metrics/prometheus/controllers"
	ccmfeatures "k8s.io/controller-manager/pkg/features"
	"k8s.io/klog/v2"

	gatewayclient "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned"
	gatewayinformers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions"

	cpkconfig "sigs.k8s.io/cloud-provider-kind/pkg/config"
	"sigs.k8s.io/cloud-provider-kind/pkg/constants"
	"sigs.k8s.io/cloud-provider-kind/pkg/container"
	"sigs.k8s.io/cloud-provider-kind/pkg/gateway"
	"sigs.k8s.io/cloud-provider-kind/pkg/ingress"
	"sigs.k8s.io/cloud-provider-kind/pkg/loadbalancer"
	"sigs.k8s.io/cloud-provider-kind/pkg/provider"
	"sigs.k8s.io/kind/pkg/cluster"
)

var once sync.Once

type Controller struct {
	kind     *cluster.Provider
	clusters map[string]*ccm
}

type ccm struct {
	factory           informers.SharedInformerFactory
	serviceController *servicecontroller.Controller
	nodeController    *nodecontroller.CloudNodeController
	gatewayController *gateway.Controller
	cancelFn          context.CancelFunc
}

func New(provider *cluster.Provider) *Controller {
	controllersmetrics.Register()
	return &Controller{
		kind:     provider,
		clusters: make(map[string]*ccm),
	}
}

func (c *Controller) Run(ctx context.Context) {
	logger := klog.FromContext(ctx)
	defer c.cleanup()

	for {
		// get existing kind clusters
		clusters, err := c.kind.List()
		if err != nil {
			logger.Error(err, "Error listing clusters, retrying...")
		}

		// add new ones
		for _, cluster := range clusters {
			select {
			case <-ctx.Done():
				return
			default:
			}
			logger := logger.WithValues("cluster", cluster)

			logger.V(3).Info("Processing cluster")
			_, ok := c.clusters[cluster]
			if ok {
				logger.V(3).Info("Cluster already exist")
				continue
			}

			restConfig, err := c.getRestConfig(ctx, cluster)
			if err != nil {
				logger.Error(err, "Failed to create kubeClient")
				continue
			}

			logger.V(2).Info("Creating new cloud provider")
			cloud := provider.New(cluster, c.kind)
			ccm, err := startCloudControllerManager(ctx, cluster, restConfig, cloud)
			if err != nil {
				logger.Error(err, "Failed to start cloud controller")
				continue
			}
			logger.Info("Starting cloud controller")
			c.clusters[cluster] = ccm
		}
		// remove expired ones
		clusterSet := sets.New(clusters...)
		for cluster, ccm := range c.clusters {
			_, ok := clusterSet[cluster]
			if !ok {
				logger.Info("Deleting resources", "cluster", cluster)
				ccm.cancelFn()
				delete(c.clusters, cluster)
			}
		}
		select {
		case <-ctx.Done():
			return
		case <-time.After(30 * time.Second):
		}
	}
}

func (c *Controller) getKubeConfig(cluster string, internal bool) (*rest.Config, error) {
	kconfig, err := c.kind.KubeConfig(cluster, internal)
	if err != nil {
		klog.Errorf("Failed to get kubeconfig for cluster %s: %v", cluster, err)
		return nil, err
	}

	config, err := clientcmd.RESTConfigFromKubeConfig([]byte(kconfig))
	if err != nil {
		klog.Errorf("Failed to convert kubeconfig for cluster %s: %v", cluster, err)
		return nil, err
	}
	return config, nil
}

// getRestConfig returns a valid rest.Config for the cluster passed as argument
// It tries first to connect to the internal endpoint.
func (c *Controller) getRestConfig(ctx context.Context, cluster string) (*rest.Config, error) {
	logger := klog.FromContext(ctx).WithValues("cluster", cluster)

	addresses := []string{}
	internalConfig, err := c.getKubeConfig(cluster, true)
	if err != nil {
		logger.Error(err, "Failed to get internal kubeconfig")
	} else {
		addresses = append(addresses, internalConfig.Host)
	}
	externalConfig, err := c.getKubeConfig(cluster, false)
	if err != nil {
		logger.Error(err, "Failed to get external kubeconfig")
	} else {
		addresses = append(addresses, externalConfig.Host)
	}

	if len(addresses) == 0 {
		return nil, fmt.Errorf("could not find kubeconfig")
	}

	var host string
	for i := 0; i < 5; i++ {
		host, err = firstSuccessfulProbe(ctx, addresses)
		if err != nil {
			logger.Error(err, "Failed to connect to any address", "addresses", addresses)
			time.Sleep(time.Second * time.Duration(i))
		} else {
			logger.Info("Connected succesfully", "host", host)
			break
		}
	}

	var config *rest.Config
	switch host {
	case internalConfig.Host:
		config = internalConfig
		// the first cluster will give us the type of connectivity between
		// cloud-provider-kind and the clusters and load balancer containers.
		// In Linux or containerized cloud-provider-kind this will be direct.
		once.Do(func() {
			cpkconfig.DefaultConfig.ControlPlaneConnectivity = cpkconfig.Direct
		})
	case externalConfig.Host:
		config = externalConfig
	default:
		return nil, fmt.Errorf("restConfig for host %s not avaliable", host)
	}

	return config, nil
}

// TODO: implement leader election to not have problems with multiple providers
// ref: https://github.com/kubernetes/kubernetes/blob/d97ea0f705847f90740cac3bc3dd8f6a4026d0b5/cmd/kube-scheduler/app/server.go#L211
func startCloudControllerManager(ctx context.Context, clusterName string, config *rest.Config, cloud cloudprovider.Interface) (*ccm, error) {
	logger := klog.FromContext(ctx).WithValues("cluster", clusterName)

	// TODO: we need to set up the ccm specific feature gates
	// but try to avoid to expose this to users
	featureGates := utilfeature.DefaultMutableFeatureGate
	err := ccmfeatures.SetupCurrentKubernetesSpecificFeatureGates(featureGates)
	if err != nil {
		return nil, err
	}

	kubeClient, err := kubernetes.NewForConfig(config)
	if err != nil {
		logger.Error(err, "Failed to create kubeClient")
		return nil, err
	}

	client := kubeClient.Discovery().RESTClient()
	// wait for health
	err = wait.PollUntilContextTimeout(ctx, 1*time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) {
		healthStatus := 0
		client.Get().AbsPath("/healthz").Do(ctx).StatusCode(&healthStatus)
		if healthStatus != http.StatusOK {
			return false, nil
		}

		return true, nil
	})
	if err != nil {
		logger.Error(err, "Failed waiting for apiserver to be ready")
		return nil, err
	}

	sharedInformers := informers.NewSharedInformerFactory(kubeClient, 60*time.Second)
	servicesInformer := sharedInformers.Core().V1().Services()
	nodesInformer := sharedInformers.Core().V1().Nodes()
	namespacesInformer := sharedInformers.Core().V1().Namespaces()
	secretsInformer := sharedInformers.Core().V1().Secrets()
	ingressInformer := sharedInformers.Networking().V1().Ingresses()
	ingressClassInformer := sharedInformers.Networking().V1().IngressClasses()

	ccmMetrics := controllersmetrics.NewControllerManagerMetrics(clusterName)
	// Start the service controller
	serviceController, err := servicecontroller.New(
		cloud,
		kubeClient,
		servicesInformer,
		nodesInformer,
		clusterName,
		featureGates,
	)
	if err != nil {
		// This error shouldn't fail. It lives like this as a legacy.
		logger.Error(err, "Failed to start service controller")
		return nil, err
	}

	ctx, cancel := context.WithCancel(ctx)
	go serviceController.Run(ctx, 5, ccmMetrics)

	nodeController := &nodecontroller.CloudNodeController{}

	hasCloudProviderTaint, err := getCloudProviderTaint(ctx, clusterName, kubeClient)
	if err != nil {
		logger.Error(err, "Failed get cluster nodes")
		cancel()
		return nil, err
	}

	if hasCloudProviderTaint {
		// Start the node controller
		nodeController, err = nodecontroller.NewCloudNodeController(
			nodesInformer,
			kubeClient,
			cloud,
			30*time.Second,
			5, // workers
		)
		if err != nil {
			// This error shouldn't fail. It lives like this as a legacy.
			logger.Error(err, "Failed to start node controller")
			cancel()
			return nil, err
		}
		go nodeController.Run(ctx.Done(), ccmMetrics)
	}

	// Gateway setup
	crdManager, err := gateway.NewCRDManager(config)
	if err != nil {
		logger.Error(err, "Failed to create Gateway API CRD manager")
		cancel()
		return nil, err
	}

	// Only install CRDs if gateway channel is not disabled
	if cpkconfig.DefaultConfig.GatewayReleaseChannel != cpkconfig.Disabled {
		err = crdManager.InstallCRDs(ctx, cpkconfig.DefaultConfig.GatewayReleaseChannel)
		if err != nil {
			logger.Error(err, "Failed to install Gateway API CRDs")
			cancel()
			return nil, err
		}
	} else {
		logger.Info("Gateway API CRDs installation skipped (disabled)")
	}

	var gatewayController *gateway.Controller

	// Only set up Gateway API controllers if not disabled
	if cpkconfig.DefaultConfig.GatewayReleaseChannel != cpkconfig.Disabled {
		gwClient, err := gatewayclient.NewForConfig(config)
		if err != nil {
			// This error shouldn't fail. It lives like this as a legacy.
			logger.Error(err, "Failed to create Gateway API client")
			cancel()
			return nil, err
		}

		sharedGwInformers := gatewayinformers.NewSharedInformerFactory(gwClient, 60*time.Second)
		gwClassInformer := sharedGwInformers.Gateway().V1().GatewayClasses()
		gwInformer := sharedGwInformers.Gateway().V1().Gateways()
		httpRouteInformer := sharedGwInformers.Gateway().V1().HTTPRoutes()
		grpcRouteInformer := sharedGwInformers.Gateway().V1().GRPCRoutes()
		referenceGrantInformer := sharedGwInformers.Gateway().V1().ReferenceGrants()

		gatewayController, err = gateway.New(
			clusterName,
			kubeClient,
			gwClient,
			namespacesInformer,
			servicesInformer,
			secretsInformer,
			gwClassInformer,
			gwInformer,
			httpRouteInformer,
			grpcRouteInformer,
			referenceGrantInformer,
		)
		if err != nil {
			logger.Error(err, "Failed to start gateway controller")
			cancel()
			return nil, err
		}

		// Ingress to Gateway controller migration
		ingressController, err := ingress.NewController(
			kubeClient,
			gwClient,
			gateway.GWClassName,
			ingressInformer,
			ingressClassInformer,
			servicesInformer,
			secretsInformer,
			httpRouteInformer,
			gwInformer,
		)
		if err != nil {
			logger.Error(err, "Failed to create Ingress controller")
			cancel()
			return nil, err
		}

		sharedInformers.Start(ctx.Done())
		sharedGwInformers.Start(ctx.Done())

		err = gatewayController.Init(ctx)
		if err != nil {
			logger.Error(err, "Failed to initialize gateway controller")
			cancel()
			return nil, err
		}

		go func() {
			_ = gatewayController.Run(ctx)
		}()

		err = ingressController.Init(ctx)
		if err != nil {
			logger.Error(err, "Failed to initialize ingress controller")
			cancel()
			return nil, err
		}

		go ingressController.Run(ctx, 5)
	} else {
		logger.Info("Gateway API controllers skipped (disabled)")
		// Start only the core informers, not gateway informers
		sharedInformers.Start(ctx.Done())
	}

	// This has to cleanup all the resources allocated by the cloud provider in this cluster
	// - containers as loadbalancers
	// - in windows and darwin ip addresses on the loopback interface
	// Find all the containers associated to the cluster and then use the cloud provider methods to delete
	// the loadbalancer, we can extract the service name from the container labels.
	cancelFn := func() {
		cancel()

		containers, err := container.ListByLabel(fmt.Sprintf("%s=%s", constants.NodeCCMLabelKey, clusterName))
		if err != nil {
			logger.Error(err, "Failed to list containers")
			return
		}

		lbController, ok := cloud.LoadBalancer()
		// this can not happen
		if !ok {
			return
		}

		for _, name := range containers {
			logger.V(2).Info("Cleaning up container", "container", name)
			cleanupLoadBalancer(lbController, name)
			cleanupGateway(name)
		}
	}

	return &ccm{
		factory:           sharedInformers,
		serviceController: serviceController,
		nodeController:    nodeController,
		gatewayController: gatewayController,
		cancelFn:          cancelFn}, nil
}

// TODO cleanup alias ip on mac
func (c *Controller) cleanup() {
	for cluster, ccm := range c.clusters {
		klog.Infof("Cleaning resources for cluster %s", cluster)
		ccm.cancelFn()
		delete(c.clusters, cluster)
	}
	// final cleanup for containers that be left behind
	containers, err := container.ListByLabel(constants.NodeCCMLabelKey)
	if err != nil {
		klog.Errorf("can't list containers: %v", err)
		return
	}

	for _, name := range containers {
		klog.V(2).Infof("cleaning up container %s", name)
		err := container.Delete(name)
		if err != nil {
			klog.Errorf("error deleting container %s : %v", name, err)
		}
	}
}

func getCloudProviderTaint(ctx context.Context, clusterName string, kubeClient kubernetes.Interface) (bool, error) {
	nodes, err := kubeClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
	if err != nil {
		return false, fmt.Errorf("failed to list nodes for cluster %s: %w", clusterName, err)
	}
	for _, node := range nodes.Items {
		for _, taint := range node.Spec.Taints {
			if taint.Key == cloudproviderapi.TaintExternalCloudProvider {
				return true, nil
			}
		}
	}
	return false, nil
}

func cleanupLoadBalancer(lbController cloudprovider.LoadBalancer, name string) {
	// create fake service to pass to the cloud provider method
	v, err := container.GetLabelValue(name, constants.LoadBalancerNameLabelKey)
	if err != nil || v == "" {
		klog.Infof("could not get the label for the loadbalancer on container %s : %v", name, err)
		return
	}
	clusterName, service := loadbalancer.ServiceFromLoadBalancerSimpleName(v)
	if service == nil {
		klog.Infof("invalid format for loadbalancer on cluster %s: %s", clusterName, v)
		return
	}
	err = lbController.EnsureLoadBalancerDeleted(context.Background(), clusterName, service)
	if err != nil {
		klog.Infof("error deleting loadbalancer %s/%s on cluster %s : %v", service.Namespace, service.Name, clusterName, err)
		return
	}
}

func cleanupGateway(name string) {
	// create fake service to pass to the cloud provider method
	v, err := container.GetLabelValue(name, constants.GatewayNameLabelKey)
	if err != nil || v == "" {
		klog.Infof("could not get the label for the loadbalancer on container %s : %v", name, err)
		return
	}
	err = container.Delete(name)
	if err != nil {
		klog.Infof("error deleting container %s gateway %s : %v", name, v, err)
		return
	}
}


================================================
FILE: pkg/controller/http.go
================================================
package controller

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"net/http"
	"sync"
	"time"

	"k8s.io/klog/v2"
)

func probeHTTP(ctx context.Context, address string) bool {
	klog.Infof("probe HTTP address %s", address)
	httpClient := &http.Client{
		Timeout: 2 * time.Second,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		},
	}
	req, err := http.NewRequest("GET", address, nil)
	if err != nil {
		return false
	}
	req = req.WithContext(ctx)
	resp, err := httpClient.Do(req)
	if err != nil {
		klog.Infof("Failed to connect to HTTP address %s: %v", address, err)
		return false
	}
	defer resp.Body.Close()
	// drain the body
	io.ReadAll(resp.Body) // nolint:errcheck
	// we only want to verify connectivity so don't need to check the http status code
	// as the apiserver may not be ready
	return true
}

// firstSuccessfulProbe probes the given addresses in parallel and returns the first address to succeed, cancelling the other probes.
func firstSuccessfulProbe(ctx context.Context, addresses []string) (string, error) {
	var wg sync.WaitGroup
	resultChan := make(chan string, 1)

	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	for _, addr := range addresses {
		wg.Add(1)
		go func(address string) {
			defer wg.Done()
			if probeHTTP(ctx, address) {
				select {
				case resultChan <- address:
				default:
				}
				cancel()
			}
		}(addr)
	}

	go func() {
		wg.Wait()
		close(resultChan)
	}()

	select {
	case result := <-resultChan:
		return result, nil
	case <-ctx.Done():
		return "", fmt.Errorf("no address succeeded")
	}
}


================================================
FILE: pkg/controller/http_test.go
================================================
package controller

import (
	"context"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

func Test_firstSuccessfulProbe(t *testing.T) {
	reqCh := make(chan struct{})
	ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		t.Logf("received connection ")
		close(reqCh)
	}))
	ts.EnableHTTP2 = true
	ts.StartTLS()
	defer ts.Close()
	// use an address that is not likely to exist to avoid flakes
	addresses := []string{"https://127.0.1.201:12349", ts.URL}
	got, err := firstSuccessfulProbe(context.Background(), addresses)
	if err != nil {
		t.Errorf("firstSuccessfulProbe() error = %v", err)
		return
	}
	if got != ts.URL {
		t.Errorf("firstSuccessfulProbe() = %v, want %v", got, ts.URL)
	}

	select {
	case <-reqCh:
	case <-time.After(10 * time.Second):
		t.Fatalf("test timed out, no request received")
	}

}


================================================
FILE: pkg/gateway/backendref.go
================================================
package gateway

import (
	"fmt"

	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
)

// backendRefToClusterName generates a unique Envoy cluster name from a Gateway API BackendRef.
// It returns a structured ControllerError with the appropriate Reason on failure.
func backendRefToClusterName(defaultNamespace string, backendRef gatewayv1.BackendRef) (string, error) {
	// 1. Validate that the Kind is a supported type (Service).
	if backendRef.Kind != nil && *backendRef.Kind != "Service" {
		return "", &ControllerError{
			Reason:  string(gatewayv1.RouteReasonInvalidKind),
			Message: fmt.Sprintf("unsupported backend kind: %s", *backendRef.Kind),
		}
	}

	// 2. Validate that the Port is specified.
	if backendRef.Port == nil {
		return "", &ControllerError{
			// Note: There isn't a specific reason for a missing port,
			// so InvalidParameters is a reasonable choice.
			Reason:  string(gatewayv1.RouteReasonUnsupportedProtocol),
			Message: "backend port must be specified",
		}
	}

	namespace := defaultNamespace
	if backendRef.Namespace != nil {
		namespace = string(*backendRef.Namespace)
	}

	port := int32(*backendRef.Port)
	group := "core"
	if backendRef.Group != nil && *backendRef.Group != "" {
		group = string(*backendRef.Group)
	}
	kind := "Service"
	if backendRef.Kind != nil && *backendRef.Kind != "" {
		kind = string(*backendRef.Kind)
	}

	// Format: <namespace>_<name>_<group>_<kind>_<port>
	clusterName := fmt.Sprintf("%s_%s_%s_%s_%d", namespace, backendRef.Name, group, kind, port)

	return clusterName, nil
}


================================================
FILE: pkg/gateway/backendref_test.go
================================================
package gateway

import (
	"errors"
	"testing"

	"k8s.io/utils/ptr"
	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
)

func TestBackendRefToClusterName(t *testing.T) {
	tests := []struct {
		name             string
		defaultNamespace string
		backendRef       gatewayv1.BackendRef
		wantName         string
		wantErr          bool
		wantReason       string
	}{
		{
			name:             "valid backendRef with all fields",
			defaultNamespace: "default",
			backendRef: gatewayv1.BackendRef{
				BackendObjectReference: gatewayv1.BackendObjectReference{
					Name:      "my-service",
					Namespace: ptr.To(gatewayv1.Namespace("my-ns")),
					Group:     ptr.To(gatewayv1.Group("my-group")),
					Kind:      ptr.To(gatewayv1.Kind("Service")),
					Port:      ptr.To(gatewayv1.PortNumber(8080)),
				},
			},
			wantName:   "my-ns_my-service_my-group_Service_8080",
			wantErr:    false,
			wantReason: "",
		},
		{
			name:             "valid backendRef with default fields",
			defaultNamespace: "default",
			backendRef: gatewayv1.BackendRef{
				BackendObjectReference: gatewayv1.BackendObjectReference{
					Name: "my-service",
					Port: ptr.To(gatewayv1.PortNumber(80)),
				},
			},
			wantName:   "default_my-service_core_Service_80",
			wantErr:    false,
			wantReason: "",
		},
		{
			name:             "unsupported kind",
			defaultNamespace: "default",
			backendRef: gatewayv1.BackendRef{
				BackendObjectReference: gatewayv1.BackendObjectReference{
					Name: "my-service",
					Kind: ptr.To(gatewayv1.Kind("UnsupportedKind")),
					Port: ptr.To(gatewayv1.PortNumber(80)),
				},
			},
			wantName:   "",
			wantErr:    true,
			wantReason: string(gatewayv1.RouteReasonInvalidKind),
		},
		{
			name:             "missing port",
			defaultNamespace: "default",
			backendRef: gatewayv1.BackendRef{
				BackendObjectReference: gatewayv1.BackendObjectReference{
					Name: "my-service",
					Port: nil,
				},
			},
			wantName:   "",
			wantErr:    true,
			wantReason: string(gatewayv1.RouteReasonUnsupportedProtocol),
		},
		{
			name:             "specified namespace",
			defaultNamespace: "default",
			backendRef: gatewayv1.BackendRef{
				BackendObjectReference: gatewayv1.BackendObjectReference{
					Name:      "my-service",
					Namespace: ptr.To(gatewayv1.Namespace("other-ns")),
					Port:      ptr.To(gatewayv1.PortNumber(80)),
				},
			},
			wantName:   "other-ns_my-service_core_Service_80",
			wantErr:    false,
			wantReason: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotName, err := backendRefToClusterName(tt.defaultNamespace, tt.backendRef)

			if (err != nil) != tt.wantErr {
				t.Errorf("backendRefToClusterName() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if tt.wantErr {
				var controllerErr *ControllerError
				if errors.As(err, &controllerErr) {
					if controllerErr.Reason != tt.wantReason {
						t.Errorf("backendRefToClusterName() error reason = %s, wantReason %s", controllerErr.Reason, tt.wantReason)
					}
				} else {
					t.Errorf("backendRefToClusterName() expected a ControllerError, but got %T", err)
				}
			}

			if gotName != tt.wantName {
				t.Errorf("backendRefToClusterName() gotName = %v, want %v", gotName, tt.wantName)
			}
		})
	}
}


================================================
FILE: pkg/gateway/controller.go
================================================
/*
Copyright 2024 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package gateway

import (
	"context"
	"fmt"
	"net"
	"sort"
	"sync/atomic"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"

	corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
	clusterv3service "github.com/envoyproxy/go-control-plane/envoy/service/cluster/v3"
	discoveryv3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
	endpointv3 "github.com/envoyproxy/go-control-plane/envoy/service/endpoint/v3"
	listenerv3service "github.com/envoyproxy/go-control-plane/envoy/service/listener/v3"
	routev3service "github.com/envoyproxy/go-control-plane/envoy/service/route/v3"
	runtimev3 "github.com/envoyproxy/go-control-plane/envoy/service/runtime/v3"
	secretv3 "github.com/envoyproxy/go-control-plane/envoy/service/secret/v3"
	envoyproxytypes "github.com/envoyproxy/go-control-plane/pkg/cache/types"
	cachev3 "github.com/envoyproxy/go-control-plane/pkg/cache/v3"
	resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3"
	serverv3 "github.com/envoyproxy/go-control-plane/pkg/server/v3"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	corev1informers "k8s.io/client-go/informers/core/v1"
	"k8s.io/client-go/kubernetes"
	corev1listers "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"
	"k8s.io/klog/v2"
	"k8s.io/utils/ptr"

	"sigs.k8s.io/cloud-provider-kind/pkg/config"
	"sigs.k8s.io/cloud-provider-kind/pkg/tunnels"
	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
	gatewayclient "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned"
	gatewayinformers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions/apis/v1"
	gatewaylisters "sigs.k8s.io/gateway-api/pkg/client/listers/apis/v1"
)

const (
	controllerName = "kind.sigs.k8s.io/gateway-controller"
	GWClassName    = "cloud-provider-kind"
	maxRetries     = 5
	workers        = 5
)

const (
	grpcKeepaliveTime        = 30 * time.Second
	grpcKeepaliveTimeout     = 5 * time.Second
	grpcKeepaliveMinTime     = 30 * time.Second
	grpcMaxConcurrentStreams = 1000000
)

type ControllerError struct {
	Reason  string
	Message string
}

// Error implements the error interface.
func (e *ControllerError) Error() string {
	return e.Message
}

type Controller struct {
	clusterName       string
	clusterNameserver string

	client   kubernetes.Interface
	gwClient gatewayclient.Interface

	namespaceLister       corev1listers.NamespaceLister
	namespaceListerSynced cache.InformerSynced

	serviceLister       corev1listers.ServiceLister
	serviceListerSynced cache.InformerSynced

	secretLister       corev1listers.SecretLister
	secretListerSynced cache.InformerSynced

	gatewayClassLister       gatewaylisters.GatewayClassLister
	gatewayClassListerSynced cache.InformerSynced

	gatewayLister       gatewaylisters.GatewayLister
	gatewayListerSynced cache.InformerSynced
	gatewayqueue        workqueue.TypedRateLimitingInterface[string]

	httprouteLister       gatewaylisters.HTTPRouteLister
	httprouteListerSynced cache.InformerSynced

	grpcrouteLister       gatewaylisters.GRPCRouteLister
	grpcrouteListerSynced cache.InformerSynced

	referenceGrantLister       gatewaylisters.ReferenceGrantLister
	referenceGrantListerSynced cache.InformerSynced

	xdscache        cachev3.SnapshotCache
	xdsserver       serverv3.Server
	xdsLocalAddress string
	xdsLocalPort    int
	xdsVersion      atomic.Uint64

	tunnelManager *tunnels.TunnelManager
}

func New(
	clusterName string,
	client *kubernetes.Clientset,
	gwClient *gatewayclient.Clientset,
	namespaceInformer corev1informers.NamespaceInformer,
	serviceInformer corev1informers.ServiceInformer,
	secretInformer corev1informers.SecretInformer,
	gatewayClassInformer gatewayinformers.GatewayClassInformer,
	gatewayInformer gatewayinformers.GatewayInformer,
	httprouteInformer gatewayinformers.HTTPRouteInformer,
	grpcrouteInformer gatewayinformers.GRPCRouteInformer,
	referenceGrantInformer gatewayinformers.ReferenceGrantInformer,
) (*Controller, error) {
	c := &Controller{
		clusterName:              clusterName,
		client:                   client,
		namespaceLister:          namespaceInformer.Lister(),
		namespaceListerSynced:    namespaceInformer.Informer().HasSynced,
		serviceLister:            serviceInformer.Lister(),
		serviceListerSynced:      serviceInformer.Informer().HasSynced,
		secretLister:             secretInformer.Lister(),
		secretListerSynced:       secretInformer.Informer().HasSynced,
		gwClient:                 gwClient,
		gatewayClassLister:       gatewayClassInformer.Lister(),
		gatewayClassListerSynced: gatewayClassInformer.Informer().HasSynced,
		gatewayLister:            gatewayInformer.Lister(),
		gatewayListerSynced:      gatewayInformer.Informer().HasSynced,
		gatewayqueue: workqueue.NewTypedRateLimitingQueueWithConfig(
			workqueue.DefaultTypedControllerRateLimiter[string](),
			workqueue.TypedRateLimitingQueueConfig[string]{Name: "gateway"},
		),
		httprouteLister:            httprouteInformer.Lister(),
		httprouteListerSynced:      httprouteInformer.Informer().HasSynced,
		grpcrouteLister:            grpcrouteInformer.Lister(),
		grpcrouteListerSynced:      grpcrouteInformer.Informer().HasSynced,
		referenceGrantLister:       referenceGrantInformer.Lister(),
		referenceGrantListerSynced: referenceGrantInformer.Informer().HasSynced,
	}
	_, err := gatewayClassInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
			if err == nil {
				c.syncGatewayClass(key)
			}
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(newObj)
			if err == nil {
				c.syncGatewayClass(key)
			}
		},
		DeleteFunc: func(obj interface{}) {
			key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
			if err == nil {
				c.syncGatewayClass(key)
			}
		},
	})
	if err != nil {
		return nil, err
	}

	_, err = gatewayInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			gw := obj.(*gatewayv1.Gateway)
			if gw.Spec.GatewayClassName != GWClassName {
				return
			}
			key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
			if err == nil {
				c.gatewayqueue.Add(key)
			}
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			gw := newObj.(*gatewayv1.Gateway)
			if gw.Spec.GatewayClassName != GWClassName {
				return
			}
			key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(newObj)
			if err == nil {
				c.gatewayqueue.Add(key)
			}
		},
		DeleteFunc: func(obj interface{}) {
			gw := obj.(*gatewayv1.Gateway)
			if gw.Spec.GatewayClassName != GWClassName {
				return
			}
			key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
			if err == nil {
				c.gatewayqueue.Add(key)
			}
		},
	})
	if err != nil {
		return nil, err
	}

	_, err = httprouteInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			httproute := obj.(*gatewayv1.HTTPRoute)
			c.processGateways(httproute.Spec.ParentRefs, httproute.Namespace)
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			oldHTTPRoute := oldObj.(*gatewayv1.HTTPRoute)
			newHTTPRoute := newObj.(*gatewayv1.HTTPRoute)
			c.processGateways(append(oldHTTPRoute.Spec.ParentRefs, newHTTPRoute.Spec.ParentRefs...), newHTTPRoute.Namespace)
		},
		DeleteFunc: func(obj interface{}) {
			httproute, ok := obj.(*gatewayv1.HTTPRoute)
			if !ok {
				tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
				if !ok {
					runtime.HandleError(fmt.Errorf("couldn't get object from tombstone %#v", obj))
					return
				}
				httproute, ok = tombstone.Obj.(*gatewayv1.HTTPRoute)
				if !ok {
					runtime.HandleError(fmt.Errorf("tombstone contained object that is not a HTTPRoute: %#v", obj))
					return
				}
			}
			c.processGateways(httproute.Spec.ParentRefs, httproute.Namespace)
		},
	})
	if err != nil {
		return nil, err
	}

	_, err = grpcrouteInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			grpcroute := obj.(*gatewayv1.GRPCRoute)
			c.processGateways(grpcroute.Spec.ParentRefs, grpcroute.Namespace)
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			oldGRPCRoute := oldObj.(*gatewayv1.GRPCRoute)
			newGRPCRoute := newObj.(*gatewayv1.GRPCRoute)
			c.processGateways(append(oldGRPCRoute.Spec.ParentRefs, newGRPCRoute.Spec.ParentRefs...), newGRPCRoute.Namespace)
		},
		DeleteFunc: func(obj interface{}) {
			grpcroute, ok := obj.(*gatewayv1.GRPCRoute)
			if !ok {
				tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
				if !ok {
					runtime.HandleError(fmt.Errorf("couldn't get object from tombstone %#v", obj))
					return
				}
				grpcroute, ok = tombstone.Obj.(*gatewayv1.GRPCRoute)
				if !ok {
					runtime.HandleError(fmt.Errorf("tombstone contained object that is not a GRPCRoute: %#v", obj))
					return
				}
			}
			c.processGateways(grpcroute.Spec.ParentRefs, grpcroute.Namespace)
		},
	})
	if err != nil {
		return nil, err
	}

	_, err = referenceGrantInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    c.processReferenceGrant,
		UpdateFunc: func(old, new interface{}) { c.processReferenceGrant(new) },
		DeleteFunc: c.processReferenceGrant,
	})
	if err != nil {
		return nil, err
	}

	if config.DefaultConfig.LoadBalancerConnectivity == config.Tunnel {
		c.tunnelManager = tunnels.NewTunnelManager()
	}

	return c, nil
}

func (c *Controller) Init(ctx context.Context) error {
	defer runtime.HandleCrashWithContext(ctx)

	klog.Info("Waiting for gateway informer caches to sync")
	if !cache.WaitForNamedCacheSync(controllerName, ctx.Done(),
		c.gatewayClassListerSynced,
		c.gatewayListerSynced,
		c.httprouteListerSynced,
		c.grpcrouteListerSynced,
		c.namespaceListerSynced,
		c.serviceListerSynced,
		c.secretListerSynced,
		c.referenceGrantListerSynced,
	) {
		return fmt.Errorf("timed out waiting for caches to sync")
	}

	kindGwClass := gatewayv1.GatewayClass{
		ObjectMeta: metav1.ObjectMeta{
			Name: GWClassName,
		},
		Spec: gatewayv1.GatewayClassSpec{
			ControllerName: controllerName,
			Description:    ptr.To("cloud-provider-kind gateway API"),
		},
	}

	_, err := c.gwClient.GatewayV1().GatewayClasses().Get(ctx, GWClassName, metav1.GetOptions{})
	if apierrors.IsNotFound(err) {
		_, err = c.gwClient.GatewayV1().GatewayClasses().Create(ctx, &kindGwClass, metav1.CreateOptions{})
		if err != nil {
			return fmt.Errorf("failed to create cloud-provider-kind GatewayClass: %w", err)
		}
	} else if err != nil {
		return fmt.Errorf("failed to get cloud-provider-kind GatewayClass: %w", err)
	}

	// This is kind/kubeadm specific as it uses kube-system/kube-dns as the nameserver
	err = wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, 10*time.Second, true, func(ctx context.Context) (bool, error) {
		svc, err := c.serviceLister.Services(metav1.NamespaceSystem).Get("kube-dns")
		if err != nil {
			return false, nil
		}
		if svc.Spec.ClusterIP == "" {
			return false, nil
		}
		c.clusterNameserver = svc.Spec.ClusterIP
		return true, nil
	})

	if err != nil {
		return fmt.Errorf("failed to get kube-dns service: %w", err)
	}
	return nil
}

func (c *Controller) syncGatewayClass(key string) {
	startTime := time.Now()
	klog.V(2).Infof("Started syncing gatewayclass %q (%v)", key, time.Since(startTime))
	defer func() {
		klog.V(2).Infof("Finished syncing gatewayclass %q (%v)", key, time.Since(startTime))
	}()

	gwc, err := c.gatewayClassLister.Get(key)
	if err != nil {
		if apierrors.IsNotFound(err) {
			klog.V(2).Infof("GatewayClass %q has been deleted", key)
		}
		return
	}

	// We only care about the GatewayClass that matches our controller name.
	if gwc.Spec.ControllerName != controllerName {
		return
	}

	newGwc := gwc.DeepCopy()
	// Set the "Accepted" condition to True and update the observedGeneration.
	meta.SetStatusCondition(&newGwc.Status.Conditions, metav1.Condition{
		Type:               string(gatewayv1.GatewayClassConditionStatusAccepted),
		Status:             metav1.ConditionTrue,
		Reason:             string(gatewayv1.GatewayClassReasonAccepted),
		Message:            "GatewayClass is accepted by this controller.",
		ObservedGeneration: gwc.Generation,
	})

	// Update the status on the API server.
	if _, err := c.gwClient.GatewayV1().GatewayClasses().UpdateStatus(context.Background(), newGwc, metav1.UpdateOptions{}); err != nil {
		klog.Errorf("failed to update gatewayclass status: %v", err)
	}
}

func (c *Controller) Run(ctx context.Context) error {
	logger := klog.FromContext(ctx).WithName("gateway")
	ctx = klog.NewContext(ctx, logger)
	defer runtime.HandleCrashWithContext(ctx)

	logger.Info("Starting Envoy proxy controller")
	c.xdscache = cachev3.NewSnapshotCache(false, cachev3.IDHash{}, nil)
	c.xdsserver = serverv3.NewServer(ctx, c.xdscache, &xdsCallbacks{})

	var grpcOptions []grpc.ServerOption
	grpcOptions = append(grpcOptions,
		grpc.MaxConcurrentStreams(grpcMaxConcurrentStreams),
		grpc.KeepaliveParams(keepalive.ServerParameters{
			Time:    grpcKeepaliveTime,
			Timeout: grpcKeepaliveTimeout,
		}),
		grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
			MinTime:             grpcKeepaliveMinTime,
			PermitWithoutStream: true,
		}),
	)
	grpcServer := grpc.NewServer(grpcOptions...)

	discoveryv3.RegisterAggregatedDiscoveryServiceServer(grpcServer, c.xdsserver)
	endpointv3.RegisterEndpointDiscoveryServiceServer(grpcServer, c.xdsserver)
	clusterv3service.RegisterClusterDiscoveryServiceServer(grpcServer, c.xdsserver)
	routev3service.RegisterRouteDiscoveryServiceServer(grpcServer, c.xdsserver)
	listenerv3service.RegisterListenerDiscoveryServiceServer(grpcServer, c.xdsserver)
	secretv3.RegisterSecretDiscoveryServiceServer(grpcServer, c.xdsserver)
	runtimev3.RegisterRuntimeDiscoveryServiceServer(grpcServer, c.xdsserver)

	address, err := GetControlPlaneAddress()
	if err != nil {
		return err
	}
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:0", address))
	if err != nil {
		return err
	}
	defer listener.Close()

	addr := listener.Addr()
	tcpAddr, ok := addr.(*net.TCPAddr)
	if !ok {
		return fmt.Errorf("could not assert listener address to TCPAddr: %s", addr.String())
	}

	c.xdsLocalAddress = address
	c.xdsLocalPort = tcpAddr.Port
	go func() {
		logger.Info(
			"XDS management server listening",
			"address", c.xdsLocalAddress,
			"port", c.xdsLocalPort)
		if err = grpcServer.Serve(listener); err != nil {
			logger.Error(err, "gRPC server error:")
		}
		grpcServer.Stop()
	}()

	defer c.gatewayqueue.ShutDown()
	logger.Info("Starting Gateway API controller")

	for i := 0; i < workers; i++ {
		go wait.UntilWithContext(ctx, c.runGatewayWorker, time.Second)
	}

	<-ctx.Done()
	logger.Info("Stopping Gateway API controller")
	return nil
}

func (c *Controller) processGateways(references []gatewayv1.ParentReference, localNamespace string) {
	gatewaysToEnqueue := make(map[string]struct{})
	for _, ref := range references {
		if (ref.Group != nil && string(*ref.Group) != gatewayv1.GroupName) ||
			(ref.Kind != nil && string(*ref.Kind) != "Gateway") {
			continue
		}
		namespace := localNamespace
		if ref.Namespace != nil {
			namespace = string(*ref.Namespace)
		}
		key := namespace + "/" + string(ref.Name)
		gatewaysToEnqueue[key] = struct{}{}
	}

	for key := range gatewaysToEnqueue {
		c.gatewayqueue.Add(key)
	}
}

// processReferenceGrant finds all Gateways that may be affected by a change to a
// ReferenceGrant and enqueues them for reconciliation. This function handles grants
// for both cross-namespace BackendRefs (from Routes) and cross-namespace
// SecretRefs (from Gateways).
func (c *Controller) processReferenceGrant(obj interface{}) {
	grant, ok := obj.(*gatewayv1.ReferenceGrant)
	if !ok {
		tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
		if !ok {
			klog.Errorf("error decoding object, invalid type")
			return
		}
		grant, ok = tombstone.Obj.(*gatewayv1.ReferenceGrant)
		if !ok {
			klog.Errorf("error decoding object tombstone, invalid type")
			return
		}
	}

	gatewaysToEnqueue := make(map[string]struct{})
	// The ReferenceGrant lives in the namespace of the resource being referenced (the "To" side).
	targetNamespace := grant.Namespace

	// Check for Grants allowing Routes to reference Services
	for _, from := range grant.Spec.From {
		// As the controller supports more route types, they should be added here.
		if !CloudProviderSupportedKinds.Has(from.Kind) {
			continue
		}

		// Check if the grant allows references TO a Service.
		isServiceGrant := false
		for _, to := range grant.Spec.To {
			if to.Kind == "Service" {
				isServiceGrant = true
				break
			}
		}
		if !isServiceGrant {
			continue
		}

		// Find all routes in the "From" namespace that could be affected.
		httpRoutes, err := c.httprouteLister.HTTPRoutes(string(from.Namespace)).List(labels.Everything())
		if err != nil {
			klog.Errorf("Failed to list HTTPRoutes in namespace %s: %v", from.Namespace, err)
			continue
		}

		for _, route := range httpRoutes {
			if routeReferencesBackendInNamespace(route, targetNamespace) {
				// This route is affected. Find its parent Gateways and add them to the queue.
				for _, parentRef := range route.Spec.ParentRefs {
					if (parentRef.Group != nil && string(*parentRef.Group) != gatewayv1.GroupName) ||
						(parentRef.Kind != nil && string(*parentRef.Kind) != "Gateway") {
						continue
					}
					gwNamespace := route.Namespace
					if parentRef.Namespace != nil {
						gwNamespace = string(*parentRef.Namespace)
					}
					key := gwNamespace + "/" + string(parentRef.Name)
					gatewaysToEnqueue[key] = struct{}{}
				}
			}
		}
	}

	// Check for Grants allowing Gateways to reference Secrets
	for _, from := range grant.Spec.From {
		// We are looking for grants FROM Gateways.
		if from.Group != gatewayv1.GroupName || from.Kind != "Gateway" {
			continue
		}

		// Check if the grant allows references TO a Secret.
		isSecretGrant := false
		for _, to := range grant.Spec.To {
			if to.Kind == "Secret" {
				isSecretGrant = true
				break
			}
		}
		if !isSecretGrant {
			continue
		}

		// Find all Gateways in the "From" namespace that could be affected.
		gateways, err := c.gatewayLister.Gateways(string(from.Namespace)).List(labels.Everything())
		if err != nil {
			klog.Errorf("Failed to list Gateways in namespace %s: %v", from.Namespace, err)
			continue
		}

		for _, gw := range gateways {
			if gatewayReferencesSecretInNamespace(gw, targetNamespace) {
				// This Gateway is affected. Add it to the queue.
				key := gw.Namespace + "/" + gw.Name
				gatewaysToEnqueue[key] = struct{}{}
			}
		}
	}

	// Enqueue all unique Gateways that were found to be affected.
	for key := range gatewaysToEnqueue {
		c.gatewayqueue.Add(key)
	}
}

// routeReferencesBackendInNamespace is a helper to check if an HTTPRoute has a backendRef
// pointing to the specified namespace.
func routeReferencesBackendInNamespace(route *gatewayv1.HTTPRoute, namespace string) bool {
	for _, rule := range route.Spec.Rules {
		for _, backendRef := range rule.BackendRefs {
			backendNamespace := route.Namespace
			if backendRef.Namespace != nil {
				backendNamespace = string(*backendRef.Namespace)
			}
			if backendNamespace == namespace {
				return true
			}
		}
	}
	return false
}

// gatewayReferencesSecretInNamespace is a helper to check if a Gateway has a certificateRef
// pointing to a Secret in the specified namespace.
func gatewayReferencesSecretInNamespace(gateway *gatewayv1.Gateway, namespace string) bool {
	for _, listener := range gateway.Spec.Listeners {
		if listener.TLS == nil {
			continue
		}
		for _, certRef := range listener.TLS.CertificateRefs {
			// We only care about references to Secrets.
			if (certRef.Group != nil && *certRef.Group != "") || (certRef.Kind != nil && *certRef.Kind != "Secret") {
				continue
			}
			secretNamespace := gateway.Namespace
			if certRef.Namespace != nil {
				secretNamespace = string(*certRef.Namespace)
			}
			if secretNamespace == namespace {
				return true
			}
		}
	}
	return false
}

func (c *Controller) runGatewayWorker(ctx context.Context) {
	for c.processNextGatewayItem(ctx) {
	}
}

func (c *Controller) processNextGatewayItem(ctx context.Context) bool {
	key, quit := c.gatewayqueue.Get()
	if quit {
		return false
	}
	defer c.gatewayqueue.Done(key)

	err := c.syncGateway(ctx, key)
	c.handleGatewayErr(err, key)
	return true
}

func (c *Controller) handleGatewayErr(err error, key string) {
	if err == nil {
		c.gatewa
Download .txt
gitextract_ix7p8aoy/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── bats.yml
│       ├── gateway.yml
│       ├── ingress.yml
│       ├── k8s.yml
│       ├── release.yml
│       └── test.yaml
├── .gitignore
├── .golangci.yaml
├── .goreleaser.yaml
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── Makefile
├── OWNERS
├── README.md
├── SECURITY.md
├── SECURITY_CONTACTS
├── cloudbuild.yaml
├── cmd/
│   └── app.go
├── code-of-conduct.md
├── compose.yml
├── docs/
│   ├── .nojekyll
│   ├── HOWTO.md
│   ├── README.md
│   ├── _coverpage.md
│   ├── _sidebar.md
│   ├── code-of-conduct.md
│   ├── contributing/
│   │   └── CONTRIBUTING.md
│   ├── index.html
│   └── user/
│       ├── example/
│       │   ├── creating_gateway_http_route.md
│       │   ├── enable_lb_port_mapping.md
│       │   └── service_expose_via_loadbalancer.md
│       ├── gateway/
│       │   ├── allow_load_balancer_access_control_plane.md
│       │   ├── configure_proxy_image_registry.md
│       │   ├── gatewayapi.md
│       │   └── running_the_provider.md
│       ├── howto.md
│       ├── ingress/
│       │   └── ingress.md
│       ├── install/
│       │   ├── install_docker.md
│       │   └── install_go.md
│       ├── os_support.md
│       └── support/
│           └── os_support.md
├── examples/
│   ├── gateway_httproute_simple.yaml
│   ├── ingress_foo_bar.yaml
│   ├── loadbalancer_affinity.yaml
│   ├── loadbalancer_deployment.yaml
│   ├── loadbalancer_etp_cluster.yaml
│   ├── loadbalancer_etp_local.yaml
│   ├── loadbalancer_multiport.yaml
│   ├── loadbalancer_static_ip.yaml
│   ├── loadbalancer_stts.yaml
│   └── loadbalancer_udp_tcp.yaml
├── go.mod
├── go.sum
├── hack/
│   ├── build-route-adder.sh
│   ├── ci/
│   │   ├── add-kubernetes-to-workspace.sh
│   │   └── e2e.sh
│   ├── download-gateway-crds.sh
│   ├── init-buildx.sh
│   └── lint.sh
├── internal/
│   └── routeadder/
│       └── main.go
├── kind.yaml
├── main.go
├── pkg/
│   ├── config/
│   │   └── config.go
│   ├── constants/
│   │   └── constants.go
│   ├── container/
│   │   └── container.go
│   ├── controller/
│   │   ├── controller.go
│   │   ├── http.go
│   │   └── http_test.go
│   ├── gateway/
│   │   ├── backendref.go
│   │   ├── backendref_test.go
│   │   ├── controller.go
│   │   ├── controller_test.go
│   │   ├── crd_manager.go
│   │   ├── crd_manager_test.go
│   │   ├── crds/
│   │   │   ├── README.md
│   │   │   ├── experimental/
│   │   │   │   ├── gateway.networking.k8s.io_backendtlspolicies.yaml
│   │   │   │   ├── gateway.networking.k8s.io_gatewayclasses.yaml
│   │   │   │   ├── gateway.networking.k8s.io_gateways.yaml
│   │   │   │   ├── gateway.networking.k8s.io_grpcroutes.yaml
│   │   │   │   ├── gateway.networking.k8s.io_httproutes.yaml
│   │   │   │   ├── gateway.networking.k8s.io_listenersets.yaml
│   │   │   │   ├── gateway.networking.k8s.io_referencegrants.yaml
│   │   │   │   ├── gateway.networking.k8s.io_tcproutes.yaml
│   │   │   │   ├── gateway.networking.k8s.io_tlsroutes.yaml
│   │   │   │   ├── gateway.networking.k8s.io_udproutes.yaml
│   │   │   │   ├── gateway.networking.k8s.io_vap_safeupgrades.yaml
│   │   │   │   ├── gateway.networking.x-k8s.io_xbackendtrafficpolicies.yaml
│   │   │   │   ├── gateway.networking.x-k8s.io_xlistenersets.yaml
│   │   │   │   ├── gateway.networking.x-k8s.io_xmeshes.yaml
│   │   │   │   └── kustomization.yaml
│   │   │   ├── kustomization.yaml
│   │   │   └── standard/
│   │   │       ├── gateway.networking.k8s.io_backendtlspolicies.yaml
│   │   │       ├── gateway.networking.k8s.io_gatewayclasses.yaml
│   │   │       ├── gateway.networking.k8s.io_gateways.yaml
│   │   │       ├── gateway.networking.k8s.io_grpcroutes.yaml
│   │   │       ├── gateway.networking.k8s.io_httproutes.yaml
│   │   │       ├── gateway.networking.k8s.io_listenersets.yaml
│   │   │       ├── gateway.networking.k8s.io_referencegrants.yaml
│   │   │       ├── gateway.networking.k8s.io_tlsroutes.yaml
│   │   │       └── gateway.networking.k8s.io_vap_safeupgrades.yaml
│   │   ├── envoy.go
│   │   ├── envoy_test.go
│   │   ├── gateway.go
│   │   ├── gateway_test.go
│   │   ├── grpcroute.go
│   │   ├── httproute.go
│   │   ├── kindcluster.go
│   │   ├── listener.go
│   │   ├── referencegrant.go
│   │   ├── referencegrant_test.go
│   │   ├── routeadder/
│   │   │   ├── README.md
│   │   │   ├── route-adder-amd64
│   │   │   └── route-adder-arm64
│   │   ├── routes.go
│   │   └── routes_test.go
│   ├── ingress/
│   │   ├── README.md
│   │   ├── controller.go
│   │   └── controller_test.go
│   ├── loadbalancer/
│   │   ├── proxy.go
│   │   ├── proxy_test.go
│   │   ├── server.go
│   │   └── server_test.go
│   ├── provider/
│   │   ├── cloud.go
│   │   ├── clusters.go
│   │   ├── instances.go
│   │   └── loadbalancer.go
│   └── tunnels/
│       ├── address_darwin.go
│       ├── address_other.go
│       ├── address_windows.go
│       └── tunnel.go
└── tests/
    ├── README.md
    ├── custom-network/
    │   ├── setup_suite.bash
    │   └── tests.bats
    ├── kind.yaml
    ├── setup_suite.bash
    └── tests.bats
Download .txt
SYMBOL INDEX (294 symbols across 40 files)

FILE: cmd/app.go
  function Main (line 34) | func Main() {
  function NewCommand (line 41) | func NewCommand() *cobra.Command {
  function newListImagesCommand (line 65) | func newListImagesCommand() *cobra.Command {
  function runE (line 75) | func runE(cmd *cobra.Command, args []string) error {
  function isWSL2 (line 185) | func isWSL2() bool {

FILE: internal/routeadder/main.go
  function main (line 13) | func main() {

FILE: main.go
  function main (line 7) | func main() {

FILE: pkg/config/config.go
  function newDefaultConfig (line 13) | func newDefaultConfig() *Config {
  type Config (line 29) | type Config struct
  type Connectivity (line 46) | type Connectivity
  constant Unknown (line 49) | Unknown Connectivity = iota
  constant Direct (line 50) | Direct
  constant Portmap (line 51) | Portmap
  constant Tunnel (line 52) | Tunnel
  type GatewayReleaseChannel (line 55) | type GatewayReleaseChannel
  constant Standard (line 58) | Standard     GatewayReleaseChannel = "standard"
  constant Experimental (line 59) | Experimental GatewayReleaseChannel = "experimental"
  constant Disabled (line 60) | Disabled     GatewayReleaseChannel = "disabled"

FILE: pkg/constants/constants.go
  constant ProviderName (line 4) | ProviderName = "kind"
  constant ContainerPrefix (line 6) | ContainerPrefix = "kindccm"
  constant DefaultProxyImageRegistry (line 8) | DefaultProxyImageRegistry = "docker.io"
  constant DefaultProxyImageName (line 10) | DefaultProxyImageName = "envoyproxy/envoy:v1.33.2"
  constant FixedNetworkName (line 12) | FixedNetworkName = "kind"
  constant NodeCCMLabelKey (line 14) | NodeCCMLabelKey = "io.x-k8s.cloud-provider-kind.cluster"
  constant LoadBalancerNameLabelKey (line 16) | LoadBalancerNameLabelKey = "io.x-k8s.cloud-provider-kind.loadbalancer.name"
  constant GatewayNameLabelKey (line 18) | GatewayNameLabelKey = "io.x-k8s.cloud-provider-kind.gateway.name"

FILE: pkg/container/container.go
  function dockerIsAvailable (line 19) | func dockerIsAvailable() bool {
  function podmanIsAvailable (line 28) | func podmanIsAvailable() bool {
  function nerdctlIsAvailable (line 37) | func nerdctlIsAvailable() bool {
  function Runtime (line 53) | func Runtime() string {
  function init (line 57) | func init() {
  function Logs (line 81) | func Logs(name string, w io.Writer) error {
  function LogDump (line 92) | func LogDump(containerName string, fileName string) error {
  function Create (line 106) | func Create(name string, args []string) error {
  function Restart (line 113) | func Restart(name string) error {
  function Delete (line 120) | func Delete(name string) error {
  function IsRunning (line 127) | func IsRunning(name string) bool {
  function Exist (line 136) | func Exist(name string) bool {
  function Signal (line 141) | func Signal(name string, signal string) error {
  function Exec (line 146) | func Exec(name string, command []string, stdin io.Reader, stdout io.Writ...
  function IPs (line 166) | func IPs(name string) (ipv4 string, ipv6 string, err error) {
  function PortMaps (line 187) | func PortMaps(name string) (map[string]string, error) {
  function ListByLabel (line 236) | func ListByLabel(label string) ([]string, error) {
  function GetLabelValue (line 251) | func GetLabelValue(name string, label string) (string, error) {

FILE: pkg/controller/controller.go
  type Controller (line 42) | type Controller struct
    method Run (line 63) | func (c *Controller) Run(ctx context.Context) {
    method getKubeConfig (line 124) | func (c *Controller) getKubeConfig(cluster string, internal bool) (*re...
    method getRestConfig (line 141) | func (c *Controller) getRestConfig(ctx context.Context, cluster string...
    method cleanup (line 422) | func (c *Controller) cleanup() {
  type ccm (line 47) | type ccm struct
  function New (line 55) | func New(provider *cluster.Provider) *Controller {
  function startCloudControllerManager (line 195) | func startCloudControllerManager(ctx context.Context, clusterName string...
  function getCloudProviderTaint (line 444) | func getCloudProviderTaint(ctx context.Context, clusterName string, kube...
  function cleanupLoadBalancer (line 459) | func cleanupLoadBalancer(lbController cloudprovider.LoadBalancer, name s...
  function cleanupGateway (line 478) | func cleanupGateway(name string) {

FILE: pkg/controller/http.go
  function probeHTTP (line 15) | func probeHTTP(ctx context.Context, address string) bool {
  function firstSuccessfulProbe (line 42) | func firstSuccessfulProbe(ctx context.Context, addresses []string) (stri...

FILE: pkg/controller/http_test.go
  function Test_firstSuccessfulProbe (line 11) | func Test_firstSuccessfulProbe(t *testing.T) {

FILE: pkg/gateway/backendref.go
  function backendRefToClusterName (line 11) | func backendRefToClusterName(defaultNamespace string, backendRef gateway...

FILE: pkg/gateway/backendref_test.go
  function TestBackendRefToClusterName (line 11) | func TestBackendRefToClusterName(t *testing.T) {

FILE: pkg/gateway/controller.go
  constant controllerName (line 65) | controllerName = "kind.sigs.k8s.io/gateway-controller"
  constant GWClassName (line 66) | GWClassName    = "cloud-provider-kind"
  constant maxRetries (line 67) | maxRetries     = 5
  constant workers (line 68) | workers        = 5
  constant grpcKeepaliveTime (line 72) | grpcKeepaliveTime        = 30 * time.Second
  constant grpcKeepaliveTimeout (line 73) | grpcKeepaliveTimeout     = 5 * time.Second
  constant grpcKeepaliveMinTime (line 74) | grpcKeepaliveMinTime     = 30 * time.Second
  constant grpcMaxConcurrentStreams (line 75) | grpcMaxConcurrentStreams = 1000000
  type ControllerError (line 78) | type ControllerError struct
    method Error (line 84) | func (e *ControllerError) Error() string {
  type Controller (line 88) | type Controller struct
    method Init (line 305) | func (c *Controller) Init(ctx context.Context) error {
    method syncGatewayClass (line 361) | func (c *Controller) syncGatewayClass(key string) {
    method Run (line 397) | func (c *Controller) Run(ctx context.Context) error {
    method processGateways (line 469) | func (c *Controller) processGateways(references []gatewayv1.ParentRefe...
    method processReferenceGrant (line 493) | func (c *Controller) processReferenceGrant(obj interface{}) {
    method runGatewayWorker (line 639) | func (c *Controller) runGatewayWorker(ctx context.Context) {
    method processNextGatewayItem (line 644) | func (c *Controller) processNextGatewayItem(ctx context.Context) bool {
    method handleGatewayErr (line 656) | func (c *Controller) handleGatewayErr(err error, key string) {
    method UpdateXDSServer (line 721) | func (c *Controller) UpdateXDSServer(ctx context.Context, nodeid strin...
  function New (line 129) | func New(
  function routeReferencesBackendInNamespace (line 600) | func routeReferencesBackendInNamespace(route *gatewayv1.HTTPRoute, names...
  function gatewayReferencesSecretInNamespace (line 617) | func gatewayReferencesSecretInNamespace(gateway *gatewayv1.Gateway, name...
  function GetControlPlaneAddress (line 673) | func GetControlPlaneAddress() (string, error) {
  type xdsCallbacks (line 740) | type xdsCallbacks struct
    method OnStreamOpen (line 742) | func (cb *xdsCallbacks) OnStreamOpen(ctx context.Context, id int64, ty...
    method OnStreamClosed (line 746) | func (cb *xdsCallbacks) OnStreamClosed(id int64, node *corev3.Node) {
    method OnStreamRequest (line 753) | func (cb *xdsCallbacks) OnStreamRequest(id int64, req *discoveryv3.Dis...
    method OnStreamResponse (line 757) | func (cb *xdsCallbacks) OnStreamResponse(ctx context.Context, id int64...
    method OnFetchRequest (line 760) | func (cb *xdsCallbacks) OnFetchRequest(ctx context.Context, req *disco...
    method OnFetchResponse (line 764) | func (cb *xdsCallbacks) OnFetchResponse(req *discoveryv3.DiscoveryRequ...
    method OnStreamDeltaRequest (line 767) | func (cb *xdsCallbacks) OnStreamDeltaRequest(id int64, req *discoveryv...
    method OnStreamDeltaResponse (line 770) | func (cb *xdsCallbacks) OnStreamDeltaResponse(id int64, req *discovery...
    method OnDeltaStreamClosed (line 772) | func (cb *xdsCallbacks) OnDeltaStreamClosed(int64, *corev3.Node) {}
    method OnDeltaStreamOpen (line 773) | func (cb *xdsCallbacks) OnDeltaStreamOpen(context.Context, int64, stri...

FILE: pkg/gateway/crd_manager.go
  constant crdsDir (line 27) | crdsDir = "crds"
  constant crdKind (line 29) | crdKind       = "CustomResourceDefinition"
  constant crdResource (line 30) | crdResource   = "customresourcedefinitions"
  constant crdGroup (line 31) | crdGroup      = "apiextensions.k8s.io"
  constant crdVersion (line 32) | crdVersion    = "v1"
  constant crdAPIVersion (line 33) | crdAPIVersion = "apiextensions.k8s.io/v1"
  type CRDManager (line 37) | type CRDManager struct
    method InstallCRDs (line 55) | func (m *CRDManager) InstallCRDs(ctx context.Context, channelDir confi...
  function NewCRDManager (line 43) | func NewCRDManager(config *rest.Config) (*CRDManager, error) {

FILE: pkg/gateway/crd_manager_test.go
  function TestYAMLFilesApply (line 11) | func TestYAMLFilesApply(t *testing.T) {

FILE: pkg/gateway/envoy.go
  constant proxyConfigPath (line 23) | proxyConfigPath = "/home/envoy/envoy.yaml"
  constant envoyAdminPort (line 24) | envoyAdminPort  = 10000
  constant dockerInternal (line 28) | dockerInternal = "host.docker.internal"
  constant limaInternal (line 29) | limaInternal   = "host.lima.internal"
  constant dynamicControlPlaneConfig (line 33) | dynamicControlPlaneConfig = `node:
  type configData (line 85) | type configData struct
  function generateEnvoyConfig (line 94) | func generateEnvoyConfig(data *configData) (string, error) {
  function gatewayName (line 117) | func gatewayName(clusterName, namespace, name string) string {
  function gatewaySimpleName (line 127) | func gatewaySimpleName(clusterName, namespace, name string) string {
  function createGateway (line 132) | func createGateway(clusterName string, nameserver string, localAddress s...

FILE: pkg/gateway/envoy_test.go
  function TestGenerateEnvoyConfigTable (line 5) | func TestGenerateEnvoyConfigTable(t *testing.T) {

FILE: pkg/gateway/gateway.go
  method syncGateway (line 48) | func (c *Controller) syncGateway(ctx context.Context, key string) error {
  method buildEnvoyResourcesForGateway (line 135) | func (c *Controller) buildEnvoyResourcesForGateway(gateway *gatewayv1.Ga...
  function getSupportedKinds (line 396) | func getSupportedKinds(listener gatewayv1.Listener) ([]gatewayv1.RouteGr...
  method updateRouteStatuses (line 434) | func (c *Controller) updateRouteStatuses(
  method getHTTPRoutesForGateway (line 478) | func (c *Controller) getHTTPRoutesForGateway(gw *gatewayv1.Gateway) []*g...
  method validateHTTPRoute (line 506) | func (c *Controller) validateHTTPRoute(
  method areBackendsValid (line 612) | func (c *Controller) areBackendsValid(httpRoute *gatewayv1.HTTPRoute) bo...
  function ruleHasRedirectFilter (line 631) | func ruleHasRedirectFilter(rule gatewayv1.HTTPRouteRule) bool {
  method translateBackendRefToCluster (line 640) | func (c *Controller) translateBackendRefToCluster(defaultNamespace strin...
  method deleteGatewayResources (line 692) | func (c *Controller) deleteGatewayResources(ctx context.Context, name, n...
  function createClusterLoadAssignment (line 727) | func createClusterLoadAssignment(clusterName, serviceHost string, servic...
  function getRouteHostnames (line 756) | func getRouteHostnames(routeHostnames []gatewayv1.Hostname, listener gat...
  function setGatewayConditions (line 772) | func setGatewayConditions(newGw *gatewayv1.Gateway, listenerStatuses []g...

FILE: pkg/gateway/gateway_test.go
  function Test_getSupportedKinds (line 11) | func Test_getSupportedKinds(t *testing.T) {

FILE: pkg/gateway/grpcroute.go
  function translateGRPCRouteToEnvoyRoutes (line 18) | func translateGRPCRouteToEnvoyRoutes(
  function buildGRPCRouteAction (line 97) | func buildGRPCRouteAction(
  function translateGRPCRouteMatch (line 151) | func translateGRPCRouteMatch(match gatewayv1.GRPCRouteMatch, generation ...

FILE: pkg/gateway/httproute.go
  function translateHTTPRouteToEnvoyRoutes (line 22) | func translateHTTPRouteToEnvoyRoutes(
  function buildHTTPRouteAction (line 154) | func buildHTTPRouteAction(namespace string, backendRefs []gatewayv1.HTTP...
  function translateHTTPRouteMatch (line 229) | func translateHTTPRouteMatch(match gatewayv1.HTTPRouteMatch, generation ...
  function createSuccessCondition (line 318) | func createSuccessCondition(generation int64) metav1.Condition {
  function createFailureCondition (line 328) | func createFailureCondition(reason gatewayv1.RouteConditionReason, messa...
  function sortRoutes (line 339) | func sortRoutes(routes []*routev3.Route) {
  function getPathMatchValue (line 389) | func getPathMatchValue(match *routev3.RouteMatch) string {
  function isCatchAll (line 413) | func isCatchAll(match *routev3.RouteMatch) bool {

FILE: pkg/gateway/kindcluster.go
  function getRouteAdderBinaryForArch (line 28) | func getRouteAdderBinaryForArch() ([]byte, error) {
  method getClusterRoutingMap (line 43) | func (c *Controller) getClusterRoutingMap(ctx context.Context) (map[stri...
  method getServiceCIDRs (line 117) | func (c *Controller) getServiceCIDRs(ctx context.Context) ([]string, err...
  method configureContainerNetworking (line 136) | func (c *Controller) configureContainerNetworking(ctx context.Context, c...
  method ensureGatewayContainer (line 183) | func (c *Controller) ensureGatewayContainer(ctx context.Context, gw *gat...

FILE: pkg/gateway/listener.go
  function setListenerCondition (line 31) | func setListenerCondition(
  method validateListeners (line 48) | func (c *Controller) validateListeners(gateway *gatewayv1.Gateway) map[g...
  method translateListenerToFilterChain (line 212) | func (c *Controller) translateListenerToFilterChain(gateway *gatewayv1.G...
  method buildDownstreamTLSContext (line 332) | func (c *Controller) buildDownstreamTLSContext(ctx context.Context, gate...
  function validateSecretCertificate (line 381) | func validateSecretCertificate(secret *corev1.Secret) error {
  function toEnvoyTlsCertificate (line 402) | func toEnvoyTlsCertificate(secret *corev1.Secret) (*tlsv3.TlsCertificate...
  function createEnvoyAddress (line 435) | func createEnvoyAddress(port uint32) *corev3.Address {
  function createListenerFilters (line 449) | func createListenerFilters() []*listener.ListenerFilter {

FILE: pkg/gateway/referencegrant.go
  function isCrossNamespaceRefAllowed (line 12) | func isCrossNamespaceRefAllowed(

FILE: pkg/gateway/referencegrant_test.go
  type fakeReferenceGrantLister (line 14) | type fakeReferenceGrantLister struct
    method List (line 25) | func (f *fakeReferenceGrantLister) List(selector labels.Selector) ([]*...
    method Get (line 33) | func (f *fakeReferenceGrantLister) Get(name string) (*gatewayv1.Refere...
    method ReferenceGrants (line 46) | func (f *fakeReferenceGrantLister) ReferenceGrants(namespace string) g...
  function newFakeReferenceGrantLister (line 20) | func newFakeReferenceGrantLister(grants []*gatewayv1.ReferenceGrant, err...
  function TestIsCrossNamespaceRefAllowed (line 52) | func TestIsCrossNamespaceRefAllowed(t *testing.T) {

FILE: pkg/gateway/routes.go
  function isAllowedByListener (line 17) | func isAllowedByListener(gateway *gatewayv1.Gateway, listener gatewayv1....
  function isAllowedByHostname (line 103) | func isAllowedByHostname(listener gatewayv1.Listener, route metav1.Objec...
  function getIntersectingHostnames (line 142) | func getIntersectingHostnames(listener gatewayv1.Listener, routeHostname...
  function isHostnameSubset (line 189) | func isHostnameSubset(routeHostname, listenerHostname string) bool {

FILE: pkg/gateway/routes_test.go
  type mockNamespaceLister (line 17) | type mockNamespaceLister struct
    method List (line 21) | func (m *mockNamespaceLister) List(selector labels.Selector) ([]*corev...
    method Get (line 31) | func (m *mockNamespaceLister) Get(name string) (*corev1.Namespace, err...
    method GetPodNamespaces (line 38) | func (m *mockNamespaceLister) GetPodNamespaces(pod *corev1.Pod) ([]*co...
    method Pods (line 42) | func (m *mockNamespaceLister) Pods(namespace string) corev1listers.Pod...
  function TestIsAllowedByListener (line 46) | func TestIsAllowedByListener(t *testing.T) {
  function TestIsAllowedByHostname (line 306) | func TestIsAllowedByHostname(t *testing.T) {
  function TestIsHostnameSubset (line 408) | func TestIsHostnameSubset(t *testing.T) {
  function TestGetIntersectingHostnames (line 517) | func TestGetIntersectingHostnames(t *testing.T) {
  function equalUnordered (line 647) | func equalUnordered(a, b []string) bool {

FILE: pkg/ingress/controller.go
  constant IngressClassName (line 41) | IngressClassName = "cloud-provider-kind"
  constant IngressClassController (line 43) | IngressClassController = "kind.sigs.k8s.io/ingress-controller"
  constant GatewayName (line 45) | GatewayName = "kind-ingress-gateway"
  type Controller (line 49) | type Controller struct
    method Init (line 175) | func (c *Controller) Init(ctx context.Context) error {
    method Run (line 220) | func (c *Controller) Run(ctx context.Context, workers int) {
    method runWorker (line 239) | func (c *Controller) runWorker(ctx context.Context) {
    method processNextWorkItem (line 244) | func (c *Controller) processNextWorkItem(ctx context.Context) bool {
    method syncHandler (line 271) | func (c *Controller) syncHandler(ctx context.Context, key string) error {
    method reconcileNamespaceGateway (line 388) | func (c *Controller) reconcileNamespaceGateway(ctx context.Context, na...
    method resolveBackendPort (line 514) | func (c *Controller) resolveBackendPort(ns string, svcBackend *network...
    method translateIngressPaths (line 540) | func (c *Controller) translateIngressPaths(ns string, paths []networki...
    method generateDesiredHTTPRoutes (line 625) | func (c *Controller) generateDesiredHTTPRoutes(ingress *networkingv1.I...
    method updateIngressStatus (line 749) | func (c *Controller) updateIngressStatus(ctx context.Context, ingress ...
    method enqueueIngress (line 808) | func (c *Controller) enqueueIngress(obj interface{}) {
    method enqueueIngressFromRoute (line 820) | func (c *Controller) enqueueIngressFromRoute(obj interface{}) {
    method handleIngressClass (line 851) | func (c *Controller) handleIngressClass(obj interface{}) {
    method handleObject (line 892) | func (c *Controller) handleObject(obj interface{}) {
    method handleGateway (line 940) | func (c *Controller) handleGateway(obj interface{}) {
    method enqueueAllIngressesInNamespace (line 964) | func (c *Controller) enqueueAllIngressesInNamespace(namespace string) {
    method ingressReferencesService (line 975) | func (c *Controller) ingressReferencesService(ingress *networkingv1.In...
    method ingressReferencesSecret (line 995) | func (c *Controller) ingressReferencesSecret(ingress *networkingv1.Ing...
    method isIngressForUs (line 1008) | func (c *Controller) isIngressForUs(ingress *networkingv1.Ingress) bool {
    method enqueueAllIngresses (line 1017) | func (c *Controller) enqueueAllIngresses() {
  function NewController (line 73) | func NewController(
  function generateRouteName (line 502) | func generateRouteName(ingressName, host string) string {

FILE: pkg/ingress/controller_test.go
  constant testGatewayClassName (line 29) | testGatewayClassName = "test-gw-class"
  constant testNamespace (line 30) | testNamespace        = "test-ns"
  constant testIngressName (line 31) | testIngressName      = "test-ingress"
  constant testServiceName (line 32) | testServiceName      = "test-service"
  constant testServicePortName (line 33) | testServicePortName  = "http"
  constant testServicePortNum (line 34) | testServicePortNum   = 8080
  type testFixture (line 38) | type testFixture struct
  function newTestFixture (line 50) | func newTestFixture(t *testing.T, kubeObjects []runtime.Object, gwObject...
  function newTestIngressClass (line 113) | func newTestIngressClass() *networkingv1.IngressClass {
  function newTestService (line 124) | func newTestService(name string) *corev1.Service {
  function newTestIngress (line 145) | func newTestIngress(name string) *networkingv1.Ingress {
  function TestSyncHandler_Create (line 183) | func TestSyncHandler_Create(t *testing.T) {
  function TestSyncHandler_Delete (line 293) | func TestSyncHandler_Delete(t *testing.T) {
  function TestSyncHandler_TranslationError_ServicePort (line 352) | func TestSyncHandler_TranslationError_ServicePort(t *testing.T) {
  function TestSyncHandler_TLS_Aggregation (line 387) | func TestSyncHandler_TLS_Aggregation(t *testing.T) {
  function TestSyncHandler_DefaultBackend (line 536) | func TestSyncHandler_DefaultBackend(t *testing.T) {
  function TestSyncHandler_DefaultRulePrecedence (line 594) | func TestSyncHandler_DefaultRulePrecedence(t *testing.T) {
  function TestSyncHandler_MultipleHostRules (line 665) | func TestSyncHandler_MultipleHostRules(t *testing.T) {
  function TestSyncHandler_Update_StaleRouteDeletion (line 752) | func TestSyncHandler_Update_StaleRouteDeletion(t *testing.T) {

FILE: pkg/loadbalancer/proxy.go
  constant proxyConfigPath (line 27) | proxyConfigPath    = "/home/envoy/envoy.yaml"
  constant proxyConfigPathCDS (line 28) | proxyConfigPathCDS = "/home/envoy/cds.yaml"
  constant proxyConfigPathLDS (line 29) | proxyConfigPathLDS = "/home/envoy/lds.yaml"
  constant envoyAdminPort (line 30) | envoyAdminPort     = 10000
  constant dynamicFilesystemConfig (line 35) | dynamicFilesystemConfig = `node:
  type proxyConfigData (line 56) | type proxyConfigData struct
  type sourceRange (line 63) | type sourceRange struct
  type servicePort (line 68) | type servicePort struct
  type endpoint (line 75) | type endpoint struct
  constant proxyLDSConfigTemplate (line 82) | proxyLDSConfigTemplate = `
  constant proxyCDSConfigTemplate (line 148) | proxyCDSConfigTemplate = `
  function proxyConfig (line 194) | func proxyConfig(configTemplate string, data *proxyConfigData) (config s...
  function generateConfig (line 208) | func generateConfig(service *v1.Service, nodes []*v1.Node) *proxyConfigD...
  function proxyUpdateLoadBalancer (line 280) | func proxyUpdateLoadBalancer(ctx context.Context, clusterName string, se...
  function waitLoadBalancerReady (line 320) | func waitLoadBalancerReady(ctx context.Context, name string, timeout tim...

FILE: pkg/loadbalancer/proxy_test.go
  function makeNode (line 15) | func makeNode(name string, ip string) *v1.Node {
  function makeService (line 29) | func makeService(name string) *v1.Service {
  function Test_generateConfig (line 43) | func Test_generateConfig(t *testing.T) {
  function Test_proxyConfig (line 317) | func Test_proxyConfig(t *testing.T) {

FILE: pkg/loadbalancer/server.go
  type Server (line 25) | type Server struct
    method GetLoadBalancer (line 40) | func (s *Server) GetLoadBalancer(ctx context.Context, clusterName stri...
    method GetLoadBalancerName (line 90) | func (s *Server) GetLoadBalancerName(ctx context.Context, clusterName ...
    method EnsureLoadBalancer (line 94) | func (s *Server) EnsureLoadBalancer(ctx context.Context, clusterName s...
    method UpdateLoadBalancer (line 141) | func (s *Server) UpdateLoadBalancer(ctx context.Context, clusterName s...
    method EnsureLoadBalancerDeleted (line 145) | func (s *Server) EnsureLoadBalancerDeleted(ctx context.Context, cluste...
    method createLoadBalancer (line 189) | func (s *Server) createLoadBalancer(clusterName string, service *v1.Se...
  function NewServer (line 31) | func NewServer() cloudprovider.LoadBalancer {
  function loadBalancerName (line 164) | func loadBalancerName(clusterName string, service *v1.Service) string {
  function loadBalancerSimpleName (line 174) | func loadBalancerSimpleName(clusterName string, service *v1.Service) str...
  function ServiceFromLoadBalancerSimpleName (line 178) | func ServiceFromLoadBalancerSimpleName(s string) (clusterName string, se...
  function isIPv6Service (line 278) | func isIPv6Service(service *v1.Service) bool {
  function listenAddressForService (line 295) | func listenAddressForService(service *v1.Service) string {

FILE: pkg/loadbalancer/server_test.go
  function TestIsIPv6Service (line 11) | func TestIsIPv6Service(t *testing.T) {
  function TestListenAddressForService (line 66) | func TestListenAddressForService(t *testing.T) {
  function TestLoadBalancerName (line 116) | func TestLoadBalancerName(t *testing.T) {

FILE: pkg/provider/cloud.go
  function New (line 12) | func New(clusterName string, kindClient *cluster.Provider) cloudprovider...
  type cloud (line 23) | type cloud struct
    method Initialize (line 30) | func (c *cloud) Initialize(clientBuilder cloudprovider.ControllerClien...
    method Clusters (line 35) | func (c *cloud) Clusters() (cloudprovider.Clusters, bool) {
    method ProviderName (line 40) | func (c *cloud) ProviderName() string {
    method LoadBalancer (line 44) | func (c *cloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
    method Instances (line 48) | func (c *cloud) Instances() (cloudprovider.Instances, bool) {
    method Zones (line 52) | func (c *cloud) Zones() (cloudprovider.Zones, bool) {
    method Routes (line 56) | func (c *cloud) Routes() (cloudprovider.Routes, bool) {
    method HasClusterID (line 60) | func (c *cloud) HasClusterID() bool {
    method InstancesV2 (line 64) | func (c *cloud) InstancesV2() (cloudprovider.InstancesV2, bool) {

FILE: pkg/provider/clusters.go
  method ListClusters (line 14) | func (c *cloud) ListClusters(ctx context.Context) ([]string, error) {
  method Master (line 20) | func (c *cloud) Master(ctx context.Context, clusterName string) (string,...

FILE: pkg/provider/instances.go
  method InstanceExists (line 19) | func (c *cloud) InstanceExists(ctx context.Context, node *v1.Node) (bool...
  method InstanceShutdown (line 32) | func (c *cloud) InstanceShutdown(ctx context.Context, node *v1.Node) (bo...
  method InstanceMetadata (line 46) | func (c *cloud) InstanceMetadata(ctx context.Context, node *v1.Node) (*c...
  method findNodeByName (line 79) | func (c *cloud) findNodeByName(name string) (nodes.Node, error) {

FILE: pkg/provider/loadbalancer.go
  method GetLoadBalancer (line 15) | func (c *cloud) GetLoadBalancer(ctx context.Context, clusterName string,...
  method GetLoadBalancerName (line 21) | func (c *cloud) GetLoadBalancerName(ctx context.Context, clusterName str...
  method EnsureLoadBalancer (line 27) | func (c *cloud) EnsureLoadBalancer(ctx context.Context, clusterName stri...
  method UpdateLoadBalancer (line 33) | func (c *cloud) UpdateLoadBalancer(ctx context.Context, clusterName stri...
  method EnsureLoadBalancerDeleted (line 41) | func (c *cloud) EnsureLoadBalancerDeleted(ctx context.Context, clusterNa...

FILE: pkg/tunnels/address_darwin.go
  function AddIPToLocalInterface (line 9) | func AddIPToLocalInterface(ip string) (string, error) {
  function RemoveIPFromLocalInterface (line 15) | func RemoveIPFromLocalInterface(ip string) (string, error) {

FILE: pkg/tunnels/address_other.go
  function AddIPToLocalInterface (line 9) | func AddIPToLocalInterface(ip string) (string, error) {
  function RemoveIPFromLocalInterface (line 14) | func RemoveIPFromLocalInterface(ip string) (string, error) {

FILE: pkg/tunnels/address_windows.go
  function AddIPToLocalInterface (line 9) | func AddIPToLocalInterface(ip string) (string, error) {
  function RemoveIPFromLocalInterface (line 14) | func RemoveIPFromLocalInterface(ip string) (string, error) {

FILE: pkg/tunnels/tunnel.go
  type TunnelManager (line 16) | type TunnelManager struct
    method SetupTunnels (line 28) | func (t *TunnelManager) SetupTunnels(containerName string) error {
    method RemoveTunnels (line 92) | func (t *TunnelManager) RemoveTunnels(containerName string) error {
  function NewTunnelManager (line 21) | func NewTunnelManager() *TunnelManager {
  type tunnel (line 120) | type tunnel struct
    method Start (line 141) | func (t *tunnel) Start() error {
    method Stop (line 208) | func (t *tunnel) Stop() error {
    method handleTCPConnection (line 224) | func (t *tunnel) handleTCPConnection(local *net.TCPConn) error {
    method handleUDPConnection (line 259) | func (t *tunnel) handleUDPConnection(conn *net.UDPConn) error {
  function NewTunnel (line 131) | func NewTunnel(localIP, localPort, protocol, remoteIP, remotePort string...
  function ipOnHost (line 298) | func ipOnHost(ip string) bool {
Condensed preview — 138 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,915K chars).
[
  {
    "path": ".github/dependabot.yml",
    "chars": 1039,
    "preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
  },
  {
    "path": ".github/workflows/bats.yml",
    "chars": 2662,
    "preview": "name: bats\n\non:\n  push:\n    branches:\n      - 'main'\n    tags:\n      - 'v*'\n  pull_request:\n    branches: [ main ]\n  wor"
  },
  {
    "path": ".github/workflows/gateway.yml",
    "chars": 4691,
    "preview": "name: gateway\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\nenv:\n  GO_VERSION: \"1.25\"\n  GATEWAY_VERSION: \"v1.4.1\"\n "
  },
  {
    "path": ".github/workflows/ingress.yml",
    "chars": 5118,
    "preview": "name: ingress\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\nenv:\n  GO_VERSION: \"1.25\"\n  K8S_VERSION: \"v1.35.0\"\n  KI"
  },
  {
    "path": ".github/workflows/k8s.yml",
    "chars": 7560,
    "preview": "name: k8s\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\nenv:\n  K8S_VERSION: \"v1.34.0\"\n  KIND_VERSION: \"v0.30.0\"\n  K"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 869,
    "preview": "# .github/workflows/release.yml\nname: goreleaser\n\non:\n  push:\n    # run only against tags\n    tags:\n      - \"*\"\n\npermiss"
  },
  {
    "path": ".github/workflows/test.yaml",
    "chars": 485,
    "preview": "name: Test\n\non: [push, pull_request]\n\nenv:\n  REGISTRY: ghcr.io\n  IMAGE_NAME: sigs.k8s.io/cloud-provider-kind\n\npermission"
  },
  {
    "path": ".gitignore",
    "chars": 299,
    "preview": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Ou"
  },
  {
    "path": ".golangci.yaml",
    "chars": 461,
    "preview": "version: \"2\"\nrun:\n  tests: false\nlinters:\n  default: none\n  enable:\n    - errcheck\n    - gocritic\n    - govet\n    - inef"
  },
  {
    "path": ".goreleaser.yaml",
    "chars": 173,
    "preview": "version: 2\nproject_name: cloud-provider-kind\nbuilds:\n  - env: [CGO_ENABLED=0]\n    goos:\n      - linux\n      - windows\n  "
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1554,
    "preview": "# Contributing Guidelines\n\nWelcome to Kubernetes. We are excited about the prospect of you joining our [community](https"
  },
  {
    "path": "Dockerfile",
    "chars": 387,
    "preview": "FROM --platform=$BUILDPLATFORM golang:1.25\nWORKDIR /go/src\n# make deps fetching cacheable\nCOPY go.mod go.sum ./\nRUN go m"
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "Makefile",
    "chars": 1248,
    "preview": "REPO_ROOT:=${CURDIR}\nOUT_DIR=$(REPO_ROOT)/bin\nKIND_CLOUD_BINARY_NAME?=cloud-provider-kind\n\n# go1.9+ can autodetect GOROO"
  },
  {
    "path": "OWNERS",
    "chars": 103,
    "preview": "# See the OWNERS docs at https://go.k8s.io/owners\n\napprovers:\n  - aojea\n  - bentheelder\n  - stmcginnis\n"
  },
  {
    "path": "README.md",
    "chars": 13519,
    "preview": "# Kubernetes Cloud Provider for KIND\n\nKIND has demonstrated to be a very versatile, efficient, cheap and very useful too"
  },
  {
    "path": "SECURITY.md",
    "chars": 837,
    "preview": "# Security Policy\n\n## Security Announcements\n\nJoin the [kubernetes-security-announce] group for security and vulnerabili"
  },
  {
    "path": "SECURITY_CONTACTS",
    "chars": 537,
    "preview": "# Defined below are the security contacts for this repo.\n#\n# They are the contact point for the Security Response Commit"
  },
  {
    "path": "cloudbuild.yaml",
    "chars": 383,
    "preview": "# See https://cloud.google.com/cloud-build/docs/build-config\noptions:\n  substitution_option: ALLOW_LOOSE\n  machineType: "
  },
  {
    "path": "cmd/app.go",
    "chars": 5252,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\n\t\"github.com/spf"
  },
  {
    "path": "code-of-conduct.md",
    "chars": 148,
    "preview": "# Kubernetes Community Code of Conduct\n\nPlease refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/co"
  },
  {
    "path": "compose.yml",
    "chars": 141,
    "preview": "services:\n  cloud-provider:\n    build: .\n    network_mode: \"${NET_MODE-host}\"\n    volumes:\n      - /var/run/docker.sock:"
  },
  {
    "path": "docs/.nojekyll",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "docs/HOWTO.md",
    "chars": 2546,
    "preview": "# Documentation Guide\n\nThis documentation site is generated and served using **Docsify**, a lightweight documentation si"
  },
  {
    "path": "docs/README.md",
    "chars": 1239,
    "preview": "# Kubernetes Cloud Provider for KIND\n\nKIND has demonstrated to be a very versatile, efficient, cheap and very useful too"
  },
  {
    "path": "docs/_coverpage.md",
    "chars": 298,
    "preview": "# Kubernetes Cloud Provider for KIND <small></small>\n\n> cloud-provider-kind aims to fill this gap and provide an agnosti"
  },
  {
    "path": "docs/_sidebar.md",
    "chars": 947,
    "preview": "- Install \n\n  - [Installation with go ](user/install/install_go.md)\n  - [Running with docker](user/install/install_docke"
  },
  {
    "path": "docs/code-of-conduct.md",
    "chars": 148,
    "preview": "# Kubernetes Community Code of Conduct\n\nPlease refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/co"
  },
  {
    "path": "docs/contributing/CONTRIBUTING.md",
    "chars": 1554,
    "preview": "# Contributing Guidelines\n\nWelcome to Kubernetes. We are excited about the prospect of you joining our [community](https"
  },
  {
    "path": "docs/index.html",
    "chars": 1727,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Kubernetes Cloud Provider for KIND</title>\n  <"
  },
  {
    "path": "docs/user/example/creating_gateway_http_route.md",
    "chars": 1593,
    "preview": "### Creating a Gateway and a HTTPRoute\n\nSimilar to Services with LoadBalancers we can use Gateway API\n\n```yaml\napiVersio"
  },
  {
    "path": "docs/user/example/enable_lb_port_mapping.md",
    "chars": 1680,
    "preview": "### Enabling Load Balancer Port Mapping\n\nWhen running `cloud-provider-kind` in a container on Windows or macOS, accessin"
  },
  {
    "path": "docs/user/example/service_expose_via_loadbalancer.md",
    "chars": 1569,
    "preview": "### Creating a Service and exposing it via a LoadBalancer\n\nLet's create an application that listens on port 8080 and exp"
  },
  {
    "path": "docs/user/gateway/allow_load_balancer_access_control_plane.md",
    "chars": 844,
    "preview": "### Allowing load balancers access to control plane nodes\n\nBy default, [Kubernetes expects workloads will not run on con"
  },
  {
    "path": "docs/user/gateway/configure_proxy_image_registry.md",
    "chars": 828,
    "preview": "### Configuring Proxy Image Registry\n\n> [!WARNING]\n> The proxy image is an implementation detail of `cloud-provider-kind"
  },
  {
    "path": "docs/user/gateway/gatewayapi.md",
    "chars": 516,
    "preview": "## Gateway API support\n\nThis provider has support for the [Gateway API](https://gateway-api.sigs.k8s.io/).\nIt implements"
  },
  {
    "path": "docs/user/gateway/running_the_provider.md",
    "chars": 798,
    "preview": "### Running the provider\n\nOnce the cluster is running, we need to run the `cloud-provider-kind` in a terminal and keep i"
  },
  {
    "path": "docs/user/howto.md",
    "chars": 479,
    "preview": "## How to use it\n\nRun a KIND cluster:\n\n```sh\n$ kind create cluster\nCreating cluster \"kind\" ...\n ✓ Ensuring node image (k"
  },
  {
    "path": "docs/user/ingress/ingress.md",
    "chars": 3291,
    "preview": "## Compatibility:\nThis guide applies to [cloud-provider-kind](https://github.com/kubernetes-sigs/cloud-provider-kind) v0"
  },
  {
    "path": "docs/user/install/install_docker.md",
    "chars": 1305,
    "preview": "### Running via Docker Image\n\nStarting with v0.4.0, the docker image for cloud-provider-kind is available\nat `registry.k"
  },
  {
    "path": "docs/user/install/install_go.md",
    "chars": 779,
    "preview": "### Installing with `go install`\n\nYou can install `cloud-provider-kind` using `go install`:\n\n```sh\ngo install sigs.k8s.i"
  },
  {
    "path": "docs/user/os_support.md",
    "chars": 1571,
    "preview": "### Mac, Windows and WSL2 support\n\nMac and Windows run the containers inside a VM and, on the contrary to Linux, the KIN"
  },
  {
    "path": "docs/user/support/os_support.md",
    "chars": 1692,
    "preview": "### Mac, Windows and WSL2 support\n\nMac and Windows run the containers inside a VM and, on the contrary to Linux, the KIN"
  },
  {
    "path": "examples/gateway_httproute_simple.yaml",
    "chars": 1077,
    "preview": "apiVersion: gateway.networking.k8s.io/v1\nkind: Gateway\nmetadata:\n  name: prod-web\nspec:\n  gatewayClassName: cloud-provid"
  },
  {
    "path": "examples/ingress_foo_bar.yaml",
    "chars": 1361,
    "preview": "kind: Pod\napiVersion: v1\nmetadata:\n  name: foo-app\n  labels:\n    app: foo\nspec:\n  containers:\n  - command:\n    - /agnhos"
  },
  {
    "path": "examples/loadbalancer_affinity.yaml",
    "chars": 1126,
    "preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: affinity\nspec:\n  selector:\n    matchLabels:\n      app: affinity\n "
  },
  {
    "path": "examples/loadbalancer_deployment.yaml",
    "chars": 1122,
    "preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: zeroapp\nspec:\n  selector:\n    matchLabels:\n      app: zeroapp\n  r"
  },
  {
    "path": "examples/loadbalancer_etp_cluster.yaml",
    "chars": 719,
    "preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: policy-cluster\n  labels:\n    app: MyClusterApp\nspec:\n  replicas: "
  },
  {
    "path": "examples/loadbalancer_etp_local.yaml",
    "chars": 705,
    "preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: policy-local\n  labels:\n    app: MyLocalApp\nspec:\n  replicas: 1\n  "
  },
  {
    "path": "examples/loadbalancer_multiport.yaml",
    "chars": 1308,
    "preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: multiport\nspec:\n  selector:\n    matchLabels:\n      app: multiport"
  },
  {
    "path": "examples/loadbalancer_static_ip.yaml",
    "chars": 1197,
    "preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: static-ip\nspec:\n  selector:\n    matchLabels:\n      app: static-ip"
  },
  {
    "path": "examples/loadbalancer_stts.yaml",
    "chars": 1040,
    "preview": "apiVersion: apps/v1\nkind: StatefulSet\nmetadata:\n  name: apache\nspec:\n  selector:\n    matchLabels:\n      app: apache\n  se"
  },
  {
    "path": "examples/loadbalancer_udp_tcp.yaml",
    "chars": 1350,
    "preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: multiprotocol\nspec:\n  selector:\n    matchLabels:\n      app: multi"
  },
  {
    "path": "go.mod",
    "chars": 4316,
    "preview": "module sigs.k8s.io/cloud-provider-kind\n\ngo 1.25.5\n\nrequire (\n\tgithub.com/envoyproxy/go-control-plane v0.14.0\n\tgithub.com"
  },
  {
    "path": "go.sum",
    "chars": 22603,
    "preview": "al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA=\nal.essio.dev/pkg/shellescape v1.6.0/"
  },
  {
    "path": "hack/build-route-adder.sh",
    "chars": 564,
    "preview": "#!/bin/bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nSCRIPT_DIR=$( cd -- \"$( dirname -- \"${BASH_SOURCE[0]}\" )\" &>"
  },
  {
    "path": "hack/ci/add-kubernetes-to-workspace.sh",
    "chars": 1049,
    "preview": "#!/usr/bin/env bash\n\n# Copyright 2024 The Kubernetes Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"L"
  },
  {
    "path": "hack/ci/e2e.sh",
    "chars": 8118,
    "preview": "#!/bin/sh\n# Copyright 2018 The Kubernetes Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n#"
  },
  {
    "path": "hack/download-gateway-crds.sh",
    "chars": 2819,
    "preview": "#!/bin/bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nSCRIPT_DIR=$( cd -- \"$( dirname -- \"${BASH_SOURCE[0]}\" )\" &>"
  },
  {
    "path": "hack/init-buildx.sh",
    "chars": 1665,
    "preview": "#!/usr/bin/env bash\n# Copyright 2020 The Kubernetes Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"Li"
  },
  {
    "path": "hack/lint.sh",
    "chars": 209,
    "preview": "#!/bin/bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nREPO_ROOT=$(dirname \"${BASH_SOURCE[0]}\")/..\n\ncd $REPO_ROOT\nd"
  },
  {
    "path": "internal/routeadder/main.go",
    "chars": 1136,
    "preview": "// main.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\n\t\"github.com/vishvananda/netlink\"\n)\n\n// usage: ./route-adder <cid"
  },
  {
    "path": "kind.yaml",
    "chars": 500,
    "preview": "kind: Cluster\napiVersion: kind.x-k8s.io/v1alpha4\nkubeadmConfigPatches:\n- |\n  kind: ClusterConfiguration\n  apiServer:\n   "
  },
  {
    "path": "main.go",
    "chars": 93,
    "preview": "package main\n\nimport (\n\t\"sigs.k8s.io/cloud-provider-kind/cmd\"\n)\n\nfunc main() {\n\tcmd.Main()\n}\n"
  },
  {
    "path": "pkg/config/config.go",
    "chars": 1688,
    "preview": "package config\n\nimport (\n\t\"os\"\n\n\t\"sigs.k8s.io/cloud-provider-kind/pkg/constants\"\n)\n\n// DefaultConfig is a global variabl"
  },
  {
    "path": "pkg/constants/constants.go",
    "chars": 740,
    "preview": "package constants\n\nconst (\n\tProviderName = \"kind\"\n\t// cloud-provider-kind\n\tContainerPrefix = \"kindccm\"\n\t// DefaultProxyI"
  },
  {
    "path": "pkg/container/container.go",
    "chars": 6646,
    "preview": "package container\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"k8s.io/klog/v2\"\n\tkindexec \"sigs"
  },
  {
    "path": "pkg/controller/controller.go",
    "chars": 14534,
    "preview": "package controller\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v"
  },
  {
    "path": "pkg/controller/http.go",
    "chars": 1594,
    "preview": "package controller\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n\n\t\"k8s.io/klog/v2\"\n)\n\nfun"
  },
  {
    "path": "pkg/controller/http_test.go",
    "chars": 859,
    "preview": "package controller\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc Test_firstSuccessful"
  },
  {
    "path": "pkg/gateway/backendref.go",
    "chars": 1535,
    "preview": "package gateway\n\nimport (\n\t\"fmt\"\n\n\tgatewayv1 \"sigs.k8s.io/gateway-api/apis/v1\"\n)\n\n// backendRefToClusterName generates a"
  },
  {
    "path": "pkg/gateway/backendref_test.go",
    "chars": 3259,
    "preview": "package gateway\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"k8s.io/utils/ptr\"\n\tgatewayv1 \"sigs.k8s.io/gateway-api/apis/v1\"\n)\n\nfunc"
  },
  {
    "path": "pkg/gateway/controller.go",
    "chars": 24948,
    "preview": "/*\nCopyright 2024 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not u"
  },
  {
    "path": "pkg/gateway/controller_test.go",
    "chars": 16,
    "preview": "package gateway\n"
  },
  {
    "path": "pkg/gateway/crd_manager.go",
    "chars": 4165,
    "preview": "package gateway\n\nimport (\n\t\"context\"\n\t\"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"path\"\n\t\"strings\"\n\n\t\"k8s.io/apimachinery/pkg/api/er"
  },
  {
    "path": "pkg/gateway/crd_manager_test.go",
    "chars": 738,
    "preview": "package gateway\n\nimport (\n\t\"testing\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/client-go/dynamic/fake\"\n\t\"sigs.k8s.io/"
  },
  {
    "path": "pkg/gateway/crds/README.md",
    "chars": 162,
    "preview": "# Gateway CRDs\n\nThis folder is updated with `hack/download-gateway-crds.sh` script to obtain\nthe lastest CRDs from https"
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.k8s.io_backendtlspolicies.yaml",
    "chars": 80676,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.k8s.io_gatewayclasses.yaml",
    "chars": 23140,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.k8s.io_gateways.yaml",
    "chars": 190565,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.k8s.io_grpcroutes.yaml",
    "chars": 134604,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.k8s.io_httproutes.yaml",
    "chars": 532541,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.k8s.io_listenersets.yaml",
    "chars": 42713,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.k8s.io_referencegrants.yaml",
    "chars": 14881,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.k8s.io_tcproutes.yaml",
    "chars": 42322,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.k8s.io_tlsroutes.yaml",
    "chars": 135105,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.k8s.io_udproutes.yaml",
    "chars": 42303,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.k8s.io_vap_safeupgrades.yaml",
    "chars": 2268,
    "preview": "apiVersion: admissionregistration.k8s.io/v1\nkind: ValidatingAdmissionPolicy\nmetadata:\n  annotations:\n    gateway.network"
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.x-k8s.io_xbackendtrafficpolicies.yaml",
    "chars": 31651,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.x-k8s.io_xlistenersets.yaml",
    "chars": 42871,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/experimental/gateway.networking.x-k8s.io_xmeshes.yaml",
    "chars": 10518,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/experimental/kustomization.yaml",
    "chars": 617,
    "preview": "resources:\n- gateway.networking.k8s.io_backendtlspolicies.yaml\n- gateway.networking.k8s.io_gatewayclasses.yaml\n- gateway"
  },
  {
    "path": "pkg/gateway/crds/kustomization.yaml",
    "chars": 510,
    "preview": "resources:\n- standard/gateway.networking.k8s.io_gatewayclasses.yaml\n- standard/gateway.networking.k8s.io_gateways.yaml\n-"
  },
  {
    "path": "pkg/gateway/crds/standard/gateway.networking.k8s.io_backendtlspolicies.yaml",
    "chars": 78637,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/standard/gateway.networking.k8s.io_gatewayclasses.yaml",
    "chars": 23136,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/standard/gateway.networking.k8s.io_gateways.yaml",
    "chars": 187741,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/standard/gateway.networking.k8s.io_grpcroutes.yaml",
    "chars": 122273,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/standard/gateway.networking.k8s.io_httproutes.yaml",
    "chars": 430575,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/standard/gateway.networking.k8s.io_listenersets.yaml",
    "chars": 42709,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/standard/gateway.networking.k8s.io_referencegrants.yaml",
    "chars": 14877,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/standard/gateway.networking.k8s.io_tlsroutes.yaml",
    "chars": 120077,
    "preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    api-approved.kubernetes."
  },
  {
    "path": "pkg/gateway/crds/standard/gateway.networking.k8s.io_vap_safeupgrades.yaml",
    "chars": 2268,
    "preview": "apiVersion: admissionregistration.k8s.io/v1\nkind: ValidatingAdmissionPolicy\nmetadata:\n  annotations:\n    gateway.network"
  },
  {
    "path": "pkg/gateway/envoy.go",
    "chars": 7479,
    "preview": "package gateway\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/netip\"\n\t\"os\"\n\t\"strings\"\n\t\"text/template\"\n\n"
  },
  {
    "path": "pkg/gateway/envoy_test.go",
    "chars": 3871,
    "preview": "package gateway\n\nimport \"testing\"\n\nfunc TestGenerateEnvoyConfigTable(t *testing.T) {\n\ttests := []struct {\n\t\tname        "
  },
  {
    "path": "pkg/gateway/gateway.go",
    "chars": 30266,
    "preview": "package gateway\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\t\"google.golang.org/protobuf"
  },
  {
    "path": "pkg/gateway/gateway_test.go",
    "chars": 2631,
    "preview": "package gateway\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"k8s.io/utils/ptr\"\n\tgatewayv1 \"sigs.k8s.io/gateway-api/apis/v1\"\n)\n\nfun"
  },
  {
    "path": "pkg/gateway/grpcroute.go",
    "chars": 8428,
    "preview": "package gateway\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\troutev3 \"github.com/envoyproxy/go-control-plane/envoy/config/route/v3\"\n\tmat"
  },
  {
    "path": "pkg/gateway/httproute.go",
    "chars": 15129,
    "preview": "package gateway\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\n\tcorev3 \"github.com/envoyproxy/go-control-plane/envoy/con"
  },
  {
    "path": "pkg/gateway/kindcluster.go",
    "chars": 7184,
    "preview": "package gateway\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"net/netip\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\n\tcorev1 \"k8s"
  },
  {
    "path": "pkg/gateway/listener.go",
    "chars": 16781,
    "preview": "package gateway\n\nimport (\n\t\"context\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\n\tcorev3 \"github.com/envoyproxy/go-control-plane/envoy/confi"
  },
  {
    "path": "pkg/gateway/referencegrant.go",
    "chars": 2136,
    "preview": "package gateway\n\nimport (\n\t\"k8s.io/apimachinery/pkg/labels\"\n\t\"k8s.io/klog/v2\"\n\tgatewayv1 \"sigs.k8s.io/gateway-api/apis/v"
  },
  {
    "path": "pkg/gateway/referencegrant_test.go",
    "chars": 8082,
    "preview": "package gateway\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg"
  },
  {
    "path": "pkg/gateway/routeadder/README.md",
    "chars": 343,
    "preview": "\nPackages generated with `hack/build-route-adder.sh` to inject a binary on the\nenvoy container image that allows to conf"
  },
  {
    "path": "pkg/gateway/routes.go",
    "chars": 8621,
    "preview": "package gateway\n\nimport (\n\t\"strings\"\n\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n\t"
  },
  {
    "path": "pkg/gateway/routes_test.go",
    "chars": 20925,
    "preview": "package gateway\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\""
  },
  {
    "path": "pkg/ingress/README.md",
    "chars": 4935,
    "preview": "# Ingress Controller (Ingress-to-Gateway Translation)\n\n\nThis is a Kubernetes controller that implements the `networkingv"
  },
  {
    "path": "pkg/ingress/controller.go",
    "chars": 34758,
    "preview": "package ingress\n\nimport (\n\t\"context\"\n\t\"crypto/sha256\" // New import\n\t\"encoding/hex\"  // New import\n\t\"encoding/json\"\n\t\"fm"
  },
  {
    "path": "pkg/ingress/controller_test.go",
    "chars": 27840,
    "preview": "package ingress\n\nimport (\n\t\"context\" // New import\n\t// New import\n\t\"fmt\"\n\t\"reflect\" // Import reflect for DeepEqual\n\t\"st"
  },
  {
    "path": "pkg/loadbalancer/proxy.go",
    "chars": 11686,
    "preview": "package loadbalancer\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"text/template"
  },
  {
    "path": "pkg/loadbalancer/proxy_test.go",
    "chars": 19514,
    "preview": "package loadbalancer\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/lithammer/dedent\"\n\tv1"
  },
  {
    "path": "pkg/loadbalancer/server.go",
    "chars": 10694,
    "preview": "package loadbalancer\n\nimport (\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\tv1 \""
  },
  {
    "path": "pkg/loadbalancer/server_test.go",
    "chars": 2985,
    "preview": "package loadbalancer\n\nimport (\n\t\"testing\"\n\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"sig"
  },
  {
    "path": "pkg/provider/cloud.go",
    "chars": 1596,
    "preview": "package provider\n\nimport (\n\t\"sigs.k8s.io/cloud-provider-kind/pkg/constants\"\n\t\"sigs.k8s.io/cloud-provider-kind/pkg/loadba"
  },
  {
    "path": "pkg/provider/clusters.go",
    "chars": 831,
    "preview": "package provider\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\tcloudprovider \"k8s.io/cloud-provider\"\n\t\"k8s.io/klog/v2\"\n)\n\nvar _ cloudpro"
  },
  {
    "path": "pkg/provider/instances.go",
    "chars": 2599,
    "preview": "package provider\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\tv1 \"k8s.io/api/core/v1\"\n\tcloudprovider \"k8s.io/cloud-provider\"\n"
  },
  {
    "path": "pkg/provider/loadbalancer.go",
    "chars": 2281,
    "preview": "package provider\n\nimport (\n\t\"context\"\n\n\tv1 \"k8s.io/api/core/v1\"\n\tcloudprovider \"k8s.io/cloud-provider\"\n\t\"k8s.io/klog/v2\""
  },
  {
    "path": "pkg/tunnels/address_darwin.go",
    "chars": 465,
    "preview": "//go:build darwin\n\npackage tunnels\n\nimport (\n\t\"os/exec\"\n)\n\nfunc AddIPToLocalInterface(ip string) (string, error) {\n\t// T"
  },
  {
    "path": "pkg/tunnels/address_other.go",
    "chars": 418,
    "preview": "//go:build !windows && !darwin\n\npackage tunnels\n\nimport (\n\t\"os/exec\"\n)\n\nfunc AddIPToLocalInterface(ip string) (string, e"
  },
  {
    "path": "pkg/tunnels/address_windows.go",
    "chars": 495,
    "preview": "//go:build windows\n\npackage tunnels\n\nimport (\n\t\"os/exec\"\n)\n\nfunc AddIPToLocalInterface(ip string) (string, error) {\n\tout"
  },
  {
    "path": "pkg/tunnels/tunnel.go",
    "chars": 8484,
    "preview": "package tunnels\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"k8s.io/klog/v2\"\n\n\t\"sigs.k8s.io/cloud-provide"
  },
  {
    "path": "tests/README.md",
    "chars": 210,
    "preview": "# Integration tests\n\n\n1. Install `bats` https://bats-core.readthedocs.io/en/stable/installation.html\n\n2. Install `kind` "
  },
  {
    "path": "tests/custom-network/setup_suite.bash",
    "chars": 1177,
    "preview": "#!/bin/bash\n\nset -eu\n\nfunction setup_suite {\n  export BATS_TEST_TIMEOUT=120\n  # Define the name of the kind cluster\n  ex"
  },
  {
    "path": "tests/custom-network/tests.bats",
    "chars": 1351,
    "preview": "#!/usr/bin/env bats\n\n@test \"Static LoadBalancerIP\" {\n    # the static IP must be part of the custom docker network - cal"
  },
  {
    "path": "tests/kind.yaml",
    "chars": 108,
    "preview": "kind: Cluster\napiVersion: kind.x-k8s.io/v1alpha4\nnodes:\n- role: control-plane\n- role: worker\n- role: worker\n"
  },
  {
    "path": "tests/setup_suite.bash",
    "chars": 857,
    "preview": "#!/bin/bash\n\nset -eu\n\nfunction setup_suite {\n  export BATS_TEST_TIMEOUT=120\n  # Define the name of the kind cluster\n  ex"
  },
  {
    "path": "tests/tests.bats",
    "chars": 8562,
    "preview": "#!/usr/bin/env bats\n\n@test \"ExternalTrafficPolicy: Local\" {\n    kubectl apply -f \"$BATS_TEST_DIRNAME\"/../examples/loadba"
  }
]

// ... and 2 more files (download for full content)

About this extraction

This page contains the full source code of the kubernetes-sigs/cloud-provider-kind GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 138 files (2.7 MB), approximately 712.3k tokens, and a symbol index with 294 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!