Full Code of open-policy-agent/kube-mgmt for AI

master 39dabfd6337c cached
101 files
250.2 KB
78.1k tokens
171 symbols
1 requests
Download .txt
Showing preview only (275K chars total). Download the full file or copy to clipboard to get everything.
Repository: open-policy-agent/kube-mgmt
Branch: master
Commit: 39dabfd6337c
Files: 101
Total size: 250.2 KB

Directory structure:
gitextract_9hv5t40p/

├── .dockerignore
├── .editorconfig
├── .github/
│   └── workflows/
│       ├── build.yaml
│       ├── cache.yaml
│       └── release.yaml
├── .gitignore
├── .ko.yaml
├── LICENSE
├── README.md
├── charts/
│   └── opa-kube-mgmt/
│       ├── Chart.yaml
│       ├── README.md
│       ├── templates/
│       │   ├── _helpers.tpl
│       │   ├── deployment.yaml
│       │   ├── ingressroute.yaml
│       │   ├── mgmt-token-secret.yaml
│       │   ├── poddisruptionbudget.yaml
│       │   ├── rbac-mgmt-replicate.yaml
│       │   ├── rbac-mgmt.yaml
│       │   ├── rbac-sar.yaml
│       │   ├── secret-opa-config.yaml
│       │   ├── service.yaml
│       │   ├── serviceaccount.yaml
│       │   ├── servicemonitor.yaml
│       │   └── webhookconfiguration.yaml
│       ├── values.schema.json
│       └── values.yaml
├── cmd/
│   └── kube-mgmt/
│       ├── flag.go
│       ├── flag_test.go
│       └── main.go
├── devbox.json
├── devspace.yaml
├── docs/
│   ├── admission-control-1.7.md
│   ├── admission-control-crd.md
│   ├── admission-control-secure.md
│   └── tls-1.7.md
├── examples/
│   └── service_validation/
│       ├── README.md
│       ├── admission_controller.yaml
│       └── install.sh
├── go.mod
├── go.sum
├── internal/
│   └── expect/
│       ├── client.go
│       ├── request.go
│       └── script.go
├── justfile
├── pkg/
│   ├── configmap/
│   │   └── configmap.go
│   ├── data/
│   │   ├── generic.go
│   │   ├── generic_test.go
│   │   └── types.go
│   ├── dynamicdata/
│   │   ├── dynamicdata.go
│   │   └── dynamicdata_test.go
│   ├── opa/
│   │   ├── opa.go
│   │   └── opa_test.go
│   ├── types/
│   │   └── types.go
│   └── version/
│       └── version.go
└── test/
    ├── e2e/
    │   ├── custom_config/
    │   │   ├── 1_bundle_loaded.hurl
    │   │   ├── chainsaw-test.yaml
    │   │   └── values.yaml
    │   ├── custom_mgmt_token/
    │   │   ├── 1_policy_loaded.hurl
    │   │   ├── 2_data_loaded.hurl
    │   │   ├── chainsaw-test.yaml
    │   │   └── values.yaml
    │   ├── default/
    │   │   ├── 1_initial_state.hurl
    │   │   ├── 2_policy_loaded.hurl
    │   │   ├── 3_data_loaded.hurl
    │   │   ├── chainsaw-test.yaml
    │   │   └── values.yaml
    │   ├── fixture-labels.yaml
    │   ├── fixture-multi.yaml
    │   ├── fixture-replication.yaml
    │   ├── fixture.yaml
    │   ├── labels/
    │   │   ├── 1_initial_state.hurl
    │   │   ├── 2_policy_loaded.hurl
    │   │   ├── 3_data_loaded.hurl
    │   │   ├── chainsaw-test.yaml
    │   │   └── values.yaml
    │   ├── multi/
    │   │   ├── 1_initial_state.hurl
    │   │   ├── 2_policies_loaded.hurl
    │   │   ├── 3_policy_unloaded.hurl
    │   │   ├── 4_policies_reloaded.hurl
    │   │   ├── chainsaw-test.yaml
    │   │   └── values.yaml
    │   ├── replicate/
    │   │   ├── 1_replication.hurl
    │   │   ├── chainsaw-test.yaml
    │   │   └── values.yaml
    │   └── replicate_auto/
    │       ├── .gitignore
    │       ├── 1_replication.hurl
    │       ├── bundle/
    │       │   ├── .manifest
    │       │   └── main.rego
    │       ├── chainsaw-test.yaml
    │       └── values.yaml
    ├── lint/
    │   ├── images.yaml
    │   ├── sa.yaml
    │   ├── service.yaml
    │   └── tsc.yaml
    └── unit/
        ├── health.yaml
        ├── kube-mgmt_args.yaml
        ├── rbac_cm.yaml
        ├── rbac_replicate.yaml
        ├── sa.yaml
        ├── service.yaml
        └── tsc.yaml

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

================================================
FILE: .dockerignore
================================================
# exclude everything, then re-include only what the Go build needs
*

!go.mod
!go.sum
!cmd/
!pkg/
!internal/


================================================
FILE: .editorconfig
================================================
root = true

[*.{sh,yaml,md}]
end_of_line = lf
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
max_line_length = 120

[justfile]
end_of_line = lf
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
max_line_length = 120


================================================
FILE: .github/workflows/build.yaml
================================================
name: Build
on:
  workflow_dispatch:
  push:
    paths-ignore:
      - "docs/**"
      - "logo/**"
      - "examples/**"
      - "README.md"
      - "charts/opa-kube-mgmt/README.md"
    branches:
      - "master"
  pull_request:
    branches:
      - "master"
      - "feat/*"
      - "fix/*"
jobs:
  build_job:
    name: Build
    runs-on: ubuntu-latest
    permissions:
      checks: write
    steps:
      - uses: actions/checkout@v6
      - uses: actions/cache@v5
        with:
          path: |
            ~/go/pkg/mod
            ~/go/bin
            ~/.local/share/helm/plugins
          key: go-tools-${{ hashFiles('devbox.json', 'go.mod', 'go.sum') }}
          restore-keys: |
            go-tools-
      - uses: jetify-com/devbox-install-action@v0.15.0
        with:
          enable-cache: true
      - name: lint and unit test
        run: devbox run -- just test
      - name: publish helm lint report
        uses: mikepenz/action-junit-report@v6
        if: always()
        with:
          report_paths: "build/test-results/helm-unittest/lint.xml"
          check_name: "Helm Lint Tests"
          fail_on_failure: true
          detailed_summary: true
          include_passed: true
      - name: publish helm unit test report
        uses: mikepenz/action-junit-report@v6
        if: always()
        with:
          report_paths: "build/test-results/helm-unittest/unit.xml"
          check_name: "Helm Unit Tests"
          fail_on_failure: true
          detailed_summary: true
          include_passed: true
      - name: e2e test
        run: devbox run -- just all && devbox run -- just test-e2e-all
      - name: publish e2e test report
        uses: mikepenz/action-junit-report@v6
        if: always()
        with:
          report_paths: "build/test-results/chainsaw/*.xml"
          check_name: "E2E Tests"
          fail_on_failure: true
          detailed_summary: true
          include_passed: true
      - name: failure logs
        if: ${{ failure() }}
        run: |
          echo "---------------------------------------"
          kubectl get all
          echo "---------------------------------------"
          kubectl describe po kube-mgmt-opa-kube-mgmt || true
          echo "---------------------------------------"
          kubectl logs -l app=kube-mgmt-opa-kube-mgmt -c opa --tail=-1
          echo "---------------------------------------"
          kubectl logs -l app=kube-mgmt-opa-kube-mgmt -c mgmt --tail=-1
          echo "---------------------------------------"


================================================
FILE: .github/workflows/cache.yaml
================================================
name: Update cache

on:
  push:
    branches:
      - master
  workflow_dispatch:
  schedule:
    - cron: '0 6 */6 * *'

jobs:
  update_cache:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/cache@v5
        with:
          path: |
            ~/go/pkg/mod
            ~/go/bin
            ~/.local/share/helm/plugins
          key: go-tools-${{ hashFiles('devbox.json', 'go.mod', 'go.sum') }}
          restore-keys: |
            go-tools-
      - uses: jetify-com/devbox-install-action@v0.15.0
        with:
          enable-cache: true
      - run: |
          eval "$(devbox shellenv -c . --init-hook)"
          devbox run -- go mod download


================================================
FILE: .github/workflows/release.yaml
================================================
name: Release

permissions:
  packages: write
  contents: write

on:
  workflow_dispatch: {}
  push:
    tags:
      - '[0-9]+.[0-9]+.[0-9]+'

jobs:
  docker_job:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: jetify-com/devbox-install-action@v0.15.0
        with:
          enable-cache: true
      - uses: docker/login-action@v4
        with:
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      - uses: docker/login-action@v4
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ github.token }}
      - name: build and publish image, create chart archive
        run: devbox run -- devspace build --profile release
      - name: upload helm artifact
        uses: actions/upload-artifact@v7
        with:
          name: "helm"
          path: "opa-kube-mgmt-*.tgz"

  helm_job:
    runs-on: ubuntu-latest
    needs: docker_job
    steps:
      - uses: actions/checkout@v6
        with:
          ref: gh-pages
      - name: download helm artifact
        uses: actions/download-artifact@v8
        id: download
        with:
          name: helm
          path: /tmp/helm
      - name: update helm index
        run: |
          helm repo index /tmp/helm --merge ./charts/index.yaml
          mv -f /tmp/helm/* ./charts
      - name: publish index and chart
        uses: actions-js/push@v1.5
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: gh-pages


================================================
FILE: .gitignore
================================================
./kube-mgmt
bin
.go
*.tgz
.idea
.vscode/settings.json
.devspace/
build/


================================================
FILE: .ko.yaml
================================================
defaultBaseImage: alpine:3.23.4
builds:
  - id: kube-mgmt
    main: ./cmd/kube-mgmt
    ldflags:
      - -X github.com/open-policy-agent/kube-mgmt/pkg/version.Version={{.Env.KO_VERSION}}
      - -X github.com/open-policy-agent/kube-mgmt/pkg/version.Git={{.Env.KO_COMMIT}}


================================================
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: README.md
================================================
# ![logo](./logo/logo.png) kube-mgmt

`kube-mgmt` manages policies / data of [Open Policy Agent](https://github.com/open-policy-agent/opa)
instances in Kubernetes.

Use `kube-mgmt` to:
* Load policies and/or static data into OPA instance from `ConfigMap`.
* Replicate Kubernetes resources
including [CustomResourceDefinitions (CRDs)](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#customresourcedefinitions) into OPA instance.

## Deployment Guide

Both `OPA` and `kube-mgmt` can be installed using [opa-kube-mgmt](
https://artifacthub.io/packages/helm/opa-kube-mgmt/opa-kube-mgmt) Helm chart.

Follow [README](charts/opa-kube-mgmt/README.md) to install it into K8s cluster.

## Policies and data loading

`kube-mgmt` automatically discovers policies and JSON data
stored in `ConfigMaps` in Kubernetes and loads them into OPA.

`kube-mgmt` assumes a `ConfigMap` contains policy or JSON data if the `ConfigMap` is:

- Created in a namespace listed in the `--namespaces` option.
  If you specify `--namespaces=*` then `kube-mgmt` will look for policies in ALL namespaces.
- Labelled with `openpolicyagent.org/policy=rego` for policies
- Labelled with `openpolicyagent.org/data=opa` for JSON data

Policies or data discovery and loading can be disabled using `--enable-policy=false` or `--enable-data=false` flags respectively.

Label names and their values can be configured using `--policy-label`, `--policy-value`, `--data-label`, `--data-value` CLI options.

When a `ConfigMap` has been successfully loaded into OPA,
the `openpolicyagent.org/kube-mgmt-status` annotation is set to `{"status": "ok"}`.

If loading fails for some reason (e.g., because of a parse error), the
`openpolicyagent.org/kube-mgmt-status` annotation is set to `{"status": "error", "error": ...}`
where the `error` field contains details about the failure.

Data loaded out of ConfigMaps is laid out as follows:

```
<namespace>/<name>/<key>
```

For example, if the following ConfigMap was created:

```yaml
kind: ConfigMap
apiVersion: v1
metadata:
  name: hello-data
  namespace: opa
  labels:
    openpolicyagent.org/data: opa
data:
  x.json: |
    {"a": [1,2,3,4]}
```
Note: "x.json" may be any key.

You could refer to the data inside your policies as follows:

```rego
data.opa["hello-data"]["x.json"].a[0]  # evaluates to 1
```

## K8s resource replication

> [!WARNING]
> K8s resource replication requires global cluster permission with `ClusterRole` and `ClusterRoleBinding`.

`kube-mgmt` can be configured to replicate Kubernetes resources into OPA so that
you can express policies over an eventually consistent cache of Kubernetes
state.

Replication is enabled with the following options:

```bash
# Replicate namespace-level resources. May be specified multiple times.
--replicate=<[group/]version/resource>

# Replicate cluster-level resources. May be specified multiple times.
--replicate-cluster=<[group/]version/resource>
```

By default resources are replicated from all namespaces.
Use `--replicate-ignore-namespaces` option to exclude particular namespaces from replication.

Kubernetes resources replicated into OPA are laid out as follows:

```
<replicate-path>/<resource>/<namespace>/<name> # namespace scoped
<replicate-path>/<resource>/<name>             # cluster scoped
```

- `<replicate-path>` is configurable (via `--replicate-path`) and
  defaults to `kubernetes`.
- `<resource>` is the Kubernetes resource plural, e.g., `nodes`,
  `pods`, `services`, etc.
- `<namespace>` is the namespace of the Kubernetes resource.
- `<name>` is the name of the Kubernetes resource.

For example, to search for services with the label `"foo"` you could write:

```
some namespace, name
service := data.kubernetes.services[namespace][name]
service.metadata.labels["foo"]
```

An alternative way to visualize the layout is as single JSON document:

```json
{
  "kubernetes": {
    "services": {
      "default": {
        "example-service": {...},
          "another-service": {...},
        }
      }
    }
  }
}
```

The example below would replicate Deployments, Services, and Nodes into OPA:

```bash
--replicate=apps/v1beta/deployments
--replicate=v1/services
--replicate-cluster=v1/nodes
```

Custom Resource Definitions can also be replicated using the same `--replicate` and `--replicate-cluster` options.

## Admission Control

To get started with admission control policy enforcement in Kubernetes 1.9 or later see the [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kubernetes-admission-control.html) tutorial. For older versions of Kubernetes, see [Admission Control (1.7)](./docs/admission-control-1.7.md).

In the [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kubernetes-admission-control.html) tutorial, OPA is **NOT** running with an authorization policy configured and hence clients can read and write policies in OPA. When deploying OPA in an insecure environment, it is recommended to configure `authentication` and `authorization` on the OPA daemon. For an example of how OPA can be securely deployed as an admission controller see [Admission Control Secure](./docs/admission-control-secure.md).

## OPA API Endpoints and Least-privilege Configuration

`kube-mgmt` is a privileged component that can load policy and data into OPA.
Other clients connecting to the OPA API only need to query for policy decisions.

To load policy and data into OPA, `kube-mgmt` uses the following OPA API
endpoints:

* `PUT v1/policy/<path>` - upserting policies
* `DELETE v1/policy/<path>` - deleting policies
* `PUT v1/data/<path>` - upserting data
* `PATCH v1/data/<path>` - updating and removing data

Many users configure OPA with a simple API authorization policy that restricts
access to the OPA APIs:

```rego
package system.authz

# Deny access by default.
default allow = false

# Allow anonymous access to decision `data.example.response`
#
# NOTE: the specific decision differs depending on your policies.
# NOTE: depending on how callers are configured, they may only require this or the default decision below.
allow {
  input.path == ["v0", "data", "example", "response"]
  input.method == "POST"
}

# Allow anonymous access to default decision.
allow {
  input.path == [""]
  input.method == "POST"
}

# This is only used for health check in liveness and readiness probe
allow {
  input.path == ["health"]
  input.method == "GET"
}

# This is only used for prometheus metrics
allow {
  input.path == ["metrics"]
  input.method == "GET"
}

# This is used by kube-mgmt to PUT/PATCH against /v1/data and PUT/DELETE against /v1/policies.
#
# NOTE: The $TOKEN value is replaced at deploy-time with the actual value that kube-mgmt will use. This is typically done by an initContainer.
allow {
  input.identity == "$TOKEN"
}
```

## Development

### Environment setup

This project uses [devbox](https://www.jetify.com/docs/devbox/installing-devbox/) to provide a fully isolated,
reproducible development environment. All required tools (Go, just, OPA CLI, staticcheck, and others)
are managed by devbox at pinned versions — no manual installation needed.

To enter the development shell:

```bash
devbox shell
```

This project uses `just` as a command runner, configured in [justfile](./justfile).
Run `just` without arguments to list all available recipes.

### Running the application locally

`kube-mgmt` runs in a local [k3d](https://k3d.io) Kubernetes cluster. Create the cluster once before first use:

```bash
just all
```

Start and stop `kube-mgmt` application with:

```bash
just up
just down
```

Delete local k8s cluster

```sh
just 3d-down
```

### Tests

The project has three categories of tests.

#### Go unit tests

Standard Go tests using the `testing` package:

```bash
just test-go
```

#### Helm chart unit tests

Chart rendering tests implemented with the [helm-unittest](https://github.com/helm-unittest/helm-unittest) plugin:

```bash
just test-helm
```

#### End-to-end tests

E2E tests deploy `kube-mgmt` to the local k3d cluster via [devspace](https://devspace.sh) and validate behavior using
[chainsaw](https://kyverno.github.io/chainsaw/) (Kubernetes-native test framework) and [hurl](https://hurl.dev)
(HTTP assertions). Each scenario is a directory under `test/e2e/`.

Run a single scenario (shows an interactive picker when no argument is given):

```bash
just test-e2e [test/e2e/<scenario>]
```

Run all scenarios sequentially:

```bash
just test-e2e-all
```

#### Linting

```bash
just lint
```

Runs `go vet` and [staticcheck](https://staticcheck.io) for Go code, and helm-unittest lint rules for the Helm chart.

#### Run all checks

```bash
just test
```

Runs lint, Go unit tests, and Helm chart unit tests.

### Release

To release a new version, create a [GitHub release](https://github.com/open-policy-agent/kube-mgmt/releases)
with a tag that follows the [semantic versioning convention](https://semver.org/).

Once the tag is pushed, the CI pipeline automatically builds and publishes all release artifacts:
Docker images for all supported architectures and the Helm chart.


================================================
FILE: charts/opa-kube-mgmt/Chart.yaml
================================================
apiVersion: v1
appVersion: 0.0.0 # managed by git tag
version: 0.0.0 # managed by git tag
description: Manage OPA in Kubernetes with kube-mgmt sidecar.
name: opa-kube-mgmt
keywords:
  - opa
  - admission control
  - policy
  - kubernetes
  - security
home: https://www.openpolicyagent.org
icon: https://raw.githubusercontent.com/open-policy-agent/opa/master/logo/logo.png
annotations:
  artifacthub.io/links: |
    - name: OPA source code
      url: https://github.com/open-policy-agent/opa
    - name: kube-mgmt source code
      url: https://github.com/open-policy-agent/kube-mgmt


================================================
FILE: charts/opa-kube-mgmt/README.md
================================================
# Manage OPA in Kubernetes with kube-mgmt sidecar.

[OPA](https://www.openpolicyagent.org) is an open-source general-purpose policy
engine designed for cloud-native environments.

## Overview

This helm chart installs `OPA` together with `kube-mgmt` sidecar,
that allows to manage OPA policies and data via Kubernetes ``ConfigMaps`.

Optionally, the chart allows to install a [Kubernetes admission
controller](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/).

## Installation

### Prerequisites

- Kubernetes 1.9 (or newer) for validating and mutating webhook admission
  controller support.
- Optional, cert-manager (https://docs.cert-manager.io/en/latest/)

### Default Installation

If you just want to see something run, install the chart with default configuration.

```sh
helm repo add opa https://open-policy-agent.github.io/kube-mgmt/charts
helm repo update
helm upgrade -i -n opa --create-namespace opa opa/opa-kube-mgmt
```

Once installed, the OPA will download a sample bundle from https://www.openpolicyagent.org.
It contains a simple policy that restricts the hostnames that can be specified on Ingress objects created in the
`opa-example` namespace.

You can download the bundle and inspect it yourself:

```sh
mkdir example && cd example
curl -s -L https://www.openpolicyagent.org/bundles/kubernetes/admission | tar xzv
```

### Installation from GitHub Packages (GHCR)

The Helm chart and Docker image are also published to GitHub Container Registry (GHCR).

Install the chart using OCI:

```sh
helm upgrade -i -n opa --create-namespace opa \
  oci://ghcr.io/open-policy-agent/helm/opa-kube-mgmt --version <version>
```

The `kube-mgmt` Docker image is also published to GHCR. To pull it directly:

```sh
# latest
docker pull ghcr.io/open-policy-agent/docker/opa-kube-mgmt:latest

# specific version
docker pull ghcr.io/open-policy-agent/docker/opa-kube-mgmt:<version>
```

To use the GHCR image when installing the chart:

```sh
helm upgrade -i -n opa --create-namespace opa \
  oci://ghcr.io/open-policy-agent/helm/opa-kube-mgmt \
  --set mgmt.image.repository=ghcr.io/open-policy-agent/docker/opa-kube-mgmt \
  --set mgmt.image.tag=latest
```

## Configuration

All configuration settings are contained and described in [values.yaml](values.yaml).

You should set the URL and credentials for the OPA to use to download policies.
The URL should identify an HTTP endpoint that implements the [OPA Bundle
API](https://www.openpolicyagent.org/docs/bundles.html).

- `opa.services.controller.url` specifies the base URL of the OPA control plane.

- `opa.services.controller.credentials.bearer.token` specifies a bearer token
  for the OPA to use to authenticate with the control plane.

For more information on OPA-specific configuration see the [OPA Configuration
Reference](https://www.openpolicyagent.org/docs/configuration.html).



================================================
FILE: charts/opa-kube-mgmt/templates/_helpers.tpl
================================================
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "opa.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "opa.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}

{{- define "opa.sarfullname" -}}
{{- $name := (include "opa.fullname" . | trunc 59 | trimSuffix "-") -}}
{{- printf "%s-sar" $name -}}
{{- end -}}

{{- define "opa.mgmtfullname" -}}
{{- $name := (include "opa.fullname" . | trunc 58 | trimSuffix "-") -}}
{{- printf "%s-mgmt" $name -}}
{{- end -}}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "opa.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Define standard labels for frequently used metadata.
*/}}
{{- define "opa.labels.standard" -}}
app: {{ template "opa.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
{{- end -}}

{{/*
Create the name of the service account to use
*/}}
{{- define "opa.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
    {{ default (include "opa.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
    {{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}

{{- define "opa.selfSignedIssuer" -}}
{{ printf "%s-selfsign" (include "opa.fullname" .) }}
{{- end -}}

{{- define "opa.rootCAIssuer" -}}
{{ printf "%s-ca" (include "opa.fullname" .) }}
{{- end -}}

{{- define "opa.rootCACertificate" -}}
{{ printf "%s-ca" (include "opa.fullname" .) }}
{{- end -}}

{{- define "opa.servingCertificate" -}}
{{ printf "%s-webhook-tls" (include "opa.fullname" .) }}
{{- end -}}

{{/*
Detect the version of cert manager crd that is installed
Error if CRD is not available
*/}}
{{- define "opa.certManagerApiVersion" -}}
{{- if (.Capabilities.APIVersions.Has "cert-manager.io/v1") -}}
cert-manager.io/v1
{{- else if (.Capabilities.APIVersions.Has "cert-manager.io/v1beta1") -}}
cert-manager.io/v1beta1
{{- else  -}}
{{- fail "cert-manager CRD does not appear to be installed" }}
{{- end -}}
{{- end -}}

{{/*
Detect the available version of admissionregistration
*/}}
{{- define "opa.admissionregistrationApiVersion" -}}
{{- if (.Capabilities.APIVersions.Has "admissionregistration.k8s.io/v1") -}}
admissionregistration.k8s.io/v1
{{- else  -}}
admissionregistration.k8s.io/v1beta1
{{- end -}}
{{- end -}}

{{- define "opa.mgmt.image" -}}
{{- $tag := .Values.mgmt.image.tag | default .Chart.AppVersion -}}
{{ printf "%s:%s" .Values.mgmt.image.repository $tag }}
{{- end -}}

{{- define "opa.dnsPolicy" -}}
{{- if .Values.dnsPolicyOverride -}}
dnsPolicy: "{{ .Values.dnsPolicyOverride }}"
{{ else if .Values.hostNetwork.enabled -}}
dnsPolicy: "ClusterFirstWithHostNet"
{{ end -}}
{{ end -}}


================================================
FILE: charts/opa-kube-mgmt/templates/deployment.yaml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ template "opa.fullname" . }}
  labels:
{{ include "opa.labels.standard" . | indent 4 }}
spec:
  replicas: {{ .Values.replicas }}
  selector:
    matchLabels:
      app: {{ template "opa.fullname" . }}
  {{- with .Values.deploymentStrategy }}
  strategy:
  {{- toYaml . | nindent 4 }}
  {{- end }}
  template:
    metadata:
      annotations:
        {{- if .Values.opa }}
        checksum/config: {{ tpl (toYaml .Values.opa) . | sha256sum }}
        {{- end }}
        {{- if .Values.admissionController.enabled }}
        checksum/webhookconfiguration: {{ include (print $.Template.BasePath "/webhookconfiguration.yaml" ) . | sha256sum }}
        {{- end }}
{{- if .Values.annotations }}
{{ toYaml .Values.annotations | indent 8 }}
{{- end }}
      labels:
        app: {{ template "opa.fullname" . }}
      name: {{ template "opa.fullname" . }}
    spec:
{{- if .Values.imagePullSecrets }}
      imagePullSecrets:
      {{- range .Values.imagePullSecrets }}
        - name: {{ . }}
      {{- end }}
{{- end }}
{{- if .Values.priorityClassName }}
      priorityClassName: {{ .Values.priorityClassName }}
{{- end }}
{{- if or .Values.authz.enabled .Values.bootstrapPolicies}}
      initContainers:
        - name: initpolicy
          image: {{ include "opa.mgmt.image" . }}
          imagePullPolicy: {{ .Values.mgmt.image.pullPolicy }}
          resources:
{{ toYaml .Values.mgmt.resources | indent 12 }}
          command:
          - /bin/sh
          - -c
          - |
{{- if .Values.authz.enabled }}
{{- if .Values.authz.mgmtToken}}
            cat /mgmt-token-secret/mgmt-token > /bootstrap/mgmt-token
{{- else }}
            tr -dc 'A-F0-9' < /dev/urandom | dd bs=1 count=32 2>/dev/null > /bootstrap/mgmt-token
{{- end }}
            TOKEN=`cat /bootstrap/mgmt-token`
            cat > /bootstrap/authz.rego <<EOF
            package system.authz
            import rego.v1
            default allow := false
            # Allow anonymous access to the default policy decision.
            allow if { input.path = [""]; input.method == "POST" }
            allow if { input.path = [""]; input.method == "GET" }
            # This is only used for health check in liveness and readiness probe
            allow if { input.path = ["health"]; input.method == "GET" }
{{- if .Values.prometheus.enabled }}
            # This allows metrics to be scraped by prometheus
            allow if { input.path = ["metrics"]; input.method == "GET" }
{{- end }}
            allow if { input.identity == "$TOKEN" }
            EOF
{{- end }}
{{- range $policyName, $policy := .Values.bootstrapPolicies }}
            cat > /bootstrap/{{ $policyName }}.rego <<EOF
{{ $policy | indent 12 }}
            EOF
{{- end }}
          volumeMounts:
            - name: bootstrap
              mountPath: /bootstrap
{{- if .Values.authz.mgmtToken}}
            - name: mgmt-token-secret
              mountPath: /mgmt-token-secret
              readOnly: true
{{- end }}
{{- end }}
{{- if .Values.hostNetwork.enabled }}
      hostNetwork: true
{{- end }}
      {{- include "opa.dnsPolicy" . | nindent 6 -}}
      containers:
        - name: opa
          ports:
          - name: opa
            containerPort: {{ .Values.port }}
{{- if .Values.prometheus.enabled }}
          - name: diag
            containerPort: {{ .Values.prometheus.port }}
{{- end }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          env:
{{- if .Values.extraEnv }}
{{ toYaml .Values.extraEnv | indent 12 }}
{{- end }}
          resources:
{{ toYaml .Values.resources | indent 12 }}
          args:
            - "run"
            - "--server"
            {{- if .Values.opa }}
            - "--config-file=/config/config.yaml"
            {{- end }}
            {{- if .Values.useHttps }}
            - "--tls-cert-file=/certs/tls.crt"
            - "--tls-private-key-file=/certs/tls.key"
            {{- end }}
            - "--addr=0.0.0.0:{{ .Values.port }}"
            - "--log-level={{ .Values.logLevel }}"
            - "--log-format={{ .Values.logFormat }}"
            {{- if .Values.authz.enabled }}
            - "--authentication=token"
            - "--authorization=basic"
            - "--ignore=.*"
            {{- end }}
            {{- if .Values.prometheus.enabled }}
            - "--diagnostic-addr=http://0.0.0.0:{{ .Values.prometheus.port }}"
            {{- end }}
            {{- if or .Values.authz.enabled .Values.bootstrapPolicies }}
            - "/bootstrap"
            {{- end }}
            {{- range .Values.extraArgs }}
            - {{ . }}
            {{- end }}
          volumeMounts:
            {{- if .Values.useHttps }}
            - name: certs
              readOnly: true
              mountPath: /certs
            {{- end }}
            {{- if .Values.opa }}
            - name: config
              readOnly: true
              mountPath: /config
            {{- end }}
{{- if or .Values.authz.enabled .Values.bootstrapPolicies }}
            - name: bootstrap
              readOnly: true
              mountPath: /bootstrap
{{- end }}
{{- if .Values.extraVolumeMounts }}
{{ toYaml .Values.extraVolumeMounts | indent 12}}
{{- end }}
          readinessProbe:
            httpGet:
              path: /health
              scheme: {{ .Values.useHttps | ternary "HTTPS" "HTTP" }}
              port: opa
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /health
              scheme: {{ .Values.useHttps | ternary "HTTPS" "HTTP" }}
              port: opa
            initialDelaySeconds: 10
            periodSeconds: 15
{{- if .Values.mgmt.enabled }}
        - name: mgmt
          image: {{ include "opa.mgmt.image" . }}
          imagePullPolicy: {{ .Values.mgmt.image.pullPolicy }}
          startupProbe:
{{ toYaml .Values.mgmt.startupProbe | nindent 12 }}
          env:
{{- if .Values.mgmt.extraEnv }}
{{ toYaml .Values.mgmt.extraEnv | indent 12 }}
{{- end }}
          resources:
            {{ toYaml .Values.mgmt.resources | nindent 12 }}
          args:
            {{- if .Values.authz.enabled }}
            - --opa-auth-token-file=/bootstrap/mgmt-token
            {{- end }}
            - --opa-url={{ .Values.useHttps | ternary "https" "http" }}://127.0.0.1:{{ .Values.port }}/v1
            - --opa-allow-insecure
            - "--namespaces={{ coalesce .Values.mgmt.namespaces (list .Release.Namespace) | join "," }}"
            - "--enable-data={{ .Values.mgmt.data.enabled }}"
            - "--enable-policies={{ .Values.mgmt.policies.enabled }}"

            - "--replicate-path={{ .Values.mgmt.replicate.path }}"
            {{- range .Values.mgmt.replicate.namespace }}
            - "--replicate={{ . }}"
            {{- end }}
            {{- range .Values.mgmt.replicate.cluster }}
            - "--replicate-cluster={{ . }}"
            {{- end }}
            {{- if .Values.mgmt.replicate.ignoreNs }}
            - "--replicate-ignore-namespaces={{ .Values.mgmt.replicate.ignoreNs | join "," }}"
            {{- else }}
            - "--replicate-ignore-namespaces="
            {{- end }}
            {{- if .Values.mgmt.replicate.auto }}
            - "--opa-config=/config/config.yaml"
            - "--health-endpoint=0.0.0.0:8000"
            {{- end }}
            {{- range .Values.mgmt.extraArgs }}
            - {{ . }}
            {{- end }}
          volumeMounts:
{{- if or .Values.authz.enabled .Values.bootstrapPolicies }}
            - name: bootstrap
              readOnly: true
              mountPath: /bootstrap
{{- end }}
{{- if .Values.opa }}
            - name: config
              readOnly: true
              mountPath: /config
{{- end }}
{{- if .Values.extraVolumeMounts }}
{{ toYaml .Values.extraVolumeMounts | indent 12}}
{{- end }}
{{- if .Values.mgmt.replicate.auto }}
          readinessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 10
            periodSeconds: 15
{{- end }}
{{- end }}
{{- if .Values.sar.enabled }}
        - name: sarproxy
          image: {{ .Values.sar.image.repository }}:{{ .Values.sar.image.tag }}
          imagePullPolicy: {{ .Values.sar.image.pullPolicy }}
          resources:
{{ toYaml .Values.sar.resources | indent 12 }}
          command:
            - kubectl
            - proxy
            - --accept-paths=^/apis/authorization.k8s.io/v1/subjectaccessreviews$
{{- end }}
{{- if .Values.extraContainers }}
{{ toYaml .Values.extraContainers | indent 8}}
{{- end }}
      {{- if .Values.securityContext.enabled }}
      securityContext:
        {{- range $key, $val := .Values.securityContext }}
        {{- if ne $key "enabled" }}
        {{ $key }}: {{ toYaml $val | nindent 10 }}
        {{- end }}
        {{- end }}
      {{- end }}
      serviceAccountName: {{ template "opa.serviceAccountName" .}}
      volumes:
        {{- if .Values.useHttps }}
        - name: certs
          secret:
            secretName: {{ template "opa.fullname" . }}-cert
        {{- end }}
        {{- if .Values.opa }}
        - name: config
          secret:
            secretName: {{ template "opa.fullname" . }}-config
        {{- end }}
{{- if or .Values.authz.enabled .Values.bootstrapPolicies}}
        - name: bootstrap
          emptyDir: {}
{{- if .Values.authz.mgmtToken}}
        - name: mgmt-token-secret
          secret:
            secretName: {{.Values.authz.mgmtToken.secretName}}
            items:
              - key: {{ .Values.authz.mgmtToken.secretKey | default "mgmtToken" }}
                path: mgmt-token
{{- end }}
{{- end }}
{{- if .Values.extraVolumes }}
{{ toYaml .Values.extraVolumes | indent 8}}
{{- end }}
      affinity:
{{ toYaml .Values.affinity | indent 8 }}
      nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
      tolerations:
{{ toYaml .Values.tolerations | indent 8 }}
      topologySpreadConstraints:
{{ toYaml .Values.topologySpreadConstraints | indent 8 }}


================================================
FILE: charts/opa-kube-mgmt/templates/ingressroute.yaml
================================================
{{- if .Values.e2e }}
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
  name: {{ include "opa.fullname" . }}-websecure
spec:
  entryPoints:
    - websecure
  routes:
    - match: HostSNI(`*`)
      services:
        - name: {{ include "opa.fullname" . }}
          namespace: {{ .Release.Namespace }}
          port: {{ .Values.port }}
  tls:
    passthrough: true
---
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
  name: {{ include "opa.fullname" . }}-web
spec:
  entryPoints:
    - web
  routes:
    - match: HostSNI(`*`)
      services:
        - name: {{ include "opa.fullname" . }}
          namespace: {{ .Release.Namespace }}
          port: {{ .Values.port }}
{{- end }}


================================================
FILE: charts/opa-kube-mgmt/templates/mgmt-token-secret.yaml
================================================
{{- if .Values.e2eMgmtTokenSecret -}}
apiVersion: v1
kind: Secret
metadata:
  name: mgmt-token-secret
type: Opaque
stringData:
  mgmtToken: mgmt-token-secret-value
{{- end -}}

================================================
FILE: charts/opa-kube-mgmt/templates/poddisruptionbudget.yaml
================================================
{{- if .Values.podDisruptionBudget.enabled }}
{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" }}
apiVersion: policy/v1
{{- else }}
apiVersion: policy/v1beta1
{{- end }}
kind: PodDisruptionBudget
metadata:
  name: {{ template "opa.fullname" . }}
  labels:
{{ include "opa.labels.standard" . | indent 4 }}
spec:
{{- if .Values.podDisruptionBudget.minAvailable }}
  minAvailable: {{ .Values.podDisruptionBudget.minAvailable }}
{{- end }}
{{- if .Values.podDisruptionBudget.maxUnavailable }}
  maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }}
{{- end }}
  selector:
    matchLabels:
      app: {{ template "opa.fullname" . }}
{{- end }}


================================================
FILE: charts/opa-kube-mgmt/templates/rbac-mgmt-replicate.yaml
================================================
{{- if and .Values.rbac.create .Values.mgmt.enabled -}}
{{- $arr := concat .Values.mgmt.replicate.cluster .Values.mgmt.replicate.namespace -}}
{{- if or (gt (len $arr) 0) .Values.mgmt.replicate.auto }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app: {{ template "opa.name" . }}
    chart: {{ template "opa.chart" . }}
    release: {{ .Release.Name }}
    component: mgmt
  name: "{{ template "opa.mgmtfullname" . }}-repl"
rules:
  {{- with .Values.rbac.extraRules }}
  {{ . | toYaml | nindent 2 }}
  {{- end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app: {{ template "opa.name" . }}
    chart: {{ template "opa.chart" . }}
    release: {{ .Release.Name }}
    component: mgmt
  name: "{{ template "opa.mgmtfullname" . }}-repl"
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: "{{ template "opa.mgmtfullname" . }}-repl"
subjects:
  - kind: ServiceAccount
    name: {{ template "opa.serviceAccountName" . }}
    namespace: {{ .Release.Namespace }}
{{- end }}
{{- end -}}


================================================
FILE: charts/opa-kube-mgmt/templates/rbac-mgmt.yaml
================================================
{{- define "opa.rbac.cm.rules" -}}
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list", "watch", "update", "patch"]
{{- end -}}

{{- if and .Values.rbac.create .Values.mgmt.enabled -}}
{{- if eq (.Values.mgmt.namespaces | join ",") "*" }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app: {{ template "opa.name" . }}
    chart: {{ template "opa.chart" . }}
    release: {{ .Release.Name }}
    component: mgmt
  name: {{ template "opa.mgmtfullname" . }}
{{ include "opa.rbac.cm.rules" . }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app: {{ template "opa.name" . }}
    chart: {{ template "opa.chart" . }}
    release: {{ .Release.Name }}
    component: mgmt
  name: {{ template "opa.mgmtfullname" . }}
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: {{ template "opa.mgmtfullname" . }}
subjects:
  - kind: ServiceAccount
    name: {{ template "opa.serviceAccountName" . }}
    namespace: {{ .Release.Namespace }}
{{- else if and (eq (kindOf .Values.mgmt.namespaces) "slice") (gt (len .Values.mgmt.namespaces) 0) }}
{{- range .Values.mgmt.namespaces }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  labels:
    app: {{ template "opa.name" $ }}
    chart: {{ template "opa.chart" $ }}
    release: {{ $.Release.Name }}
    component: mgmt
  name: {{ template "opa.mgmtfullname" $ }}
  namespace: {{ . }}
{{ include "opa.rbac.cm.rules" $ }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    app: {{ template "opa.name" $ }}
    chart: {{ template "opa.chart" $ }}
    release: {{ $.Release.Name }}
    component: mgmt
  name: {{ template "opa.mgmtfullname" $ }}
  namespace: {{ . }}
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: {{ template "opa.mgmtfullname" $ }}
subjects:
  - kind: ServiceAccount
    name: {{ template "opa.serviceAccountName" $ }}
    namespace: {{ $.Release.Namespace }}
{{- end }}
{{- else }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  labels:
    app: {{ template "opa.name" . }}
    chart: {{ template "opa.chart" . }}
    release: {{ .Release.Name }}
    component: mgmt
  name: {{ template "opa.mgmtfullname" . }}
  namespace: {{ .Release.Namespace }}
{{ include "opa.rbac.cm.rules" . }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    app: {{ template "opa.name" . }}
    chart: {{ template "opa.chart" . }}
    release: {{ .Release.Name }}
    component: mgmt
  name: {{ template "opa.mgmtfullname" . }}
  namespace: {{ .Release.Namespace }}
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: {{ template "opa.mgmtfullname" . }}
subjects:
  - kind: ServiceAccount
    name: {{ template "opa.serviceAccountName" . }}
    namespace: {{ .Release.Namespace }}
{{- end }}
{{- end -}}


================================================
FILE: charts/opa-kube-mgmt/templates/rbac-sar.yaml
================================================
{{- if (and .Values.rbac.create .Values.sar.enabled) -}}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app: {{ template "opa.name" . }}
    chart: {{ template "opa.chart" . }}
    heritage: {{ .Release.Service }}
    release: {{ .Release.Name }}
    component: sar
  name: {{ template "opa.sarfullname" . }}
rules:
  - apiGroups:
      - "authorization.k8s.io"
    resources:
    - subjectaccessreviews
    verbs:
    - create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app: {{ template "opa.name" . }}
    chart: {{ template "opa.chart" . }}
    heritage: {{ .Release.Service }}
    release: {{ .Release.Name }}
    component: sar
  name: {{ template "opa.sarfullname" . }}
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: {{ template "opa.sarfullname" . }}
subjects:
  - kind: ServiceAccount
    name: {{ template "opa.serviceAccountName" . }}
    namespace: {{ .Release.Namespace }}
{{- end -}}


================================================
FILE: charts/opa-kube-mgmt/templates/secret-opa-config.yaml
================================================
{{- if .Values.opa -}}
apiVersion: v1
kind: Secret
metadata:
  name: {{ template "opa.fullname" . }}-config
  labels:
{{ include "opa.labels.standard" . | indent 4 }}
type: Opaque
data:
  config.yaml: {{ toYaml .Values.opa | b64enc }}
{{- end -}}

================================================
FILE: charts/opa-kube-mgmt/templates/service.yaml
================================================
kind: Service
apiVersion: v1
metadata:
  name: {{ template "opa.fullname" . }}
  labels:
{{ include "opa.labels.standard" . | indent 4 }}
{{- with .Values.service.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
{{- end }}
spec:
  selector:
    app: {{ template "opa.fullname" . }}
  ports:
  - name: opa
    port: {{ .Values.port }}
    targetPort: opa
{{- if .Values.prometheus.enabled }}
  - name: diag
    port: {{ .Values.prometheus.port }}
    targetPort: diag
{{- end }}
{{- if .Values.extraPorts }}
{{ toYaml .Values.extraPorts | indent 2}}
{{- end }}
{{- if .Values.service.trafficDistribution }}
  trafficDistribution: {{ .Values.service.trafficDistribution }}
{{- end }}


================================================
FILE: charts/opa-kube-mgmt/templates/serviceaccount.yaml
================================================
{{- if .Values.serviceAccount.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ template "opa.serviceAccountName" .}}
  {{- with .Values.serviceAccount.annotations }}
  annotations:
    {{ toYaml . }}
  {{- end }}
  labels:
    app: {{ template "opa.fullname" . }}
    chart: {{ template "opa.chart" . }}
    release: "{{ .Release.Name }}"
    heritage: "{{ .Release.Service }}"
{{- end }}


================================================
FILE: charts/opa-kube-mgmt/templates/servicemonitor.yaml
================================================
{{- if and (.Capabilities.APIVersions.Has "monitoring.coreos.com/v1") .Values.prometheus.enabled .Values.serviceMonitor.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    app: {{ template "opa.name" . }}
    chart: {{ template "opa.chart" . }}
    heritage: {{ .Release.Service }}
    {{- if not .Values.serviceMonitor.additionalLabels.release }}
    release: {{ .Release.Name }}
    {{- end }}
    {{- if .Values.serviceMonitor.additionalLabels }}
    {{- toYaml .Values.serviceMonitor.additionalLabels | nindent 4}}
    {{- end }}
  name: {{ template "opa.fullname" . }}
  {{- if .Values.serviceMonitor.namespace }}
  namespace: {{ .Values.serviceMonitor.namespace }}
  {{- end }}
spec:
  endpoints:
  - port: diag
    interval: {{ .Values.serviceMonitor.interval }}
  jobLabel: {{ template "opa.fullname" . }}
  namespaceSelector:
    matchNames:
    - {{ .Release.Namespace }}
  selector:
    matchLabels:
      app: {{ template "opa.fullname" . }}
      release: {{ .Release.Name }}
{{- end }}


================================================
FILE: charts/opa-kube-mgmt/templates/webhookconfiguration.yaml
================================================
{{- $cn := printf "%s.%s.svc" ( include "opa.fullname" . ) .Release.Namespace }}
{{- $ca := genCA "opa-admission-ca" 3650 -}}
{{- $cert := genSignedCert $cn nil (list $cn) 3650 $ca -}}
{{- if .Values.admissionController.enabled }}
kind: {{ .Values.admissionController.kind }}
apiVersion: {{ include "opa.admissionregistrationApiVersion" . }}
metadata:
  name: {{ template "opa.fullname" . }}
  annotations:
{{- if .Values.certManager.enabled }}
    certmanager.k8s.io/inject-ca-from: {{ printf "%s/%s" .Release.Namespace (include "opa.rootCACertificate" .) | quote }}
    cert-manager.io/inject-ca-from: {{ printf "%s/%s" .Release.Namespace (include "opa.rootCACertificate" .) | quote }}
{{- end }}
{{- if .Values.admissionController.annotations }}
{{ toYaml .Values.admissionController.annotations | indent 4 }}
{{- end }}
  labels:
{{ include "opa.labels.standard" . | indent 4 }}
webhooks:
  - name: webhook.openpolicyagent.org
    admissionReviewVersions: ["v1", "v1beta1"]
{{- with .Values.admissionController.namespaceSelector }}
    namespaceSelector:
{{ toYaml . | indent 6 }}
{{ end }}
    failurePolicy: {{ .Values.admissionController.failurePolicy }}
    rules:
{{ toYaml .Values.admissionController.rules | indent 6 }}
    clientConfig:
      {{- if .Values.useHttps }}
      {{- if not .Values.certManager.enabled }}
      {{- if .Values.generateCerts }}
      caBundle: {{ b64enc $ca.Cert }}
      {{- else }}
      caBundle: {{ b64enc .Values.CA }}
      {{- end }}
      {{- end }}
      {{- end }}
      service:
        name: {{ template "opa.fullname" . }}
        namespace: {{ .Release.Namespace }}
        port: {{ .Values.port }}
    sideEffects: {{ .Values.admissionController.sideEffect }}
    {{- if .Values.timeoutSeconds }}
    timeoutSeconds: {{ .Values.timeoutSeconds }}
    {{- end }}
{{- end }}

{{/* HTTP certificates  */}}

{{- if .Values.useHttps }}
{{- if .Values.certManager.enabled }}
--- {{/* Create a selfsigned Issuer, in order to create a root CA certificate for signing webhook serving certificates */}}
apiVersion: {{ include "opa.certManagerApiVersion" . }}
kind: Issuer
metadata:
{{- if .Values.admissionController.annotations }}
  annotations:
{{ toYaml .Values.admissionController.annotations | indent 4 }}
{{- end }}
  name: {{ include "opa.selfSignedIssuer" . }}
  labels:
{{ include "opa.labels.standard" . | indent 4 }}
spec:
  selfSigned: {}
--- {{/* Generate a CA Certificate used to sign certificates for the webhook */}}
apiVersion: {{ include "opa.certManagerApiVersion" . }}
kind: Certificate
metadata:
{{- if .Values.admissionController.annotations }}
  annotations:
{{ toYaml .Values.admissionController.annotations | indent 4 }}
{{- end }}
  name: {{ include "opa.rootCACertificate" . }}
  labels:
{{ include "opa.labels.standard" . | indent 4 }}
spec:
  secretName: {{ include "opa.rootCACertificate" . }}
  duration: {{ .Values.certManager.rootCACertificateDuration | quote }}
  issuerRef:
    name: {{ include "opa.selfSignedIssuer" . }}
  commonName: "ca.webhook.opa"
  isCA: true
--- {{/* Create an Issuer that uses the above generated CA certificate to issue certs */}}
apiVersion: {{ include "opa.certManagerApiVersion" . }}
kind: Issuer
metadata:
{{- if .Values.admissionController.annotations }}
  annotations:
{{ toYaml .Values.admissionController.annotations | indent 4 }}
{{- end }}
  name: {{ include "opa.rootCAIssuer" . }}
  labels:
{{ include "opa.labels.standard" . | indent 4 }}
spec:
  ca:
    secretName: {{ include "opa.rootCACertificate" . }}
--- {{/* Finally, generate a serving certificate for the webhook to use */}}
apiVersion: {{ include "opa.certManagerApiVersion" . }}
kind: Certificate
metadata:
{{- if .Values.admissionController.annotations }}
  annotations:
{{ toYaml .Values.admissionController.annotations | indent 4 }}
{{- end }}
  name: {{ include "opa.servingCertificate" . }}
  labels:
{{ include "opa.labels.standard" . | indent 4 }}
spec:
  secretName: {{ template "opa.fullname" . }}-cert
  duration: {{ .Values.certManager.servingCertificateDuration | quote }}
  issuerRef:
    name: {{ include "opa.rootCAIssuer" . }}
  dnsNames:
  - {{ include "opa.fullname" . }}
  - {{ include "opa.fullname" . }}.{{ .Release.Namespace }}
  - {{ include "opa.fullname" . }}.{{ .Release.Namespace }}.svc
{{- else }}
---  {{/* create static secret  */}}
apiVersion: v1
kind: Secret
metadata:
{{- if .Values.admissionController.annotations }}
  annotations:
{{ toYaml .Values.admissionController.annotations | indent 4 }}
{{- end }}
  name: {{ template "opa.fullname" . }}-cert
  labels:
    app: {{ template "opa.fullname" . }}
    chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
    release: "{{ .Release.Name }}"
    heritage: "{{ .Release.Service }}"
type: Opaque
data:
{{- if .Values.generateCerts }}
  tls.crt: {{ b64enc $cert.Cert }}
  tls.key: {{ b64enc $cert.Key }}
{{- else }}
  tls.crt: {{ b64enc .Values.cert }}
  tls.key: {{ b64enc .Values.key }}
{{- end }}
{{- end }}
{{- end }}


================================================
FILE: charts/opa-kube-mgmt/values.schema.json
================================================
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://github.com/open-policy-agent/kube-mgmt",
  "title": "kube-mgmt helm values",

  "definitions": {
    "image": {
      "type": "object", "title": "OPA docker image configuration", "required": ["repository", "tag"],
      "properties": {
        "repository": {"type": "string"},
        "tag": {"type": "string"},
        "pullPolicy": {"type": "string", "default": "IfNotPresent"}
      }
    }
  },

  "type": "object", "required": ["image", "mgmt"], "additionalProperties": true,
  "properties": {
    "image": {"$ref":  "#/definitions/image"},
    "mgmt": {
      "type": "object", "additionalProperties": true, "required": ["image", "enabled"],
      "properties": {
        "enabled": {"type": "boolean", "default": true},
        "image": {"$ref": "#/definitions/image"}
      }
    },
    "serviceAccount": {
      "type": "object",
      "properties": {
        "create": {"type": "boolean", "default": true},
        "annotations": {"type": "object", "additionalProperties": {"type": "string"}, "default": {}},
        "name": {"type": ["string", "null"], "default": null}
      }
    },
    "service": {
      "type": "object",
      "properties": {
        "annotations": {
          "type": "object",
          "additionalProperties": {"type": "string"},
          "default": {}
        },
        "trafficDistribution": {
          "type": ["null","string"],
          "enum": ["PreferClose", "PreferSameNode", "PreferSameZone", null],
          "default": null
        }
      }
    },
    "topologySpreadConstraints": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["maxSkew", "topologyKey", "whenUnsatisfiable"],
        "properties": {
          "maxSkew": {
            "type": "integer",
            "minimum": 1
          },
          "topologyKey": {
            "type": "string",
            "minLength": 1
          },
          "whenUnsatisfiable": {
            "type": "string",
            "enum": ["DoNotSchedule", "ScheduleAnyway"]
          },
          "labelSelector": {
            "type": "object",
            "properties": {
              "matchLabels": {
                "type": "object",
                "additionalProperties": {"type": "string"}
              },
              "matchExpressions": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": ["key", "operator"],
                  "properties": {
                    "key": {"type": "string"},
                    "operator": {"type": "string", "enum": ["In", "NotIn", "Exists", "DoesNotExist"]},
                    "values": {"type": "array", "items": {"type": "string"}}
                  }
                }
              }
            }
          },
          "matchLabelKeys": {
            "type": "array",
            "items": {"type": "string"}
          },
          "minDomains": {
            "type": "integer",
            "minimum": 1
          },
          "nodeAffinityPolicy": {
            "type": "string",
            "enum": ["Honor", "Ignore"]
          },
          "nodeTaintsPolicy": {
            "type": "string",
            "enum": ["Honor", "Ignore"]
          }
        }
      },
      "default": []
    }
  }
}


================================================
FILE: charts/opa-kube-mgmt/values.yaml
================================================
# Default values for opa.
# -----------------------
#
# OPA configuration file. See https://www.openpolicyagent.org/docs/configuration.html for more details.
opa: {}

# Setup the webhook using cert-manager
certManager:
  enabled: false
  rootCACertificateDuration: 43800h # 5y
  servingCertificateDuration: 8760h # 1y

# Expose the prometheus scraping endpoint
prometheus:
  enabled: false
  port: 8182

## ServiceMonitor consumed by prometheus-operator
serviceMonitor:
  ## If the operator is installed in your cluster, set to true to create a Service Monitor Entry
  enabled: false
  interval: "15s"
  ## Namespace in which the service monitor is created
  # namespace: monitoring
  # Added to the ServiceMonitor object so that prometheus-operator is able to discover it
  ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#prometheusspec
  additionalLabels: {}

# Service configuration
service:
  # Annotations to add to the service
  annotations: {}
  # Configure trafficDistribution if needed.
  # trafficDistribution: PreferSameZone

# Annotations in the deployment template
annotations: {}

# Bootstrap policies to load upon startup
# Define policies in the form of:
# <policyName> : |-
#   <regoBody>
# For example, to mask the entire input body in the decision logs:
# bootstrapPolicies:
#   log: |-
#     package system.log
#     mask["/input"]
bootstrapPolicies: {}

# Admission controller configuration.
admissionController:
  enabled: false

  # To enforce mutating policies, change to MutatingWebhookConfiguration.
  kind: ValidatingWebhookConfiguration

  # To set annotations on all admissionController resources (Secret/Certificate/Issuer/AdmissionController)
  # annotations:
  #   example: value

  # To _fail closed_ on failures, change to Fail. During initial testing, we
  # recommend leaving the failure policy as Ignore.
  failurePolicy: Ignore

  # Adds a namespace selector to the admission controller webhook
  namespaceSelector:
    matchExpressions:
      - {key: openpolicyagent.org/webhook, operator: NotIn, values: [ignore]}

  # SideEffectClass for the webhook, setting to NoneOnDryRun enables dry-run.
  # Only None and NoneOnDryRun are permitted for admissionregistration.k8s.io/v1.
  sideEffect: None

  # To restrict the kinds of operations and resources that are subject to OPA
  # policy checks, see the settings below. By default, all resources and
  # operations are subject to OPA policy checks.
  rules:
    - operations: ["*"]
      apiGroups: ["*"]
      apiVersions: ["*"]
      resources: ["*"]

# The helm Chart will automatically generate a CA and server certificate for
# the OPA. If you want to supply your own certificates, set the field below to
# false and add the PEM encoded CA certificate and server key pair below.
#
# WARNING: The common name name in the server certificate MUST match the
# hostname of the service that exposes the OPA to the apiserver. For example.
# if the service name is created in the "default" nanamespace with name "opa"
# the common name MUST be set to "opa.default.svc".
#
# If the common name is not set correctly, the apiserver will refuse to
# communicate with the OPA.
generateCerts: true
CA: ""
cert: ""
key: ""

# Controls a PodDisruptionBudget for the OPA pod. Suggested use if having opa
# always running for admission control is important
podDisruptionBudget:
  enabled: false
  minAvailable: 1
# maxUnavailable: 1

authz:
  # Disable if you don't want authorization.
  # Mostly useful for debugging.
  enabled: true
  # Used for setting the mgmt token used for authz instead of auto generated default
  # mgmtToken:
  #    secretName: name of the secret
  #    secretKey: (optional) key from the secret - default value is: "mgmtToken"

# Use hostNetwork setting on OPA pod
hostNetwork:
  enabled: false

# OPA docker image configuration.
image:
  repository: openpolicyagent/opa
  tag: 1.3.0
  pullPolicy: IfNotPresent

# One or more secrets to be used when pulling images
imagePullSecrets: []
# - registrySecretName

# Should OPA use TLS or not.
useHttps: true

# Port to which the opa pod will bind itself,
port: 8181

extraArgs: []

# Extra environment variables to be loaded into the OPA container
extraEnv: []

mgmt:
  enabled: true
  image:
    repository: openpolicyagent/kube-mgmt
    tag: "" # appVersion is used by default, set to desired value to override
    pullPolicy: IfNotPresent
  extraArgs: []
  extraEnv: []
  resources: {}

  # if empty - the current namespaces is watched
  # if `*` - all namespaces are watched
  namespaces: []

  # kube-mgmt container will wait until OPA container comes to running state.
  # Configure values for the startup probe, where kube-mgmt queries for the health
  # of OPA container before it starts.
  startupProbe:
    failureThreshold: 5
    httpGet:
      path: /health
      port: 8181 # Port on which OPA is configured
      scheme: HTTPS
    initialDelaySeconds: 20
    successThreshold: 1
    timeoutSeconds: 10

  data:
    enabled: true
  policies:
    enabled: true
  # NOTE IF you use these, remember to update the RBAC rules below to allow
  #      permissions to replicate these things
  replicate:
    cluster: []
#     - [group/]version/resource
    namespace: []
#     - [group/]version/resource
    path: kubernetes

    ignoreNs: []

    # Turn on auto-replication. kube-mgmt will apply OPA configuration file
    # and analyze any configured bundles to determine which Kubernetes
    # resources to replicate into OPA's in-memory store.
    auto: false

# Log level for OPA ('debug', 'info', 'error') (app default=info)
logLevel: info

# Log format for OPA ('text', 'json') (app default=text)
logFormat: json

# Number of OPA replicas to deploy. OPA maintains an eventually consistent
# cache of policies and data. If you want high availability you can deploy two
# or more replicas.
replicas: 1

# To control how the OPA is scheduled on the cluster, set the affinity,
# tolerations and nodeSelector values below. For example, to deploy OPA onto
# the master nodes, 1 replica per node:
#
# affinity:
#   podAntiAffinity:
#     requiredDuringSchedulingIgnoredDuringExecution:
#     - labelSelector:
#         matchExpressions:
#         - key: "app"
#           operator: In
#           values:
#           - opa
#       topologyKey: "kubernetes.io/hostname"
# tolerations:
# - key: "node-role.kubernetes.io/master"
#   effect: NoSchedule
#   operator: Exists
# nodeSelector:
#   kubernetes.io/role: "master"
affinity: {}
tolerations: []
nodeSelector: {}

# To control pod distribution across topology domains, set topologySpreadConstraints
# below.
#
# topologySpreadConstraints:
# - maxSkew: 1
#   topologyKey: topology.kubernetes.io/zone
#   whenUnsatisfiable: DoNotSchedule
#   labelSelector:
#     matchLabels:
#       app: opa
topologySpreadConstraints: []

# To control the CPU and memory resource limits and requests for OPA, set the
# field below.
resources: {}

rbac:
  # should ClusterRole for kube-mgmt be created
  create: true
  # extra rules to be added to a ClusterRole
  extraRules: []
    # - apiGroups: [""]
    #   resources: ["configmaps"]
    #   verbs: ["*"]

serviceAccount:
  # Specifies whether a ServiceAccount should be created
  create: true
  # Annotations for the ServiceAccount
  annotations: {}
  # The name of the ServiceAccount to use.
  # If not set and create is true, a name is generated using the fullname template
  name:

# This proxy allows opa to make Kubernetes SubjectAccessReview checks against the
# Kubernetes API. You can get a rego function at github.com/open-policy-agent/library
sar:
  enabled: false
  image:
    repository: lachlanevenson/k8s-kubectl
    tag: latest
    pullPolicy: IfNotPresent
  resources: {}

# Set a priorityClass using priorityClassName
# priorityClassName:

# Timeout for a webhook call in seconds.
# Starting in kubernetes 1.14 you can set the timeout and it is
# encouraged to use a small timeout for webhooks. If the webhook call times out, the request
# the request is handled according to the webhook'sfailure policy.
# timeoutSeconds: 20

securityContext:
  enabled: false
  runAsNonRoot: true
  runAsUser: 1

deploymentStrategy: {}
  # rollingUpdate:
  #   maxSurge: 1
  #   maxUnavailable: 0
  # type: RollingUpdate

extraContainers: []
## Additional containers to be added to the opa pod.
# - name: example-app
#   image: example/example-app:latest
#   args:
#     - "run"
#     - "--port=11811"
#     - "--config=/etc/example-app-conf/config.yaml"
#     - "--opa-endpoint=https://localhost:443"
#   ports:
#     - name: http
#       containerPort: 11811
#       protocol: TCP
#   volumeMounts:
#     - name: example-app-auth-config
#       mountPath: /etc/example-app-conf

extraVolumes: []
## Additional volumes to the opa pod.
# - name: example-app-auth-config
#   secret:
#     secretName: example-app-auth-config

extraVolumeMounts: []
## Mounting config for using the additional volumes
#  - name: example-app-auth-config
#    mountPath: /mount/path

extraPorts: []
## Additional ports to the opa services. Useful to expose extra container ports.
# - port: 11811
#   protocol: TCP
#   name: http
#   targetPort: http


================================================
FILE: cmd/kube-mgmt/flag.go
================================================
// Copyright 2017 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package main

import (
	"errors"
	"fmt"
	"strings"
)

type groupVersionKind struct {
	Group   string
	Version string
	Kind    string
}

var errBadFormat = errors.New("format: group/version/kind")

func (gvk groupVersionKind) String() string {
	if gvk.Group != "" {
		return fmt.Sprintf("%v/%v/%v", gvk.Group, gvk.Version, gvk.Kind)
	}
	return fmt.Sprintf("%v/%v", gvk.Version, gvk.Kind)
}

func (gvk *groupVersionKind) Parse(value string) error {
	parts := strings.SplitN(value, "/", 3)
	for i := range parts {
		if len(parts[i]) == 0 {
			return errBadFormat
		}
		parts[i] = strings.ToLower(parts[i])
	}
	if len(parts) < 2 {
		return errBadFormat
	}
	if len(parts) == 2 {
		gvk.Version = parts[0]
		gvk.Kind = parts[1]
	} else {
		gvk.Group = parts[0]
		gvk.Version = parts[1]
		gvk.Kind = parts[2]
	}
	return nil
}

type gvkFlag []groupVersionKind

func (f *gvkFlag) String() string {
	return fmt.Sprint(*f)
}

func (f *gvkFlag) Set(value string) error {
	var gvk groupVersionKind
	if err := gvk.Parse(value); err != nil {
		return err
	}
	*f = append(*f, gvk)
	return nil
}

func (f *gvkFlag) Type() string {
	return "[group/]version/resource"
}


================================================
FILE: cmd/kube-mgmt/flag_test.go
================================================
package main

import (
	"errors"
	"reflect"
	"testing"

	"github.com/open-policy-agent/kube-mgmt/pkg/configmap"
	"github.com/spf13/cobra"
)

func TestFlagParsing(t *testing.T) {
	var f gvkFlag

	badPaths := []string{
		"foo/bar/",
		"foo",
	}

	for _, tc := range badPaths {
		if err := f.Set(tc); err == nil {
			t.Fatalf("Expected error from %v", tc)
		}
	}

	expected := gvkFlag{
		{"example.org", "foo", "bar"},
	}

	if err := f.Set("example.org/Foo/bar"); err != nil || !reflect.DeepEqual(expected, f) {
		t.Fatalf("Expected %v but got: %v (err: %v)", expected, f, err)
	}

	expected = append(expected, groupVersionKind{"example.org", "bar", "baz"})

	if err := f.Set("example.org/Bar/baz"); err != nil || !reflect.DeepEqual(expected, f) {
		t.Fatalf("Expected %v but got: %v (err: %v)", expected, f, err)
	}

	expected = append(expected, groupVersionKind{"", "v2", "corge"})

	if err := f.Set("v2/corge"); err != nil || !reflect.DeepEqual(expected, f) {
		t.Fatalf("Expected %v but got: %v (err: %v)", expected, f, err)
	}

}

func TestFlagString(t *testing.T) {

	var f gvkFlag
	expected := "[example.org/foo/bar]"

	if err := f.Set("example.org/foo/bar"); err != nil || f.String() != expected {
		t.Fatalf("Exepcted %v but got: %v (err: %v)", expected, f.String(), err)
	}
}

func TestPolicyFlags(t *testing.T) {
	tt := []struct {
		name           string
		flag           string
		value          string
		expectFullFlag string
		err            error
	}{
		{
			name:           "valid",
			flag:           "openpolicyagent.org/policy",
			value:          "rego",
			expectFullFlag: "openpolicyagent.org/policy=rego",
			err:            nil,
		},
		{
			name:           "invalidFlag",
			flag:           "-foo",
			value:          "rego",
			expectFullFlag: "",
			err:            errors.New(`key: Invalid value: "-foo": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName',  or 'my.name',  or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')`),
		},
		{
			name:           "invalidValue",
			flag:           "foo",
			value:          "-rego",
			expectFullFlag: "",
			err:            errors.New(`values[0][foo]: Invalid value: "-rego": a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyValue',  or 'my_value',  or '12345', regex used for validation is '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?')`),
		},
	}

	for _, tc := range tt {
		t.Run(tc.name, func(t *testing.T) {
			rootCmd := &cobra.Command{
				Use:   "test",
				Short: "test",
				RunE: func(cmd *cobra.Command, args []string) error {
					return nil
				},
			}

			var params params
			rootCmd.Flags().StringVarP(&params.policyLabel, "policy-label", "", "", "replace label openpolicyagent.org/policy")
			rootCmd.Flags().StringVarP(&params.policyValue, "policy-value", "", "", "replace value rego")

			rootCmd.SetArgs([]string{"--policy-label=" + tc.flag, "--policy-value=" + tc.value})
			rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
				if rootCmd.Flag("policy-label").Value.String() != "" || rootCmd.Flag("policy-value").Value.String() != "" {
					err := configmap.CustomLabel(params.policyLabel, params.policyValue)
					if err != nil {
						if tc.err.Error() != err.Error() {
							t.Errorf("exp: %v\ngot: %v\n", tc.err.Error(), err.Error())
							t.FailNow()
						}
					}
				}
				return nil
			}
			rootCmd.Execute()
		})
	}
}


================================================
FILE: cmd/kube-mgmt/main.go
================================================
// Copyright 2017 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"net/http"
	"os"
	"path"
	"strings"

	"github.com/open-policy-agent/kube-mgmt/pkg/configmap"
	"github.com/open-policy-agent/kube-mgmt/pkg/data"
	"github.com/open-policy-agent/kube-mgmt/pkg/dynamicdata"
	"github.com/open-policy-agent/kube-mgmt/pkg/opa"
	"github.com/open-policy-agent/kube-mgmt/pkg/types"
	"github.com/open-policy-agent/kube-mgmt/pkg/version"

	//lint:ignore SA1019 using OPA v0.x to ensure backwards compatible with pre-1.0 bundles
	"github.com/open-policy-agent/opa/logging"

	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
)

type params struct {
	version            bool
	kubeconfigFile     string
	opaURL             string
	opaAuth            string
	opaAuthFile        string
	opaCAFile          string
	opaAllowInsecure   bool
	policyLabel        string
	policyValue        string
	dataLabel          string
	dataValue          string
	enablePolicies     bool
	enableData         bool
	namespaces         []string
	opaConfigFile      string
	replicateCluster   gvkFlag
	replicateNamespace gvkFlag
	replicatePath      string
	logLevel           string
	replicateIgnoreNs  []string
	analysisEntrypoint string
	healthEndpoint     string
}

func main() {

	var params params
	commandName := path.Base(os.Args[0])

	rootCmd := &cobra.Command{
		Use:   commandName,
		Short: fmt.Sprintf("%v manages OPA on top of Kubernetes", commandName),
		FParseErrWhitelist: cobra.FParseErrWhitelist{
			UnknownFlags: true,
		},
		Run: func(cmd *cobra.Command, args []string) {
			if params.version {
				fmt.Println("Version:", version.Version)
				fmt.Println("Git:", version.Git)
			} else {
				run(&params)
			}
		},
	}

	// Miscellaenous options.
	rootCmd.Flags().BoolVarP(&params.version, "version", "v", false, "print version and exit")
	rootCmd.Flags().StringVarP(&params.kubeconfigFile, "kubeconfig", "", "", "set path to kubeconfig manually")
	rootCmd.Flags().StringVarP(&params.opaURL, "opa-url", "", "http://localhost:8181/v1", "set URL of OPA API endpoint")
	rootCmd.Flags().StringVarP(&params.opaAuth, "opa-auth-token", "", "", "set authentication token for OPA API endpoint")
	rootCmd.Flags().StringVarP(&params.opaAuthFile, "opa-auth-token-file", "", "", "set file containing authentication token for OPA API endpoint")
	rootCmd.Flags().StringVarP(&params.opaCAFile, "opa-ca-file", "", "", "set file containing certificate authority for OPA certificate")
	rootCmd.Flags().BoolVarP(&params.opaAllowInsecure, "opa-allow-insecure", "", false, "allow insecure https connections to OPA")
	rootCmd.Flags().StringVar(&params.logLevel, "log-level", "info", "set log level {debug, info, warn}")

	// policy / data
	rootCmd.Flags().BoolVarP(&params.enablePolicies, "enable-policies", "", true, "whether to automatically discover policies from labelled ConfigMaps")
	rootCmd.Flags().StringVar(&params.policyLabel, "policy-label", "openpolicyagent.org/policy", "label name for filtering ConfigMaps with policies")
	rootCmd.Flags().StringVar(&params.policyValue, "policy-value", "rego", "label value for filtering ConfigMaps with policies")
	rootCmd.Flags().BoolVarP(&params.enableData, "enable-data", "", true, "whether to automatically discover data from labelled ConfigMaps")
	rootCmd.Flags().StringVar(&params.dataLabel, "data-label", "openpolicyagent.org/data", "label name for filtering ConfigMaps with data")
	rootCmd.Flags().StringVar(&params.dataValue, "data-value", "opa", "label value for filtering ConfigMaps with data")
	rootCmd.Flags().StringSliceVarP(&params.namespaces, "namespaces", "", []string{""}, "namespaces to load policies and data from")

	// replication
	rootCmd.Flags().VarP(&params.replicateNamespace, "replicate", "", "replicate namespace-level resources")
	rootCmd.Flags().VarP(&params.replicateCluster, "replicate-cluster", "", "replicate cluster-level resources")
	rootCmd.Flags().StringVarP(&params.replicatePath, "replicate-path", "", "kubernetes", "set path to replicate data into")
	rootCmd.Flags().StringSliceVarP(&params.replicateIgnoreNs, "replicate-ignore-namespaces", "", []string{""}, "namespaces that are ignored by replication")
	rootCmd.Flags().StringVarP(&params.opaConfigFile, "opa-config", "", "", "set file containing OPA configuration to enable data replication based on configured bundles")
	rootCmd.Flags().StringVarP(&params.analysisEntrypoint, "analysis-entrypoint", "", "main/main", "set decision to analyze for dynamic data replication configuration (requires --opa-config)")
	rootCmd.Flags().StringVarP(&params.healthEndpoint, "health-endpoint", "", "", "set health check listening endpoint (e.g., localhost:8000)")

	rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
		if rootCmd.Flag("policy-label").Value.String() != "" || rootCmd.Flag("policy-value").Value.String() != "" {
			err := configmap.CustomLabel(params.policyLabel, params.policyValue)
			if err != nil {
				logrus.Fatalf("Invalid --policy-label:%v || --policy-value:%v, %v", params.policyLabel, params.policyValue, err)
			}
		}
		if rootCmd.Flag("data-label").Value.String() != "" || rootCmd.Flag("data-value").Value.String() != "" {
			err := configmap.CustomLabel(params.dataLabel, params.dataValue)
			if err != nil {
				logrus.Fatalf("Invalid --data-label:%v || --data-value:%v, %v", params.dataLabel, params.dataValue, err)
			}
		}
		return nil
	}

	if err := rootCmd.Execute(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

}

func run(params *params) {

	switch params.logLevel {
	case "debug":
		logrus.SetLevel(logrus.DebugLevel)
	case "info":
		logrus.SetLevel(logrus.InfoLevel)
	case "warn":
		logrus.SetLevel(logrus.WarnLevel)
	default:
		logrus.Fatalf("Invalid log level %v", params.logLevel)
	}

	kubeconfig, err := loadRESTConfig(params.kubeconfigFile)
	if err != nil {
		logrus.Fatalf("Failed to load kubeconfig: %v", err)
	}

	if params.opaAuthFile != "" && params.opaAuth != "" {
		logrus.Fatalf("You can not use both --opa-auth-token and --opa-auth-token-file")
	}

	if params.opaAuthFile != "" {
		file, err := os.ReadFile(params.opaAuthFile)
		if err != nil {
			logrus.Fatalf("Failed to read opa auth token file %s", params.opaAuthFile)
		}
		params.opaAuth = strings.Split(string(file), "\n")[0]
	}

	if params.opaAllowInsecure && params.opaCAFile != "" {
		logrus.Fatalf("You can not use both --opa-allow-insecure and --opa-ca-file")
	}

	if params.opaAllowInsecure {
		config := &tls.Config{InsecureSkipVerify: params.opaAllowInsecure}
		http.DefaultTransport.(*http.Transport).TLSClientConfig = config
	}

	if params.opaCAFile != "" {
		rootCAs, _ := x509.SystemCertPool()
		if rootCAs == nil {
			rootCAs = x509.NewCertPool()
		}
		certs, err := os.ReadFile(params.opaCAFile)
		if err != nil {
			logrus.Fatalf("Failed to read opa certificate authority file %s", params.opaCAFile)
		}
		if ok := rootCAs.AppendCertsFromPEM(certs); !ok {
			logrus.Println("No certs appended, using system certs only")
		}
		config := &tls.Config{RootCAs: rootCAs}
		http.DefaultTransport.(*http.Transport).TLSClientConfig = config
	}

	if params.enablePolicies || params.enableData {
		sync := configmap.New(
			kubeconfig,
			opa.New(params.opaURL, params.opaAuth),
			configmap.DefaultConfigMapMatcher(
				params.namespaces,
				params.enablePolicies,
				params.enableData,
				params.policyLabel,
				params.policyValue,
				params.dataLabel,
				params.dataValue,
			),
		)
		_, err = sync.Run(params.namespaces)
		if err != nil {
			logrus.Fatalf("Failed to start configmap sync: %v", err)
		}
	}

	if len(params.replicateCluster)+len(params.replicateNamespace) > 0 {
		client, err := dynamic.NewForConfig(kubeconfig)
		if err != nil {
			logrus.Fatalf("Failed to get dynamic client: %v", err)
		}

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

		opts := data.WithIgnoreNamespaces(params.replicateIgnoreNs)

		for _, gvk := range params.replicateCluster {
			sync := data.NewFromInterface(client, opa.New(params.opaURL, params.opaAuth).Prefix(params.replicatePath), getResourceType(gvk, false), opts)
			go sync.RunContext(ctx)
		}

		for _, gvk := range params.replicateNamespace {
			sync := data.NewFromInterface(client, opa.New(params.opaURL, params.opaAuth).Prefix(params.replicatePath), getResourceType(gvk, true), opts)
			go sync.RunContext(ctx)
		}
	}

	var sync *dynamicdata.Sync

	if params.opaConfigFile != "" {
		logger := logging.New()
		switch params.logLevel {
		case "debug":
			logger.SetLevel(logging.Debug)
		case "info":
			logger.SetLevel(logging.Info)
		case "error":
			logger.SetLevel(logging.Error)
		}
		sync, err = dynamicdata.New(params.opaConfigFile, params.analysisEntrypoint, params.opaURL, params.opaAuth, params.replicateIgnoreNs, params.replicatePath, kubeconfig, logger)
		if err != nil {
			logrus.Fatalf("Failed to create dynamic synchronizer: %v", err)
		}
		go sync.Run(context.Background())
	}

	if params.healthEndpoint != "" {
		go func() {
			mux := http.NewServeMux()
			mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
				if sync == nil || sync.Ready() {
					logrus.Debugf("health check: READY")
					w.WriteHeader(http.StatusOK)
				} else {
					logrus.Debugf("health check: NOT READY")
					w.WriteHeader(http.StatusInternalServerError)
				}
			})
			server := &http.Server{
				Addr:    params.healthEndpoint,
				Handler: mux,
			}

			logrus.Infof("Starting health server on %v", params.healthEndpoint)
			if err := server.ListenAndServe(); err != nil {
				logrus.Fatalf("Error starting health server: %v", err)
			}
		}()
	}

	quit := make(chan struct{})
	<-quit
}

func loadRESTConfig(path string) (*rest.Config, error) {
	if path != "" {
		return clientcmd.BuildConfigFromFlags("", path)
	}
	return rest.InClusterConfig()
}

func getResourceType(gvk groupVersionKind, namespaced bool) types.ResourceType {
	return types.ResourceType{
		Namespaced: namespaced,
		Group:      gvk.Group,
		Version:    gvk.Version,
		Resource:   gvk.Kind,
	}
}


================================================
FILE: devbox.json
================================================
{
  "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.13.7/.schema/devbox.schema.json",
  "packages": [
    "go@1.24",
    "just@1",
    "kubectl@1.33",
    "k3d@5.8",
    "kubernetes-helm@3",
    "go-tools@2026",
    "open-policy-agent@1",
    "hurl@7",
    "fzf@0",
    "devspace@6",
    "yq-go@4",
    "ko@0",
    "oras@1"
  ],
  "env": {
    "GOPATH": "$HOME/go/",
    "PATH":   "$PATH:$HOME/go/bin"
  },
  "shell": {
    "init_hook": [
      "command -v chainsaw &>/dev/null || go install github.com/kyverno/chainsaw@v0.2.13",
      "helm plugin list | grep -q unittest || helm plugin install https://github.com/helm-unittest/helm-unittest --version v1.0.3"
    ]
  }
}


================================================
FILE: devspace.yaml
================================================
version: v2beta1
name: opa-kube-mgmt

vars:
  DEVSPACE_FLAGS: "-n default --no-warn"
  KO_PLATFORMS: "linux/amd64"
  KO_EXTRA_TAGS: ""

images:
  default:
    image: localhost:5001/openpolicyagent/kube-mgmt
    tags:
      - $(git describe --tags --always --dirty)
    custom:
      command: |
        export KO_DOCKER_REPO=${runtime.images.default.image}
        export KO_VERSION=${runtime.images.default.tag}
        export KO_COMMIT=${DEVSPACE_GIT_COMMIT}
        ko build --bare --tags ${KO_VERSION}${KO_EXTRA_TAGS} --platform=${KO_PLATFORMS} ./cmd/kube-mgmt

deployments:
  default:
    namespace: default
    helm:
      releaseName: ${DEVSPACE_NAME}
      chart:
        path: charts/${DEVSPACE_NAME}
      values:
        e2e: true
        mgmt:
          image:
            repository: ${runtime.images.default.image}
            tag: ${runtime.images.default.tag}
      valuesFiles:
        - "${E2E_TEST}/values.yaml"
      upgradeArgs:
        - "--wait"
        - "--install"

hooks:
  - name: "helm package and copy to ghcr"
    events: ["after:build:default"]
    disabled: true
    command: |
      helm package charts/${DEVSPACE_NAME} \
        --version ${runtime.images.default.tag} --app-version ${runtime.images.default.tag}
      helm push ${DEVSPACE_NAME}-${runtime.images.default.tag}.tgz oci://ghcr.io/open-policy-agent/helm
  - name: "copy docker image to ghcr"
    events: ["after:build:default"]
    disabled: true
    command: |
      oras cp docker.io/${runtime.images.default.image}:${runtime.images.default.tag} \
        ghcr.io/open-policy-agent/docker/${DEVSPACE_NAME}:${runtime.images.default.tag},latest
  - name: "e2e cleanup"
    events: ["before:deploy:default"]
    command: |
      kubectl delete cm -l kube-mgmt/e2e=true -n ${DEVSPACE_NAMESPACE} --ignore-not-found
      kubectl delete svc -l kube-mgmt/e2e=true -n ${DEVSPACE_NAMESPACE} --ignore-not-found

profiles:
  - name: release
    patches:
      - op: replace
        path: images.default.image
        value: openpolicyagent/kube-mgmt
      - op: replace
        path: vars.KO_PLATFORMS
        value: "linux/amd64,linux/arm64"
      - op: replace
        path: vars.KO_EXTRA_TAGS
        value: ",latest"
      - op: replace
        path: hooks[0].disabled
        value: false
      - op: replace
        path: hooks[1].disabled
        value: false


================================================
FILE: docs/admission-control-1.7.md
================================================
# Admission Control (1.7 and 1.8)

**Note: Admission Control has undergone changes in Kubernetes 1.7 through 1.9. If you are running Kubernetes 1.9, see [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kubernetes-admission-control.html) instead.**

To use OPA as an [Admission Controller](https://kubernetes.io/docs/admin/admission-controllers/#what-are-they) in Kubernetes 1.7 or 1.8, follow the steps in [External Admission Webhooks](https://kubernetes.io/docs/admin/extensible-admission-controllers/#external-admission-webhooks) to enable webhooks in the Kubernetes API server. Once you have configured the Kubernetes API server and generated the necessary certificates you can start `kube-mgmt` with the following options:

```bash
--register-admission-controller
--admission-controller-ca-cert-file=/path/to/ca/cert.pem
--admission-controller-service-name=<name-of-opa-service>
--admission-controller-service-namespace=<namespace-of-opa-service>
```

In addition to the command line arguments above, you must provide `--pod-name` and `--pod-namespace` using [Kubernetes' Downward API](https://kubernetes.io/docs/tasks/inject-data-application/downward-api-volume-expose-pod-information/). The example manifest below shows how to set these.

You will need to create Secrets containing the server certificate and private key as well as the CA certificate:

```bash
kubectl create secret generic opa-ca --from-file=ca.crt
kubectl create secret tls opa-server --cert=server.crt --key=server.key
```

> See [Generating TLS Certificates](./tls-1.7.md) below for examples of how to generate the certificate files.

The example below shows how to deploy OPA and enable admission control:

```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: opa
  name: opa
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: opa
      name: opa
    spec:
      containers:
        - name: opa
          image: openpolicyagent/opa
          args:
            - "run"
            - "--server"
            - "--tls-cert-file=/certs/tls.crt"
            - "--tls-private-key-file=/certs/tls.key"
            - "--addr=0.0.0.0:443"
            - "--insecure-addr=127.0.0.1:8181"
          volumeMounts:
            - readOnly: true
              mountPath: /certs
              name: opa-server
        - name: kube-mgmt
          image: openpolicyagent/kube-mgmt:0.6
          args:
            - "--pod-name=$(MY_POD_NAME)"
            - "--pod-namespace=$(MY_POD_NAMESPACE)"
            - "--register-admission-controller"
            - "--admission-controller-ca-cert-file=/certs/ca.crt"
            - "--admission-controller-service-name=opa"
            - "--admission-controller-service-namespace=$(MY_POD_NAMESPACE)"
          volumeMounts:
            - readOnly: true
              mountPath: /certs
              name: opa-ca
          env:
            - name: MY_POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: MY_POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
      volumes:
        - name: opa-server
          secret:
            secretName: opa-server
        - name: opa-ca
          secret:
            secretName: opa-ca
---
kind: Service
apiVersion: v1
metadata:
  name: opa
spec:
  clusterIP: 10.0.0.222
  selector:
    app: opa
  ports:
  - name: https
    protocol: TCP
    port: 443
    targetPort: 443
```

Admission control policies must produce a document at `/system/main` that
represents the admission control decision (i.e., allow or deny).

#### Example Policy

To test that admission control is working, define a policy that rejects the
request if the `test-reject` label is found:

```ruby
package system

main = {
  "apiVersion": "admission.k8s.io/v1alpha1",
  "kind": "AdmissionReview",
  "status": status,
}

default status = {"allowed": true}

status = reject {
  input.spec.operation = "CREATE"
  input.spec.object.labels["test-reject"]
}

reject = {
  "allowed": false,
  "status": {
    "reason": "testing rejection"
  }
}
```



================================================
FILE: docs/admission-control-crd.md
================================================
# Admission Control For Custom Resources

In the [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kubernetes-admission-control.html) tutorial we have seen how OPA can be deployed as an admission controller to enforce custom policies on Kubernetes objects. In that tutorial, policies were enforced on native Kubernetes objects such as ingresses.

## Goal

This tutorial will show how OPA can be used to enforce polices on custom resources. A custom resource is an extension of the Kubernetes API that is not necessarily available on every Kubernetes cluster. More inforation on Kubernetes custom resources is available [here](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/).

The additional steps that need to be taken to achieve this are:

1. Define a role for reading Kubernetes custom resources.
2. Grant OPA/kube-mgmt permissions to read Kubernetes custom resources.
3. Configure `kube-mgmt` to load Kubernetes custom resources into OPA.

## Prerequisites

Same as the [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kubernetes-admission-control.html) tutorial.

## Steps

### 1. Start minikube

```bash
minikube start
```

Follow the steps in the [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kubernetes-admission-control.html) tutorial to create the `opa` namespace and configure TLS.

### 2. Create a CustomResourceDefinition

Save the following CustomResourceDefinition to **resourcedefinition.yaml**:

```yaml
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: cats.opa.example.com
spec:
  group: opa.example.com
  version: "v1"
  scope: Namespaced
  names:
    plural: cats
    singular: cat
    kind: Cat
    shortNames:
    - ct
```

And create it:

```bash
kubectl create -f resourcedefinition.yaml
```

### 3. Deploy OPA on top of Kubernetes

Use the **admission-controlller.yaml** file from the [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kubernetes-admission-control.html) tutorial to deploy OPA as an admission controller with the following changes:

1. Define a role for reading the Kubernetes custom resource created in the previous step.

```yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: crd-reader
rules:
- apiGroups: ["opa.example.com"]
  resources: ["cats"]
  verbs: ["get", "list", "watch"]
```

2. Grant OPA/kube-mgmt permissions to the above role.

```yaml
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: opa-crd-reader
roleRef:
  kind: ClusterRole
  name: crd-reader
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
  name: system:serviceaccounts:opa
  apiGroup: rbac.authorization.k8s.io
```

3. Update the `kube-mgmt` container spec to load the Kubernetes custom resources into OPA.

```yaml
name: kube-mgmt
args:
    - "--replicate=opa.example.com/v1/cats"        # replicate custom resources
```

Now follow the [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kubernetes-admission-control.html) tutorial to deploy OPA on top of Kubernetes and register OPA as an admission controller.

### 4. Define a policy and load it into OPA via Kubernetes

Create a policy that rejects objects of kind `Cat` from sharing the same cat name.

**name-conflicts.rego**:

```ruby
package kubernetes.admission

import data.kubernetes.cats

# Cat names must be unique.
deny[msg] {
    input.request.kind.kind = "Cat"
    input.request.operation = "CREATE"
    name := input.request.object.spec.name
    cat := cats[other_ns][other_cat]
    cat.spec.name == name
    msg = sprintf("duplicate cat name %q (conflicts with %v/%v)", [name, other_ns, other_cat])
}
```

```bash
kubectl create configmap name-conflicts --from-file=name-conflicts.rego
```

### 5. Exercise the policy

Define two objects of kind `Cat`. The first one will be permitted and the second will be rejected.

**cat.yaml**:

```yaml
apiVersion: "opa.example.com/v1"
kind: Cat
metadata:
  name: my-new-cat-object
spec:
  name: Whiskers
```

**cat-duplicate.yaml**:

```yaml
apiVersion: "opa.example.com/v1"
kind: Cat
metadata:
  name: my-duplicate-cat-object
spec:
  name: Whiskers
```

Finally, try to create both `Cat` objects:

```bash
kubectl create -f cat.yaml
kubectl create -f cat-duplicate.yaml
```

The second object will be rejected since an object with the cat name `Whiskers` was created earlier.


================================================
FILE: docs/admission-control-secure.md
================================================
# Admission Control Secure

In the [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kubernetes-admission-control.html) tutorial we have seen
how OPA can be deployed as an admission controller. In that tutorial, OPA is not configured to `authenticate` and `authorize` client requests.

## Goal

This tutorial will show how to securely deploy OPA as an admission controller. The additional steps that need to be taken to achieve this are:

1. Start `OPA` with authentication and authorization enabled using the `--authentication` and `--authorization` options respectively.
2. Volume mount OPA's startup authorization policy into the OPA container.
3. Start `kube-mgmt` with `Bearer` token flag using the `--opa-auth-token-file` option.
4. Configure `kube-mgmt` to load polices stored in ConfigMaps that are created in the `opa` namespace and are labelled `openpolicyagent.org/policy=rego`.
5. Configure the Kubernetes API server to use `Bearer` token.


## Prerequisites

Same as the [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kubernetes-admission-control.html) tutorial.

## Steps

### 1. Configure Kubernetes API server

OPA will `authenticate` clients by extracting the `Bearer` token from the incoming API requests. Hence the Kubernetes API server needs to be configured
to send a `Bearer` token in all requests to OPA. To do this, the API server must be provided with an admission control configuration file via the `--admission-control-config-file`
flag during startup. This means the configuration file should be present inside the minikube VM at a location which is accessible to the API server pod.

Start minikube:

```bash
minikube start
```

`ssh` into the minikube VM and place the configuration files (**admission-control-config.yaml** and **kube-config.yaml**) below
inside `/var/lib/minikube/certs`. This directory is accessible inside the API server pod.

**admission-control-config.yaml**

```yaml
apiVersion: apiserver.k8s.io/v1alpha1
kind: AdmissionConfiguration
plugins:
- name: ValidatingAdmissionWebhook
  configuration:
    apiVersion: apiserver.config.k8s.io/v1alpha1
    kind: WebhookAdmission
    kubeConfigFile: /var/lib/minikube/certs/kube-config.yaml
```

**kube-config.yaml**

```yaml
apiVersion: v1
kind: Config
users:
# '*' is the default match.
- name: '*'
  user:
    token: <apiserver_secret_token>
```

With the above configuration, all requests the API server makes to OPA will include a `Bearer` token. You will need to generate the `Bearer`
token (`<apiserver_secret_token>`) and later include it in OPA's startup authorization policy so that OPA can verify the identity of the API server.

Now exit the minikube VM and stop it:

```bash
minikube stop
```

Start minikube by passing information about the admission control configuration file to the API server:

```bash
minikube start --extra-config=apiserver.admission-control-config-file=/var/lib/minikube/certs/admission-control-config.yaml
```

Make sure that the minikube ingress addon is enabled:

```bash
minikube addons enable ingress
```

Follow the steps in the [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kubernetes-admission-control.html) tutorial to create the `opa`
namespace and configure TLS. Now use the **admission-controller.yaml** file from the tutorial to deploy OPA as an admission controller with the following changes:

1. Use the below `opa` and `kube-mgmt` container spec which enables OPA's security features and configures `kube-mgmt` to include a `Bearer` token
in calls to OPA. We also volume mount OPA's startup authorization policy `authz.rego` inside the OPA container in the `/policies` directory.

```yaml
spec:
  containers:
    - name: opa
      image: openpolicyagent/opa:0.42.1
      args:
        - "run"
        - "--server"
        - "--tls-cert-file=/certs/tls.crt"
        - "--tls-private-key-file=/certs/tls.key"
        - "--addr=0.0.0.0:443"
        - "--addr=http://127.0.0.1:8181"
        - "--authentication=token"
        - "--authorization=basic"
        - "/policies/authz.rego" # authorization policy used on startup
        - "--ignore=.*"          # exclude hidden dirs created by Kubernetes
      volumeMounts:
        - readOnly: true
          mountPath: /certs
          name: opa-server
        - readOnly: true
          mountPath: /policies
          name: inject-policy
    - name: kube-mgmt
      image: openpolicyagent/kube-mgmt:7.0.6
      args:
        - "--replicate-cluster=v1/namespaces"
        - "--replicate=extensions/v1beta1/ingresses"
        - "--opa-auth-token-file=/policies/token"
      volumeMounts:
        - readOnly: true
          mountPath: /policies
          name: inject-policy
  volumes:
    - name: opa-server
      secret:
        secretName: opa-server
    - name: inject-policy
      secret:
        secretName: inject-policy
```

2. Include the Secret that contains OPA's startup authorization policy.

```bash
cat > authz.rego <<EOF
package system.authz

default allow = false

allow {
  "kube-mgmt" = input.identity
}

allow {
  <apiserver_secret_token> = input.identity
}
EOF

kubectl create secret generic inject-policy -n opa --from-file=authz.rego --from-literal=token=kube-mgmt

```

If you have liveness or readiness probes configured on the OPA server for `/health` you will need to add the following `allow` rule
to ensure Kubernetes can still access these endpoints.

```
# Allow anonymouse access to /health otherwise K8s get 403 and kills pod.
allow {
    input.path = ["health"]
}
```

3. Label the `opa-default-system-main` ConfigMap.

```yaml
---
kind: ConfigMap
apiVersion: v1

metadata:
  name: opa-default-system-main
  namespace: opa
  labels:
    openpolicyagent.org/policy: rego
data:
  main: |
    package system

    import data.kubernetes.admission

    main = {
      "apiVersion": "admission.k8s.io/v1beta1",
      "kind": "AdmissionReview",
      "response": response,
    }

    default response = {"allowed": true}

    response = {
        "allowed": false,
        "status": {
            "reason": reason,
        },
    } {
        reason = concat(", ", admission.deny)
        reason != ""
    }
```

When OPA starts, the `kube-mgmt` container will load Kubernetes Namespace and Ingress objects into OPA. `kube-mgmt` will automatically
discover policies stored in ConfigMaps in Kubernetes and load them into OPA. `kube-mgmt` assumes a ConfigMap contains policies if the ConfigMap is:

- Created in a namespace listed in the `--namespaces` option. Default namespace is `opa`.
- Labelled with `openpolicyagent.org/policy=rego`.

`kube-mgmt` is started with the `--opa-auth-token-file` flag and hence all requests made to OPA will include a `Bearer` token(`kube-mgmt` in this case).

You can now follow the [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kubernetes-admission-control.html)
tutorial to deploy OPA on top of Kubernetes and test admission control. **Make sure to label the ConfigMap when you store a policy inside it.**


================================================
FILE: docs/tls-1.7.md
================================================
# Generating TLS Certificates (1.7)

External Admission Controllers must be secured with TLS. At a minimum you must:

- Provide the Kubernetes API server with a client key to use for
  webhook calls (`client.key` and `client.crt` below).

- Provide OPA with a server key so that the Kubernetes API server can
  authenticate it (`server.key` and `server.crt` below).

- Provide `kube-mgmt` with the CA certificate to register with the Kubernetes
  API server (`ca.crt` below).

Follow the steps below to generate the necessary files for test purposes.

First, generate create the required OpenSSL configuration files:

**client.conf**:

```
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, serverAuth
subjectAltName = @alt_names
[alt_names]
IP.1 = 127.0.0.1
```

**server.conf**:

```
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, serverAuth
subjectAltName = @alt_names
[alt_names]
IP.1 = 10.0.0.222
```

> The subjectAltName/IP address in the certificate MUST match the one configured
> on the Kubernetes Service.

Finally, generate the CA and client/server key pairs.

```bash
# Create a certificate authority
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -days 100000 -out ca.crt -subj "/CN=admission_ca"

# Create a server certiticate
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/CN=admission_server" -config server.conf
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 100000 -extensions v3_req -extfile server.conf

# Create a client certiticate
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/CN=admission_client" -config client.conf
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 100000 -extensions v3_req -extfile client.conf
```

If you are using minikube, you can specify the client TLS credentials with the following `minikube start` options:

```
--extra-config=apiserver.ProxyClientCertFile=/path/to/client.crt  # in VM
--extra-config=apiserver.ProxyClientKeyFile=/path/to/client.key   # in VM
```

================================================
FILE: examples/service_validation/README.md
================================================
# Kubernetes Admission Control for preventing open AWS LoadBalancers

Kubernetes Service objects of type [LoadBalancer](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/) on AWS create an Elastic LoadBalancer or Network LoadBalancer. However, if not properly configured, these LoadBalancers can be open to the world exposing EC2 instances behind them to security breaches.

OPA can provide a good ValidationWebhook for ensuring that Service objects of type LoadBalancer do not accidentally create a LoadBalancer open to the world.

## Goals

This tutorial shows how to create validation webhooks for Service objects and enforcing the LoadBalancer policies.

- Kubernetes Service objects of type LoadBalancer that do not have `spec.loadBalancerSourceRanges` are rejected.
- Users are required to explicitly set `spec.loadBalancerSourceRanges`. If users want to create LoadBalancers that are actually open to the world, they should explicitly set `spec.loadBalancerSourceRanges` to `0.0.0.0/0`.

## Prerequisites

This tutorial has been tested with Kubernetes 1.10 running on AWS with RBAC enabled. But it should work with Kubernetes 1.9 or higher.

## Steps

### The simplest way to setup opa and policies would be to run the install.sh script.

```bash
$ ./install.sh
```

Otherwise, here are the detailed steps:

### 1. Start Kubernetes with ValidatingAdmissionWebhook admission controller enabled.

### 2. Create the namespace called `opa` in it.

```bash
kubectl create namespace opa
```

### 3. Create the SSL certs required for the webhook. Same as [this](https://github.com/open-policy-agent/opa/blob/master/docs/book/kubernetes-admission-control.md#3-deploy-opa-on-top-of-kubernetes)

```bash
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -days 100000 -out ca.crt -subj "/CN=admission_ca"
```

Generate the TLS key and certificate for OPA:

```bash
cat >server.conf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, serverAuth
EOF
```

```bash
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/CN=opa.opa.svc" -config server.conf
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 100000 -extensions v3_req -extfile server.conf
```

> Note: the Common Name value you give to openssl MUST match the name of the OPA service created below.

Create a Secret to store the TLS credentials for OPA:

```bash
kubectl create secret tls opa-server --cert=server.crt --key=server.key
```

In the admission_controller.yaml file in this example, replace the REPLACE_WITH_SECRET with the base64 encoded 

```bash
kubectl apply -f ./examples/service_validation/admission-controller.yaml
```

This creates the OPA deployment, the validation webhook as well as the config map which has the policy.

### 4. Exercise the policy

Create a service object and ensure that it is enforcing the policy.

**service_invalid.yaml**:

```yaml
apiVersion: v1
kind: Service
metadata:
  name: no-whitelist-ips
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    run: nginx
  type: LoadBalancer
```

**service_valid.yaml**:

```yaml
apiVersion: v1
kind: Service
metadata:
  name: whitelist-ips
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    run: nginx
  type: LoadBalancer
  loadBalancerSourceRanges:
  - 10.0.0.0/8
```

```bash
kubectl create -f service_invalid.yaml
kubectl create -f service_valid.yaml
```

This tutorial showed how you can leverage OPA to enforce admission control of Service objects to prevent accidentally exposing AWS resources to the world.



================================================
FILE: examples/service_validation/admission_controller.yaml
================================================
apiVersion: v1
kind: ServiceAccount
metadata:
  name: opa-sa
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: opa-rolebinding
subjects:
- kind: ServiceAccount
  name: opa-sa
  namespace: opa
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io
---
kind: Service
apiVersion: v1
metadata:
  name: opa
spec:
  selector:
    app: opa
  ports:
  - name: https
    protocol: TCP
    port: 443
    targetPort: 443
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: opa
  name: opa
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: opa
      name: opa
    spec:
      serviceAccountName: opa-sa
      containers:
        - name: opa
          image: openpolicyagent/opa:0.8.0
          args:
            - "run"
            - "--server"
            - "--tls-cert-file=/certs/tls.crt"
            - "--tls-private-key-file=/certs/tls.key"
            - "--addr=0.0.0.0:443"
            - "--insecure-addr=127.0.0.1:8181"
          volumeMounts:
            - readOnly: true
              mountPath: /certs
              name: opa-server
        - name: kube-mgmt
          image: openpolicyagent/kube-mgmt:0.12.1
      volumes:
        - name: opa-server
          secret:
            secretName: opa-server
---
kind: ValidatingWebhookConfiguration
apiVersion: admissionregistration.k8s.io/v1
metadata:
  name: opa-validating-webhook
webhooks:
  - name: validating-webhook.openpolicyagent.org
    admissionReviewVersions: ["v1beta1"]
    rules:
      - operations: ["CREATE"]
        apiGroups: ["*"]
        apiVersions: ["v1"]
        resources: ["services"]
    clientConfig:
      caBundle: REPLACE_WITH_SECRET
      service:
        namespace: opa
        name: opa
    sideEffects: None
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: service-check
data:
  main: |
    package kubernetes.admission

    import data.kubernetes.namespaces

    deny[msg] {
        input.request.kind.kind = "Service"
        input.request.operation = "CREATE"
        servicetype = input.request.object.spec.type
        contains(servicetype, "LoadBalancer")
        not input.request.object.spec.loadBalancerSourceRanges
        msg = sprintf("Rejecting service of type %q without specifying spec.loadBalancerSourceRanges", [servicetype])
    }
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: opa-default-system-main
data:
  main: |
    package system

    import data.kubernetes.admission

    main = {
      "apiVersion": "admission.k8s.io/v1beta1",
      "kind": "AdmissionReview",
      "response": response,
    }

    default response = {"allowed": true}

    response = {
        "allowed": false,
        "status": {
            "reason": reason,
        },
    } {
        reason = concat(", ", admission.deny)
        reason != ""
    }


================================================
FILE: examples/service_validation/install.sh
================================================
#!/bin/bash

set -ex

OUT_DIR=/tmp/opa
rm -rf ${OUT_DIR}; mkdir -p ${OUT_DIR}

openssl genrsa -out ${OUT_DIR}/ca.key 2048
openssl req -x509 -new -nodes -key ${OUT_DIR}/ca.key -days 100000 -out ${OUT_DIR}/ca.crt -subj "/CN=admission_ca"
cat >${OUT_DIR}/server.conf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, serverAuth
EOF
openssl genrsa -out ${OUT_DIR}/server.key 2048
openssl req -new -key ${OUT_DIR}/server.key -out ${OUT_DIR}/server.csr -subj "/CN=opa.opa.svc" -config ${OUT_DIR}/server.conf
openssl x509 -req -in ${OUT_DIR}/server.csr -CA ${OUT_DIR}/ca.crt -CAkey ${OUT_DIR}/ca.key -CAcreateserial -out ${OUT_DIR}/server.crt -days 100000 -extensions v3_req -extfile ${OUT_DIR}/server.conf

install_namespace=opa
caBundle=$(base64 ${OUT_DIR}/ca.crt)
cp admission_controller.yaml ${OUT_DIR}/admission_controller.yaml
gsed -i "s/REPLACE_WITH_SECRET/${caBundle}/" ${OUT_DIR}/admission_controller.yaml

kubectl create namespace ${install_namespace}
kubectl create secret tls opa-server --cert=${OUT_DIR}/server.crt --key=${OUT_DIR}/server.key --namespace ${install_namespace}
kubectl apply -f ${OUT_DIR}/admission_controller.yaml --namespace ${install_namespace}



================================================
FILE: go.mod
================================================
module github.com/open-policy-agent/kube-mgmt

go 1.24.11

require (
	github.com/open-policy-agent/opa v1.5.1
	github.com/sirupsen/logrus v1.9.3
	github.com/spf13/cobra v1.9.1
	k8s.io/api v0.32.3
	k8s.io/apimachinery v0.32.3
	k8s.io/client-go v0.32.3
)

require (
	github.com/agnivade/levenshtein v1.2.1 // indirect
	github.com/beorn7/perks v1.0.1 // indirect
	github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 // indirect
	github.com/cespare/xxhash/v2 v2.3.0 // indirect
	github.com/containerd/containerd/v2 v2.1.1 // indirect
	github.com/containerd/errdefs v1.0.0 // indirect
	github.com/containerd/log v0.1.0 // indirect
	github.com/containerd/platforms v1.0.0-rc.1 // indirect
	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
	github.com/felixge/httpsnoop v1.0.4 // indirect
	github.com/fsnotify/fsnotify v1.9.0 // indirect
	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
	github.com/go-ini/ini v1.67.0 // indirect
	github.com/go-logr/stdr v1.2.2 // indirect
	github.com/go-openapi/jsonpointer v0.21.0 // indirect
	github.com/go-openapi/jsonreference v0.20.2 // indirect
	github.com/go-openapi/swag v0.23.0 // indirect
	github.com/gobwas/glob v0.2.3 // indirect
	github.com/google/gnostic-models v0.6.8 // indirect
	github.com/google/uuid v1.6.0 // indirect
	github.com/gorilla/mux v1.8.1 // indirect
	github.com/josharian/intern v1.0.0 // indirect
	github.com/klauspost/compress v1.18.0 // indirect
	github.com/mailru/easyjson v0.7.7 // indirect
	github.com/moby/locker v1.0.1 // indirect
	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
	github.com/opencontainers/go-digest v1.0.0 // indirect
	github.com/opencontainers/image-spec v1.1.1 // indirect
	github.com/prometheus/client_golang v1.22.0 // indirect
	github.com/prometheus/client_model v0.6.2 // indirect
	github.com/prometheus/common v0.62.0 // indirect
	github.com/prometheus/procfs v0.15.1 // indirect
	github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
	github.com/tchap/go-patricia/v2 v2.3.2 // indirect
	github.com/vektah/gqlparser/v2 v2.5.26 // indirect
	github.com/x448/float16 v0.8.4 // indirect
	github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
	github.com/yashtewari/glob-intersection v0.2.0 // indirect
	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
	go.opentelemetry.io/otel v1.35.0 // indirect
	go.opentelemetry.io/otel/metric v1.35.0 // indirect
	go.opentelemetry.io/otel/sdk v1.35.0 // indirect
	go.opentelemetry.io/otel/trace v1.35.0 // indirect
	golang.org/x/sync v0.14.0 // indirect
	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
	oras.land/oras-go/v2 v2.5.0 // indirect
)

require (
	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
	github.com/go-logr/logr v1.4.2 // indirect
	github.com/gogo/protobuf v1.3.2 // indirect
	github.com/golang/protobuf v1.5.4 // indirect; indire4ct
	github.com/google/go-cmp v0.7.0 // indirect
	github.com/google/gofuzz v1.2.0 // indirect
	github.com/inconshreveable/mousetrap v1.1.0 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pkg/errors v0.9.1 // indirect
	github.com/spf13/pflag v1.0.6 // indirect
	golang.org/x/net v0.39.0 // indirect
	golang.org/x/oauth2 v0.27.0 // indirect
	golang.org/x/sys v0.33.0 // indirect
	golang.org/x/term v0.31.0 // indirect
	golang.org/x/text v0.24.0 // indirect
	golang.org/x/time v0.11.0 // indirect
	google.golang.org/protobuf v1.36.6 // indirect
	gopkg.in/inf.v0 v0.9.1 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
	k8s.io/klog/v2 v2.130.1 // indirect
	k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
	k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
	sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
	sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
	sigs.k8s.io/yaml v1.4.0 // indirect
)


================================================
FILE: go.sum
================================================
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
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/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA=
github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
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/containerd/containerd/v2 v2.1.1 h1:znnkm7Ajz8lg8BcIPMhc/9yjBRN3B+OkNKqKisKfwwM=
github.com/containerd/containerd/v2 v2.1.1/go.mod h1:zIfkQj4RIodclYQkX7GSSswSwgP8d/XxDOtOAoSDIGU=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E=
github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/dgraph-io/badger/v4 v4.7.0 h1:Q+J8HApYAY7UMpL8d9owqiB+odzEc0zn/aqOD9jhc6Y=
github.com/dgraph-io/badger/v4 v4.7.0/go.mod h1:He7TzG3YBy3j4f5baj5B7Zl2XyfNe5bl4Udl0aPemVA=
github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM=
github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI=
github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/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-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
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/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
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/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
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.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
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/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/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.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/open-policy-agent/opa v1.5.1 h1:LTxxBJusMVjfs67W4FoRcnMfXADIGFMzpqnfk6D08Cg=
github.com/open-policy-agent/opa v1.5.1/go.mod h1:bYbS7u+uhTI+cxHQIpzvr5hxX0hV7urWtY+38ZtjMgk=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
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.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tchap/go-patricia/v2 v2.3.2 h1:xTHFutuitO2zqKAQ5rCROYgUb7Or/+IC3fts9/Yc7nM=
github.com/tchap/go-patricia/v2 v2.3.2/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
github.com/vektah/gqlparser/v2 v2.5.26 h1:REqqFkO8+SOEgZHR/eHScjjVjGS8Nk3RMO/juiTobN4=
github.com/vektah/gqlparser/v2 v2.5.26/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg=
github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
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.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c=
oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=


================================================
FILE: internal/expect/client.go
================================================
package expect

import (
	"encoding/json"
	"errors"

	opa_client "github.com/open-policy-agent/kube-mgmt/pkg/opa"
)

// Client emulates OPA Client API
type Client struct {
	PrefixList []string
	// This function will be called on every request
	actor func(req Request, value interface{}) error
}

// Prefix implements Data
func (f *Client) Prefix(path string) opa_client.Data {
	f.PrefixList = append(f.PrefixList, path)
	return f
}

// PatchData implements Data.
func (f *Client) PatchData(path string, op string, value *interface{}) (err error) {
	req := Request{
		req:  patchRequest,
		path: path,
		op:   op,
	}
	var actualValue interface{}
	if value != nil {
		actualValue = *value
	}
	return f.actor(req, actualValue)
}

// PutData implements Data
func (f *Client) PutData(path string, value interface{}) (err error) {
	req := Request{
		req:  putRequest,
		path: path,
	}
	return f.actor(req, value)
}

var errNotSupported = errors.New("PostData not supported")

// PostData implements Data. Currently not supported.
func (*Client) PostData(string, interface{}) (json.RawMessage, error) {
	return nil, errNotSupported
}

// InsertPolicy implements Policies
func (f *Client) InsertPolicy(path string, value []byte) (err error) {
	req := Request{
		req:  insertPolicyRequest,
		path: path,
	}
	return f.actor(req, value)
}

// DeletePolicy implements Policies
func (f *Client) DeletePolicy(path string) (err error) {
	req := Request{
		req:  deletePolicyRequest,
		path: path,
	}
	return f.actor(req, nil)
}


================================================
FILE: internal/expect/request.go
================================================
package expect

import (
	"fmt"
	"reflect"
	"time"
)

type request string

const (
	patchRequest        request = "PatchData"
	putRequest          request = "PutData"
	insertPolicyRequest request = "InsertPolicy"
	deletePolicyRequest request = "DeletePolicy"
	noRequest           request = "Nothing"
)

// Request represents an operation against the mock client.
type Request struct {
	req      request
	path     string
	op       string
	value    []byte
	interval time.Duration // Only applies to script.Expect(Nothing())
}

// Equals compares two requests
func (expected Request) Equals(actual Request) bool {
	return expected.req == actual.req &&
		expected.path == actual.path &&
		(expected.req != patchRequest || expected.op == actual.op) &&
		(expected.value == nil || reflect.DeepEqual(expected.value, actual.value))
}

// String implements fmt.Stringer
func (r Request) String() string {
	if r.value != nil {
		return fmt.Sprintf("{req: %q, path: %q, op: %q, value: %q}", r.req, r.path, r.op, string(r.value))
	}
	return fmt.Sprintf("{req: %q, path: %q, op: %q}", r.req, r.path, r.op)
}

func optional(expected ...[]byte) []byte {
	if len(expected) > 0 {
		return expected[0]
	}
	return nil
}

// PutData describes a PutData request with an optional expected value
// (expected value can be omitted)
func PutData(path string, expected ...[]byte) Request {
	return Request{
		req:   putRequest,
		path:  path,
		value: optional(expected...),
	}
}

// PatchData describes a PatchData request with an optional expected value
// (expected value can be omitted)
func PatchData(path string, op string, expected ...[]byte) Request {
	return Request{
		req:   patchRequest,
		path:  path,
		op:    op,
		value: optional(expected...),
	}
}

// InsertPolicy describes a InsertPolicy request with an optional expected value
// (expected value can be omitted)
func InsertPolicy(path string, expected ...[]byte) Request {
	return Request{
		req:   insertPolicyRequest,
		path:  path,
		value: optional(expected...),
	}
}

// DeletePolicy describes a DeletePolicy request with an optional expected value
// (expected value can be omitted)
func DeletePolicy(path string) Request {
	return Request{
		req:  deletePolicyRequest,
		path: path,
	}
}

// Nothing describes an empty action. The client must not get any request for the given time
func Nothing(duration time.Duration) Request {
	return Request{
		req:      noRequest,
		interval: duration,
	}
}

// Action performed when an expected Request arrives.
// The request will return the result of invoking the Action.
type Action func() error

// Step combines a Request and an Action
type Step struct {
	Request
	Action
}

// Do turns a Request into a Step
func (req Request) Do(action Action) Step {
	return Step{
		Request: req,
		Action:  action,
	}
}

// DoError is a shortcut for Do(func() error { return err })
func (req Request) DoError(err error) Step {
	return Step{
		Request: req,
		Action: func() error {
			return err
		},
	}
}

// End is a shortcut for Do(nil)
func (req Request) End() Step {
	return Step{
		Request: req,
	}
}


================================================
FILE: internal/expect/script.go
================================================
package expect

import (
	"context"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"testing"
	"time"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/tools/cache"
)

// Script is a sequence of expected Requests for a Client,
// and the Actions to perform on each Request.
type Script []Step

// String implements fmt.Stringer
func (s Script) String() string {
	return s.strings("\n")
}

// Play creates a client with the script provided, and runs the show.
// When the script ends, the show is cancelled and the final state
// of the client returned.
func Play(t *testing.T, script Script, show func(ctx context.Context, client *Client)) *Client {
	steps := len(script)
	if steps <= 0 || show == nil {
		return nil
	}

	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second))
	// Arrange to cancel the context on the last step
	last := script[steps-1]
	script[steps-1].Action = func() error {
		defer cancel()
		if last.Action == nil {
			return nil
		}
		return last.Action()
	}

	var (
		actor     func(req Request, value interface{}) error
		improvise func(cursor int)
		cursor    int = 0
	)

	actor = func(req Request, value interface{}) error {
		if cursor >= len(script) {
			t.Fatalf("Expected at most %d steps, got one more request %v", len(script), req)
		}
		// Save the actual value received to req. We do it
		// here because we have the *testing.T instance, and
		// can call t.Fatal if conversions fail.
		if req.req == insertPolicyRequest {
			req.value = value.([]byte)
		} else {
			req.value = MustRoundTrip(t, value)
		}
		// Check that the request matches the cue
		cue := script[cursor]
		if !cue.Equals(req) {
			seq := script[:cursor+1].strings("\n\t")
			t.Fatalf("Expected sequence:\n\t%v\nError at step %d, got:\n\t%v", seq, cursor, req)
		}
		cursor++
		if cursor < len(script) && script[cursor].req == noRequest {
			// If the next update is timed, schedule it.
			go improvise(cursor)
		}
		if cue.Action == nil {
			return nil
		}
		return cue.Action()
	}

	// improvise triggers the step without any external input
	improvise = func(cursor int) {
		<-time.After(script[cursor].interval)
		actor(script[cursor].Request, nil)
	}

	client := &Client{actor: actor}
	if script[0].req == noRequest {
		// boot the script if the first step is a wait.
		go improvise(0)
	}

	show(ctx, client)
	if deadline, ok := ctx.Deadline(); ok && deadline.Before(time.Now()) {
		t.Fatalf("Test %s failed because of timeout", t.Name())
	}
	return client
}

// MustMarshal marshals the objet to JSON, calls t.Fatal on error
func MustMarshal(t *testing.T, obj interface{}) []byte {
	t.Helper()
	data, err := json.Marshal(obj)
	if err != nil {
		t.Fatalf("error marshalling JSON: %s", err)
	}
	return data
}

// MustUmnarshal unmarshals the objet from JSON, calls t.Fatal on error
func MustUnmarshal(t *testing.T, data []byte) interface{} {
	t.Helper()

	var result interface{}
	if len(data) > 0 {
		err := json.Unmarshal(data, &result)
		if err != nil {
			t.Fatalf("error unmarshalling JSON: %s", err)
		}
	}
	return result
}

// mustRoundtrip marshals the object to JSON consistently.
//
// Kubernetes objects have custom marshallers that output the
// json in a custom order. So comparing the marshalled representation of
// a kubernetes object with that of a map or an *unstructured.Unstructured
// built from that same object will fail.
//
// MustRoundTrip will make sure the generated string is comparable.
func MustRoundTrip(t *testing.T, obj interface{}) []byte {
	return MustMarshal(t, MustUnmarshal(t, MustMarshal(t, obj)))
}

// MustEqual compares the values and calls t.Fatal on error
func MustEqual(t *testing.T, result, expected interface{}) {
	t.Helper()

	if !reflect.DeepEqual(result, expected) {
		t.Fatalf("Expected:\n\n%q\n\nActual:\n\n%q\n", expected, result)
	}
}

// MustKey gets the mentaNamespaceKey of an object
func MustKey(t *testing.T, obj runtime.Object) string {
	t.Helper()

	path, err := cache.MetaNamespaceKeyFunc(obj)
	if err != nil {
		t.Fatalf("Failed to get path from object %v: %v", obj, err)
	}
	return path
}

// strings formats the Script as a list of strings for printing
func (s Script) strings(sep string) string {
	steps := make([]string, 0, len(s))
	for cursor, step := range s {
		steps = append(steps, fmt.Sprintf("%d: %s", cursor, step.String()))
	}
	return strings.Join(steps, sep)
}


================================================
FILE: justfile
================================================
K3D := "kube-mgmt"
TEST_RESULTS := 'build/test-results'

@_default:
  @just --list

# golang linter
[group('code quality')]
lint-go:
  go vet ./...
  staticcheck ./...

# helm linter
[group('code quality')]
lint-helm filter="*":
  #!/usr/bin/env -S bash -euo pipefail

  mkdir -p {{TEST_RESULTS}}/helm-unittest

  helm unittest -f '../../test/lint/{{filter}}.yaml' \
    --output-file {{TEST_RESULTS}}/helm-unittest/lint.xml --output-type JUnit charts/opa-kube-mgmt

# run all linters
[group('code quality')]
lint: lint-go lint-helm

# run helm unit tests
[group('code quality')]
test-helm filter="*":
  #!/usr/bin/env -S bash -euo pipefail

  mkdir -p {{TEST_RESULTS}}/helm-unittest

  helm unittest -f '../../test/unit/{{filter}}.yaml' \
    --output-file {{TEST_RESULTS}}/helm-unittest/unit.xml --output-type JUnit charts/opa-kube-mgmt

# run golang unit tests
[group('code quality')]
test-go:
  go test ./...

# run linters and unit tests
[group('code quality')]
test: lint test-go test-helm

@_token:
  kubectl exec deploy/opa-kube-mgmt -n default -c mgmt -- cat /bootstrap/mgmt-token

# run e2e test using chainsaw and hurl
[group('code quality')]
test-e2e E2E_TEST="": _ctx
  #!/usr/bin/env -S bash -euo pipefail

  SCENARIO="{{E2E_TEST}}"
  if [ -z "$SCENARIO" ]; then
    SCENARIO=$(find test/e2e/ -mindepth 1 -maxdepth 1 -type d | sort | fzf --header "Select e2e scenario")
  fi

  devspace purge
  devspace deploy --var E2E_TEST="$SCENARIO"

  mkdir -p {{TEST_RESULTS}}/chainsaw

  OPA_TOKEN=$(just _token 2>/dev/null || true) chainsaw test "$SCENARIO" --quiet --namespace default \
    --report-format JUNIT-TEST \
    --report-name "$(basename "$SCENARIO")" --report-path {{TEST_RESULTS}}/chainsaw

# run all e2e tests
[group('code quality')]
test-e2e-all:
  #!/usr/bin/env -S bash -euo pipefail

  for E in $(find test/e2e/ -name 'chainsaw-test.yaml'|xargs -n1 dirname|sort); do
    just test-e2e "${E}"
  done

# start kube-mgmt in local k8s cluster
[group('deployment')]
@up: _ctx
  devspace deploy --var E2E_TEST=test/e2e/default

# stop kube-mgmt in local k8s cluster
[group('deployment')]
@down: _ctx
  devspace purge --force-purge && rm -rf .devspace/

@_ctx:
  kubectl config use-context k3d-{{K3D}}

_bundle:
  #!/usr/bin/env -S bash -euo pipefail

  opa build -b ./test/e2e/replicate_auto/bundle -o ./test/e2e/replicate_auto/bundle.tar.gz
  kubectl delete configmap -n default bundle --ignore-not-found
  kubectl create configmap -n default bundle --from-file ./test/e2e/replicate_auto/bundle.tar.gz

# delete local k8s cluster
[group('deployment')]
@k3d-down:
  k3d cluster delete {{K3D}} || true

# (re) create local k8s cluster using k3d
[group('deployment')]
all: k3d-down && _ctx _bundle
  #!/usr/bin/env -S bash -euo pipefail

  echo '
  apiVersion: k3d.io/v1alpha5
  kind: Simple
  metadata:
    name: {{K3D}}
  servers: 1
  agents: 0
  image: rancher/k3s:v1.33.9-k3s1
  registries:
    create:
      name: k3d-{{K3D}}-registry
      host: "0.0.0.0"
      hostPort: "5001"
    config: |
      mirrors:
        "localhost:5001":
          endpoint:
            - http://k3d-{{K3D}}-registry:5000
  ports:
    - port: 8080:80
      nodeFilters: ["loadbalancer"]
    - port: 8443:443
      nodeFilters: ["loadbalancer"]
  options:
    k3s:
      extraArgs:
        - arg: "--disable=local-storage,metrics-server"
          nodeFilters: ["server:*"]
  ' | k3d cluster create --config /dev/stdin

  kubectl config set-context k3d-{{K3D}} --namespace default

  docker login -u {{K3D}} -p {{K3D}} localhost:5001

  kubectl wait --for=create crd/ingressroutetcps.traefik.io --timeout=2m
  sleep 3
  kubectl wait --for=condition=Established crd/ingressroutetcps.traefik.io --timeout=30s



================================================
FILE: pkg/configmap/configmap.go
================================================
// Copyright 2017 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package configmap

import (
	"context"
	"encoding/json"
	"fmt"
	"hash/fnv"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/open-policy-agent/kube-mgmt/pkg/opa"
	"github.com/sirupsen/logrus"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/selection"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
)

const (
	defaultRetries       = 2
	statusAnnotationKey  = "openpolicyagent.org/kube-mgmt-status"
	retriesAnnotationKey = "openpolicyagent.org/kube-mgmt-retries"
	// Special namespace in Kubernetes federation that holds scheduling policies.
	// commented because staticcheck: 'const kubeFederationSchedulingPolicy is unused (U1000)'
	// kubeFederationSchedulingPolicy = "kube-federation-scheduling-policy"
	resyncPeriod        = time.Second * 60
	syncResetBackoffMin = time.Second
	syncResetBackoffMax = time.Second * 30
)

// Label validator
func CustomLabel(key, value string) error {
	_, err := labels.NewRequirement(key, selection.Equals, []string{value})
	if err != nil {
		return err
	}
	return nil
}

// DefaultConfigMapMatcher returns a function that will match configmaps in
// specified namespaces and/or with a policy or data label. The first bool return
// value specifies a policy/data match and the second bool indicates if the configmap
// contains a policy.
func DefaultConfigMapMatcher(namespaces []string, enablePolicies, enableData bool, policyLabelKey, policyLabelValue, dataLabelKey, dataLabelValue string) func(*v1.ConfigMap) (bool, bool) {
	return func(cm *v1.ConfigMap) (bool, bool) {
		var match, isPolicy bool

		if enableData {
			match = matchesNamespace(cm, namespaces) && matchesLabel(cm, dataLabelKey, dataLabelValue)
		}

		if !match && enablePolicies {
			match = matchesNamespace(cm, namespaces) && matchesLabel(cm, policyLabelKey, policyLabelValue)

			if match {
				isPolicy = true
			}
		}
		return match, isPolicy
	}
}

func matchesLabel(cm *v1.ConfigMap, labelKey, labelValue string) bool {
	return cm.Labels[labelKey] == labelValue
}

func matchesNamespace(cm *v1.ConfigMap, namespaces []string) bool {
	for _, ns := range namespaces {
		if ns == cm.Namespace || ns == "*" {
			return true
		}
	}
	return false
}

// Sync replicates policies or data stored in the API server as ConfigMaps into OPA.
type Sync struct {
	kubeconfig *rest.Config
	opa        opa.Client
	clientset  *kubernetes.Clientset
	matcher    func(*v1.ConfigMap) (bool, bool)
}

// New returns a new Sync that can be started.
func New(kubeconfig *rest.Config, opa opa.Client, matcher func(*v1.ConfigMap) (bool, bool)) *Sync {
	cpy := *kubeconfig
	cpy.GroupVersion = &schema.GroupVersion{
		Version: "v1",
	}
	cpy.APIPath = "/api"
	cpy.ContentType = runtime.ContentTypeJSON
	scheme := runtime.NewScheme()
	cpy.NegotiatedSerializer = serializer.WithoutConversionCodecFactory{CodecFactory: serializer.NewCodecFactory(scheme)}
	builder := runtime.NewSchemeBuilder(func(scheme *runtime.Scheme) error {
		scheme.AddKnownTypes(
			*cpy.GroupVersion,
			&metav1.ListOptions{},
			&metav1.Status{},
			&v1.ConfigMapList{},
			&v1.ConfigMap{})
		return nil
	})
	builder.AddToScheme(scheme)
	return &Sync{
		kubeconfig: &cpy,
		opa:        opa,
		matcher:    matcher,
	}
}

// Run starts the synchronizer. To stop the synchronizer send a message to the
// channel.
func (s *Sync) Run(namespaces []string) (chan struct{}, error) {
	client, err := rest.RESTClientFor(s.kubeconfig)
	if err != nil {
		return nil, err
	}
	s.clientset, err = kubernetes.NewForConfig(s.kubeconfig)
	if err != nil {
		return nil, err
	}
	quit := make(chan struct{})

	logrus.Infof("Policy/data ConfigMap processor connected to K8s: namespaces=%v", namespaces)
	for _, namespace := range namespaces {
		if namespace == "*" {
			namespace = v1.NamespaceAll
		}
		listerWatcher := cache.NewListWatchFromClient(
			client,
			"configmaps",
			namespace,
			fields.Everything())
		_, controller := cache.NewInformerWithOptions(cache.InformerOptions{
			ListerWatcher: listerWatcher,
			ObjectType:    &v1.ConfigMap{},
			Handler: cache.ResourceEventHandlerFuncs{
				AddFunc:    s.add,
				UpdateFunc: s.update,
				DeleteFunc: s.delete,
			},
			ResyncPeriod: 0, // Set to 0 as in the original code
		})
		go controller.Run(quit)
	}
	return quit, nil
}

func (s *Sync) add(obj interface{}) {
	cm := obj.(*v1.ConfigMap)
	if match, isPolicy := s.matcher(cm); match {
		logrus.Debugf("OnAdd cm=%v/%v, isPolicy=%v", cm.Namespace, cm.Name, isPolicy)
		s.syncAdd(cm, isPolicy)
	}
}

func (s *Sync) update(oldObj, obj interface{}) {
	oldCm, cm := oldObj.(*v1.ConfigMap), obj.(*v1.ConfigMap)
	if match, isPolicy := s.matcher(cm); match {
		logrus.Debugf("OnUpdate cm=%v/%v, isPolicy=%v, oldVer=%v, newVer=%v",
			cm.Namespace, cm.Name, isPolicy, oldCm.GetResourceVersion(), cm.GetResourceVersion())
		if cm.GetResourceVersion() != oldCm.GetResourceVersion() {
			newFp, oldFp := fingerprint(cm), fingerprint(oldCm)
			rtrVal := cm.Annotations[retriesAnnotationKey]
			logrus.Debugf("OnUpdate cm=%v/%v, retries=%v, oldFp=%v, newFp=%v", cm.Namespace, cm.Name, rtrVal, oldFp, newFp)
			if newFp != oldFp || rtrVal != "0" {
				s.syncAdd(cm, isPolicy)
			}
		}
	} else {
		// check if the label was removed
		if match, isPolicy := s.matcher(oldCm); match {
			s.syncRemove(oldCm, isPolicy)
		}
	}
}

func (s *Sync) delete(obj interface{}) {
	if d, ok := obj.(cache.DeletedFinalStateUnknown); ok {
		obj = d.Obj
	}
	cm := obj.(*v1.ConfigMap)
	if match, isPolicy := s.matcher(cm); match {
		logrus.Debugf("OnDelete cm=%v/%v", cm.Namespace, cm.Name)
		s.syncRemove(cm, isPolicy)
	}
}

func (s *Sync) syncAdd(cm *v1.ConfigMap, isPolicy bool) {
	path := fmt.Sprintf("%v/%v", cm.Namespace, cm.Name)
	logrus.Debugf("Adding cm=%v, isPolicy=%v", path, isPolicy)
	// sort keys so that errors, if any, are always in the same order
	sortedKeys := make([]string, 0, len(cm.Data))
	for key := range cm.Data {
		sortedKeys = append(sortedKeys, key)
	}
	sort.Strings(sortedKeys)
	var syncErr errList
	for _, key := range sortedKeys {
		value := cm.Data[key]
		id := fmt.Sprintf("%v/%v", path, key)
		var err error
		if isPolicy {
			err = s.opa.InsertPolicy(id, []byte(value))
			logrus.Infof("Added policy %v, err=%v", id, err)
		} else {
			// We don't need to know the JSON structure, just pass it
			// directly to the OPA data store.
			var data map[string]interface{}
			if err = json.Unmarshal([]byte(value), &data); err != nil {
				logrus.Errorf("Failed to parse JSON data in configmap with id=%s", id)
			} else {
				err = s.opa.PutData(id, data)
				logrus.Infof("Added data %v, err=%v", id, err)
			}
		}
		if err != nil {
			syncErr = append(syncErr, err)
		}
	}
	if syncErr != nil {
		var retries int = 0
		if isPolicy {
			if rStr, ok := cm.Annotations[retriesAnnotationKey]; ok {
				r, err := strconv.Atoi(rStr)
				if err == nil && r > 0 {
					retries = r - 1
					logrus.Debugf("Adding policies error cm=%v, old retry=%v, new retry=%v", path, rStr, retries)
				} else if err == nil && r == 0 {
					retries = defaultRetries
					logrus.Debugf("Adding policies error cm=%v, old retry=%v, new retry=%v", path, rStr, retries)
				}
			} else {
				retries = defaultRetries
				logrus.Debugf("Adding policies error cm=%v, no retry annotation, new retry=%v", path, retries)
			}
		}
		s.setAnnotations(cm, status{
			Status: "error",
			Error:  syncErr,
		}, retries)
	} else {
		s.setAnnotations(cm, status{
			Status: "ok",
		}, 0)
	}
}

func (s *Sync) syncRemove(cm *v1.ConfigMap, isPolicy bool) {
	logrus.Debugf("Attempting to remove cm=%v/%v, isPolicy=%v", cm.Namespace, cm.Name, isPolicy)
	path := fmt.Sprintf("%v/%v", cm.Namespace, cm.Name)
	for key := range cm.Data {
		id := fmt.Sprintf("%v/%v", path, key)
		if isPolicy {
			if err := s.opa.DeletePolicy(id); err != nil {
				logrus.Errorf("Failed to delete policy %v: %v", id, err)
			}
		} else {
			if err := s.opa.PatchData(path, "remove", nil); err != nil {
				logrus.Errorf("Failed to remove %v (will reset OPA data and resync in %v): %v", id, resyncPeriod, err)
				s.syncReset(id)
			}
		}
	}
}

func (s *Sync) setAnnotations(cm *v1.ConfigMap, st status, retries int) {
	bs, err := json.Marshal(st)
	if err != nil {
		logrus.Errorf("Failed to serialize status for cm=%v/%v, err=%v", cm.Namespace, cm.Name, err)
		return
	}
	patch := map[string]interface{}{
		"metadata": map[string]interface{}{
			"annotations": map[string]interface{}{
				statusAnnotationKey:  string(bs),
				retriesAnnotationKey: strconv.Itoa(retries),
			},
		},
	}
	bs, err = json.Marshal(patch)
	if err != nil {
		logrus.Errorf("Failed to serialize patch for %v/%v: %v", cm.Namespace, cm.Name, err)
		return
	}
	_, err = s.clientset.CoreV1().ConfigMaps(cm.Namespace).Patch(context.TODO(), cm.Name, types.StrategicMergePatchType, bs, metav1.PatchOptions{})
	if err != nil {
		logrus.Errorf("Failed to %v for %v/%v: %v", statusAnnotationKey, cm.Namespace, cm.Name, err)
	}
}

func (s *Sync) syncReset(id string) {
	logrus.Debugf("Attempting to reset %v", id)
	d := syncResetBackoffMin
	for {
		if err := s.opa.PutData("/", map[string]interface{}{}); err != nil {
			logrus.Errorf("Failed to reset OPA data for %v (will retry after %v): %v", id, d, err)
		} else {
			return
		}
		time.Sleep(d)
		d = d * 2
		if d > syncResetBackoffMax {
			d = syncResetBackoffMax
		}
	}
}

// fingerprint for the labels and data of a configmap.
func fingerprint(cm *v1.ConfigMap) uint64 {
	hash := fnv.New64a()
	data := json.NewEncoder(hash)
	data.Encode(cm.Labels)
	data.Encode(cm.Data)
	return hash.Sum64()
}

// errList is an error type that can marshal a list of errors to json
type errList []error

var (
	// Make sure we implement the proper interfaces
	_ error          = errList{}
	_ json.Marshaler = errList{}
)

type status struct {
	Status string  `json:"status"`
	Error  errList `json:"error,omitempty"`
}

// MarshalJSON implements json.Marshaler
func (m errList) MarshalJSON() ([]byte, error) {
	if len(m) <= 0 {
		return []byte(`""`), nil
	}
	list := make([]json.RawMessage, 0, len(m))
	for _, err := range m {
		if b, marshalErr := json.Marshal(err); marshalErr == nil {
			list = append(list, b)
		} else {
			// fallback to quoted .Error() string if marshalling fails
			list = append(list, []byte(fmt.Sprintf("%q", err.Error())))
		}
	}
	if len(list) == 1 {
		return list[0], nil // for backward compatibility
	}
	return json.Marshal(list)
}

// Error implements error
func (m errList) Error() string {
	if len(m) <= 0 {
		return ""
	}
	text := make([]string, 0, len(m))
	for _, err := range m {
		text = append(text, err.Error())
	}
	return strings.Join(text, "\n")
}


================================================
FILE: pkg/data/generic.go
================================================
// Copyright 2017 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package data

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	opa_client "github.com/open-policy-agent/kube-mgmt/pkg/opa"
	"github.com/open-policy-agent/kube-mgmt/pkg/types"

	"github.com/sirupsen/logrus"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"
)

// The min/max amount of time to wait when resetting the synchronizer.
const (
	backoffMax   = time.Second * 30
	backoffMin   = time.Second
	jitterFactor = 1.2
	FieldMeta    = "metadata.namespace!="
)

// GenericSync replicates Kubernetes resources into OPA as raw JSON.
type GenericSync struct {
	createError      error // to support deprecated calls to New / Run
	client           dynamicClient
	opa              opa_client.Data
	ns               types.ResourceType
	limiter          workqueue.TypedRateLimiter[any]
	jitterFactor     float64
	ignoreNamespaces []string
	mu               sync.Mutex
	ready            bool
}

// New returns a new GenericSync that can be started.
// Deprecated: Please Use NewFromInterface instead.
func New(kubeconfig *rest.Config, opa opa_client.Data, ns types.ResourceType) *GenericSync {
	client, err := dynamic.NewForConfig(kubeconfig)
	if err != nil {
		return &GenericSync{createError: err}
	}
	return NewFromInterface(client, opa, ns)
}

type Option func(s *GenericSync)

// NewFromInterface returns a new GenericSync that can be started.
func NewFromInterface(client dynamic.Interface, opa opa_client.Data, ns types.ResourceType, opts ...Option) *GenericSync {
	s := &GenericSync{
		client:       dynamicClient{client},
		ns:           ns,
		opa:          opa.Prefix(ns.Resource),
		jitterFactor: jitterFactor,
	}
	for _, opt := range opts {
		opt(s)
	}
	if s.limiter == nil { // Use default rateLimiter if not configured
		s.limiter = workqueue.NewTypedItemExponentialFailureRateLimiter[any](backoffMin, backoffMax)
	}
	return s
}

// WithIgnoreNamespaces provides a list of namespaces to ignore
func WithIgnoreNamespaces(ignoreNamespaces []string) Option {
	return func(s *GenericSync) {
		s.ignoreNamespaces = ignoreNamespaces
	}
}

// WithBackoff tunes the values of exponential backoff and jitter factor
func WithBackoff(min, max time.Duration, jitterFactor float64) Option {
	return func(s *GenericSync) {
		s.limiter = workqueue.NewTypedItemExponentialFailureRateLimiter[any](min, max)
		s.jitterFactor = jitterFactor
	}
}

// Run starts the synchronizer. To stop the synchronizer send a message to the
// channel.
// Deprecated: Please use RunContext instead.
func (s *GenericSync) Run() (chan struct{}, error) {

	// To support legacy way of creating GenericSync from *rest.Config
	if s.createError != nil {
		return nil, s.createError
	}

	quit := make(chan struct{})
	ctx, cancel := context.WithCancel(context.Background())
	go func() { // propagate cancel signal from channel to context
		<-quit
		cancel()
	}()
	go s.RunContext(ctx)
	return quit, nil
}

// RunContext starts the synchronizer in the foreground.
// To stop the synchronizer, cancel the context.
func (s *GenericSync) RunContext(ctx context.Context) error {
	if s.createError != nil {
		return s.createError
	}

	store, queue := s.setup(ctx)
	go func() {
		<-ctx.Done()
		queue.ShutDown()
	}()

	s.loop(store, queue)
	return nil
}

func (s *GenericSync) Ready() bool {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.ready
}

// setup the store and queue for this GenericSync instance
func (s *GenericSync) setup(ctx context.Context) (cache.Store, workqueue.TypedDelayingInterface[any]) {
	ignoreNs := s.ignoreNs()

	resource := s.client.ResourceFor(s.ns, metav1.NamespaceAll)
	queue := workqueue.NewNamedDelayingQueue(s.ns.String())
	store, controller := cache.NewInformerWithOptions(cache.InformerOptions{
		ListerWatcher: &cache.ListWatch{
			ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
				options.FieldSelector = ignoreNs
				return resource.List(ctx, options)
			},
			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
				options.FieldSelector = ignoreNs
				return resource.Watch(ctx, options)
			},
		},
		ObjectType:   &unstructured.Unstructured{},
		Handler:      resourceEventQueue{queue},
		ResyncPeriod: 0,
	})

	start, quit := time.Now(), ctx.Done()
	go controller.Run(quit)
	for !cache.WaitForCacheSync(quit, controller.HasSynced) {
		logrus.Warnf("Failed to sync cache for %v, retrying...", s.ns)
	}
	if controller.HasSynced() {
		logrus.Infof("Initial informer sync for %v completed, took %v", s.ns, time.Since(start))
	}

	return store, queue
}

func (s *GenericSync) ignoreNs() string {
	var ignoreNs string
	if !s.ns.Namespaced {
		return ignoreNs
	}
	if len(s.ignoreNamespaces) >= 1 {
		for _, ns := range s.ignoreNamespaces {
			ignoreNs = FieldMeta + ns + "," + ignoreNs
		}
	}
	ignoreNs = strings.TrimSuffix(ignoreNs, ",")
	return ignoreNs
}

// resourceEventQueue is a cache.ResourceEventHandler that queues all events
type resourceEventQueue struct {
	workqueue.Interface
}

// OnAdd implements ResourceHandler
func (q resourceEventQueue) OnAdd(obj interface{}, isInInitialList bool) {
	key, err := cache.MetaNamespaceKeyFunc(obj)
	if err != nil {
		logrus.Warnf("failed to retrieve key: %v", err)
		return
	}
	q.Add(key)
}

func (q resourceEventQueue) resourceVersionMatch(oldObj, newObj interface{}) bool {
	var (
		oldMeta metav1.Object
		newMeta metav1.Object
		err     error
	)
	oldMeta, err = meta.Accessor(oldObj)
	if err == nil {
		newMeta, err = meta.Accessor(newObj)
	}
	if err != nil {
		logrus.Warnf("failed to retrieve meta: %v", err)
		return false
	}
	return newMeta.GetResourceVersion() == oldMeta.GetResourceVersion()
}

// OnUpdate implements ResourceHandler
func (q resourceEventQueue) OnUpdate(oldObj, newObj interface{}) {
	if !q.resourceVersionMatch(oldObj, newObj) { // Avoid sync flood on relist. We don't use resync.
		q.OnAdd(newObj, false)
	}
}

// OnDelete implements ResourceHandler
func (q resourceEventQueue) OnDelete(obj interface{}) {
	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
	if err != nil {
		logrus.Warnf("failed to retrieve key: %v", err)
		return
	}
	q.Add(key)
}

const initPath = ""

// loop starts replicating Kubernetes resources into OPA. If an error occurs
// during the replication process, this function will backoff and reload
// all resources into OPA from scratch.
func (s *GenericSync) loop(store cache.Store, queue workqueue.TypedDelayingInterface[any]) {

	logrus.Infof("Syncing %v.", s.ns)
	defer func() {
		logrus.Infof("Sync for %v finished. Exiting.", s.ns)
	}()

	var delay time.Duration
	for !queue.ShuttingDown() {

		queue.AddAfter(initPath, delay) // this special path will trigger a full load
		syncDone := false               // discard everything until initPath

		var err error
		for err == nil {
			key, shuttingDown := queue.Get()
			if shuttingDown {
				return
			}
			err = s.processNext(store, key.(string), &syncDone)
			if key == initPath && syncDone {
				s.limiter.Forget(initPath)
			}
			queue.Done(key)
		}

		delay := wait.Jitter(s.limiter.When(initPath), s.jitterFactor)
		logrus.Errorf("Sync for %v failed, trying again in %v. Reason: %v", s.ns, delay, err)
	}
}

func (s *GenericSync) processNext(store cache.Store, path string, syncDone *bool) error {

	// On receiving the initPath, load a full dump of the data store
	if path == initPath {
		if *syncDone {
			return nil
		}
		start, list := time.Now(), store.List()
		if err := s.syncAll(list); err != nil {
			return err
		}
		s.mu.Lock()
		s.ready = true
		s.mu.Unlock()
		logrus.Infof("Loaded %d resources of kind %v into OPA. Took %v", len(list), s.ns, time.Since(start))
		*syncDone = true // sync is now Done
		return nil
	}

	// Ignore updates queued before the initial load
	if !*syncDone {
		return nil
	}

	obj, exists, err := store.GetByKey(path)
	if err != nil {
		return fmt.Errorf("store error: %w", err)
	}
	if exists {
		if err := s.opa.PutData(path, obj); err != nil {
			return fmt.Errorf("add event: %w", err)
		}
	} else {
		if err := s.opa.PatchData(path, "remove", nil); err != nil {
			return fmt.Errorf("delete event: %w", err)
		}
	}
	return nil
}

func (s *GenericSync) syncAll(objs []interface{}) error {

	// Build a list of patches to apply.
	payload, err := generateSyncPayload(objs, s.ns.Namespaced)
	if err != nil {
		return err
	}

	return s.opa.PutData("/", payload)
}

func generateSyncPayload(objs []interface{}, namespaced bool) (map[string]interface{}, error) {
	combined := make(map[string]interface{}, len(objs))
	for _, obj := range objs {
		path, err := cache.MetaNamespaceKeyFunc(obj)
		if err != nil {
			return nil, err
		}

		// Ensure the path in the map up to our value exists
		// We make some assumptions about the paths that do exist
		// being the correct types due to the expected uniform
		// paths for each of the similar object types being
		// sync'd with the GenericSync instance.
		segments := strings.Split(path, "/")
		dir := combined
		for i := 0; i < len(segments)-1; i++ {
			next, ok := combined[segments[i]]
			if !ok {
				next = map[string]interface{}{}
				dir[segments[i]] = next
			}
			dir = next.(map[string]interface{})
		}
		dir[segments[len(segments)-1]] = obj
	}

	return combined, nil
}


================================================
FILE: pkg/data/generic_test.go
================================================
package data

import (
	"context"
	"errors"
	"fmt"
	"testing"
	"time"

	"github.com/open-policy-agent/kube-mgmt/internal/expect"
	"github.com/open-policy-agent/kube-mgmt/pkg/types"

	apiv1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/dynamic/fake"
	"k8s.io/client-go/kubernetes/scheme"
)

type testCase struct {
	Label        string
	ResourceType types.ResourceType
	Prefix       string
	Objs         []runtime.Object
	Expected     string
}

// NewFakeDynamicClient builds a new FakeDynamicClient
func newFakeDynamicClient(t *testing.T, objs ...runtime.Object) dynamicClient {
	sc := runtime.NewScheme()
	if err := scheme.AddToScheme(sc); err != nil {
		t.Fatalf("Failed to build initial scheme: %v", err)
	}
	return dynamicClient{resourceInterface: fake.NewSimpleDynamicClient(sc, objs...)}
}

func TestGenericSync(t *testing.T) {
	t.Parallel()

	testCases := []testCase{
		{
			Label: "Single Cluster Resource",
			ResourceType: types.ResourceType{
				Namespaced: false,
				Resource:   "nodes",
				Version:    "v1",
			},
			Prefix: "",
			Objs: []runtime.Object{
				&apiv1.Node{
					TypeMeta: metav1.TypeMeta{
						APIVersion: "v1",
						Kind:       "Node",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name:            "node1",
						ResourceVersion: "0",
					},
					Spec:   apiv1.NodeSpec{},
					Status: apiv1.NodeStatus{},
				},
			},
			Expected: `{
				"node1":{
					"apiVersion": "v1",
					"kind": "Node",
					"metadata":{
						"creationTimestamp":null,
						"name":"node1",
						"resourceVersion":"0"
					},
					"spec":{
					},
					"status":{
						"daemonEndpoints":{
							"kubeletEndpoint":{
								"Port":0
							}
						},
						"nodeInfo":{
							"architecture":"",
							"bootID":"",
							"containerRuntimeVersion":"",
							"kernelVersion":"",
							"kubeProxyVersion":"",
							"kubeletVersion":"",
							"machineID":"",
							"operatingSystem":"",
							"osImage":"",
							"systemUUID":""
						}
					}
				}
			}`,
		},
		{
			Label: "Single Cluster Resource With Prefix",
			ResourceType: types.ResourceType{
				Namespaced: false,
				Resource:   "nodes",
				Version:    "v1",
			},
			Prefix: "kube",
			Objs: []runtime.Object{
				&apiv1.Node{
					TypeMeta: metav1.TypeMeta{
						APIVersion: "v1",
						Kind:       "Node",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name:            "node1",
						ResourceVersion: "0",
					},
					Spec:   apiv1.NodeSpec{},
					Status: apiv1.NodeStatus{},
				},
			},
			Expected: `{
				"node1":{
					"apiVersion": "v1",
					"kind": "Node",
					"metadata":{
						"creationTimestamp":null,
						"name":"node1",
						"resourceVersion":"0"
					},
					"spec":{
					},
					"status":{
						"daemonEndpoints":{
							"kubeletEndpoint":{
								"Port":0
							}
						},
						"nodeInfo":{
							"architecture":"",
							"bootID":"",
							"containerRuntimeVersion":"",
							"kernelVersion":"",
							"kubeProxyVersion":"",
							"kubeletVersion":"",
							"machineID":"",
							"operatingSystem":"",
							"osImage":"",
							"systemUUID":""
						}
					}
				}
			}`,
		},
		{
			Label: "Multiple Cluster Resources With Prefix",
			ResourceType: types.ResourceType{
				Namespaced: false,
				Resource:   "nodes",
				Version:    "v1",
			},
			Prefix: "kube",
			Objs: []runtime.Object{
				&apiv1.Node{
					TypeMeta: metav1.TypeMeta{
						APIVersion: "v1",
						Kind:       "Node",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name:            "node1",
						ResourceVersion: "0",
					},
					Spec:   apiv1.NodeSpec{},
					Status: apiv1.NodeStatus{},
				},
				&apiv1.Node{
					TypeMeta: metav1.TypeMeta{
						APIVersion: "v1",
						Kind:       "Node",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name:            "node2",
						ResourceVersion: "0",
					},
					Spec:   apiv1.NodeSpec{},
					Status: apiv1.NodeStatus{},
				},
				&apiv1.Node{
					TypeMeta: metav1.TypeMeta{
						APIVersion: "v1",
						Kind:       "Node",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name:            "node3",
						ResourceVersion: "0",
					},
					Spec:   apiv1.NodeSpec{},
					Status: apiv1.NodeStatus{},
				},
			},
			Expected: `{
				"node1":{
					"apiVersion": "v1",
					"kind": "Node",
					"metadata":{
						"creationTimestamp":null,
						"name":"node1",
						"resourceVersion":"0"
					},
					"spec":{
					},
					"status":{
						"daemonEndpoints":{
							"kubeletEndpoint":{
								"Port":0
							}
						},
						"nodeInfo":{
							"architecture":"",
							"bootID":"",
							"containerRuntimeVersion":"",
							"kernelVersion":"",
							"kubeProxyVersion":"",
							"kubeletVersion":"",
							"machineID":"",
							"operatingSystem":"",
							"osImage":"",
							"systemUUID":""
						}
					}
				},
				"node2":{
					"apiVersion": "v1",
					"kind": "Node",
					"metadata":{
						"creationTimestamp":null,
						"name":"node2",
						"resourceVersion":"0"
					},
					"spec":{
					},
					"status":{
						"daemonEndpoints":{
							"kubeletEndpoint":{
								"Port":0
							}
						},
						"nodeInfo":{
							"architecture":"",
							"bootID":"",
							"containerRuntimeVersion":"",
							"kernelVersion":"",
							"kubeProxyVersion":"",
							"kubeletVersion":"",
							"machineID":"",
							"operatingSystem":"",
							"osImage":"",
							"systemUUID":""
						}
					}
				},
				"node3":{
					"apiVersion": "v1",
					"kind": "Node",
					"metadata":{
						"creationTimestamp":null,
						"name":"node3",
						"resourceVersion":"0"
					},
					"spec":{
					},
					"status":{
						"daemonEndpoints":{
							"kubeletEndpoint":{
								"Port":0
							}
						},
						"nodeInfo":{
							"architecture":"",
							"bootID":"",
							"containerRuntimeVersion":"",
							"kernelVersion":"",
							"kubeProxyVersion":"",
							"kubeletVersion":"",
							"machineID":"",
							"operatingSystem":"",
							"osImage":"",
							"systemUUID":""
						}
					}
				}
			}`,
		},
		{
			Label: "Single Namespaced Resource",
			ResourceType: types.ResourceType{
				Namespaced: true,
				Resource:   "pods",
				Version:    "v1",
			},
			Prefix: "",
			Objs: []runtime.Object{
				&apiv1.Pod{
					TypeMeta: metav1.TypeMeta{
						APIVersion: "v1",
						Kind:       "Pod",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name:            "pod1",
						Namespace:       "ns1",
						ResourceVersion: "0",
					},
					Spec:   apiv1.PodSpec{},
					Status: apiv1.PodStatus{},
				},
			},
			Expected: `{
				"ns1":{
					"pod1":{
						"apiVersion": "v1",
						"kind": "Pod",
						"metadata":{
							"creationTimestamp":null,
							"name":"pod1",
							"namespace":"ns1",
							"resourceVersion":"0"
						},
						"spec":{
							"containers":null
						},
						"status":{
						}
					}
				}
			}`,
		},
		{
			Label: "Single Namespaced Resource With Prefix",
			ResourceType: types.ResourceType{
				Namespaced: true,
				Resource:   "pods",
				Version:    "v1",
			},
			Prefix: "kube",
			Objs: []runtime.Object{
				&apiv1.Pod{
					TypeMeta: metav1.TypeMeta{
						APIVersion: "v1",
						Kind:       "Pod",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name:            "pod1",
						Namespace:       "ns1",
						ResourceVersion: "0",
					},
					Spec:   apiv1.PodSpec{},
					Status: apiv1.PodStatus{},
				},
			},
			Expected: `{
				"ns1":{
					"pod1":{
						"apiVersion": "v1",
						"kind": "Pod",
						"metadata":{
							"creationTimestamp":null,
							"name":"pod1",
							"namespace":"ns1",
							"resourceVersion":"0"
						},
						"spec":{
							"containers":null
						},
						"status":{
						}
					}
				}
			}`,
		},
		{
			Label: "Multiple Namespaced Resources With Prefix",
			ResourceType: types.ResourceType{
				Namespaced: true,
				Resource:   "pods",
				Version:    "v1",
			},
			Prefix: "kube",
			Objs: []runtime.Object{
				&apiv1.Pod{
					TypeMeta: metav1.TypeMeta{
						APIVersion: "v1",
						Kind:       "Pod",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name:            "pod1",
						Namespace:       "ns1",
						ResourceVersion: "0",
					},
					Spec:   apiv1.PodSpec{},
					Status: apiv1.PodStatus{},
				},
				&apiv1.Pod{
					TypeMeta: metav1.TypeMeta{
						APIVersion: "v1",
						Kind:       "Pod",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name:            "pod2",
						Namespace:       "ns1",
						ResourceVersion: "0",
					},
					Spec:   apiv1.PodSpec{},
					Status: apiv1.PodStatus{},
				},
				&apiv1.Pod{
					TypeMeta: metav1.TypeMeta{
						APIVersion: "v1",
						Kind:       "Pod",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name:            "pod1",
						Namespace:       "ns2",
						ResourceVersion: "0",
					},
					Spec:   apiv1.PodSpec{},
					Status: apiv1.PodStatus{},
				},
			},
			Expected: `{
				"ns1":{
					"pod1":{
						"apiVersion": "v1",
						"kind": "Pod",
						"metadata":{
							"creationTimestamp":null,
							"name":"pod1",
							"namespace":"ns1",
							"resourceVersion":"0"
						},
						"spec":{
							"containers":null
						},
						"status":{
						}
					},
					"pod2":{
						"apiVersion": "v1",
						"kind": "Pod",
						"metadata":{
							"creationTimestamp":null,
							"name":"pod2",
							"namespace":"ns1",
							"resourceVersion":"0"
						},
						"spec":{
							"containers":null
						},
						"status":{
						}
					}
				},
				"ns2":{
					"pod1": {
						"apiVersion": "v1",
						"kind": "Pod",
						"metadata":{
							"creationTimestamp":null,
							"name":"pod1",
							"namespace":"ns2",
							"resourceVersion":"0"
						},
						"spec":{
							"containers":null
						},
						"status":{
						}
					}
				}
			}`,
		},
	}

	for _, tc := range testCases {

		tc := tc // We will be running the tests in parallel, so avoid issues with loop var
		expected := expect.MustMarshal(t, expect.MustUnmarshal(t, []byte(tc.Expected)))

		t.Run(fmt.Sprintf("%s - Must Generate Sync Payload", tc.Label), func(t *testing.T) {
			t.Parallel()
			tc.testGenerateSyncPayload(t, expected)
		})

		t.Run(fmt.Sprintf("%s - Must Load Existing Resources", tc.Label), func(t *testing.T) {
			t.Parallel()
			tc.testLoad(t, expected)
		})

		t.Run(fmt.Sprintf("%s - Must Add New Resources", tc.Label), func(t *testing.T) {
			t.Parallel()
			tc.testAdd(t)
		})

		t.Run(fmt.Sprintf("%s - Must Remove Resources", tc.Label), func(t *testing.T) {
			t.Parallel()
			tc.testDelete(t)
		})

		t.Run(fmt.Sprintf("%s - Must Update Resources", tc.Label), func(t *testing.T) {
			t.Parallel()
			tc.testUpdate(t)
		})

		t.Run(fmt.Sprintf("%s - Must Retry Load On Error", tc.Label), func(t *testing.T) {
			t.Parallel()
			tc.testRetryLoad(t, expected)
		})

		t.Run(fmt.Sprintf("%s - Must Retry Add On Error", tc.Label), func(t *testing.T) {
			t.Parallel()
			tc.testRetryAdd(t)
		})

		t.Run(fmt.Sprintf("%s - Must Retry Update On Error", tc.Label), func(t *testing.T) {
			t.Parallel()
			tc.testRetryUpdate(t)
		})

		t.Run(fmt.Sprintf("%s - Must Retry Delete On Error", tc.Label), func(t *testing.T) {
			t.Parallel()
			tc.testRetryDelete(t)
		})
	}
}

func TestEventQueue(t *testing.T) {
	t.Parallel()

	tc := testCase{
		Label: "Single Cluster Resource",
		ResourceType: types.ResourceType{
			Namespaced: false,
			Resource:   "nodes",
			Version:    "v1",
		},
		Prefix: "",
		Objs: []runtime.Object{
			&apiv1.Node{
				TypeMeta: metav1.TypeMeta{
					APIVersion: "v1",
					Kind:       "Node",
				},
				ObjectMeta: metav1.ObjectMeta{
					Name:            "node1",
					ResourceVersion: "0",
				},
				Spec:   apiv1.NodeSpec{},
				Status: apiv1.NodeStatus{},
			},
		},
		Expected: `{
				"node1":{
					"apiVersion": "v1",
					"kind": "Node",
					"metadata":{
						"creationTimestamp":null,
						"name":"node1",
						"resourceVersion":"0"
					},
					"spec":{
					},
					"status":{
						"daemonEndpoints":{
							"kubeletEndpoint":{
								"Port":0
							}
						},
						"nodeInfo":{
							"architecture":"",
							"bootID":"",
							"containerRuntimeVersion":"",
							"kernelVersion":"",
							"kubeProxyVersion":"",
							"kubeletVersion":"",
							"machineID":"",
							"operatingSystem":"",
							"osImage":"",
							"systemUUID":""
						}
					}
				}
			}`,
	}

	t.Run(fmt.Sprintf("%s - Must Update On Different ResourceVersion", tc.Label), func(t *testing.T) {
		t.Parallel()
		tc.testUpdateDifferentVersion(t)
	})

	t.Run(fmt.Sprintf("%s - Must Skip On Same ResourceVersion", tc.Label), func(t *testing.T) {
		t.Parallel()
		tc.testUpdateSameVersion(t)
	})
}

func (tc *testCase) testGenerateSyncPayload(t *testing.T, expected []byte) {

	data := make([]interface{}, 0, len(tc.Objs))
	for _, obj := range tc.Objs {
		data = append(data, obj)
	}

	patches, err := generateSyncPayload(data, tc.ResourceType.Namespaced)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	result := expect.MustRoundTrip(t, patches)
	expect.MustEqual(t, result, expected)
}

func (tc *testCase) Play(t *testing.T, client dynamicClient, play expect.Script) *expect.Client {
	t.Helper()

	return expect.Play(t, play, func(ctx context.Context, mockClient *expect.Client) {
		data := mockClient.Prefix(tc.Prefix)
		sync := NewFromInterface(
			client,
			data,
			tc.ResourceType,
			WithBackoff(0, 5*time.Second, 0),
		)
		sync.RunContext(ctx)
	})
}

func (tc *testCase) testLoad(t *testing.T, expected []byte) {

	client := newFakeDynamicClient(t, tc.Objs...)
	play := expect.Script{
		expect.PutData("/", expected).End(),
	}

	data := tc.Play(t, client, play)
	expect.MustEqual(t, data.PrefixList, []string{tc.Prefix, tc.ResourceType.Resource})
}

func (tc *testCase) testAdd(t *testing.T) {

	client, obj := newFakeDynamicClient(t), tc.Objs[0]
	play := expect.Script{
		expect.PutData("/", []byte("{}")).Do(client.MustCreate(t, tc.ResourceType, obj)),
		expect.PutData(expect.MustKey(t, obj), expect.MustRoundTrip(t, obj)).End(),
	}

	tc.Play(t, client, play)
}

func (tc *testCase) testDelete(t *testing.T) {

	client, obj := newFakeDynamicClient(t, tc.Objs...), tc.Objs[0]
	play := expect.Script{
		expect.PutData("/").Do(client.MustRemove(t, tc.ResourceType, obj)),
		expect.PatchData(expect.MustKey(t, obj), "remove").End(),
	}

	tc.Play(t, client, play)
}

func (tc *testCase) testUpdate(t *testing.T) {

	change := mustUnstructure(t, tc.Objs[0])
	change.SetLabels(map[string]string{"test": "update"})
	change.SetResourceVersion("1")

	client := newFakeDynamicClient(t, tc.Objs...)
	play := expect.Script{
		expect.PutData("/").Do(client.MustUpdate(t, tc.ResourceType, change)),
		expect.PutData(expect.MustKey(t, change), expect.MustRoundTrip(t, change.Object)).End(),
	}

	tc.Play(t, client, play)
}

func (tc *testCase) testRetryLoad(t *testing.T, expected []byte) {

	client := newFakeDynamicClient(t, tc.Objs...)
	play := expect.Script{
		expect.PutData("/").DoError(errors.New("test fail update")),
		expect.PutData("/", expected).End(),
	}

	tc.Pla
Download .txt
gitextract_9hv5t40p/

├── .dockerignore
├── .editorconfig
├── .github/
│   └── workflows/
│       ├── build.yaml
│       ├── cache.yaml
│       └── release.yaml
├── .gitignore
├── .ko.yaml
├── LICENSE
├── README.md
├── charts/
│   └── opa-kube-mgmt/
│       ├── Chart.yaml
│       ├── README.md
│       ├── templates/
│       │   ├── _helpers.tpl
│       │   ├── deployment.yaml
│       │   ├── ingressroute.yaml
│       │   ├── mgmt-token-secret.yaml
│       │   ├── poddisruptionbudget.yaml
│       │   ├── rbac-mgmt-replicate.yaml
│       │   ├── rbac-mgmt.yaml
│       │   ├── rbac-sar.yaml
│       │   ├── secret-opa-config.yaml
│       │   ├── service.yaml
│       │   ├── serviceaccount.yaml
│       │   ├── servicemonitor.yaml
│       │   └── webhookconfiguration.yaml
│       ├── values.schema.json
│       └── values.yaml
├── cmd/
│   └── kube-mgmt/
│       ├── flag.go
│       ├── flag_test.go
│       └── main.go
├── devbox.json
├── devspace.yaml
├── docs/
│   ├── admission-control-1.7.md
│   ├── admission-control-crd.md
│   ├── admission-control-secure.md
│   └── tls-1.7.md
├── examples/
│   └── service_validation/
│       ├── README.md
│       ├── admission_controller.yaml
│       └── install.sh
├── go.mod
├── go.sum
├── internal/
│   └── expect/
│       ├── client.go
│       ├── request.go
│       └── script.go
├── justfile
├── pkg/
│   ├── configmap/
│   │   └── configmap.go
│   ├── data/
│   │   ├── generic.go
│   │   ├── generic_test.go
│   │   └── types.go
│   ├── dynamicdata/
│   │   ├── dynamicdata.go
│   │   └── dynamicdata_test.go
│   ├── opa/
│   │   ├── opa.go
│   │   └── opa_test.go
│   ├── types/
│   │   └── types.go
│   └── version/
│       └── version.go
└── test/
    ├── e2e/
    │   ├── custom_config/
    │   │   ├── 1_bundle_loaded.hurl
    │   │   ├── chainsaw-test.yaml
    │   │   └── values.yaml
    │   ├── custom_mgmt_token/
    │   │   ├── 1_policy_loaded.hurl
    │   │   ├── 2_data_loaded.hurl
    │   │   ├── chainsaw-test.yaml
    │   │   └── values.yaml
    │   ├── default/
    │   │   ├── 1_initial_state.hurl
    │   │   ├── 2_policy_loaded.hurl
    │   │   ├── 3_data_loaded.hurl
    │   │   ├── chainsaw-test.yaml
    │   │   └── values.yaml
    │   ├── fixture-labels.yaml
    │   ├── fixture-multi.yaml
    │   ├── fixture-replication.yaml
    │   ├── fixture.yaml
    │   ├── labels/
    │   │   ├── 1_initial_state.hurl
    │   │   ├── 2_policy_loaded.hurl
    │   │   ├── 3_data_loaded.hurl
    │   │   ├── chainsaw-test.yaml
    │   │   └── values.yaml
    │   ├── multi/
    │   │   ├── 1_initial_state.hurl
    │   │   ├── 2_policies_loaded.hurl
    │   │   ├── 3_policy_unloaded.hurl
    │   │   ├── 4_policies_reloaded.hurl
    │   │   ├── chainsaw-test.yaml
    │   │   └── values.yaml
    │   ├── replicate/
    │   │   ├── 1_replication.hurl
    │   │   ├── chainsaw-test.yaml
    │   │   └── values.yaml
    │   └── replicate_auto/
    │       ├── .gitignore
    │       ├── 1_replication.hurl
    │       ├── bundle/
    │       │   ├── .manifest
    │       │   └── main.rego
    │       ├── chainsaw-test.yaml
    │       └── values.yaml
    ├── lint/
    │   ├── images.yaml
    │   ├── sa.yaml
    │   ├── service.yaml
    │   └── tsc.yaml
    └── unit/
        ├── health.yaml
        ├── kube-mgmt_args.yaml
        ├── rbac_cm.yaml
        ├── rbac_replicate.yaml
        ├── sa.yaml
        ├── service.yaml
        └── tsc.yaml
Download .txt
SYMBOL INDEX (171 symbols across 15 files)

FILE: cmd/kube-mgmt/flag.go
  type groupVersionKind (line 13) | type groupVersionKind struct
    method String (line 21) | func (gvk groupVersionKind) String() string {
    method Parse (line 28) | func (gvk *groupVersionKind) Parse(value string) error {
  type gvkFlag (line 50) | type gvkFlag
    method String (line 52) | func (f *gvkFlag) String() string {
    method Set (line 56) | func (f *gvkFlag) Set(value string) error {
    method Type (line 65) | func (f *gvkFlag) Type() string {

FILE: cmd/kube-mgmt/flag_test.go
  function TestFlagParsing (line 12) | func TestFlagParsing(t *testing.T) {
  function TestFlagString (line 48) | func TestFlagString(t *testing.T) {
  function TestPolicyFlags (line 58) | func TestPolicyFlags(t *testing.T) {

FILE: cmd/kube-mgmt/main.go
  type params (line 34) | type params struct
  function main (line 59) | func main() {
  function run (line 131) | func run(params *params) {
  function loadRESTConfig (line 275) | func loadRESTConfig(path string) (*rest.Config, error) {
  function getResourceType (line 282) | func getResourceType(gvk groupVersionKind, namespaced bool) types.Resour...

FILE: internal/expect/client.go
  type Client (line 11) | type Client struct
    method Prefix (line 18) | func (f *Client) Prefix(path string) opa_client.Data {
    method PatchData (line 24) | func (f *Client) PatchData(path string, op string, value *interface{})...
    method PutData (line 38) | func (f *Client) PutData(path string, value interface{}) (err error) {
    method PostData (line 49) | func (*Client) PostData(string, interface{}) (json.RawMessage, error) {
    method InsertPolicy (line 54) | func (f *Client) InsertPolicy(path string, value []byte) (err error) {
    method DeletePolicy (line 63) | func (f *Client) DeletePolicy(path string) (err error) {

FILE: internal/expect/request.go
  type request (line 9) | type request
  constant patchRequest (line 12) | patchRequest        request = "PatchData"
  constant putRequest (line 13) | putRequest          request = "PutData"
  constant insertPolicyRequest (line 14) | insertPolicyRequest request = "InsertPolicy"
  constant deletePolicyRequest (line 15) | deletePolicyRequest request = "DeletePolicy"
  constant noRequest (line 16) | noRequest           request = "Nothing"
  type Request (line 20) | type Request struct
    method Equals (line 29) | func (expected Request) Equals(actual Request) bool {
    method String (line 37) | func (r Request) String() string {
    method Do (line 110) | func (req Request) Do(action Action) Step {
    method DoError (line 118) | func (req Request) DoError(err error) Step {
    method End (line 128) | func (req Request) End() Step {
  function optional (line 44) | func optional(expected ...[]byte) []byte {
  function PutData (line 53) | func PutData(path string, expected ...[]byte) Request {
  function PatchData (line 63) | func PatchData(path string, op string, expected ...[]byte) Request {
  function InsertPolicy (line 74) | func InsertPolicy(path string, expected ...[]byte) Request {
  function DeletePolicy (line 84) | func DeletePolicy(path string) Request {
  function Nothing (line 92) | func Nothing(duration time.Duration) Request {
  type Action (line 101) | type Action
  type Step (line 104) | type Step struct

FILE: internal/expect/script.go
  type Script (line 18) | type Script
    method String (line 21) | func (s Script) String() string {
    method strings (line 156) | func (s Script) strings(sep string) string {
  function Play (line 28) | func Play(t *testing.T, script Script, show func(ctx context.Context, cl...
  function MustMarshal (line 100) | func MustMarshal(t *testing.T, obj interface{}) []byte {
  function MustUnmarshal (line 110) | func MustUnmarshal(t *testing.T, data []byte) interface{} {
  function MustRoundTrip (line 131) | func MustRoundTrip(t *testing.T, obj interface{}) []byte {
  function MustEqual (line 136) | func MustEqual(t *testing.T, result, expected interface{}) {
  function MustKey (line 145) | func MustKey(t *testing.T, obj runtime.Object) string {

FILE: pkg/configmap/configmap.go
  constant defaultRetries (line 34) | defaultRetries       = 2
  constant statusAnnotationKey (line 35) | statusAnnotationKey  = "openpolicyagent.org/kube-mgmt-status"
  constant retriesAnnotationKey (line 36) | retriesAnnotationKey = "openpolicyagent.org/kube-mgmt-retries"
  constant resyncPeriod (line 40) | resyncPeriod        = time.Second * 60
  constant syncResetBackoffMin (line 41) | syncResetBackoffMin = time.Second
  constant syncResetBackoffMax (line 42) | syncResetBackoffMax = time.Second * 30
  function CustomLabel (line 46) | func CustomLabel(key, value string) error {
  function DefaultConfigMapMatcher (line 58) | func DefaultConfigMapMatcher(namespaces []string, enablePolicies, enable...
  function matchesLabel (line 77) | func matchesLabel(cm *v1.ConfigMap, labelKey, labelValue string) bool {
  function matchesNamespace (line 81) | func matchesNamespace(cm *v1.ConfigMap, namespaces []string) bool {
  type Sync (line 91) | type Sync struct
    method Run (line 127) | func (s *Sync) Run(namespaces []string) (chan struct{}, error) {
    method add (line 163) | func (s *Sync) add(obj interface{}) {
    method update (line 171) | func (s *Sync) update(oldObj, obj interface{}) {
    method delete (line 192) | func (s *Sync) delete(obj interface{}) {
    method syncAdd (line 203) | func (s *Sync) syncAdd(cm *v1.ConfigMap, isPolicy bool) {
    method syncRemove (line 263) | func (s *Sync) syncRemove(cm *v1.ConfigMap, isPolicy bool) {
    method setAnnotations (line 281) | func (s *Sync) setAnnotations(cm *v1.ConfigMap, st status, retries int) {
    method syncReset (line 306) | func (s *Sync) syncReset(id string) {
  function New (line 99) | func New(kubeconfig *rest.Config, opa opa.Client, matcher func(*v1.Confi...
  function fingerprint (line 324) | func fingerprint(cm *v1.ConfigMap) uint64 {
  type errList (line 333) | type errList
    method MarshalJSON (line 347) | func (m errList) MarshalJSON() ([]byte, error) {
    method Error (line 367) | func (m errList) Error() string {
  type status (line 341) | type status struct

FILE: pkg/data/generic.go
  constant backoffMax (line 33) | backoffMax   = time.Second * 30
  constant backoffMin (line 34) | backoffMin   = time.Second
  constant jitterFactor (line 35) | jitterFactor = 1.2
  constant FieldMeta (line 36) | FieldMeta    = "metadata.namespace!="
  type GenericSync (line 40) | type GenericSync struct
    method Run (line 99) | func (s *GenericSync) Run() (chan struct{}, error) {
    method RunContext (line 118) | func (s *GenericSync) RunContext(ctx context.Context) error {
    method Ready (line 133) | func (s *GenericSync) Ready() bool {
    method setup (line 140) | func (s *GenericSync) setup(ctx context.Context) (cache.Store, workque...
    method ignoreNs (line 173) | func (s *GenericSync) ignoreNs() string {
    method loop (line 241) | func (s *GenericSync) loop(store cache.Store, queue workqueue.TypedDel...
    method processNext (line 272) | func (s *GenericSync) processNext(store cache.Store, path string, sync...
    method syncAll (line 312) | func (s *GenericSync) syncAll(objs []interface{}) error {
  function New (line 54) | func New(kubeconfig *rest.Config, opa opa_client.Data, ns types.Resource...
  type Option (line 62) | type Option
  function NewFromInterface (line 65) | func NewFromInterface(client dynamic.Interface, opa opa_client.Data, ns ...
  function WithIgnoreNamespaces (line 82) | func WithIgnoreNamespaces(ignoreNamespaces []string) Option {
  function WithBackoff (line 89) | func WithBackoff(min, max time.Duration, jitterFactor float64) Option {
  type resourceEventQueue (line 188) | type resourceEventQueue struct
    method OnAdd (line 193) | func (q resourceEventQueue) OnAdd(obj interface{}, isInInitialList boo...
    method resourceVersionMatch (line 202) | func (q resourceEventQueue) resourceVersionMatch(oldObj, newObj interf...
    method OnUpdate (line 220) | func (q resourceEventQueue) OnUpdate(oldObj, newObj interface{}) {
    method OnDelete (line 227) | func (q resourceEventQueue) OnDelete(obj interface{}) {
  constant initPath (line 236) | initPath = ""
  function generateSyncPayload (line 323) | func generateSyncPayload(objs []interface{}, namespaced bool) (map[strin...

FILE: pkg/data/generic_test.go
  type testCase (line 22) | type testCase struct
    method testGenerateSyncPayload (line 603) | func (tc *testCase) testGenerateSyncPayload(t *testing.T, expected []b...
    method Play (line 619) | func (tc *testCase) Play(t *testing.T, client dynamicClient, play expe...
    method testLoad (line 634) | func (tc *testCase) testLoad(t *testing.T, expected []byte) {
    method testAdd (line 645) | func (tc *testCase) testAdd(t *testing.T) {
    method testDelete (line 656) | func (tc *testCase) testDelete(t *testing.T) {
    method testUpdate (line 667) | func (tc *testCase) testUpdate(t *testing.T) {
    method testRetryLoad (line 682) | func (tc *testCase) testRetryLoad(t *testing.T, expected []byte) {
    method testRetryAdd (line 693) | func (tc *testCase) testRetryAdd(t *testing.T) {
    method testRetryUpdate (line 705) | func (tc *testCase) testRetryUpdate(t *testing.T) {
    method testRetryDelete (line 723) | func (tc *testCase) testRetryDelete(t *testing.T) {
    method testUpdateSameVersion (line 737) | func (tc *testCase) testUpdateSameVersion(t *testing.T) {
    method testUpdateDifferentVersion (line 751) | func (tc *testCase) testUpdateDifferentVersion(t *testing.T) {
  function newFakeDynamicClient (line 31) | func newFakeDynamicClient(t *testing.T, objs ...runtime.Object) dynamicC...
  function TestGenericSync (line 39) | func TestGenericSync(t *testing.T) {
  function TestEventQueue (line 533) | func TestEventQueue(t *testing.T) {
  method MustCreate (line 767) | func (f dynamicClient) MustCreate(t *testing.T, resourceType types.Resou...
  method MustRemove (line 779) | func (f dynamicClient) MustRemove(t *testing.T, resourceType types.Resou...
  method MustUpdate (line 791) | func (f dynamicClient) MustUpdate(t *testing.T, resourceType types.Resou...
  function mustUnstructure (line 803) | func mustUnstructure(t *testing.T, obj runtime.Object) *unstructured.Uns...
  function mustAccess (line 813) | func mustAccess(t *testing.T, obj runtime.Object) metav1.Object {
  function TestGenericSync_ignoreNs (line 821) | func TestGenericSync_ignoreNs(t *testing.T) {

FILE: pkg/data/types.go
  type resourceInterface (line 11) | type resourceInterface interface
  type dynamicClient (line 16) | type dynamicClient struct
    method ResourceFor (line 21) | func (f dynamicClient) ResourceFor(resourceType types.ResourceType, na...

FILE: pkg/dynamicdata/dynamicdata.go
  type Sync (line 43) | type Sync struct
    method Run (line 78) | func (s *Sync) Run(ctx context.Context) error {
    method Ready (line 103) | func (s *Sync) Ready() bool {
    method loop (line 119) | func (s *Sync) loop(ctx context.Context, a *analyzer, rts map[string]t...
    method processAnalysisResult (line 132) | func (s *Sync) processAnalysisResult(ctx context.Context, result analy...
  function New (line 56) | func New(configFile string, analysisEntrypoint string, opaURL, opaAuth s...
  function resolveResourceTypes (line 172) | func resolveResourceTypes(config *rest.Config) (map[string]types.Resourc...
  type cancellableSync (line 211) | type cancellableSync struct
  type analyzer (line 216) | type analyzer struct
    method Stop (line 272) | func (a *analyzer) Stop(ctx context.Context) error {
    method trigger (line 278) | func (a *analyzer) trigger(_ context.Context, txn storage.Transaction,...
    method loop (line 287) | func (a *analyzer) loop(ctx context.Context) {
  type analysisResult (line 225) | type analysisResult struct
  function newAnalyzer (line 229) | func newAnalyzer(ctx context.Context, bs []byte, replicatePath, analysis...
  type ref (line 268) | type ref struct
  function analyzeRefs (line 308) | func analyzeRefs(c *ast.Compiler, entrypoints []ast.Ref, prefix ast.Ref,...

FILE: pkg/dynamicdata/dynamicdata_test.go
  function TestAnalyzer (line 14) | func TestAnalyzer(t *testing.T) {
  function TestAnalyzerNoDeps (line 57) | func TestAnalyzerNoDeps(t *testing.T) {

FILE: pkg/opa/opa.go
  type Error (line 17) | type Error struct
    method Error (line 23) | func (err *Error) Error() string {
  type Undefined (line 28) | type Undefined struct
    method Error (line 30) | func (Undefined) Error() string {
  function IsUndefinedErr (line 36) | func IsUndefinedErr(err error) bool {
  type Client (line 42) | type Client interface
  type Policies (line 48) | type Policies interface
  type Data (line 54) | type Data interface
  function New (line 62) | func New(url string, auth string) Client {
  type httpClient (line 66) | type httpClient struct
    method Prefix (line 72) | func (c *httpClient) Prefix(path string) Data {
    method PatchData (line 78) | func (c *httpClient) PatchData(path string, op string, value *interfac...
    method PutData (line 90) | func (c *httpClient) PutData(path string, value interface{}) error {
    method PostData (line 103) | func (c *httpClient) PostData(path string, value interface{}) (json.Ra...
    method InsertPolicy (line 133) | func (c *httpClient) InsertPolicy(id string, bs []byte) error {
    method DeletePolicy (line 143) | func (c *httpClient) DeletePolicy(id string) error {
    method makePatch (line 152) | func (c *httpClient) makePatch(path, op string, value *interface{}) (i...
    method handleErrors (line 171) | func (c *httpClient) handleErrors(resp *http.Response) error {
    method do (line 183) | func (c *httpClient) do(verb, path string, body io.Reader) (*http.Resp...
  function slashPath (line 197) | func slashPath(paths ...string) string {
  function makePath (line 201) | func makePath(join string, paths ...string) string {
  function joinPaths (line 205) | func joinPaths(join string, paths ...string) string {

FILE: pkg/opa/opa_test.go
  function TestHTTPClientMakePatch (line 14) | func TestHTTPClientMakePatch(t *testing.T) {
  function mustMakePatch (line 93) | func mustMakePatch(client *httpClient, path, op string, value *interface...
  function mustUnmarshalJSON (line 103) | func mustUnmarshalJSON(r io.Reader) interface{} {

FILE: pkg/types/types.go
  type ResourceType (line 11) | type ResourceType struct
    method String (line 19) | func (t ResourceType) String() string {
Condensed preview — 101 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (280K chars).
[
  {
    "path": ".dockerignore",
    "chars": 109,
    "preview": "# exclude everything, then re-include only what the Go build needs\n*\n\n!go.mod\n!go.sum\n!cmd/\n!pkg/\n!internal/\n"
  },
  {
    "path": ".editorconfig",
    "chars": 346,
    "preview": "root = true\n\n[*.{sh,yaml,md}]\nend_of_line = lf\ncharset = utf-8\ninsert_final_newline = true\ntrim_trailing_whitespace = tr"
  },
  {
    "path": ".github/workflows/build.yaml",
    "chars": 2521,
    "preview": "name: Build\non:\n  workflow_dispatch:\n  push:\n    paths-ignore:\n      - \"docs/**\"\n      - \"logo/**\"\n      - \"examples/**\""
  },
  {
    "path": ".github/workflows/cache.yaml",
    "chars": 694,
    "preview": "name: Update cache\n\non:\n  push:\n    branches:\n      - master\n  workflow_dispatch:\n  schedule:\n    - cron: '0 6 */6 * *'\n"
  },
  {
    "path": ".github/workflows/release.yaml",
    "chars": 1533,
    "preview": "name: Release\n\npermissions:\n  packages: write\n  contents: write\n\non:\n  workflow_dispatch: {}\n  push:\n    tags:\n      - '"
  },
  {
    "path": ".gitignore",
    "chars": 72,
    "preview": "./kube-mgmt\nbin\n.go\n*.tgz\n.idea\n.vscode/settings.json\n.devspace/\nbuild/\n"
  },
  {
    "path": ".ko.yaml",
    "chars": 272,
    "preview": "defaultBaseImage: alpine:3.23.4\nbuilds:\n  - id: kube-mgmt\n    main: ./cmd/kube-mgmt\n    ldflags:\n      - -X github.com/o"
  },
  {
    "path": "LICENSE",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "README.md",
    "chars": 9096,
    "preview": "# ![logo](./logo/logo.png) kube-mgmt\n\n`kube-mgmt` manages policies / data of [Open Policy Agent](https://github.com/open"
  },
  {
    "path": "charts/opa-kube-mgmt/Chart.yaml",
    "chars": 583,
    "preview": "apiVersion: v1\nappVersion: 0.0.0 # managed by git tag\nversion: 0.0.0 # managed by git tag\ndescription: Manage OPA in Kub"
  },
  {
    "path": "charts/opa-kube-mgmt/README.md",
    "chars": 2882,
    "preview": "# Manage OPA in Kubernetes with kube-mgmt sidecar.\n\n[OPA](https://www.openpolicyagent.org) is an open-source general-pur"
  },
  {
    "path": "charts/opa-kube-mgmt/templates/_helpers.tpl",
    "chars": 3418,
    "preview": "{{/* vim: set filetype=mustache: */}}\n{{/*\nExpand the name of the chart.\n*/}}\n{{- define \"opa.name\" -}}\n{{- default .Cha"
  },
  {
    "path": "charts/opa-kube-mgmt/templates/deployment.yaml",
    "chars": 10269,
    "preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: {{ template \"opa.fullname\" . }}\n  labels:\n{{ include \"opa.labels."
  },
  {
    "path": "charts/opa-kube-mgmt/templates/ingressroute.yaml",
    "chars": 714,
    "preview": "{{- if .Values.e2e }}\napiVersion: traefik.io/v1alpha1\nkind: IngressRouteTCP\nmetadata:\n  name: {{ include \"opa.fullname\" "
  },
  {
    "path": "charts/opa-kube-mgmt/templates/mgmt-token-secret.yaml",
    "chars": 175,
    "preview": "{{- if .Values.e2eMgmtTokenSecret -}}\napiVersion: v1\nkind: Secret\nmetadata:\n  name: mgmt-token-secret\ntype: Opaque\nstrin"
  },
  {
    "path": "charts/opa-kube-mgmt/templates/poddisruptionbudget.yaml",
    "chars": 670,
    "preview": "{{- if .Values.podDisruptionBudget.enabled }}\n{{- if .Capabilities.APIVersions.Has \"policy/v1/PodDisruptionBudget\" }}\nap"
  },
  {
    "path": "charts/opa-kube-mgmt/templates/rbac-mgmt-replicate.yaml",
    "chars": 1094,
    "preview": "{{- if and .Values.rbac.create .Values.mgmt.enabled -}}\n{{- $arr := concat .Values.mgmt.replicate.cluster .Values.mgmt.r"
  },
  {
    "path": "charts/opa-kube-mgmt/templates/rbac-mgmt.yaml",
    "chars": 2919,
    "preview": "{{- define \"opa.rbac.cm.rules\" -}}\nrules:\n  - apiGroups: [\"\"]\n    resources: [\"configmaps\"]\n    verbs: [\"get\", \"list\", \""
  },
  {
    "path": "charts/opa-kube-mgmt/templates/rbac-sar.yaml",
    "chars": 1016,
    "preview": "{{- if (and .Values.rbac.create .Values.sar.enabled) -}}\n---\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\n"
  },
  {
    "path": "charts/opa-kube-mgmt/templates/secret-opa-config.yaml",
    "chars": 246,
    "preview": "{{- if .Values.opa -}}\napiVersion: v1\nkind: Secret\nmetadata:\n  name: {{ template \"opa.fullname\" . }}-config\n  labels:\n{{"
  },
  {
    "path": "charts/opa-kube-mgmt/templates/service.yaml",
    "chars": 695,
    "preview": "kind: Service\napiVersion: v1\nmetadata:\n  name: {{ template \"opa.fullname\" . }}\n  labels:\n{{ include \"opa.labels.standard"
  },
  {
    "path": "charts/opa-kube-mgmt/templates/serviceaccount.yaml",
    "chars": 407,
    "preview": "{{- if .Values.serviceAccount.create }}\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: {{ template \"opa.serviceAc"
  },
  {
    "path": "charts/opa-kube-mgmt/templates/servicemonitor.yaml",
    "chars": 1041,
    "preview": "{{- if and (.Capabilities.APIVersions.Has \"monitoring.coreos.com/v1\") .Values.prometheus.enabled .Values.serviceMonitor."
  },
  {
    "path": "charts/opa-kube-mgmt/templates/webhookconfiguration.yaml",
    "chars": 4981,
    "preview": "{{- $cn := printf \"%s.%s.svc\" ( include \"opa.fullname\" . ) .Release.Namespace }}\n{{- $ca := genCA \"opa-admission-ca\" 365"
  },
  {
    "path": "charts/opa-kube-mgmt/values.schema.json",
    "chars": 3333,
    "preview": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"$id\": \"https://github.com/open-policy-agent/kube-mgmt\",\n  \""
  },
  {
    "path": "charts/opa-kube-mgmt/values.yaml",
    "chars": 9209,
    "preview": "# Default values for opa.\n# -----------------------\n#\n# OPA configuration file. See https://www.openpolicyagent.org/docs"
  },
  {
    "path": "cmd/kube-mgmt/flag.go",
    "chars": 1311,
    "preview": "// Copyright 2017 The OPA Authors.  All rights reserved.\n// Use of this source code is governed by an Apache2\n// license"
  },
  {
    "path": "cmd/kube-mgmt/flag_test.go",
    "chars": 3574,
    "preview": "package main\n\nimport (\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/open-policy-agent/kube-mgmt/pkg/configmap\"\n\t\"github"
  },
  {
    "path": "cmd/kube-mgmt/main.go",
    "chars": 10275,
    "preview": "// Copyright 2017 The OPA Authors.  All rights reserved.\n// Use of this source code is governed by an Apache2\n// license"
  },
  {
    "path": "devbox.json",
    "chars": 691,
    "preview": "{\n  \"$schema\": \"https://raw.githubusercontent.com/jetify-com/devbox/0.13.7/.schema/devbox.schema.json\",\n  \"packages\": [\n"
  },
  {
    "path": "devspace.yaml",
    "chars": 2355,
    "preview": "version: v2beta1\nname: opa-kube-mgmt\n\nvars:\n  DEVSPACE_FLAGS: \"-n default --no-warn\"\n  KO_PLATFORMS: \"linux/amd64\"\n  KO_"
  },
  {
    "path": "docs/admission-control-1.7.md",
    "chars": 4156,
    "preview": "# Admission Control (1.7 and 1.8)\n\n**Note: Admission Control has undergone changes in Kubernetes 1.7 through 1.9. If you"
  },
  {
    "path": "docs/admission-control-crd.md",
    "chars": 4449,
    "preview": "# Admission Control For Custom Resources\n\nIn the [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kube"
  },
  {
    "path": "docs/admission-control-secure.md",
    "chars": 7036,
    "preview": "# Admission Control Secure\n\nIn the [Kubernetes Admission Control](http://www.openpolicyagent.org/docs/kubernetes-admissi"
  },
  {
    "path": "docs/tls-1.7.md",
    "chars": 2492,
    "preview": "# Generating TLS Certificates (1.7)\n\nExternal Admission Controllers must be secured with TLS. At a minimum you must:\n\n- "
  },
  {
    "path": "examples/service_validation/README.md",
    "chars": 3846,
    "preview": "# Kubernetes Admission Control for preventing open AWS LoadBalancers\n\nKubernetes Service objects of type [LoadBalancer]("
  },
  {
    "path": "examples/service_validation/admission_controller.yaml",
    "chars": 2864,
    "preview": "apiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: opa-sa\n---\nkind: ClusterRoleBinding\napiVersion: rbac.authorization"
  },
  {
    "path": "examples/service_validation/install.sh",
    "chars": 1356,
    "preview": "#!/bin/bash\n\nset -ex\n\nOUT_DIR=/tmp/opa\nrm -rf ${OUT_DIR}; mkdir -p ${OUT_DIR}\n\nopenssl genrsa -out ${OUT_DIR}/ca.key 204"
  },
  {
    "path": "go.mod",
    "chars": 4149,
    "preview": "module github.com/open-policy-agent/kube-mgmt\n\ngo 1.24.11\n\nrequire (\n\tgithub.com/open-policy-agent/opa v1.5.1\n\tgithub.co"
  },
  {
    "path": "go.sum",
    "chars": 26811,
    "preview": "github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=\n"
  },
  {
    "path": "internal/expect/client.go",
    "chars": 1513,
    "preview": "package expect\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\n\topa_client \"github.com/open-policy-agent/kube-mgmt/pkg/opa\"\n)\n\n// "
  },
  {
    "path": "internal/expect/request.go",
    "chars": 3089,
    "preview": "package expect\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"time\"\n)\n\ntype request string\n\nconst (\n\tpatchRequest        request = \"Patch"
  },
  {
    "path": "internal/expect/script.go",
    "chars": 4383,
    "preview": "package expect\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"k8s.io/apimachin"
  },
  {
    "path": "justfile",
    "chars": 3710,
    "preview": "K3D := \"kube-mgmt\"\nTEST_RESULTS := 'build/test-results'\n\n@_default:\n  @just --list\n\n# golang linter\n[group('code quality"
  },
  {
    "path": "pkg/configmap/configmap.go",
    "chars": 11051,
    "preview": "// Copyright 2017 The OPA Authors.  All rights reserved.\n// Use of this source code is governed by an Apache2\n// license"
  },
  {
    "path": "pkg/data/generic.go",
    "chars": 9648,
    "preview": "// Copyright 2017 The OPA Authors.  All rights reserved.\n// Use of this source code is governed by an Apache2\n// license"
  },
  {
    "path": "pkg/data/generic_test.go",
    "chars": 20826,
    "preview": "package data\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/open-policy-agent/kube-mgmt/internal"
  },
  {
    "path": "pkg/data/types.go",
    "chars": 891,
    "preview": "package data\n\nimport (\n\t\"github.com/open-policy-agent/kube-mgmt/pkg/types\"\n\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\""
  },
  {
    "path": "pkg/dynamicdata/dynamicdata.go",
    "chars": 9547,
    "preview": "package dynamicdata\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"sort\"\n\t\"sync\"\n\n\t\"github.com/open-policy-agent/kube-mgmt"
  },
  {
    "path": "pkg/dynamicdata/dynamicdata_test.go",
    "chars": 2107,
    "preview": "package dynamicdata\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t//lint:ignore SA1019 using OPA v0.x to ensure backwards com"
  },
  {
    "path": "pkg/opa/opa.go",
    "chars": 4970,
    "preview": "// Copyright 2017 The OPA Authors.  All rights reserved.\n// Use of this source code is governed by an Apache2\n// license"
  },
  {
    "path": "pkg/opa/opa_test.go",
    "chars": 1930,
    "preview": "// Copyright 2018 The OPA Authors.  All rights reserved.\n// Use of this source code is governed by an Apache2\n// license"
  },
  {
    "path": "pkg/types/types.go",
    "chars": 746,
    "preview": "// Copyright 2017 The OPA Authors.  All rights reserved.\n// Use of this source code is governed by an Apache2\n// license"
  },
  {
    "path": "pkg/version/version.go",
    "chars": 259,
    "preview": "// Copyright 2017 The OPA Authors.  All rights reserved.\n// Use of this source code is governed by an Apache2\n// license"
  },
  {
    "path": "test/e2e/custom_config/1_bundle_loaded.hurl",
    "chars": 152,
    "preview": "GET https://localhost:8443/v1/data\nAuthorization: Bearer {{token}}\n\nHTTP 200\n[Asserts]\njsonpath \"$.result.test_helm_kube"
  },
  {
    "path": "test/e2e/custom_config/chainsaw-test.yaml",
    "chars": 311,
    "preview": "apiVersion: chainsaw.kyverno.io/v1alpha1\nkind: Test\nmetadata:\n  name: custom-config\nspec:\n  namespace: default\n  steps:\n"
  },
  {
    "path": "test/e2e/custom_config/values.yaml",
    "chars": 255,
    "preview": "opa:\n  services:\n    controller:\n      url: 'https://www.openpolicyagent.org'\n  bundles:\n    quickstart:\n      service: "
  },
  {
    "path": "test/e2e/custom_mgmt_token/1_policy_loaded.hurl",
    "chars": 310,
    "preview": "GET https://localhost:8443/v1/policies\nAuthorization: Bearer {{token}}\n\nHTTP 200\n[Asserts]\njsonpath \"$.result[?(@.id == "
  },
  {
    "path": "test/e2e/custom_mgmt_token/2_data_loaded.hurl",
    "chars": 332,
    "preview": "GET https://localhost:8443/v1/data/default\nAuthorization: Bearer {{token}}\n\nHTTP 200\n[Asserts]\njsonpath \"$.result.*\" cou"
  },
  {
    "path": "test/e2e/custom_mgmt_token/chainsaw-test.yaml",
    "chars": 1301,
    "preview": "apiVersion: chainsaw.kyverno.io/v1alpha1\nkind: Test\nmetadata:\n  name: custom-mgmt-token\nspec:\n  namespace: default\n  ste"
  },
  {
    "path": "test/e2e/custom_mgmt_token/values.yaml",
    "chars": 113,
    "preview": "e2eMgmtTokenSecret: true\nopa:\n  replicas: 2\nauthz:\n  enabled: true\n  mgmtToken:\n    secretName: mgmt-token-secret"
  },
  {
    "path": "test/e2e/default/1_initial_state.hurl",
    "chars": 120,
    "preview": "GET https://localhost:8443/v1/data\nAuthorization: Bearer {{token}}\n\nHTTP 200\n[Asserts]\njsonpath \"$.result.*\" count == 0\n"
  },
  {
    "path": "test/e2e/default/2_policy_loaded.hurl",
    "chars": 310,
    "preview": "GET https://localhost:8443/v1/policies\nAuthorization: Bearer {{token}}\n\nHTTP 200\n[Asserts]\njsonpath \"$.result[?(@.id == "
  },
  {
    "path": "test/e2e/default/3_data_loaded.hurl",
    "chars": 332,
    "preview": "GET https://localhost:8443/v1/data/default\nAuthorization: Bearer {{token}}\n\nHTTP 200\n[Asserts]\njsonpath \"$.result.*\" cou"
  },
  {
    "path": "test/e2e/default/chainsaw-test.yaml",
    "chars": 1468,
    "preview": "apiVersion: chainsaw.kyverno.io/v1alpha1\nkind: Test\nmetadata:\n  name: default\nspec:\n  namespace: default\n  steps:\n    - "
  },
  {
    "path": "test/e2e/default/values.yaml",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/e2e/fixture-labels.yaml",
    "chars": 1099,
    "preview": "---\nkind: ConfigMap\nmetadata:\n  name: policy-include\n  labels:\n    kube-mgmt/e2e: \"true\"\n    qweqwe/policy: \"111\"\napiVer"
  },
  {
    "path": "test/e2e/fixture-multi.yaml",
    "chars": 741,
    "preview": "---\nkind: ConfigMap\nmetadata:\n  name: multi-file-policy\n  labels:\n    kube-mgmt/e2e: \"true\"\n    openpolicyagent.org/poli"
  },
  {
    "path": "test/e2e/fixture-replication.yaml",
    "chars": 479,
    "preview": "---\napiVersion: v1\nkind: Namespace\nmetadata:\n  name: ignore-me\n---\napiVersion: v1\nkind: Namespace\nmetadata:\n  name: dont"
  },
  {
    "path": "test/e2e/fixture.yaml",
    "chars": 1127,
    "preview": "---\nkind: ConfigMap\nmetadata:\n  name: policy-include\n  labels:\n    kube-mgmt/e2e: \"true\"\n    openpolicyagent.org/policy:"
  },
  {
    "path": "test/e2e/labels/1_initial_state.hurl",
    "chars": 86,
    "preview": "GET http://localhost:8080/v1/data\nHTTP 200\n[Asserts]\njsonpath \"$.result.*\" count == 0\n"
  },
  {
    "path": "test/e2e/labels/2_policy_loaded.hurl",
    "chars": 242,
    "preview": "GET http://localhost:8080/v1/policies\nHTTP 200\n[Asserts]\njsonpath \"$.result[?(@.id == 'default/policy-include/include.re"
  },
  {
    "path": "test/e2e/labels/3_data_loaded.hurl",
    "chars": 264,
    "preview": "GET http://localhost:8080/v1/data/default\nHTTP 200\n[Asserts]\njsonpath \"$.result.*\" count == 1\njsonpath \"$.result.data-in"
  },
  {
    "path": "test/e2e/labels/chainsaw-test.yaml",
    "chars": 1341,
    "preview": "apiVersion: chainsaw.kyverno.io/v1alpha1\nkind: Test\nmetadata:\n  name: labels\nspec:\n  namespace: default\n  steps:\n    - n"
  },
  {
    "path": "test/e2e/labels/values.yaml",
    "chars": 268,
    "preview": "useHttps: false\n\nopa: null\n\nauthz:\n  enabled: false\n\nmgmt:\n  startupProbe:\n    httpGet:\n      scheme: HTTP\n  extraArgs:\n"
  },
  {
    "path": "test/e2e/multi/1_initial_state.hurl",
    "chars": 99,
    "preview": "GET http://localhost:8080/v1/data/my_pkg/my_rule\nHTTP 200\n[Asserts]\njsonpath \"$.result\" not exists\n"
  },
  {
    "path": "test/e2e/multi/2_policies_loaded.hurl",
    "chars": 569,
    "preview": "GET http://localhost:8080/v1/policies\nHTTP 200\n[Asserts]\njsonpath \"$.result\" count == 2\njsonpath \"$.result[?(@.id == 'de"
  },
  {
    "path": "test/e2e/multi/3_policy_unloaded.hurl",
    "chars": 99,
    "preview": "GET http://localhost:8080/v1/data/my_pkg/my_rule\nHTTP 200\n[Asserts]\njsonpath \"$.result\" not exists\n"
  },
  {
    "path": "test/e2e/multi/4_policies_reloaded.hurl",
    "chars": 569,
    "preview": "GET http://localhost:8080/v1/policies\nHTTP 200\n[Asserts]\njsonpath \"$.result\" count == 2\njsonpath \"$.result[?(@.id == 'de"
  },
  {
    "path": "test/e2e/multi/chainsaw-test.yaml",
    "chars": 2055,
    "preview": "apiVersion: chainsaw.kyverno.io/v1alpha1\nkind: Test\nmetadata:\n  name: multi\nspec:\n  namespace: default\n  steps:\n    - na"
  },
  {
    "path": "test/e2e/multi/values.yaml",
    "chars": 146,
    "preview": "useHttps: false\n\nopa: null\n\nauthz:\n  enabled: false\n\nmgmt:\n  startupProbe:\n    httpGet:\n      scheme: HTTP\n  extraArgs:\n"
  },
  {
    "path": "test/e2e/replicate/1_replication.hurl",
    "chars": 393,
    "preview": "GET http://localhost:8080/v1/data/kubernetes/services/ignore-me\nHTTP 200\n[Asserts]\njsonpath \"$.result\" not exists\n\nGET h"
  },
  {
    "path": "test/e2e/replicate/chainsaw-test.yaml",
    "chars": 411,
    "preview": "apiVersion: chainsaw.kyverno.io/v1alpha1\nkind: Test\nmetadata:\n  name: replicate\nspec:\n  steps:\n    - name: apply fixture"
  },
  {
    "path": "test/e2e/replicate/values.yaml",
    "chars": 378,
    "preview": "useHttps: false\n\nopa: null\n\nauthz:\n  enabled: false\n\nmgmt:\n  data:\n    enabled: false\n  policies:\n    enabled: false\n  s"
  },
  {
    "path": "test/e2e/replicate_auto/.gitignore",
    "chars": 13,
    "preview": "bundle.tar.gz"
  },
  {
    "path": "test/e2e/replicate_auto/1_replication.hurl",
    "chars": 393,
    "preview": "GET http://localhost:8080/v1/data/kubernetes/services/ignore-me\nHTTP 200\n[Asserts]\njsonpath \"$.result\" not exists\n\nGET h"
  },
  {
    "path": "test/e2e/replicate_auto/bundle/.manifest",
    "chars": 25,
    "preview": "{\n    \"roots\": [\"main\"]\n}"
  },
  {
    "path": "test/e2e/replicate_auto/bundle/main.rego",
    "chars": 123,
    "preview": "package main\n\nimport rego.v1\n\nmain if {\n    some ns, name\n    data.kubernetes.services[ns][name].metadata.labels == \"foo"
  },
  {
    "path": "test/e2e/replicate_auto/chainsaw-test.yaml",
    "chars": 416,
    "preview": "apiVersion: chainsaw.kyverno.io/v1alpha1\nkind: Test\nmetadata:\n  name: replicate-auto\nspec:\n  steps:\n    - name: apply fi"
  },
  {
    "path": "test/e2e/replicate_auto/values.yaml",
    "chars": 570,
    "preview": "useHttps: false\n\nopa: {\n  bundles: {\n    test: {\n      resource: file:///bundle/bundle.tar.gz\n    }\n  }\n}\n\nauthz:\n  enab"
  },
  {
    "path": "test/lint/images.yaml",
    "chars": 1960,
    "preview": "suite: lint image and mgmt.image\ntemplates:\n  - fake.yaml\ntests:\n  - it: image is null\n    set:\n      image: null\n    as"
  },
  {
    "path": "test/lint/sa.yaml",
    "chars": 246,
    "preview": "suite: lint serviceaccount\ntemplates:\n  - fake.yaml\ntests:\n  - it: annotations not string\n    set:\n      serviceAccount:"
  },
  {
    "path": "test/lint/service.yaml",
    "chars": 1101,
    "preview": "suite: lint service\ntemplates:\n  - service.yaml\ntests:\n  - it: fails when service annotation is boolean\n    set:\n      s"
  },
  {
    "path": "test/lint/tsc.yaml",
    "chars": 2068,
    "preview": "suite: lint topologySpreadConstraints\ntemplates:\n  - deployment.yaml\ntests:\n  - it: fails when maxSkew is missing\n    se"
  },
  {
    "path": "test/unit/health.yaml",
    "chars": 1996,
    "preview": "suite: test health probes\ntemplates:\n  - deployment.yaml\ntests:\n  - it: should have only liveness and readiness for OPA\n"
  },
  {
    "path": "test/unit/kube-mgmt_args.yaml",
    "chars": 1886,
    "preview": "suite: test kube-mgmt container args\ntemplates:\n  - deployment.yaml\ntests:\n  - it: should have default args\n    asserts:"
  },
  {
    "path": "test/unit/rbac_cm.yaml",
    "chars": 2401,
    "preview": "suite: test configmap rbac\ntemplates:\n  - rbac-mgmt.yaml\ntests:\n  - it: should create current namespace role by default\n"
  },
  {
    "path": "test/unit/rbac_replicate.yaml",
    "chars": 1453,
    "preview": "suite: test replicate rbac\ntemplates:\n  - rbac-mgmt-replicate.yaml\ntests:\n  - it: should not create cluster role by defa"
  },
  {
    "path": "test/unit/sa.yaml",
    "chars": 485,
    "preview": "suite: test serviceaccount annotations\ntemplates:\n  - serviceaccount.yaml\ntests:\n  - it: should omit serviceaccount anno"
  },
  {
    "path": "test/unit/service.yaml",
    "chars": 765,
    "preview": "suite: test service definition\ntemplates:\n  - service.yaml\ntests:\n  - it: should omit service annotations when null\n    "
  },
  {
    "path": "test/unit/tsc.yaml",
    "chars": 5250,
    "preview": "suite: test topologySpreadConstraints\ntemplates:\n  - deployment.yaml\ntests:\n  - it: renders with DoNotSchedule policy\n  "
  }
]

About this extraction

This page contains the full source code of the open-policy-agent/kube-mgmt GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 101 files (250.2 KB), approximately 78.1k tokens, and a symbol index with 171 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!