Showing preview only (525K chars total). Download the full file or copy to clipboard to get everything.
Repository: redhat-cop/operator-utils
Branch: master
Commit: 09e27b5626f9
Files: 121
Total size: 489.2 KB
Directory structure:
gitextract_fz5mf0kc/
├── .github/
│ └── workflows/
│ ├── pr.yaml
│ └── push.yaml
├── .gitignore
├── Dockerfile
├── LICENSE
├── Makefile
├── PROJECT
├── README.md
├── api/
│ └── v1alpha1/
│ ├── enforcingcrd_types.go
│ ├── enforcingpatch_types.go
│ ├── enforcingreconcilerstatus.go
│ ├── groupversion_info.go
│ ├── lockedpatch.go
│ ├── lockedresource.go
│ ├── mycrd_types.go
│ ├── templatedenforcingcrd_types.go
│ └── zz_generated.deepcopy.go
├── ci.Dockerfile
├── config/
│ ├── certmanager/
│ │ ├── certificate.yaml
│ │ ├── kustomization.yaml
│ │ └── kustomizeconfig.yaml
│ ├── crd/
│ │ ├── bases/
│ │ │ ├── operator-utils.example.io_enforcingcrds.yaml
│ │ │ ├── operator-utils.example.io_enforcingpatches.yaml
│ │ │ ├── operator-utils.example.io_mycrds.yaml
│ │ │ └── operator-utils.example.io_templatedenforcingcrds.yaml
│ │ ├── kustomization.yaml
│ │ ├── kustomizeconfig.yaml
│ │ └── patches/
│ │ ├── cainjection_in_enforcingcrds.yaml
│ │ ├── cainjection_in_enforcingpatches.yaml
│ │ ├── cainjection_in_mycrds.yaml
│ │ ├── cainjection_in_templatedenforcingcrds.yaml
│ │ ├── webhook_in_enforcingcrds.yaml
│ │ ├── webhook_in_enforcingpatches.yaml
│ │ ├── webhook_in_mycrds.yaml
│ │ └── webhook_in_templatedenforcingcrds.yaml
│ ├── default/
│ │ ├── kustomization.yaml
│ │ ├── manager_auth_proxy_patch.yaml
│ │ ├── manager_webhook_patch.yaml
│ │ └── webhookcainjection_patch.yaml
│ ├── helmchart/
│ │ ├── .helmignore
│ │ ├── Chart.yaml.tpl
│ │ ├── kustomization.yaml
│ │ ├── templates/
│ │ │ ├── _helpers.tpl
│ │ │ └── manager.yaml
│ │ └── values.yaml.tpl
│ ├── local-development/
│ │ └── kustomization.yaml
│ ├── manager/
│ │ ├── kustomization.yaml
│ │ └── manager.yaml
│ ├── manifests/
│ │ ├── bases/
│ │ │ └── operator-utils.clusterserviceversion.yaml
│ │ └── kustomization.yaml
│ ├── prometheus/
│ │ ├── kustomization.yaml
│ │ ├── kustomizeconfig.yaml
│ │ └── monitor.yaml
│ ├── rbac/
│ │ ├── auth_proxy_client_clusterrole.yaml
│ │ ├── auth_proxy_role.yaml
│ │ ├── auth_proxy_role_binding.yaml
│ │ ├── auth_proxy_service.yaml
│ │ ├── enforcingcrd_editor_role.yaml
│ │ ├── enforcingcrd_viewer_role.yaml
│ │ ├── enforcingpatch_editor_role.yaml
│ │ ├── enforcingpatch_viewer_role.yaml
│ │ ├── kustomization.yaml
│ │ ├── leader_election_role.yaml
│ │ ├── leader_election_role_binding.yaml
│ │ ├── mycrd_editor_role.yaml
│ │ ├── mycrd_viewer_role.yaml
│ │ ├── role.yaml
│ │ ├── role_binding.yaml
│ │ ├── templatedenforcingcrd_editor_role.yaml
│ │ └── templatedenforcingcrd_viewer_role.yaml
│ ├── samples/
│ │ ├── kustomization.yaml
│ │ ├── operator-utils_v1alpha1_enforcingcrd.yaml
│ │ ├── operator-utils_v1alpha1_enforcingpatch.yaml
│ │ ├── operator-utils_v1alpha1_mycrd.yaml
│ │ └── operator-utils_v1alpha1_templatedenforcingcrd.yaml
│ ├── scorecard/
│ │ ├── bases/
│ │ │ └── config.yaml
│ │ ├── kustomization.yaml
│ │ └── patches/
│ │ ├── basic.config.yaml
│ │ └── olm.config.yaml
│ └── webhook/
│ ├── kustomization.yaml
│ ├── kustomizeconfig.yaml
│ └── service.yaml
├── controllers/
│ ├── enforcingcrd_controller.go
│ ├── enforcingpatch_controller.go
│ ├── mycrd_controller.go
│ ├── suite_test.go
│ └── templatedenforcingcrd_controller.go
├── go.mod
├── go.sum
├── hack/
│ └── boilerplate.go.txt
├── main.go
├── pkg/
│ └── util/
│ ├── apis/
│ │ ├── conditions.go
│ │ └── key.go
│ ├── crud/
│ │ └── crudutils.go
│ ├── discoveryclient/
│ │ └── discoveryclientutils.go
│ ├── dynamicclient/
│ │ └── dynamicclientutils.go
│ ├── finalizer.go
│ ├── lockedresourcecontroller/
│ │ ├── enforcing-reconciler.go
│ │ ├── locked-resource-manager.go
│ │ ├── lockedpatch/
│ │ │ └── lockedpatch.go
│ │ ├── lockedresource/
│ │ │ ├── lockedresource.go
│ │ │ ├── lockedresourceset/
│ │ │ │ ├── lockedresourceset.go
│ │ │ │ ├── lockedresourceset_bench_test.go
│ │ │ │ └── lockedresourceset_test.go
│ │ │ └── patch.go
│ │ ├── patch-reconciler.go
│ │ └── resource-reconciler.go
│ ├── owner.go
│ ├── predicates.go
│ ├── reconciler.go
│ ├── stoppablemanager/
│ │ └── stoppable-manager.go
│ └── templates/
│ ├── advanced-funcmap.go
│ └── templates.go
├── test/
│ ├── enforcing-patch-multiple-cluster-level.yaml
│ ├── enforcing-patch-multiple.yaml
│ ├── enforcing-patch.yaml
│ ├── enforcing_cr.yaml
│ ├── failing-enforcing_cr.yaml
│ ├── mycrd_cr.yaml
│ └── templatedenforcing_cr.yaml
└── testbin/
└── setup-envtest.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/pr.yaml
================================================
name: pull request
on:
pull_request:
branches:
- master
- main
jobs:
setup:
runs-on: ubuntu-latest
name: setup
env:
BUILD_PLATFORMS: "linux/amd64,linux/arm64,linux/ppc64le,linux/s390x"
GO_VERSION: "~1.21"
steps:
- name: Setting Workflow Variables
id: set-variables
run: |
echo "::set-output name=repository_name::$(basename $GITHUB_REPOSITORY)"
echo "::set-output name=bin_dir::$(pwd)/bin"
# Create Distribution Matrix
echo "::set-output name=dist_matrix::$(echo -n "${{ env.BUILD_PLATFORMS }}" | jq -csR '. | split(",")')"
# Set versions based on presence of tag
if [[ "${{ github.ref }}" =~ ^refs/tags/ ]]; then
TAG="${GITHUB_REF/refs\/tags\//}"
echo "::set-output name=tag_event::true"
echo "::set-output name=operator_version::$TAG"
else
echo "::set-output name=tag_event::false"
echo "::set-output name=operator_version::$DEFAULT_OPERATOR_VERSION"
fi
- name: Build Go Cache Paths
id: go-cache-paths
run: |
echo "::set-output name=go-build::$(go env GOCACHE)"
echo "::set-output name=go-mod::$(go env GOMODCACHE)"
- name: Set up Go 1.x
uses: actions/setup-go@v1
with:
go-version: ${{ inputs.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
- name: Go Build Cache
uses: actions/cache@v2
with:
path: ${{ steps.go-cache-paths.outputs.go-build }}
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
- name: Go Mod Cache
uses: actions/cache@v2
with:
path: ${{ steps.go-cache-paths.outputs.go-mod }}
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
- name: Go Dependencies
run: go mod download
- name: Download Binaries
env:
OPERATOR_SDK_VERSION: ${{ inputs.OPERATOR_SDK_VERSION }}
run: |
# Create Binary Directory
mkdir -p ${{ steps.set-variables.outputs.bin_dir }}
# Operator SDK
curl -L -o ${{ steps.set-variables.outputs.bin_dir }}/operator-sdk https://github.com/operator-framework/operator-sdk/releases/download/${{ env.OPERATOR_SDK_VERSION }}/operator-sdk_linux_amd64
# Controller-gen
make controller-gen
# Kustomize
make kustomize
- name: Upload Support Binaries
uses: actions/upload-artifact@v2
with:
name: support-binaries
path: ${{ steps.set-variables.outputs.bin_dir }}
outputs:
repository_name: ${{ steps.set-variables.outputs.repository_name }}
bin_dir: ${{ steps.set-variables.outputs.bin_dir }}
go_build: ${{ steps.go-cache-paths.outputs.go-build }}
go_mod: ${{ steps.go-cache-paths.outputs.go-mod }}
tag_event: ${{ steps.set-variables.outputs.tag_event }}
dist_matrix: ${{ steps.set-variables.outputs.dist_matrix }}
build-operator:
runs-on: ubuntu-latest
name: build-operator
needs: ["setup"]
strategy:
matrix:
platform: ${{ fromJson(needs.setup.outputs.dist_matrix) }}
env:
REPOSITORY_NAME: ${{ needs.setup.outputs.repository_name }}
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v1
with:
go-version: ${{ inputs.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
- name: Go Build Cache
uses: actions/cache@v2
with:
path: ${{ needs.setup.outputs.go_build }}
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
- name: Go Mod Cache
uses: actions/cache@v2
with:
path: ${{ needs.setup.outputs.go_mod }}
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
- name: Download Support Binaries
uses: actions/download-artifact@v2
with:
name: support-binaries
path: ${{ needs.setup.outputs.bin_dir }}
- name: Prepare Build Step
id: setup-build-step
run: |
# Setup Path
echo "${{ needs.setup.outputs.bin_dir }}" >> $GITHUB_PATH
# Make Binaries Executable
chmod +x ${{ needs.setup.outputs.bin_dir }}/*
# Configure Platform Variables
echo "::set-output name=platform_os::$(echo ${{ matrix.platform }} | cut -d/ -f1)"
echo "::set-output name=platform_arch::$(echo ${{ matrix.platform }} | cut -d/ -f2)"
- name: Download Dependencies
shell: bash
run: |
make generate
make fmt
make vet
- name: build code
shell: bash
env:
VERSION: latest
GOOS: ${{ steps.setup-build-step.outputs.platform_os }}
GOARCH: ${{ steps.setup-build-step.outputs.platform_arch }}
run: make
test-operator:
runs-on: ubuntu-latest
name: test-operator
needs: ["setup"]
env:
REPOSITORY_NAME: ${{ needs.setup.outputs.repository_name }}
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v1
with:
go-version: ${{ inputs.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
- name: Go Build Cache
uses: actions/cache@v2
with:
path: ${{ needs.setup.outputs.go_build }}
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
- name: Go Mod Cache
uses: actions/cache@v2
with:
path: ${{ needs.setup.outputs.go_mod }}
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
- name: Download Binaries
uses: actions/download-artifact@v2
with:
name: support-binaries
path: ${{ needs.setup.outputs.bin_dir }}
- name: Prepare Build Step
id: setup-build-step
run: |
# Setup Path
echo "${{ needs.setup.outputs.bin_dir }}" >> $GITHUB_PATH
# Make Binaries Executable
chmod +x ${{ needs.setup.outputs.bin_dir }}/*
- name: Run unit tests
shell: bash
run: make test
================================================
FILE: .github/workflows/push.yaml
================================================
name: push
on:
push:
branches:
- main
- master
tags:
- v*
jobs:
setup:
runs-on: ubuntu-latest
name: setup
env:
BUILD_PLATFORMS: "linux/amd64,linux/arm64,linux/ppc64le,linux/s390x"
GO_VERSION: "~1.21"
steps:
- name: Setting Workflow Variables
id: set-variables
run: |
echo "::set-output name=repository_name::$(basename $GITHUB_REPOSITORY)"
echo "::set-output name=bin_dir::$(pwd)/bin"
# Create Distribution Matrix
echo "::set-output name=dist_matrix::$(echo -n "${{ env.BUILD_PLATFORMS }}" | jq -csR '. | split(",")')"
# Set versions based on presence of tag
if [[ "${{ github.ref }}" =~ ^refs/tags/ ]]; then
TAG="${GITHUB_REF/refs\/tags\//}"
echo "::set-output name=tag_event::true"
echo "::set-output name=operator_version::$TAG"
else
echo "::set-output name=tag_event::false"
echo "::set-output name=operator_version::$DEFAULT_OPERATOR_VERSION"
fi
- name: Build Go Cache Paths
id: go-cache-paths
run: |
echo "::set-output name=go-build::$(go env GOCACHE)"
echo "::set-output name=go-mod::$(go env GOMODCACHE)"
- name: Set up Go 1.x
uses: actions/setup-go@v1
with:
go-version: ${{ inputs.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
- name: Go Build Cache
uses: actions/cache@v2
with:
path: ${{ steps.go-cache-paths.outputs.go-build }}
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
- name: Go Mod Cache
uses: actions/cache@v2
with:
path: ${{ steps.go-cache-paths.outputs.go-mod }}
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
- name: Go Dependencies
run: go mod download
- name: Download Binaries
env:
OPERATOR_SDK_VERSION: ${{ inputs.OPERATOR_SDK_VERSION }}
run: |
# Create Binary Directory
mkdir -p ${{ steps.set-variables.outputs.bin_dir }}
# Operator SDK
curl -L -o ${{ steps.set-variables.outputs.bin_dir }}/operator-sdk https://github.com/operator-framework/operator-sdk/releases/download/${{ env.OPERATOR_SDK_VERSION }}/operator-sdk_linux_amd64
# Controller-gen
make controller-gen
# Kustomize
make kustomize
- name: Upload Support Binaries
uses: actions/upload-artifact@v2
with:
name: support-binaries
path: ${{ steps.set-variables.outputs.bin_dir }}
outputs:
repository_name: ${{ steps.set-variables.outputs.repository_name }}
bin_dir: ${{ steps.set-variables.outputs.bin_dir }}
go_build: ${{ steps.go-cache-paths.outputs.go-build }}
go_mod: ${{ steps.go-cache-paths.outputs.go-mod }}
tag_event: ${{ steps.set-variables.outputs.tag_event }}
dist_matrix: ${{ steps.set-variables.outputs.dist_matrix }}
build-operator:
runs-on: ubuntu-latest
name: build-operator
needs: ["setup"]
strategy:
matrix:
platform: ${{ fromJson(needs.setup.outputs.dist_matrix) }}
env:
REPOSITORY_NAME: ${{ needs.setup.outputs.repository_name }}
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v1
with:
go-version: ${{ inputs.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
- name: Go Build Cache
uses: actions/cache@v2
with:
path: ${{ needs.setup.outputs.go_build }}
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
- name: Go Mod Cache
uses: actions/cache@v2
with:
path: ${{ needs.setup.outputs.go_mod }}
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
- name: Download Support Binaries
uses: actions/download-artifact@v2
with:
name: support-binaries
path: ${{ needs.setup.outputs.bin_dir }}
- name: Prepare Build Step
id: setup-build-step
run: |
# Setup Path
echo "${{ needs.setup.outputs.bin_dir }}" >> $GITHUB_PATH
# Make Binaries Executable
chmod +x ${{ needs.setup.outputs.bin_dir }}/*
# Configure Platform Variables
echo "::set-output name=platform_os::$(echo ${{ matrix.platform }} | cut -d/ -f1)"
echo "::set-output name=platform_arch::$(echo ${{ matrix.platform }} | cut -d/ -f2)"
- name: Download Dependencies
shell: bash
run: |
make generate
make fmt
make vet
- name: build code
shell: bash
env:
VERSION: latest
GOOS: ${{ steps.setup-build-step.outputs.platform_os }}
GOARCH: ${{ steps.setup-build-step.outputs.platform_arch }}
run: make
test-operator:
runs-on: ubuntu-latest
name: test-operator
needs: ["setup"]
env:
REPOSITORY_NAME: ${{ needs.setup.outputs.repository_name }}
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v1
with:
go-version: ${{ inputs.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
- name: Go Build Cache
uses: actions/cache@v2
with:
path: ${{ needs.setup.outputs.go_build }}
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
- name: Go Mod Cache
uses: actions/cache@v2
with:
path: ${{ needs.setup.outputs.go_mod }}
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
- name: Download Binaries
uses: actions/download-artifact@v2
with:
name: support-binaries
path: ${{ needs.setup.outputs.bin_dir }}
- name: Prepare Build Step
id: setup-build-step
run: |
# Setup Path
echo "${{ needs.setup.outputs.bin_dir }}" >> $GITHUB_PATH
# Make Binaries Executable
chmod +x ${{ needs.setup.outputs.bin_dir }}/*
- name: Run unit tests
shell: bash
run: make test
github-release:
runs-on: ubuntu-latest
name: github-release
if: ${{ needs.setup.outputs.tag_event == 'true' }}
needs:
[
"setup",
"test-operator",
"build-operator",
]
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Create Release
uses: softprops/action-gh-release@v1
with:
generate_release_notes: true
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .gitignore
================================================
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
bin
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Kubernetes Generated files - skip generated files, except for vendored files
!vendor/**/zz_generated.*
# editor and IDE paraphernalia
.idea
*.swp
*.swo
*~
bundle/
bundle.Dockerfile
================================================
FILE: Dockerfile
================================================
# Build the manager binary
FROM golang:1.18 as builder
WORKDIR /workspace
# Copy the Go Modules manifests
COPY go.mod go.mod
COPY go.sum go.sum
# cache deps before building and copying source so that we don't need to re-download as much
# and so that source changes don't invalidate our downloaded layer
RUN go mod download
# Copy the go source
COPY main.go main.go
COPY api/ api/
COPY controllers/ controllers/
COPY pkg/ pkg/
# Build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go
# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /
COPY --from=builder /workspace/manager .
USER 65532:65532
ENTRYPOINT ["/manager"]
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: Makefile
================================================
# VERSION defines the project version for the bundle.
# Update this value when you upgrade the version of your project.
# To re-generate a bundle for another specific version without changing the standard setup, you can:
# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2)
# - use environment variables to overwrite this value (e.g export VERSION=0.0.2)
VERSION ?= 0.0.1
# CHANNELS define the bundle channels used in the bundle.
# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable")
# To re-generate a bundle for other specific channels without changing the standard setup, you can:
# - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable)
# - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable")
ifneq ($(origin CHANNELS), undefined)
BUNDLE_CHANNELS := --channels=$(CHANNELS)
endif
# DEFAULT_CHANNEL defines the default channel used in the bundle.
# Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable")
# To re-generate a bundle for any other default channel without changing the default setup, you can:
# - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable)
# - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable")
ifneq ($(origin DEFAULT_CHANNEL), undefined)
BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL)
endif
BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)
# IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images.
# This variable is used to construct full image tags for bundle and catalog images.
#
# For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both
# example.com/memcached-operator-bundle:$VERSION and example.com/memcached-operator-catalog:$VERSION.
IMAGE_TAG_BASE ?= example.com/memcached-operator
# BUNDLE_IMG defines the image:tag used for the bundle.
# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=<some-registry>/<project-name-bundle>:<tag>)
BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command
BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests
# You can enable this value if you would like to use SHA Based Digests
# To enable set flag to true
USE_IMAGE_DIGESTS ?= false
ifeq ($(USE_IMAGE_DIGESTS), true)
BUNDLE_GEN_FLAGS += --use-image-digests
endif
# Image URL to use all building/pushing image targets
IMG ?= controller:latest
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.24.1
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
GOBIN=$(shell go env GOPATH)/bin
else
GOBIN=$(shell go env GOBIN)
endif
# Setting SHELL to bash allows bash commands to be executed by recipes.
# This is a requirement for 'setup-envtest.sh' in the test target.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec
CHART_REPO_URL ?= http://example.com
HELM_REPO_DEST ?= /tmp/gh-pages
.PHONY: all
all: build
##@ General
# The help target prints out all targets with their descriptions organized
# beneath their categories. The categories are represented by '##@' and the
# target descriptions by '##'. The awk commands is responsible for reading the
# entire set of makefiles included in this invocation, looking for lines of the
# file as xyz: ## something, and then pretty-format the target and help. Then,
# if there's a line with ##@ something, that gets pretty-printed as a category.
# More info on the usage of ANSI control characters for terminal formatting:
# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
# More info on the awk command:
# http://linuxcommand.org/lc3_adv_awk.php
.PHONY: help
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
##@ Development
.PHONY: manifests
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
.PHONY: generate
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
.PHONY: fmt
fmt: ## Run go fmt against code.
go fmt ./...
.PHONY: vet
vet: ## Run go vet against code.
go vet ./...
.PHONY: test
test: manifests generate fmt vet envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out
##@ Build
.PHONY: build
build: generate fmt vet ## Build manager binary.
go build -o bin/manager main.go
.PHONY: run
run: manifests generate fmt vet ## Run a controller from your host.
go run ./main.go
.PHONY: docker-build
docker-build: test ## Build docker image with the manager.
docker build -t ${IMG} .
.PHONY: docker-push
docker-push: ## Push docker image with the manager.
docker push ${IMG}
##@ Deployment
ifndef ignore-not-found
ignore-not-found = false
endif
.PHONY: install
install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
$(KUSTOMIZE) build config/crd | kubectl apply -f -
.PHONY: uninstall
uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
$(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f -
.PHONY: deploy
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
$(KUSTOMIZE) build config/default | kubectl apply -f -
.PHONY: undeploy
undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
$(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f -
##@ Build Dependencies
## Location to install dependencies to
LOCALBIN ?= $(shell pwd)/bin
$(LOCALBIN):
mkdir -p $(LOCALBIN)
## Tool Binaries
KUSTOMIZE ?= $(LOCALBIN)/kustomize
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
ENVTEST ?= $(LOCALBIN)/setup-envtest
## Tool Versions
KUSTOMIZE_VERSION ?= v3.8.7
CONTROLLER_TOOLS_VERSION ?= v0.9.0
KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"
.PHONY: kustomize
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
$(KUSTOMIZE): $(LOCALBIN)
curl -s $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN)
.PHONY: controller-gen
controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
$(CONTROLLER_GEN): $(LOCALBIN)
GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION)
.PHONY: envtest
envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.
$(ENVTEST): $(LOCALBIN)
GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
.PHONY: bundle
bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files.
operator-sdk generate kustomize manifests --interactive=false -q
cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle $(BUNDLE_GEN_FLAGS)
operator-sdk bundle validate ./bundle
.PHONY: bundle-build
bundle-build: ## Build the bundle image.
docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .
.PHONY: bundle-push
bundle-push: ## Push the bundle image.
$(MAKE) docker-push IMG=$(BUNDLE_IMG)
.PHONY: opm
OPM = ./bin/opm
opm: ## Download opm locally if necessary.
ifeq (,$(wildcard $(OPM)))
ifeq (,$(shell which opm 2>/dev/null))
@{ \
set -e ;\
mkdir -p $(dir $(OPM)) ;\
OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \
curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$${OS}-$${ARCH}-opm ;\
chmod +x $(OPM) ;\
}
else
OPM = $(shell which opm)
endif
endif
# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0).
# These images MUST exist in a registry and be pull-able.
BUNDLE_IMGS ?= $(BUNDLE_IMG)
# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0).
CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION)
# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image.
ifneq ($(origin CATALOG_BASE_IMG), undefined)
FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG)
endif
# Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'.
# This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see:
# https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator
.PHONY: catalog-build
catalog-build: opm ## Build a catalog image.
$(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT)
# Push the catalog image.
.PHONY: catalog-push
catalog-push: ## Push a catalog image.
$(MAKE) docker-push IMG=$(CATALOG_IMG)
# Generate helm chart
.PHONY: kustomize
helmchart: kustomize
mkdir -p ./charts/${OPERATOR_NAME}/templates
cp ./config/helmchart/templates/* ./charts/${OPERATOR_NAME}/templates
$(KUSTOMIZE) build ./config/helmchart | sed 's/release-namespace/{{.Release.Namespace}}/' > ./charts/${OPERATOR_NAME}/templates/rbac.yaml
version=${VERSION} envsubst < ./config/helmchart/Chart.yaml.tpl > ./charts/${OPERATOR_NAME}/Chart.yaml
version=${VERSION} image_repo=$${IMG%:*} envsubst < ./config/helmchart/values.yaml.tpl > ./charts/${OPERATOR_NAME}/values.yaml
helm lint ./charts/${OPERATOR_NAME}
.PHONY: helmchart
helmchart-repo: helmchart
mkdir -p ${HELM_REPO_DEST}/${OPERATOR_NAME}
helm package -d ${HELM_REPO_DEST}/${OPERATOR_NAME} ./charts/${OPERATOR_NAME}
helm repo index --url ${CHART_REPO_URL} ${HELM_REPO_DEST}
.PHONY: helmchart-repo
helmchart-repo-push: helmchart-repo
git -C ${HELM_REPO_DEST} add .
git -C ${HELM_REPO_DEST} status
git -C ${HELM_REPO_DEST} commit -m "Release ${VERSION}"
git -C ${HELM_REPO_DEST} push origin "gh-pages"
================================================
FILE: PROJECT
================================================
domain: example.io
layout:
- go.kubebuilder.io/v3
projectName: operator-utils
repo: github.com/redhat-cop/operator-utils
resources:
- domain: example.io
group: operator-utils
kind: MyCRD
path: github.com/redhat-cop/operator-utils/api/v1alpha1
version: v1alpha1
- domain: example.io
group: operator-utils
kind: EnforcingCRD
path: github.com/redhat-cop/operator-utils/api/v1alpha1
version: v1alpha1
- domain: example.io
group: operator-utils
kind: EnforcingPatch
path: github.com/redhat-cop/operator-utils/api/v1alpha1
version: v1alpha1
- domain: example.io
group: operator-utils
kind: EnforcingPatch
path: github.com/redhat-cop/operator-utils/api/v1alpha1
version: v1alpha1
version: "3"
plugins:
manifests.sdk.operatorframework.io/v2: {}
scorecard.sdk.operatorframework.io/v2: {}
================================================
FILE: README.md
================================================
# Operator Utility Library

[](https://pkg.go.dev/github.com/redhat-cop/operator-utils)
[](https://goreportcard.com/report/github.com/redhat-cop/operator-utils)

This library layers on top of the Operator SDK and with the objective of helping writing better and more consistent operators.
*NOTICE* versions of this library up to `v0.3.7` are compatible with [operator-sdk](https://github.com/operator-framework/operator-sdk) `0.x`, starting from version v0.4.0 this library will be compatible only with [operator-sdk](https://github.com/operator-framework/operator-sdk) 1.x.
## Scope of this library
This library covers three main areas:
1. [Utility Methods](#Utility-Methods) Utility methods that are callable by any operator.
2. [Idempotent methods](#Idempotent-Methods-to-Manipulate-Resources) to manipulate resources and arrays of resources
3. [Basic operator lifecycle](#Basic-Operator-Lifecycle-Management) needs (validation, initialization, status and error management, finalization)
4. [Enforcing resources operator support](#Enforcing-Resource-Operator-Support). For those operators which calculate a set of resources that need to exist and then enforce them, generalized support for the enforcing phase is provided.
## Utility Methods
Prior to version v1.3.x the general philosophy of this library was that new operator would inherit from `ReconcilerBase` and in doing so they would have access to a bunch of utility methods.
With release v1.3.0 a new approach is available. Utility methods are callable by any operator without having to inherit. This makes it easier to use this library and does not conflict with autogenerate code from `kube-builder` and `operator-sdk`.
Most of the Utility methods receive a context.Context parameter. Normally this context must be initialized with a `logr.Logger` and a `rest.Config`. Some utility methods may require more, see each individual documentation.
Utility methods are currently organized in the following folders:
1. crud: idempotent create/update/delete functions.
2. discoveryclient: methods related to the discovery client, typically used to load `apiResource` objects.
3. dynamicclient: methods related to building client based on object whose type is not known at compile time.
4. templates: utility methods for dealing with templates whose output is an object or a list of objects.
## Idempotent Methods to Manipulate Resources
The following idempotent methods are provided (and their corresponding array version):
1. createIfNotExists
2. createOrUpdate
3. deleteIfExists
Also there are utility methods to manage finalizers, test ownership and process templates of resources.
## Basic Operator Lifecycle Management
---
Note
This part of the library is largely deprecated. For initialization and defaulting a MutatingWebHook should be used. For validation a Validating WebHook should be used.
The part regarding the finalization is still relevant.
---
To get started with this library do the following:
Change your reconciler initialization as exemplified below to add a set of utility methods to it
```go
import "github.com/redhat-cop/operator-utils/pkg/util"
...
type MyReconciler struct {
util.ReconcilerBase
Log logr.Logger
... other optional fields ...
}
```
in main.go change like this
```go
if err = (&controllers.MyReconciler{
ReconcilerBase: util.NewReconcilerBase(mgr.GetClient(), mgr.GetScheme(), mgr.GetConfig(), mgr.GetEventRecorderFor("My_controller"), mgr.GetAPIReader()),
Log: ctrl.Log.WithName("controllers").WithName("My"),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "My")
os.Exit(1)
}
```
Also make sure to create the manager with `configmap` as the lease option for leader election:
```go
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Port: 9443,
LeaderElection: enableLeaderElection,
LeaderElectionID: "dcb036b8.redhat.io",
LeaderElectionResourceLock: "configmaps",
})
```
If you want status management, add this to your CRD:
```go
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
}
func (m *MyCRD) GetConditions() []metav1.Condition {
return m.Status.Conditions
}
func (m *MyCRD) SetConditions(conditions []metav1.Condition) {
m.Status.Conditions = conditions
}
```
At this point your controller is able to leverage the utility methods of this library:
1. [managing CR validation](#managing-cr-validation)
2. [managing CR initialization](#managing-cr-initialization)
3. [managing status and error conditions](#managing-status-and-error-conditions)
4. [managing CR finalization](#managing-cr-finalization)
5. high-level object manipulation functions such as:
- createOrUpdate, createIfNotExists, deleteIfExists
- same functions on an array of objects
- go template processing of objects
A full example is provided [here](./controllers/mycrd_controller.go)
### Managing CR validation
To enable CR validation add this to your controller:
```go
if ok, err := r.IsValid(instance); !ok {
return r.ManageError(ctx, instance, err)
}
```
The implement the following function:
```go
func (r *ReconcileMyCRD) IsValid(obj metav1.Object) (bool, error) {
mycrd, ok := obj.(*examplev1alpha1.MyCRD)
...
}
```
### Managing CR Initialization
To enable CR initialization, add this to your controller:
```go
if ok := r.IsInitialized(instance); !ok {
err := r.GetClient().Update(context.TODO(), instance)
if err != nil {
log.Error(err, "unable to update instance", "instance", instance)
return r.ManageError(ctx, instance, err)
}
return reconcile.Result{}, nil
}
```
Then implement the following function:
```go
func (r *ReconcileMyCRD) IsInitialized(obj metav1.Object) bool {
mycrd, ok := obj.(*examplev1alpha1.MyCRD)
}
```
### Managing Status and Error Conditions
To update the status with success and return from the reconciliation cycle, code the following:
```go
return r.ManageSuccess(ctx, instance)
```
To update the status with failure, record an event and return from the reconciliation cycle, code the following:
```go
return r.ManageError(ctx, instance, err)
```
notice that this function will reschedule a reconciliation cycle with increasingly longer wait time up to six hours.
There are also variants of these calls to allow for requeuing after a given delay.
Requeuing is handy when reconciliation depends on a cluster-external state which is not observable from within the api-server.
```go
return r.ManageErrorWithRequeue(ctx, instance, err, 3*time.Second)
```
```go
return r.ManageSuccessWithRequeue(ctx, instance, 3*time.Second)
```
or simply using the convenience function:
```go
return r.ManageOutcomeWithRequeue(ctx, instance, err, 3*time.Second)
```
which will delegate to the error or success variant depending on `err` being `nil` or not.
### Managing CR Finalization
to enable CR finalization add this to your controller:
```go
if util.IsBeingDeleted(instance) {
if !util.HasFinalizer(instance, controllerName) {
return reconcile.Result{}, nil
}
err := r.manageCleanUpLogic(instance)
if err != nil {
log.Error(err, "unable to delete instance", "instance", instance)
return r.ManageError(ctx, instance, err)
}
util.RemoveFinalizer(instance, controllerName)
err = r.GetClient().Update(context.TODO(), instance)
if err != nil {
log.Error(err, "unable to update instance", "instance", instance)
return r.ManageError(ctx, instance, err)
}
return reconcile.Result{}, nil
}
```
Then implement this method:
```go
func (r *ReconcileMyCRD) manageCleanUpLogic(mycrd *examplev1alpha1.MyCRD) error {
...
}
```
## Support for operators that need to enforce a set of resources to a defined state
Many operators have the following logic:
1. Phase 1: based on the CR and potentially additional status, a set of resources that need to exist is calculated.
2. Phase 2: These resources are then created or updated against the master API.
3. Phase 3: A well written operator also ensures that these resources stay in place and are not accidentally or maliciously changed by third parties.
These phases are of increasing difficulty to implement. It's also true that phase 2 and 3 can be generalized.
Operator-utils offers some scaffolding to assist in writing these kinds of operators.
Similarly to the `BaseReconciler` class, we have a base type to extend called: `EnforcingReconciler`. This class extends from `BaseReconciler`, so you have all the same facilities as above.
When initializing the EnforcingReconciler, one must chose whether watchers will be created at the cluster level or at the namespace level.
- if cluster level is chosen a watch per CR and type defined in it will be created. This will require the operator to have cluster level access.
- if namespace level watchers is chosen a watch per CR, type and namespace will be created. This will minimize the needed permissions, but depending on what the operator needs to do may open a very high number of connections to the API server.
The body of the reconciler function will look something like this:
```golang
validation...
initialization...
(optional) finalization...
Phase1 ... calculate a set of resources to be enforced -> LockedResources
err = r.UpdateLockedResources(context,instance, lockedResources, ...)
if err != nil {
log.Error(err, "unable to update locked resources")
return r.ManageError(ctx, instance, err)
}
return r.ManageSuccess(ctx, instance)
```
this is all you have to do for basic functionality. For more details see the [example](pkg/controller/apis/enforcingcrd/enforcingcrd_controller.go)
the EnforcingReconciler will do the following:
1. restore the resources to the desired stated if the are changed. Notice that you can exclude paths from being considered when deciding whether to restore a resource. As set of JSON Path can be passed together with the LockedResource. It is recommended to set these paths:
1. `.metadata`
2. `.status`
2. restore resources when they are deleted.
The `UpdateLockedResources` will validate the input as follows:
1. the passed resource must be defined in the current apiserver
2. the passed resource must be syntactically compliant with the OpenAPI definition of the resource defined in the server.
3. if the passed resource is namespaced, the namespace field must be initialized.
The finalization method will look like this:
```golang
func (r *ReconcileEnforcingCRD) manageCleanUpLogic(instance *examplev1alpha1.EnforcingCRD) error {
err := r.Terminate(instance, true)
if err != nil {
log.Error(err, "unable to terminate enforcing reconciler for", "instance", instance)
return err
}
... additional finalization logic ...
return nil
}
```
Convenience methods are also available for when resources are templated. See the [templatedenforcingcrd](./pkgcontroller/templatedenforcingcrd/templatedenforcingcrd_controller.go) controller as an example.
## Support for operators that need to enforce a set of patches
For similar reasons stated in the previous paragraphs, operators might need to enforce patches.
A patch modifies an object created by another entity. Because in this case the CR does not own the to-be-modified object a patch must be enforced against changes made on it.
One must be careful not to create circular situations where an operator deletes the patch and this operator recreates the patch.
In some situations, a patch must be parametric on some state of the cluster. For this reason, it's possible to monitor source objects that will be used as parameters to calculate the patch.
A patch is defined as follows:
```golang
type LockedPatch struct {
Name string `json:"name,omitempty"`
SourceObjectRefs []utilsapi.SourceObjectReference `json:"sourceObjectRefs,omitempty"`
TargetObjectRef utilsapi.TargetObjectReference `json:"targetObjectRef,omitempty"`
PatchType types.PatchType `json:"patchType,omitempty"`
PatchTemplate string `json:"patchTemplate,omitempty"`
Template template.Template `json:"-"`
}
```
the targetObjectRef and sourceObjectRefs are watched for changes by the reconciler.
targetObjectRef can select multiple objects, this is the logic
| Namespaced Type | Namespace | Name | Selection type |
| --- | --- | --- | --- |
| yes | null | null | multiple selection across namespaces |
| yes | null | not null | multiple selection across namespaces where the name corresponds to the passed name |
| yes | not null | null | multiple selection within a namespace |
| yes | not null | not nul | single selection |
| no | N/A | null | multiple selection |
| no | N/A | not null | single selection |
Selection can be further narrowed down by filtering by labels and/or annotations. The patch will be applied to all of the selected instances.
Name and Namespace of sourceRefObjects are interpreted as golang templates with the current target instance and the only parameter. This allows to select different source object for each target object.
The relevant part of the operator code would look like this:
```golang
validation...
initialization...
Phase1 ... calculate a set of patches to be enforced -> LockedPatches
err = r.UpdateLockedResources(context, instance, ..., lockedPatches...)
if err != nil {
log.Error(err, "unable to update locked resources")
return r.ManageError(ctx, instance, err)
}
return r.ManageSuccess(ctx, instance)
```
The `UpdateLockedResources` will validate the input as follows:
1. the passed patch target/source `ObjectRef` resource must be defined in the current apiserver
2. if the passed patch target/source `ObjectRef` resources are namespaced the corresponding namespace field must be initialized.
3. the ID must have a not null and unique value in the array of the passed patches.
Patches cannot be undone so there is no need to manage a finalizer.
[Here](./pkg/controller/enforcingpatch/enforcingpatch_controller.go) you can find an example of how to implement an operator with this the ability to enforce patches.
## Support for operators that need dynamic creation of locked resources using templates
Operators may also need to leverage locked resources created dynamically through templates. This can be done using [go templates](https://golang.org/pkg/text/template/) and leveraging the `GetLockedResourcesFromTemplates` function.
```golang
lockedResources, err := r.GetLockedResourcesFromTemplates(templates..., params...)
if err != nil {
log.Error(err, "unable to process templates with param")
return err
}
```
The `GetLockedResourcesFromTemplates` will validate the input as follows:
1. check that the passed template is valid
2. format the template using the properties of the passed object in the params parameter
3. create an array of `LockedResource` objects based on parsed template
The example below shows how templating can be used to reference the name of the resource passed as the parameter and use it as a property in the creation of the `LockedResource`.
```golang
objectTemplate: |
apiVersion: v1
kind: Namespace
metadata:
name: {{ .Name }}
```
This functionality can leverage advanced features of go templating, such as loops, to generate more than one object following a set pattern. The below example will create an array of namespace `LockedResources` using the title of any key where the associated value matches the text *devteam* in the key/value pair of the `Labels` property of the resource passed in the params parameter.
```golang
objectTemplate: |
{{range $key, $value := $.Labels}}
{{if eq $value "devteam"}}
- apiVersion: v1
kind: Namespace
metadata:
name: {{ $key }}
{{end}}
{{end}}
```
## Support for operators that need advanced templating functionality
Operators may need to utilize advanced templating functions not found in the base go templating library. This advanced template functionality matches the same available in the popular k8s management tool [Helm](https://helm.sh/). `LockedPatch` templates uses this functionality by default. To utilize these features when using `LockedResources` the following function is required,
```golang
lockedResources, err := r.GetLockedResourcesFromTemplatesWithRestConfig(templates..., rest.Config..., params...)
if err != nil {
log.Error(err, "unable to process templates with param")
return err
}
```
## Deployment
### Deploying with Helm
Here are the instructions to install the latest release with Helm.
```shell
oc new-project operator-utils
helm repo add operator-utils https://redhat-cop.github.io/operator-utils
helm repo update
helm install operator-utils operator-utils/operator-utils
```
This can later be updated with the following commands:
```shell
helm repo update
helm upgrade operator-utils operator-utils/operator-utils
```
## Development
## Running the operator locally
```shell
make install
oc new-project operator-utils-operator-local
kustomize build ./config/local-development | oc apply -f - -n operator-utils-operator-local
export token=$(oc serviceaccounts get-token 'operator-utils-operator-controller-manager' -n operator-utils-operator-local)
oc login --token ${token}
make run ENABLE_WEBHOOKS=false
```
### testing
Patches
```shell
oc new-project patch-test
oc create sa test -n patch-test
oc adm policy add-cluster-role-to-user cluster-admin -z default -n patch-test
oc apply -f ./test/enforcing-patch.yaml -n patch-test
oc apply -f ./test/enforcing-patch-multiple.yaml -n patch-test
oc apply -f ./test/enforcing-patch-multiple-cluster-level.yaml -n patch-test
```
## Building/Pushing the operator image
```shell
export repo=raffaelespazzoli #replace with yours
docker login quay.io/$repo
make docker-build IMG=quay.io/$repo/operator-utils:latest
make docker-push IMG=quay.io/$repo/operator-utils:latest
```
## Deploy to OLM via bundle
```shell
make manifests
make bundle IMG=quay.io/$repo/operator-utils:latest
operator-sdk bundle validate ./bundle --select-optional name=operatorhub
make bundle-build BUNDLE_IMG=quay.io/$repo/operator-utils-bundle:latest
docker push quay.io/$repo/operator-utils-bundle:latest
operator-sdk bundle validate quay.io/$repo/operator-utils-bundle:latest --select-optional name=operatorhub
oc new-project operator-utils
oc label namespace operator-utils openshift.io/cluster-monitoring="true"
operator-sdk cleanup operator-utils -n operator-utils
operator-sdk run bundle --install-mode AllNamespaces -n operator-utils quay.io/$repo/operator-utils-bundle:latest
```
## Releasing
```shell
git tag -a "<tagname>" -m "<commit message>"
git push upstream <tagname>
```
If you need to remove a release:
```shell
git tag -d <tagname>
git push upstream --delete <tagname>
```
If you need to "move" a release to the current main
```shell
git tag -f <tagname>
git push upstream -f <tagname>
```
### Cleaning up
```shell
operator-sdk cleanup operator-utils -n operator-utils
oc delete operatorgroup operator-sdk-og
oc delete catalogsource operator-utils-catalog
```
================================================
FILE: api/v1alpha1/enforcingcrd_types.go
================================================
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// EnforcingCRDSpec defines the desired state of EnforcingCRD
type EnforcingCRDSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
// Resources is a list of resource manifests that should be locked into the specified configuration
// +kubebuilder:validation:Optional
// +listType=atomic
Resources []LockedResource `json:"resources,omitempty"`
}
// EnforcingCRDStatus defines the observed state of EnforcingCRD
type EnforcingCRDStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
// +kubebuilder:validation:Optional
EnforcingReconcileStatus `json:",inline,omitempty"`
}
func (m *EnforcingCRD) GetEnforcingReconcileStatus() EnforcingReconcileStatus {
return m.Status.EnforcingReconcileStatus
}
func (m *EnforcingCRD) SetEnforcingReconcileStatus(reconcileStatus EnforcingReconcileStatus) {
m.Status.EnforcingReconcileStatus = reconcileStatus
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// EnforcingCRD is the Schema for the enforcingcrds API
type EnforcingCRD struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec EnforcingCRDSpec `json:"spec,omitempty"`
Status EnforcingCRDStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// EnforcingCRDList contains a list of EnforcingCRD
type EnforcingCRDList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []EnforcingCRD `json:"items"`
}
func init() {
SchemeBuilder.Register(&EnforcingCRD{}, &EnforcingCRDList{})
}
================================================
FILE: api/v1alpha1/enforcingpatch_types.go
================================================
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// EnforcingPatchSpec defines the desired state of EnforcingPatch
type EnforcingPatchSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
// Patches is a list of pacthes that should be encforced at runtime.
// +kubebuilder:validation:Optional
Patches map[string]PatchSpec `json:"patches,omitempty"`
}
// EnforcingPatchStatus defines the observed state of EnforcingPatch
type EnforcingPatchStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
EnforcingReconcileStatus `json:",inline,omitempty"`
}
func (m *EnforcingPatch) GetEnforcingReconcileStatus() EnforcingReconcileStatus {
return m.Status.EnforcingReconcileStatus
}
func (m *EnforcingPatch) SetEnforcingReconcileStatus(reconcileStatus EnforcingReconcileStatus) {
m.Status.EnforcingReconcileStatus = reconcileStatus
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// EnforcingPatch is the Schema for the enforcingpatches API
type EnforcingPatch struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec EnforcingPatchSpec `json:"spec,omitempty"`
Status EnforcingPatchStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// EnforcingPatchList contains a list of EnforcingPatch
type EnforcingPatchList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []EnforcingPatch `json:"items"`
}
func init() {
SchemeBuilder.Register(&EnforcingPatch{}, &EnforcingPatchList{})
}
================================================
FILE: api/v1alpha1/enforcingreconcilerstatus.go
================================================
package v1alpha1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// +listType=map
// +listMapKey=type
type Conditions []metav1.Condition
// +mapType=granular
type ConditionMap map[string]Conditions
// EnforcingReconcileStatus represents the status of the last reconcile cycle. It's used to communicate success or failure and the error message
type EnforcingReconcileStatus struct {
// ReconcileStatus this is the general status of the main reconciler
// +kubebuilder:validation:Optional
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
//LockedResourceStatuses contains the reconcile status for each of the managed resources
// +kubebuilder:validation:Optional
LockedResourceStatuses map[string]Conditions `json:"lockedResourceStatuses,omitempty"`
//LockedResourceStatuses contains the reconcile status for each of the managed resources
// +kubebuilder:validation:Optional
LockedPatchStatuses map[string]ConditionMap `json:"lockedPatchStatuses,omitempty"`
}
// EnforcingReconcileStatusAware is an interfce that must be implemented by a CRD type that has been enabled with ReconcileStatus, it can then benefit of a series of utility methods.
// +kubebuilder:object:generate:=false
type EnforcingReconcileStatusAware interface {
GetEnforcingReconcileStatus() EnforcingReconcileStatus
SetEnforcingReconcileStatus(enforcingReconcileStatus EnforcingReconcileStatus)
}
================================================
FILE: api/v1alpha1/groupversion_info.go
================================================
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package v1alpha1 contains API Schema definitions for the operator-utils v1alpha1 API group
// +kubebuilder:object:generate=true
// +groupName=operator-utils.example.io
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "operator-utils.example.io", Version: "v1alpha1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
================================================
FILE: api/v1alpha1/lockedpatch.go
================================================
package v1alpha1
import (
"bytes"
"context"
"errors"
"text/template"
"github.com/redhat-cop/operator-utils/pkg/util/discoveryclient"
"github.com/redhat-cop/operator-utils/pkg/util/dynamicclient"
utiltemplates "github.com/redhat-cop/operator-utils/pkg/util/templates"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
)
// Patch describes a patch to be enforced at runtime
// +k8s:openapi-gen=true
type PatchSpec struct {
//Name represents a unique name for this patch, it has no particular effect, except for internal bookeeping
// SourceObjectRefs is an arrays of refereces to source objects that will be used as input for the template processing. These refernces must resolve to single instance. The resolution rule is as follows (+ present, - absent):
// the King and APIVersion field are mandatory
// +Namespace +Name: resolves to object <Namespace>/<Name>
// +Namespace -Name: results in an error
// -Namespace +Name: resolves to cluster-level object <Name>. If Kind is namespaced, this results in an error.
// -Namespace -Name: results in an error
// Name manespaces Namespace are evaluated as golang templates with the input of the template being the target object. When selecting multiple target, this allows for having specific source objects for each target.
// ResourceVersion and UID are always ignored
// If FieldPath is specified, the restuned object is calculated from the path, so for example if FieldPath=.spec, the only the spec portion of the object is returned.
// The target object is always added as element zero of the array of the SourceObjectRefs
// +kubebuilder:validation:Optional
// +listType=atomic
SourceObjectRefs []SourceObjectReference `json:"sourceObjectRefs,omitempty"`
// TargetObjectRef is a reference to the object to which the pacth should be applied.
// the King and APIVersion field are mandatory
// the Name and Namespace field have the following meaning (+ present, - absent)
// +Namespace +Name: apply the patch to the object: <Namespace>/<Name>
// +Namespace -Name: apply the patch to all of the objects in <Namespace>
// -Namespace +Name: apply the patch to the cluster-level object <Name>. If Kind is namespaced, this results in an error.
// -Namespace -Name: if the kind is namespaced apply the patch to all of the objects in all of the namespaces. If the kind is not namespaced, apply the patch to all of the cluster level objects.
// The lable selector can be used to further filter the selected objects.
// +kubebuilder:validation:Required
TargetObjectRef TargetObjectReference `json:"targetObjectRef,omitempty"`
// PatchType is the type of patch to be applied, one of "application/json-patch+json"'"application/merge-patch+json","application/strategic-merge-patch+json","application/apply-patch+yaml"
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum="application/json-patch+json";"application/merge-patch+json";"application/strategic-merge-patch+json";"application/apply-patch+yaml"
// default:="application/strategic-merge-patch+json"
PatchType types.PatchType `json:"patchType,omitempty"`
// PatchTemplate is a go template that will be resolved using the SourceObjectRefs as parameters. The result must be a valid patch based on the pacth type and the target object.
// +kubebuilder:validation:Required
PatchTemplate string `json:"patchTemplate,omitempty"`
}
type TargetObjectReference struct {
// API version of the referent.
// +kubebuilder:validation:Required
APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,5,opt,name=apiVersion"`
// Kind of the referent.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// +kubebuilder:validation:Required
Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"`
// Namespace of the referent.
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
// +kubebuilder:validation:Optional
Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,opt,name=namespace"`
// Name of the referent.
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
// +kubebuilder:validation:Optional
Name string `json:"name,omitempty" protobuf:"bytes,3,opt,name=name"`
// LabelSelector selects objects by label
// +kubebuilder:validation:Optional
LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"`
// AnnotationSelector selects objects by label
AnnotationSelector *metav1.LabelSelector `json:"annotationSelector,omitempty"`
//apiResource caches apiResource for this targetReference
apiResource *metav1.APIResource `json:"-"`
}
func (t *TargetObjectReference) getAPIReourceForGVK(context context.Context) (*metav1.APIResource, bool, error) {
if t.apiResource != nil {
return t.apiResource, true, nil
}
apiresource, found, err := discoveryclient.GetAPIResourceForGVK(context, schema.FromAPIVersionAndKind(t.APIVersion, t.Kind))
if err != nil && found {
t.apiResource = apiresource
}
return apiresource, found, err
}
func (t *TargetObjectReference) getDynamicClient(context context.Context) (dynamic.ResourceInterface, error) {
log := log.FromContext(context)
_, namespacedSelection, err := t.IsSelectingMultipleInstances(context)
if err != nil {
log.Error(err, "unable to determine if the target reference is selecting multiple instance", "targetReference", t)
return nil, err
}
var ri dynamic.ResourceInterface
nri, namespaced, err := dynamicclient.GetDynamicClientForGVK(context, schema.FromAPIVersionAndKind(t.APIVersion, t.Kind))
if err != nil {
log.Error(err, "unable to get dynamicClient on ", "gvk", schema.FromAPIVersionAndKind(t.APIVersion, t.Kind))
return nil, err
}
if namespaced && namespacedSelection {
ri = nri.Namespace(t.Namespace)
} else {
ri = nri
}
return ri, nil
}
func (t *TargetObjectReference) GetReferencedObjectWithName(context context.Context, namespacedName types.NamespacedName) (*unstructured.Unstructured, error) {
log := log.FromContext(context)
multiple, _, err := t.IsSelectingMultipleInstances(context)
if err != nil {
log.Error(err, "unable to determine if the target reference is selecting multiple instance", "targetReference", t)
return nil, err
}
if !multiple {
return t.GetReferencedObject(context)
}
targetCopy := t.DeepCopy()
targetCopy.Name = namespacedName.Name
namespaced, err := t.IsNamespaced(context)
if err != nil {
log.Error(err, "unable to determine if the target reference is namespaced", "targetReference", t)
return nil, err
}
if namespaced {
targetCopy.Namespace = namespacedName.Namespace
}
client, err := targetCopy.getDynamicClient(context)
if err != nil {
log.Error(err, "unable to get dynamic client with", "targetReference", targetCopy)
return nil, err
}
obj, err := client.Get(context, targetCopy.Name, metav1.GetOptions{})
if err != nil {
log.Error(err, "unable to get referenced object", "targetReference", targetCopy)
return nil, err
}
return obj, nil
}
func (t *TargetObjectReference) GetReferencedObject(context context.Context) (*unstructured.Unstructured, error) {
log := log.FromContext(context)
multiple, _, err := t.IsSelectingMultipleInstances(context)
if err != nil {
log.Error(err, "unable to determine if the target reference is selecting multiple instance", "targetReference", t)
return nil, err
}
if multiple {
return nil, errors.New("cannot call this method on a target that selects multiple instances")
}
dclient, err := t.getDynamicClient(context)
if err != nil {
log.Error(err, "unable to get dynamic client on", "targetReference", t)
return nil, err
}
obj, err := dclient.Get(context, t.Name, metav1.GetOptions{})
if err != nil {
log.Error(err, "unable to get referenced ", "object", t)
return nil, err
}
return obj, nil
}
func (t *TargetObjectReference) GetReferencedObjects(context context.Context) ([]unstructured.Unstructured, error) {
log := log.FromContext(context)
multiple, _, err := t.IsSelectingMultipleInstances(context)
if err != nil {
log.Error(err, "unable to determine if the target reference is selecting multiple instance", "targetReference", t)
return nil, err
}
if !multiple {
return nil, errors.New("cannot call this method on a target that does not select multiple instances")
}
dclient, err := t.getDynamicClient(context)
if err != nil {
log.Error(err, "unable to get dynamic client on", "targetReference", t)
return nil, err
}
labelSelector, err := metav1.LabelSelectorAsSelector(t.LabelSelector)
if err != nil {
log.Error(err, "unable to process ", "labelSelector", t.LabelSelector)
return nil, err
}
objList, err := dclient.List(context, metav1.ListOptions{
LabelSelector: labelSelector.String(),
})
if err != nil {
log.Error(err, "unable to list referenced ", "objects", t)
return nil, err
}
var annotatonSelector labels.Selector
if t.AnnotationSelector != nil {
annotatonSelector, err = metav1.LabelSelectorAsSelector(t.AnnotationSelector)
if err != nil {
return nil, err
}
} else {
annotatonSelector = labels.Everything()
}
//filter by annotation
annotationFilteredList := []unstructured.Unstructured{}
for i := range objList.Items {
if annotatonSelector.Matches(labels.Set(objList.Items[i].GetAnnotations())) {
annotationFilteredList = append(annotationFilteredList, objList.Items[i])
}
}
//filter by name
if t.Name != "" {
filteredList := []unstructured.Unstructured{}
for i := range annotationFilteredList {
if t.Name == annotationFilteredList[i].GetName() {
filteredList = append(filteredList, annotationFilteredList[i])
}
}
return filteredList, nil
}
return objList.Items, nil
}
func (t *TargetObjectReference) IsNamespaced(context context.Context) (bool, error) {
apiresource, found, err := t.getAPIReourceForGVK(context)
if err != nil {
return false, err
}
if !found {
return false, errors.New("resource not found" + schema.FromAPIVersionAndKind(t.APIVersion, t.Kind).String())
}
return apiresource.Namespaced, nil
}
// IsSelectingMultipleInstances is a helper function to determine whether this targetObjectReference selects one or multiple instance.
func (t *TargetObjectReference) IsSelectingMultipleInstances(context context.Context) (multiple bool, namespacedSelection bool, err error) {
log := log.FromContext(context)
namespaced, err := t.IsNamespaced(context)
if err != nil {
log.Error(err, "Unable to determine if targetObjectReference is namespaced", "TargetObjectReference", t)
return false, false, err
}
if namespaced {
if t.Namespace == "" {
return true, false, nil
} else {
if t.Name == "" {
return true, true, nil
} else {
return false, true, nil
}
}
} else {
return t.Name == "", false, nil
}
}
// Selects returns whether the passed object is selected by the current target reference
// requires context with log and restConfig
func (t *TargetObjectReference) Selects(context context.Context, obj client.Object) (bool, error) {
log := log.FromContext(context)
if apiversion, kind := obj.GetObjectKind().GroupVersionKind().ToAPIVersionAndKind(); t.Kind != kind || t.APIVersion != apiversion {
return false, nil
}
var labelSelector labels.Selector
var annotatonSelector labels.Selector
var err error
if t.LabelSelector != nil {
labelSelector, err = metav1.LabelSelectorAsSelector(t.LabelSelector)
if err != nil {
return false, err
}
} else {
labelSelector = labels.Everything()
}
if t.AnnotationSelector != nil {
annotatonSelector, err = metav1.LabelSelectorAsSelector(t.AnnotationSelector)
if err != nil {
return false, err
}
} else {
annotatonSelector = labels.Everything()
}
namespaced, err := discoveryclient.IsGVKNamespaced(context, obj.GetObjectKind().GroupVersionKind())
if err != nil {
log.Error(err, "Unable to determine if GVK is namespaced", "GVK", obj.GetObjectKind().GroupVersionKind())
return false, err
}
if namespaced {
if t.Namespace != "" {
//we are selecting within a namespace
if t.Namespace != obj.GetNamespace() {
return false, nil
}
}
if t.Name != "" {
// we are matching on name
return t.Name == obj.GetName(), nil
} else {
// we select via selectors
return labelSelector.Matches(labels.Set(obj.GetLabels())) && annotatonSelector.Matches(labels.Set(obj.GetAnnotations())), nil
}
} else {
//cluster object, we ignore namespace
if t.Name != "" {
// we select via name
return t.Name == obj.GetName(), nil
} else {
// we select via selectors
return labelSelector.Matches(labels.Set(obj.GetLabels())) && annotatonSelector.Matches(labels.Set(obj.GetAnnotations())), nil
}
}
}
// GetNameAndNamespace processes the templates for Name and Namespace of the sourceObjectReference
// requires context with log and restConfig
func (s *SourceObjectReference) GetNameAndNamespace(context context.Context, target *unstructured.Unstructured) (name string, namespace string, err error) {
log := log.FromContext(context)
name, err = processTemplate(context, s.Name, target.UnstructuredContent())
if err != nil {
log.Error(err, "unable to process template for", "name", s.Name)
return "", "", err
}
namespace, err = processTemplate(context, s.Namespace, target.UnstructuredContent())
if err != nil {
log.Error(err, "unable to process template for", "namespace", s.Name)
return "", "", err
}
return
}
func (t *SourceObjectReference) getAPIReourceForGVK(context context.Context) (*metav1.APIResource, bool, error) {
if t.apiResource != nil {
return t.apiResource, true, nil
}
apiresource, found, err := discoveryclient.GetAPIResourceForGVK(context, schema.FromAPIVersionAndKind(t.APIVersion, t.Kind))
if err != nil && found {
t.apiResource = apiresource
}
return apiresource, found, err
}
func (t *SourceObjectReference) getDynamicClient(context context.Context) (dynamic.ResourceInterface, error) {
log := log.FromContext(context)
var ri dynamic.ResourceInterface
nri, namespaced, err := dynamicclient.GetDynamicClientForGVK(context, schema.FromAPIVersionAndKind(t.APIVersion, t.Kind))
if err != nil {
log.Error(err, "unable to get dynamicClient on ", "gvk", schema.FromAPIVersionAndKind(t.APIVersion, t.Kind))
return nil, err
}
if namespaced {
ri = nri.Namespace(t.Namespace)
} else {
ri = nri
}
return ri, nil
}
func (s *SourceObjectReference) GetReferencedObject(context context.Context, target *unstructured.Unstructured) (*unstructured.Unstructured, error) {
log := log.FromContext(context)
name, namespace, err := s.GetNameAndNamespace(context, target)
if err != nil {
log.Error(err, "unable to get name and namespaces on ", "SourceObjectReference", s, "with target", target)
return nil, err
}
sourceCopy := s.DeepCopy()
sourceCopy.Name = name
sourceCopy.Namespace = namespace
client, err := sourceCopy.getDynamicClient(context)
if err != nil {
log.Error(err, "unable to get dynamic client for ", "source", sourceCopy)
return nil, err
}
obj, err := client.Get(context, name, metav1.GetOptions{})
if err != nil {
log.Error(err, "unable to get referenced object ", "sourceCopy", sourceCopy)
return nil, err
}
return obj, nil
}
func processTemplate(context context.Context, templateString string, param interface{}) (string, error) {
log := log.FromContext(context)
restConfig := context.Value("restConfig").(*rest.Config)
template, err := template.New(templateString).Funcs(utiltemplates.AdvancedTemplateFuncMap(restConfig, log)).Parse(templateString)
if err != nil {
log.Error(err, "unable to parse", "template", templateString)
return "", err
}
var b bytes.Buffer
err = template.Execute(&b, param)
if err != nil {
log.Error(err, "unable to process", "template", templateString, "with param", param)
return "", err
}
return b.String(), nil
}
type SourceObjectReference struct {
// API version of the referent.
// +kubebuilder:validation:Required
APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,5,opt,name=apiVersion"`
// Kind of the referent.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// +kubebuilder:validation:Required
Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"`
// Namespace of the referent.
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
// +kubebuilder:validation:Optional
Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,opt,name=namespace"`
// Name of the referent.
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
// +kubebuilder:validation:Optional
Name string `json:"name,omitempty" protobuf:"bytes,3,opt,name=name"`
// If referring to a piece of an object instead of an entire object, this string
// should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
// For example, if the object reference is to a container within a pod, this would take on a value like:
// "spec.containers{name}" (where "name" refers to the name of the container that triggered
// the event) or if no container name is specified "spec.containers[2]" (container with
// index 2 in this pod). This syntax is chosen only to have some well-defined way of
// referencing a part of an object.
// +kubebuilder:validation:Optional
FieldPath string `json:"fieldPath,omitempty" protobuf:"bytes,7,opt,name=fieldPath"`
//apiResource caches apiResource for this targetReference
apiResource *metav1.APIResource `json:"-"`
}
================================================
FILE: api/v1alpha1/lockedresource.go
================================================
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
)
// LockedResource represents a resource to be enforced in a LockedResourceController and can be used in a API specification
// +k8s:openapi-gen=true
type LockedResource struct {
// Object is a yaml representation of an API resource
// +kubebuilder:validation:Required
Object runtime.RawExtension `json:"object"`
// ExludedPaths are a set of json paths that need not be considered by the LockedResourceReconciler
// +kubebuilder:validation:Optional
// +listType=set
ExcludedPaths []string `json:"excludedPaths,omitempty"`
}
// LockedResourceTemplate represents a resource template in go language to be enforced in a LockedResourceController and can be used in a API specification
// +k8s:openapi-gen=true
type LockedResourceTemplate struct {
// ObjectTemplate is a goland template. Whne processed, it must resolve to a yaml representation of an API resource
// +kubebuilder:validation:Required
ObjectTemplate string `json:"objectTemplate"`
// ExludedPaths are a set of json paths that need not be considered by the LockedResourceReconciler
// +kubebuilder:validation:Optional
// +listType=set
ExcludedPaths []string `json:"excludedPaths,omitempty"`
}
================================================
FILE: api/v1alpha1/mycrd_types.go
================================================
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// MyCRDSpec defines the desired state of MyCRD
type MyCRDSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book.kubebuilder.io/beyond_basics/generating_crd.html
Initialized bool `json:"initialized"`
Valid bool `json:"valid"`
Error bool `json:"error"`
}
// MyCRDStatus defines the observed state of MyCRD
type MyCRDStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book.kubebuilder.io/beyond_basics/generating_crd.html
// // +patchMergeKey=type
// // +patchStrategy=merge
// // +listType=map
// // +listMapKey=type
// Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
}
func (m *MyCRD) GetConditions() []metav1.Condition {
return m.Status.Conditions
}
func (m *MyCRD) SetConditions(conditions []metav1.Condition) {
m.Status.Conditions = conditions
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// MyCRD is the Schema for the mycrds API
type MyCRD struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec MyCRDSpec `json:"spec,omitempty"`
Status MyCRDStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// MyCRDList contains a list of MyCRD
type MyCRDList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []MyCRD `json:"items"`
}
func init() {
SchemeBuilder.Register(&MyCRD{}, &MyCRDList{})
}
================================================
FILE: api/v1alpha1/templatedenforcingcrd_types.go
================================================
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// TemplatedEnforcingCRDSpec defines the desired state of TemplatedEnforcingCRD
type TemplatedEnforcingCRDSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
// +kubebuilder:validation:Optional
// +listType=atomic
Templates []LockedResourceTemplate `json:"templates,omitempty"`
}
// TemplatedEnforcingCRDStatus defines the observed state of TemplatedEnforcingCRD
type TemplatedEnforcingCRDStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
// +kubebuilder:validation:Optional
EnforcingReconcileStatus `json:",inline,omitempty"`
}
func (m *TemplatedEnforcingCRD) GetEnforcingReconcileStatus() EnforcingReconcileStatus {
return m.Status.EnforcingReconcileStatus
}
func (m *TemplatedEnforcingCRD) SetEnforcingReconcileStatus(reconcileStatus EnforcingReconcileStatus) {
m.Status.EnforcingReconcileStatus = reconcileStatus
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// TemplatedEnforcingCRD is the Schema for the templatedenforcingcrds API
type TemplatedEnforcingCRD struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec TemplatedEnforcingCRDSpec `json:"spec,omitempty"`
Status TemplatedEnforcingCRDStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// TemplatedEnforcingCRDList contains a list of TemplatedEnforcingCRD
type TemplatedEnforcingCRDList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []TemplatedEnforcingCRD `json:"items"`
}
func init() {
SchemeBuilder.Register(&TemplatedEnforcingCRD{}, &TemplatedEnforcingCRDList{})
}
================================================
FILE: api/v1alpha1/zz_generated.deepcopy.go
================================================
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
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.
*/
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha1
import (
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in ConditionMap) DeepCopyInto(out *ConditionMap) {
{
in := &in
*out = make(ConditionMap, len(*in))
for key, val := range *in {
var outVal []v1.Condition
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = make(Conditions, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
(*out)[key] = outVal
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConditionMap.
func (in ConditionMap) DeepCopy() ConditionMap {
if in == nil {
return nil
}
out := new(ConditionMap)
in.DeepCopyInto(out)
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in Conditions) DeepCopyInto(out *Conditions) {
{
in := &in
*out = make(Conditions, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Conditions.
func (in Conditions) DeepCopy() Conditions {
if in == nil {
return nil
}
out := new(Conditions)
in.DeepCopyInto(out)
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EnforcingCRD) DeepCopyInto(out *EnforcingCRD) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnforcingCRD.
func (in *EnforcingCRD) DeepCopy() *EnforcingCRD {
if in == nil {
return nil
}
out := new(EnforcingCRD)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *EnforcingCRD) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EnforcingCRDList) DeepCopyInto(out *EnforcingCRDList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]EnforcingCRD, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnforcingCRDList.
func (in *EnforcingCRDList) DeepCopy() *EnforcingCRDList {
if in == nil {
return nil
}
out := new(EnforcingCRDList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *EnforcingCRDList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EnforcingCRDSpec) DeepCopyInto(out *EnforcingCRDSpec) {
*out = *in
if in.Resources != nil {
in, out := &in.Resources, &out.Resources
*out = make([]LockedResource, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnforcingCRDSpec.
func (in *EnforcingCRDSpec) DeepCopy() *EnforcingCRDSpec {
if in == nil {
return nil
}
out := new(EnforcingCRDSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EnforcingCRDStatus) DeepCopyInto(out *EnforcingCRDStatus) {
*out = *in
in.EnforcingReconcileStatus.DeepCopyInto(&out.EnforcingReconcileStatus)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnforcingCRDStatus.
func (in *EnforcingCRDStatus) DeepCopy() *EnforcingCRDStatus {
if in == nil {
return nil
}
out := new(EnforcingCRDStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EnforcingPatch) DeepCopyInto(out *EnforcingPatch) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnforcingPatch.
func (in *EnforcingPatch) DeepCopy() *EnforcingPatch {
if in == nil {
return nil
}
out := new(EnforcingPatch)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *EnforcingPatch) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EnforcingPatchList) DeepCopyInto(out *EnforcingPatchList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]EnforcingPatch, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnforcingPatchList.
func (in *EnforcingPatchList) DeepCopy() *EnforcingPatchList {
if in == nil {
return nil
}
out := new(EnforcingPatchList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *EnforcingPatchList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EnforcingPatchSpec) DeepCopyInto(out *EnforcingPatchSpec) {
*out = *in
if in.Patches != nil {
in, out := &in.Patches, &out.Patches
*out = make(map[string]PatchSpec, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnforcingPatchSpec.
func (in *EnforcingPatchSpec) DeepCopy() *EnforcingPatchSpec {
if in == nil {
return nil
}
out := new(EnforcingPatchSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EnforcingPatchStatus) DeepCopyInto(out *EnforcingPatchStatus) {
*out = *in
in.EnforcingReconcileStatus.DeepCopyInto(&out.EnforcingReconcileStatus)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnforcingPatchStatus.
func (in *EnforcingPatchStatus) DeepCopy() *EnforcingPatchStatus {
if in == nil {
return nil
}
out := new(EnforcingPatchStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EnforcingReconcileStatus) DeepCopyInto(out *EnforcingReconcileStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.LockedResourceStatuses != nil {
in, out := &in.LockedResourceStatuses, &out.LockedResourceStatuses
*out = make(map[string]Conditions, len(*in))
for key, val := range *in {
var outVal []v1.Condition
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = make(Conditions, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
(*out)[key] = outVal
}
}
if in.LockedPatchStatuses != nil {
in, out := &in.LockedPatchStatuses, &out.LockedPatchStatuses
*out = make(map[string]ConditionMap, len(*in))
for key, val := range *in {
var outVal map[string]Conditions
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = make(ConditionMap, len(*in))
for key, val := range *in {
var outVal []v1.Condition
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = make(Conditions, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
(*out)[key] = outVal
}
}
(*out)[key] = outVal
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnforcingReconcileStatus.
func (in *EnforcingReconcileStatus) DeepCopy() *EnforcingReconcileStatus {
if in == nil {
return nil
}
out := new(EnforcingReconcileStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LockedResource) DeepCopyInto(out *LockedResource) {
*out = *in
in.Object.DeepCopyInto(&out.Object)
if in.ExcludedPaths != nil {
in, out := &in.ExcludedPaths, &out.ExcludedPaths
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LockedResource.
func (in *LockedResource) DeepCopy() *LockedResource {
if in == nil {
return nil
}
out := new(LockedResource)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LockedResourceTemplate) DeepCopyInto(out *LockedResourceTemplate) {
*out = *in
if in.ExcludedPaths != nil {
in, out := &in.ExcludedPaths, &out.ExcludedPaths
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LockedResourceTemplate.
func (in *LockedResourceTemplate) DeepCopy() *LockedResourceTemplate {
if in == nil {
return nil
}
out := new(LockedResourceTemplate)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MyCRD) DeepCopyInto(out *MyCRD) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MyCRD.
func (in *MyCRD) DeepCopy() *MyCRD {
if in == nil {
return nil
}
out := new(MyCRD)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MyCRD) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MyCRDList) DeepCopyInto(out *MyCRDList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]MyCRD, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MyCRDList.
func (in *MyCRDList) DeepCopy() *MyCRDList {
if in == nil {
return nil
}
out := new(MyCRDList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MyCRDList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MyCRDSpec) DeepCopyInto(out *MyCRDSpec) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MyCRDSpec.
func (in *MyCRDSpec) DeepCopy() *MyCRDSpec {
if in == nil {
return nil
}
out := new(MyCRDSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MyCRDStatus) DeepCopyInto(out *MyCRDStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MyCRDStatus.
func (in *MyCRDStatus) DeepCopy() *MyCRDStatus {
if in == nil {
return nil
}
out := new(MyCRDStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PatchSpec) DeepCopyInto(out *PatchSpec) {
*out = *in
if in.SourceObjectRefs != nil {
in, out := &in.SourceObjectRefs, &out.SourceObjectRefs
*out = make([]SourceObjectReference, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
in.TargetObjectRef.DeepCopyInto(&out.TargetObjectRef)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PatchSpec.
func (in *PatchSpec) DeepCopy() *PatchSpec {
if in == nil {
return nil
}
out := new(PatchSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SourceObjectReference) DeepCopyInto(out *SourceObjectReference) {
*out = *in
if in.apiResource != nil {
in, out := &in.apiResource, &out.apiResource
*out = new(v1.APIResource)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceObjectReference.
func (in *SourceObjectReference) DeepCopy() *SourceObjectReference {
if in == nil {
return nil
}
out := new(SourceObjectReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TargetObjectReference) DeepCopyInto(out *TargetObjectReference) {
*out = *in
if in.LabelSelector != nil {
in, out := &in.LabelSelector, &out.LabelSelector
*out = new(v1.LabelSelector)
(*in).DeepCopyInto(*out)
}
if in.AnnotationSelector != nil {
in, out := &in.AnnotationSelector, &out.AnnotationSelector
*out = new(v1.LabelSelector)
(*in).DeepCopyInto(*out)
}
if in.apiResource != nil {
in, out := &in.apiResource, &out.apiResource
*out = new(v1.APIResource)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetObjectReference.
func (in *TargetObjectReference) DeepCopy() *TargetObjectReference {
if in == nil {
return nil
}
out := new(TargetObjectReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TemplatedEnforcingCRD) DeepCopyInto(out *TemplatedEnforcingCRD) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TemplatedEnforcingCRD.
func (in *TemplatedEnforcingCRD) DeepCopy() *TemplatedEnforcingCRD {
if in == nil {
return nil
}
out := new(TemplatedEnforcingCRD)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *TemplatedEnforcingCRD) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TemplatedEnforcingCRDList) DeepCopyInto(out *TemplatedEnforcingCRDList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]TemplatedEnforcingCRD, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TemplatedEnforcingCRDList.
func (in *TemplatedEnforcingCRDList) DeepCopy() *TemplatedEnforcingCRDList {
if in == nil {
return nil
}
out := new(TemplatedEnforcingCRDList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *TemplatedEnforcingCRDList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TemplatedEnforcingCRDSpec) DeepCopyInto(out *TemplatedEnforcingCRDSpec) {
*out = *in
if in.Templates != nil {
in, out := &in.Templates, &out.Templates
*out = make([]LockedResourceTemplate, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TemplatedEnforcingCRDSpec.
func (in *TemplatedEnforcingCRDSpec) DeepCopy() *TemplatedEnforcingCRDSpec {
if in == nil {
return nil
}
out := new(TemplatedEnforcingCRDSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TemplatedEnforcingCRDStatus) DeepCopyInto(out *TemplatedEnforcingCRDStatus) {
*out = *in
in.EnforcingReconcileStatus.DeepCopyInto(&out.EnforcingReconcileStatus)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TemplatedEnforcingCRDStatus.
func (in *TemplatedEnforcingCRDStatus) DeepCopy() *TemplatedEnforcingCRDStatus {
if in == nil {
return nil
}
out := new(TemplatedEnforcingCRDStatus)
in.DeepCopyInto(out)
return out
}
================================================
FILE: ci.Dockerfile
================================================
FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /
COPY bin/manager .
USER 65532:65532
ENTRYPOINT ["/manager"]
================================================
FILE: config/certmanager/certificate.yaml
================================================
# The following manifests contain a self-signed issuer CR and a certificate CR.
# More document can be found at https://docs.cert-manager.io
# WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for
# breaking changes
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: selfsigned-issuer
namespace: system
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml
namespace: system
spec:
# $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize
dnsNames:
- $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc
- $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local
issuerRef:
kind: Issuer
name: selfsigned-issuer
secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize
================================================
FILE: config/certmanager/kustomization.yaml
================================================
resources:
- certificate.yaml
configurations:
- kustomizeconfig.yaml
================================================
FILE: config/certmanager/kustomizeconfig.yaml
================================================
# This configuration is for teaching kustomize how to update name ref and var substitution
nameReference:
- kind: Issuer
group: cert-manager.io
fieldSpecs:
- kind: Certificate
group: cert-manager.io
path: spec/issuerRef/name
varReference:
- kind: Certificate
group: cert-manager.io
path: spec/commonName
- kind: Certificate
group: cert-manager.io
path: spec/dnsNames
================================================
FILE: config/crd/bases/operator-utils.example.io_enforcingcrds.yaml
================================================
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.9.0
creationTimestamp: null
name: enforcingcrds.operator-utils.example.io
spec:
group: operator-utils.example.io
names:
kind: EnforcingCRD
listKind: EnforcingCRDList
plural: enforcingcrds
singular: enforcingcrd
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: EnforcingCRD is the Schema for the enforcingcrds API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: EnforcingCRDSpec defines the desired state of EnforcingCRD
properties:
resources:
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
Important: Run "operator-sdk generate k8s" to regenerate code after
modifying this file Add custom validation using kubebuilder tags:
https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
Resources is a list of resource manifests that should be locked
into the specified configuration'
items:
description: LockedResource represents a resource to be enforced
in a LockedResourceController and can be used in a API specification
properties:
excludedPaths:
description: ExludedPaths are a set of json paths that need
not be considered by the LockedResourceReconciler
items:
type: string
type: array
x-kubernetes-list-type: set
object:
description: Object is a yaml representation of an API resource
type: object
required:
- object
type: object
type: array
x-kubernetes-list-type: atomic
type: object
status:
description: EnforcingCRDStatus defines the observed state of EnforcingCRD
properties:
conditions:
description: ReconcileStatus this is the general status of the main
reconciler
items:
description: "Condition contains details for one aspect of the current
state of this API Resource. --- This struct is intended for direct
use as an array at the field path .status.conditions. For example,
\n type FooStatus struct{ // Represents the observations of a
foo's current state. // Known .status.conditions.type are: \"Available\",
\"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
// +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be when
the underlying condition changed. If that is not known, then
using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if .metadata.generation
is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the current
state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values and
meanings for this field, and whether the values are considered
a guaranteed API. The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across resources
like Available, but because arbitrary conditions can be useful
(see .node.status.conditions), the ability to deconflict is
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
lockedPatchStatuses:
additionalProperties:
additionalProperties:
items:
description: "Condition contains details for one aspect of the
current state of this API Resource. --- This struct is intended
for direct use as an array at the field path .status.conditions.
\ For example, \n type FooStatus struct{ // Represents the
observations of a foo's current state. // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
// +patchStrategy=merge // +listType=map // +listMapKey=type
Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
\n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be
when the underlying condition changed. If that is not
known, then using the time when the API field changed
is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if
.metadata.generation is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the
current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values
and meanings for this field, and whether the values are
considered a guaranteed API. The value should be a CamelCase
string. This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False,
Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across
resources like Available, but because arbitrary conditions
can be useful (see .node.status.conditions), the ability
to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
type: object
description: LockedResourceStatuses contains the reconcile status
for each of the managed resources
type: object
lockedResourceStatuses:
additionalProperties:
items:
description: "Condition contains details for one aspect of the
current state of this API Resource. --- This struct is intended
for direct use as an array at the field path .status.conditions.
\ For example, \n type FooStatus struct{ // Represents the observations
of a foo's current state. // Known .status.conditions.type are:
\"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
// +patchStrategy=merge // +listType=map // +listMapKey=type
Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
\n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be
when the underlying condition changed. If that is not known,
then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if
.metadata.generation is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the current
state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values and
meanings for this field, and whether the values are considered
a guaranteed API. The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False,
Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across resources
like Available, but because arbitrary conditions can be
useful (see .node.status.conditions), the ability to deconflict
is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
description: LockedResourceStatuses contains the reconcile status
for each of the managed resources
type: object
type: object
type: object
served: true
storage: true
subresources:
status: {}
================================================
FILE: config/crd/bases/operator-utils.example.io_enforcingpatches.yaml
================================================
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.9.0
creationTimestamp: null
name: enforcingpatches.operator-utils.example.io
spec:
group: operator-utils.example.io
names:
kind: EnforcingPatch
listKind: EnforcingPatchList
plural: enforcingpatches
singular: enforcingpatch
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: EnforcingPatch is the Schema for the enforcingpatches API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: EnforcingPatchSpec defines the desired state of EnforcingPatch
properties:
patches:
additionalProperties:
description: Patch describes a patch to be enforced at runtime
properties:
patchTemplate:
description: PatchTemplate is a go template that will be resolved
using the SourceObjectRefs as parameters. The result must
be a valid patch based on the pacth type and the target object.
type: string
patchType:
description: PatchType is the type of patch to be applied, one
of "application/json-patch+json"'"application/merge-patch+json","application/strategic-merge-patch+json","application/apply-patch+yaml"
default:="application/strategic-merge-patch+json"
enum:
- application/json-patch+json
- application/merge-patch+json
- application/strategic-merge-patch+json
- application/apply-patch+yaml
type: string
sourceObjectRefs:
description: 'SourceObjectRefs is an arrays of refereces to
source objects that will be used as input for the template
processing. These refernces must resolve to single instance.
The resolution rule is as follows (+ present, - absent): the
King and APIVersion field are mandatory -Namespace +Name:
resolves to cluster-level object <Name>. If Kind is namespaced,
this results in an error. -Namespace -Name: results in an
error Name manespaces Namespace are evaluated as golang templates
with the input of the template being the target object. When
selecting multiple target, this allows for having specific
source objects for each target. ResourceVersion and UID are
always ignored If FieldPath is specified, the restuned object
is calculated from the path, so for example if FieldPath=.spec,
the only the spec portion of the object is returned. The target
object is always added as element zero of the array of the
SourceObjectRefs'
items:
properties:
apiVersion:
description: API version of the referent.
type: string
fieldPath:
description: 'If referring to a piece of an object instead
of an entire object, this string should contain a valid
JSON/Go field access statement, such as desiredState.manifest.containers[2].
For example, if the object reference is to a container
within a pod, this would take on a value like: "spec.containers{name}"
(where "name" refers to the name of the container that
triggered the event) or if no container name is specified
"spec.containers[2]" (container with index 2 in this
pod). This syntax is chosen only to have some well-defined
way of referencing a part of an object.'
type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
type: string
type: object
type: array
x-kubernetes-list-type: atomic
targetObjectRef:
description: 'TargetObjectRef is a reference to the object to
which the pacth should be applied. the King and APIVersion
field are mandatory the Name and Namespace field have the
following meaning (+ present, - absent) -Namespace +Name:
apply the patch to the cluster-level object <Name>. If Kind
is namespaced, this results in an error. -Namespace -Name:
if the kind is namespaced apply the patch to all of the objects
in all of the namespaces. If the kind is not namespaced, apply
the patch to all of the cluster level objects. The lable selector
can be used to further filter the selected objects.'
properties:
annotationSelector:
description: AnnotationSelector selects objects by label
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
apiVersion:
description: API version of the referent.
type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
labelSelector:
description: LabelSelector selects objects by label
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
type: string
type: object
type: object
description: Patches is a list of pacthes that should be encforced
at runtime.
type: object
type: object
status:
description: EnforcingPatchStatus defines the observed state of EnforcingPatch
properties:
conditions:
description: ReconcileStatus this is the general status of the main
reconciler
items:
description: "Condition contains details for one aspect of the current
state of this API Resource. --- This struct is intended for direct
use as an array at the field path .status.conditions. For example,
\n type FooStatus struct{ // Represents the observations of a
foo's current state. // Known .status.conditions.type are: \"Available\",
\"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
// +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be when
the underlying condition changed. If that is not known, then
using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if .metadata.generation
is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the current
state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values and
meanings for this field, and whether the values are considered
a guaranteed API. The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across resources
like Available, but because arbitrary conditions can be useful
(see .node.status.conditions), the ability to deconflict is
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
lockedPatchStatuses:
additionalProperties:
additionalProperties:
items:
description: "Condition contains details for one aspect of the
current state of this API Resource. --- This struct is intended
for direct use as an array at the field path .status.conditions.
\ For example, \n type FooStatus struct{ // Represents the
observations of a foo's current state. // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
// +patchStrategy=merge // +listType=map // +listMapKey=type
Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
\n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be
when the underlying condition changed. If that is not
known, then using the time when the API field changed
is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if
.metadata.generation is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the
current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values
and meanings for this field, and whether the values are
considered a guaranteed API. The value should be a CamelCase
string. This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False,
Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across
resources like Available, but because arbitrary conditions
can be useful (see .node.status.conditions), the ability
to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
type: object
description: LockedResourceStatuses contains the reconcile status
for each of the managed resources
type: object
lockedResourceStatuses:
additionalProperties:
items:
description: "Condition contains details for one aspect of the
current state of this API Resource. --- This struct is intended
for direct use as an array at the field path .status.conditions.
\ For example, \n type FooStatus struct{ // Represents the observations
of a foo's current state. // Known .status.conditions.type are:
\"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
// +patchStrategy=merge // +listType=map // +listMapKey=type
Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
\n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be
when the underlying condition changed. If that is not known,
then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if
.metadata.generation is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the current
state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values and
meanings for this field, and whether the values are considered
a guaranteed API. The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False,
Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across resources
like Available, but because arbitrary conditions can be
useful (see .node.status.conditions), the ability to deconflict
is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
description: LockedResourceStatuses contains the reconcile status
for each of the managed resources
type: object
type: object
type: object
served: true
storage: true
subresources:
status: {}
================================================
FILE: config/crd/bases/operator-utils.example.io_mycrds.yaml
================================================
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.9.0
creationTimestamp: null
name: mycrds.operator-utils.example.io
spec:
group: operator-utils.example.io
names:
kind: MyCRD
listKind: MyCRDList
plural: mycrds
singular: mycrd
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: MyCRD is the Schema for the mycrds API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: MyCRDSpec defines the desired state of MyCRD
properties:
error:
type: boolean
initialized:
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
Important: Run "operator-sdk generate k8s" to regenerate code after
modifying this file Add custom validation using kubebuilder tags:
https://book.kubebuilder.io/beyond_basics/generating_crd.html'
type: boolean
valid:
type: boolean
required:
- error
- initialized
- valid
type: object
status:
description: MyCRDStatus defines the observed state of MyCRD
properties:
conditions:
items:
description: "Condition contains details for one aspect of the current
state of this API Resource. --- This struct is intended for direct
use as an array at the field path .status.conditions. For example,
\n type FooStatus struct{ // Represents the observations of a
foo's current state. // Known .status.conditions.type are: \"Available\",
\"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
// +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be when
the underlying condition changed. If that is not known, then
using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if .metadata.generation
is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the current
state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values and
meanings for this field, and whether the values are considered
a guaranteed API. The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across resources
like Available, but because arbitrary conditions can be useful
(see .node.status.conditions), the ability to deconflict is
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
type: object
type: object
served: true
storage: true
subresources:
status: {}
================================================
FILE: config/crd/bases/operator-utils.example.io_templatedenforcingcrds.yaml
================================================
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.9.0
creationTimestamp: null
name: templatedenforcingcrds.operator-utils.example.io
spec:
group: operator-utils.example.io
names:
kind: TemplatedEnforcingCRD
listKind: TemplatedEnforcingCRDList
plural: templatedenforcingcrds
singular: templatedenforcingcrd
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: TemplatedEnforcingCRD is the Schema for the templatedenforcingcrds
API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: TemplatedEnforcingCRDSpec defines the desired state of TemplatedEnforcingCRD
properties:
templates:
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
Important: Run "operator-sdk generate k8s" to regenerate code after
modifying this file Add custom validation using kubebuilder tags:
https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html'
items:
description: LockedResourceTemplate represents a resource template
in go language to be enforced in a LockedResourceController and
can be used in a API specification
properties:
excludedPaths:
description: ExludedPaths are a set of json paths that need
not be considered by the LockedResourceReconciler
items:
type: string
type: array
x-kubernetes-list-type: set
objectTemplate:
description: ObjectTemplate is a goland template. Whne processed,
it must resolve to a yaml representation of an API resource
type: string
required:
- objectTemplate
type: object
type: array
x-kubernetes-list-type: atomic
type: object
status:
description: TemplatedEnforcingCRDStatus defines the observed state of
TemplatedEnforcingCRD
properties:
conditions:
description: ReconcileStatus this is the general status of the main
reconciler
items:
description: "Condition contains details for one aspect of the current
state of this API Resource. --- This struct is intended for direct
use as an array at the field path .status.conditions. For example,
\n type FooStatus struct{ // Represents the observations of a
foo's current state. // Known .status.conditions.type are: \"Available\",
\"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
// +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be when
the underlying condition changed. If that is not known, then
using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if .metadata.generation
is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the current
state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values and
meanings for this field, and whether the values are considered
a guaranteed API. The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across resources
like Available, but because arbitrary conditions can be useful
(see .node.status.conditions), the ability to deconflict is
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
lockedPatchStatuses:
additionalProperties:
additionalProperties:
items:
description: "Condition contains details for one aspect of the
current state of this API Resource. --- This struct is intended
for direct use as an array at the field path .status.conditions.
\ For example, \n type FooStatus struct{ // Represents the
observations of a foo's current state. // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
// +patchStrategy=merge // +listType=map // +listMapKey=type
Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
\n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be
when the underlying condition changed. If that is not
known, then using the time when the API field changed
is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if
.metadata.generation is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the
current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values
and meanings for this field, and whether the values are
considered a guaranteed API. The value should be a CamelCase
string. This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False,
Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across
resources like Available, but because arbitrary conditions
can be useful (see .node.status.conditions), the ability
to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
type: object
description: LockedResourceStatuses contains the reconcile status
for each of the managed resources
type: object
lockedResourceStatuses:
additionalProperties:
items:
description: "Condition contains details for one aspect of the
current state of this API Resource. --- This struct is intended
for direct use as an array at the field path .status.conditions.
\ For example, \n type FooStatus struct{ // Represents the observations
of a foo's current state. // Known .status.conditions.type are:
\"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
// +patchStrategy=merge // +listType=map // +listMapKey=type
Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
\n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be
when the underlying condition changed. If that is not known,
then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if
.metadata.generation is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the current
state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values and
meanings for this field, and whether the values are considered
a guaranteed API. The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False,
Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across resources
like Available, but because arbitrary conditions can be
useful (see .node.status.conditions), the ability to deconflict
is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
description: LockedResourceStatuses contains the reconcile status
for each of the managed resources
type: object
type: object
type: object
served: true
storage: true
subresources:
status: {}
================================================
FILE: config/crd/kustomization.yaml
================================================
# This kustomization.yaml is not intended to be run by itself,
# since it depends on service name and namespace that are out of this kustomize package.
# It should be run by config/default
resources:
- bases/operator-utils.example.io_mycrds.yaml
- bases/operator-utils.example.io_enforcingcrds.yaml
- bases/operator-utils.example.io_enforcingpatches.yaml
- bases/operator-utils.example.io_templatedenforcingcrds.yaml
# +kubebuilder:scaffold:crdkustomizeresource
patchesStrategicMerge:
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
# patches here are for enabling the conversion webhook for each CRD
#- patches/webhook_in_mycrds.yaml
#- patches/webhook_in_enforcingcrds.yaml
#- patches/webhook_in_enforcingpatches.yaml
#- patches/webhook_in_templatedenforcingcrds.yaml
# +kubebuilder:scaffold:crdkustomizewebhookpatch
# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
# patches here are for enabling the CA injection for each CRD
#- patches/cainjection_in_mycrds.yaml
#- patches/cainjection_in_enforcingcrds.yaml
#- patches/cainjection_in_enforcingpatches.yaml
#- patches/cainjection_in_templatedenforcingcrds.yaml
# +kubebuilder:scaffold:crdkustomizecainjectionpatch
# the following config is for teaching kustomize how to do kustomization for CRDs.
configurations:
- kustomizeconfig.yaml
================================================
FILE: config/crd/kustomizeconfig.yaml
================================================
# This file is for teaching kustomize how to substitute name and namespace reference in CRD
nameReference:
- kind: Service
version: v1
fieldSpecs:
- kind: CustomResourceDefinition
group: apiextensions.k8s.io
path: spec/conversion/webhookClientConfig/service/name
namespace:
- kind: CustomResourceDefinition
group: apiextensions.k8s.io
path: spec/conversion/webhookClientConfig/service/namespace
create: false
varReference:
- path: metadata/annotations
================================================
FILE: config/crd/patches/cainjection_in_enforcingcrds.yaml
================================================
# The following patch adds a directive for certmanager to inject CA into the CRD
# CRD conversion requires k8s 1.13 or later.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: enforcingcrds.operator-utils.example.io
================================================
FILE: config/crd/patches/cainjection_in_enforcingpatches.yaml
================================================
# The following patch adds a directive for certmanager to inject CA into the CRD
# CRD conversion requires k8s 1.13 or later.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: enforcingpatches.operator-utils.example.io
================================================
FILE: config/crd/patches/cainjection_in_mycrds.yaml
================================================
# The following patch adds a directive for certmanager to inject CA into the CRD
# CRD conversion requires k8s 1.13 or later.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: mycrds.operator-utils.example.io
================================================
FILE: config/crd/patches/cainjection_in_templatedenforcingcrds.yaml
================================================
# The following patch adds a directive for certmanager to inject CA into the CRD
# CRD conversion requires k8s 1.13 or later.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: templatedenforcingcrds.operator-utils.example.io
================================================
FILE: config/crd/patches/webhook_in_enforcingcrds.yaml
================================================
# The following patch enables conversion webhook for CRD
# CRD conversion requires k8s 1.13 or later.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: enforcingcrds.operator-utils.example.io
spec:
conversion:
strategy: Webhook
webhookClientConfig:
# this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
# but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
caBundle: Cg==
service:
namespace: system
name: webhook-service
path: /convert
================================================
FILE: config/crd/patches/webhook_in_enforcingpatches.yaml
================================================
# The following patch enables conversion webhook for CRD
# CRD conversion requires k8s 1.13 or later.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: enforcingpatches.operator-utils.example.io
spec:
conversion:
strategy: Webhook
webhookClientConfig:
# this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
# but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
caBundle: Cg==
service:
namespace: system
name: webhook-service
path: /convert
================================================
FILE: config/crd/patches/webhook_in_mycrds.yaml
================================================
# The following patch enables conversion webhook for CRD
# CRD conversion requires k8s 1.13 or later.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: mycrds.operator-utils.example.io
spec:
conversion:
strategy: Webhook
webhookClientConfig:
# this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
# but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
caBundle: Cg==
service:
namespace: system
name: webhook-service
path: /convert
================================================
FILE: config/crd/patches/webhook_in_templatedenforcingcrds.yaml
================================================
# The following patch enables conversion webhook for CRD
# CRD conversion requires k8s 1.13 or later.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: templatedenforcingcrds.operator-utils.example.io
spec:
conversion:
strategy: Webhook
webhookClientConfig:
# this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
# but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
caBundle: Cg==
service:
namespace: system
name: webhook-service
path: /convert
================================================
FILE: config/default/kustomization.yaml
================================================
# Adds namespace to all resources.
namespace: operator-utils
# Value of this field is prepended to the
# names of all resources, e.g. a deployment named
# "wordpress" becomes "alices-wordpress".
# Note that it should also match with the prefix (text before '-') of the namespace
# field above.
namePrefix: operator-utils-
# Labels to add to all resources and selectors.
#commonLabels:
# someName: someValue
bases:
- ../crd
- ../rbac
- ../manager
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- ../webhook
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
#- ../certmanager
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
- ../prometheus
patchesStrategicMerge:
# Protect the /metrics endpoint by putting it behind auth.
# If you want your controller-manager to expose the /metrics
# endpoint w/o any authn/z, please comment the following line.
- manager_auth_proxy_patch.yaml
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- manager_webhook_patch.yaml
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
# 'CERTMANAGER' needs to be enabled to use ca injection
#- webhookcainjection_patch.yaml
# the following config is for teaching kustomize how to do var substitution
vars:
- name: METRICS_SERVICE_NAME
objref:
kind: Service
version: v1
name: controller-manager-metrics
- name: METRICS_SERVICE_NAMESPACE
objref:
kind: Service
version: v1
name: controller-manager-metrics
fieldref:
fieldpath: metadata.namespace
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1alpha2
# name: serving-cert # this name should match the one in certificate.yaml
# fieldref:
# fieldpath: metadata.namespace
#- name: CERTIFICATE_NAME
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1alpha2
# name: serving-cert # this name should match the one in certificate.yaml
#- name: SERVICE_NAMESPACE # namespace of the service
# objref:
# kind: Service
# version: v1
# name: webhook-service
# fieldref:
# fieldpath: metadata.namespace
#- name: SERVICE_NAME
# objref:
# kind: Service
# version: v1
# name: webhook-service
================================================
FILE: config/default/manager_auth_proxy_patch.yaml
================================================
# This patch inject a sidecar container which is a HTTP proxy for the
# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: kube-rbac-proxy
image: quay.io/coreos/kube-rbac-proxy:v0.11.0
args:
- "--secure-listen-address=0.0.0.0:8443"
- "--upstream=http://127.0.0.1:8080/"
- "--logtostderr=true"
- "--v=0"
- "--tls-cert-file=/etc/certs/tls/tls.crt"
- "--tls-private-key-file=/etc/certs/tls/tls.key"
volumeMounts:
- mountPath: /etc/certs/tls
name: tls-cert
ports:
- containerPort: 8443
name: https
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 5m
memory: 64Mi
- name: manager
args:
- "--metrics-addr=127.0.0.1:8080"
- "--enable-leader-election"
volumes:
- name: tls-cert
secret:
defaultMode: 420
secretName: operator-utils-operator-certs
================================================
FILE: config/default/manager_webhook_patch.yaml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: manager
ports:
- containerPort: 9443
name: webhook-server
protocol: TCP
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
volumes:
- name: cert
secret:
defaultMode: 420
secretName: webhook-server-cert
================================================
FILE: config/default/webhookcainjection_patch.yaml
================================================
# This patch add annotation to admission webhook config and
# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize.
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
name: mutating-webhook-configuration
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
name: validating-webhook-configuration
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
================================================
FILE: config/helmchart/.helmignore
================================================
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
================================================
FILE: config/helmchart/Chart.yaml.tpl
================================================
apiVersion: v1
name: operator-utils
version: ${version}
appVersion: ${version}
description: Helm chart that deploys operator-utils
keywords:
- volume
- storage
- csi
- expansion
- monitoring
sources:
- https://github.com/redhat-cop/operator-utils
engine: gotpl
================================================
FILE: config/helmchart/kustomization.yaml
================================================
# Adds namespace to all resources.
namespace: release-namespace
# Value of this field is prepended to the
# names of all resources, e.g. a deployment named
# "wordpress" becomes "alices-wordpress".
# Note that it should also match with the prefix (text before '-') of the namespace
# field above.
namePrefix: operator-utils-
# Labels to add to all resources and selectors.
#commonLabels:
# someName: someValue
bases:
- ../rbac
- ../prometheus
vars:
- name: METRICS_SERVICE_NAME
objref:
kind: Service
version: v1
name: controller-manager-metrics
- name: METRICS_SERVICE_NAMESPACE
objref:
kind: Service
version: v1
name: controller-manager-metrics
fieldref:
fieldpath: metadata.namespace
================================================
FILE: config/helmchart/templates/_helpers.tpl
================================================
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "operator-utils.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 "operator-utils.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 }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "operator-utils.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "operator-utils.labels" -}}
helm.sh/chart: {{ include "operator-utils.chart" . }}
{{ include "operator-utils.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "operator-utils.selectorLabels" -}}
app.kubernetes.io/name: {{ include "operator-utils.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "operator-utils.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "operator-utils.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
================================================
FILE: config/helmchart/templates/manager.yaml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "operator-utils.fullname" . }}
labels:
{{- include "operator-utils.labels" . | nindent 4 }}
operator: operator-utils-operator
spec:
selector:
matchLabels:
{{- include "operator-utils.selectorLabels" . | nindent 6 }}
replicas: {{ .Values.replicaCount }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "operator-utils.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: operator-utils-controller-manager
containers:
- args:
- --secure-listen-address=0.0.0.0:8443
- --upstream=http://127.0.0.1:8080/
- --logtostderr=true
- --tls-cert-file=/etc/certs/tls/tls.crt
- --tls-private-key-file=/etc/certs/tls/tls.key
- --v=10
image: "{{ .Values.kube_rbac_proxy.image.repository }}:{{ .Values.kube_rbac_proxy.image.tag }}"
name: kube-rbac-proxy
ports:
- containerPort: 8443
name: https
volumeMounts:
- mountPath: /etc/certs/tls
name: tls-cert
imagePullPolicy: {{ .Values.kube_rbac_proxy.image.pullPolicy }}
resources:
{{- toYaml .Values.kube_rbac_proxy.resources | nindent 10 }}
- command:
- /manager
args:
- --enable-leader-election
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
name: {{ .C
gitextract_fz5mf0kc/
├── .github/
│ └── workflows/
│ ├── pr.yaml
│ └── push.yaml
├── .gitignore
├── Dockerfile
├── LICENSE
├── Makefile
├── PROJECT
├── README.md
├── api/
│ └── v1alpha1/
│ ├── enforcingcrd_types.go
│ ├── enforcingpatch_types.go
│ ├── enforcingreconcilerstatus.go
│ ├── groupversion_info.go
│ ├── lockedpatch.go
│ ├── lockedresource.go
│ ├── mycrd_types.go
│ ├── templatedenforcingcrd_types.go
│ └── zz_generated.deepcopy.go
├── ci.Dockerfile
├── config/
│ ├── certmanager/
│ │ ├── certificate.yaml
│ │ ├── kustomization.yaml
│ │ └── kustomizeconfig.yaml
│ ├── crd/
│ │ ├── bases/
│ │ │ ├── operator-utils.example.io_enforcingcrds.yaml
│ │ │ ├── operator-utils.example.io_enforcingpatches.yaml
│ │ │ ├── operator-utils.example.io_mycrds.yaml
│ │ │ └── operator-utils.example.io_templatedenforcingcrds.yaml
│ │ ├── kustomization.yaml
│ │ ├── kustomizeconfig.yaml
│ │ └── patches/
│ │ ├── cainjection_in_enforcingcrds.yaml
│ │ ├── cainjection_in_enforcingpatches.yaml
│ │ ├── cainjection_in_mycrds.yaml
│ │ ├── cainjection_in_templatedenforcingcrds.yaml
│ │ ├── webhook_in_enforcingcrds.yaml
│ │ ├── webhook_in_enforcingpatches.yaml
│ │ ├── webhook_in_mycrds.yaml
│ │ └── webhook_in_templatedenforcingcrds.yaml
│ ├── default/
│ │ ├── kustomization.yaml
│ │ ├── manager_auth_proxy_patch.yaml
│ │ ├── manager_webhook_patch.yaml
│ │ └── webhookcainjection_patch.yaml
│ ├── helmchart/
│ │ ├── .helmignore
│ │ ├── Chart.yaml.tpl
│ │ ├── kustomization.yaml
│ │ ├── templates/
│ │ │ ├── _helpers.tpl
│ │ │ └── manager.yaml
│ │ └── values.yaml.tpl
│ ├── local-development/
│ │ └── kustomization.yaml
│ ├── manager/
│ │ ├── kustomization.yaml
│ │ └── manager.yaml
│ ├── manifests/
│ │ ├── bases/
│ │ │ └── operator-utils.clusterserviceversion.yaml
│ │ └── kustomization.yaml
│ ├── prometheus/
│ │ ├── kustomization.yaml
│ │ ├── kustomizeconfig.yaml
│ │ └── monitor.yaml
│ ├── rbac/
│ │ ├── auth_proxy_client_clusterrole.yaml
│ │ ├── auth_proxy_role.yaml
│ │ ├── auth_proxy_role_binding.yaml
│ │ ├── auth_proxy_service.yaml
│ │ ├── enforcingcrd_editor_role.yaml
│ │ ├── enforcingcrd_viewer_role.yaml
│ │ ├── enforcingpatch_editor_role.yaml
│ │ ├── enforcingpatch_viewer_role.yaml
│ │ ├── kustomization.yaml
│ │ ├── leader_election_role.yaml
│ │ ├── leader_election_role_binding.yaml
│ │ ├── mycrd_editor_role.yaml
│ │ ├── mycrd_viewer_role.yaml
│ │ ├── role.yaml
│ │ ├── role_binding.yaml
│ │ ├── templatedenforcingcrd_editor_role.yaml
│ │ └── templatedenforcingcrd_viewer_role.yaml
│ ├── samples/
│ │ ├── kustomization.yaml
│ │ ├── operator-utils_v1alpha1_enforcingcrd.yaml
│ │ ├── operator-utils_v1alpha1_enforcingpatch.yaml
│ │ ├── operator-utils_v1alpha1_mycrd.yaml
│ │ └── operator-utils_v1alpha1_templatedenforcingcrd.yaml
│ ├── scorecard/
│ │ ├── bases/
│ │ │ └── config.yaml
│ │ ├── kustomization.yaml
│ │ └── patches/
│ │ ├── basic.config.yaml
│ │ └── olm.config.yaml
│ └── webhook/
│ ├── kustomization.yaml
│ ├── kustomizeconfig.yaml
│ └── service.yaml
├── controllers/
│ ├── enforcingcrd_controller.go
│ ├── enforcingpatch_controller.go
│ ├── mycrd_controller.go
│ ├── suite_test.go
│ └── templatedenforcingcrd_controller.go
├── go.mod
├── go.sum
├── hack/
│ └── boilerplate.go.txt
├── main.go
├── pkg/
│ └── util/
│ ├── apis/
│ │ ├── conditions.go
│ │ └── key.go
│ ├── crud/
│ │ └── crudutils.go
│ ├── discoveryclient/
│ │ └── discoveryclientutils.go
│ ├── dynamicclient/
│ │ └── dynamicclientutils.go
│ ├── finalizer.go
│ ├── lockedresourcecontroller/
│ │ ├── enforcing-reconciler.go
│ │ ├── locked-resource-manager.go
│ │ ├── lockedpatch/
│ │ │ └── lockedpatch.go
│ │ ├── lockedresource/
│ │ │ ├── lockedresource.go
│ │ │ ├── lockedresourceset/
│ │ │ │ ├── lockedresourceset.go
│ │ │ │ ├── lockedresourceset_bench_test.go
│ │ │ │ └── lockedresourceset_test.go
│ │ │ └── patch.go
│ │ ├── patch-reconciler.go
│ │ └── resource-reconciler.go
│ ├── owner.go
│ ├── predicates.go
│ ├── reconciler.go
│ ├── stoppablemanager/
│ │ └── stoppable-manager.go
│ └── templates/
│ ├── advanced-funcmap.go
│ └── templates.go
├── test/
│ ├── enforcing-patch-multiple-cluster-level.yaml
│ ├── enforcing-patch-multiple.yaml
│ ├── enforcing-patch.yaml
│ ├── enforcing_cr.yaml
│ ├── failing-enforcing_cr.yaml
│ ├── mycrd_cr.yaml
│ └── templatedenforcing_cr.yaml
└── testbin/
└── setup-envtest.sh
SYMBOL INDEX (342 symbols across 34 files)
FILE: api/v1alpha1/enforcingcrd_types.go
type EnforcingCRDSpec (line 27) | type EnforcingCRDSpec struct
type EnforcingCRDStatus (line 38) | type EnforcingCRDStatus struct
type EnforcingCRD (line 58) | type EnforcingCRD struct
method GetEnforcingReconcileStatus (line 46) | func (m *EnforcingCRD) GetEnforcingReconcileStatus() EnforcingReconcil...
method SetEnforcingReconcileStatus (line 50) | func (m *EnforcingCRD) SetEnforcingReconcileStatus(reconcileStatus Enf...
type EnforcingCRDList (line 69) | type EnforcingCRDList struct
function init (line 75) | func init() {
FILE: api/v1alpha1/enforcingpatch_types.go
type EnforcingPatchSpec (line 27) | type EnforcingPatchSpec struct
type EnforcingPatchStatus (line 38) | type EnforcingPatchStatus struct
type EnforcingPatch (line 57) | type EnforcingPatch struct
method GetEnforcingReconcileStatus (line 45) | func (m *EnforcingPatch) GetEnforcingReconcileStatus() EnforcingReconc...
method SetEnforcingReconcileStatus (line 49) | func (m *EnforcingPatch) SetEnforcingReconcileStatus(reconcileStatus E...
type EnforcingPatchList (line 68) | type EnforcingPatchList struct
function init (line 74) | func init() {
FILE: api/v1alpha1/enforcingreconcilerstatus.go
type Conditions (line 7) | type Conditions
type ConditionMap (line 10) | type ConditionMap
type EnforcingReconcileStatus (line 13) | type EnforcingReconcileStatus struct
type EnforcingReconcileStatusAware (line 32) | type EnforcingReconcileStatusAware interface
FILE: api/v1alpha1/lockedpatch.go
type PatchSpec (line 25) | type PatchSpec struct
type TargetObjectReference (line 64) | type TargetObjectReference struct
method getAPIReourceForGVK (line 95) | func (t *TargetObjectReference) getAPIReourceForGVK(context context.Co...
method getDynamicClient (line 106) | func (t *TargetObjectReference) getDynamicClient(context context.Conte...
method GetReferencedObjectWithName (line 127) | func (t *TargetObjectReference) GetReferencedObjectWithName(context co...
method GetReferencedObject (line 160) | func (t *TargetObjectReference) GetReferencedObject(context context.Co...
method GetReferencedObjects (line 183) | func (t *TargetObjectReference) GetReferencedObjects(context context.C...
method IsNamespaced (line 240) | func (t *TargetObjectReference) IsNamespaced(context context.Context) ...
method IsSelectingMultipleInstances (line 252) | func (t *TargetObjectReference) IsSelectingMultipleInstances(context c...
method Selects (line 276) | func (t *TargetObjectReference) Selects(context context.Context, obj c...
function processTemplate (line 400) | func processTemplate(context context.Context, templateString string, par...
type SourceObjectReference (line 417) | type SourceObjectReference struct
method GetNameAndNamespace (line 334) | func (s *SourceObjectReference) GetNameAndNamespace(context context.Co...
method getAPIReourceForGVK (line 349) | func (t *SourceObjectReference) getAPIReourceForGVK(context context.Co...
method getDynamicClient (line 360) | func (t *SourceObjectReference) getDynamicClient(context context.Conte...
method GetReferencedObject (line 377) | func (s *SourceObjectReference) GetReferencedObject(context context.Co...
FILE: api/v1alpha1/lockedresource.go
type LockedResource (line 9) | type LockedResource struct
type LockedResourceTemplate (line 23) | type LockedResourceTemplate struct
FILE: api/v1alpha1/mycrd_types.go
type MyCRDSpec (line 27) | type MyCRDSpec struct
type MyCRDStatus (line 37) | type MyCRDStatus struct
type MyCRD (line 66) | type MyCRD struct
method GetConditions (line 54) | func (m *MyCRD) GetConditions() []metav1.Condition {
method SetConditions (line 58) | func (m *MyCRD) SetConditions(conditions []metav1.Condition) {
type MyCRDList (line 77) | type MyCRDList struct
function init (line 83) | func init() {
FILE: api/v1alpha1/templatedenforcingcrd_types.go
type TemplatedEnforcingCRDSpec (line 27) | type TemplatedEnforcingCRDSpec struct
type TemplatedEnforcingCRDStatus (line 37) | type TemplatedEnforcingCRDStatus struct
type TemplatedEnforcingCRD (line 57) | type TemplatedEnforcingCRD struct
method GetEnforcingReconcileStatus (line 45) | func (m *TemplatedEnforcingCRD) GetEnforcingReconcileStatus() Enforcin...
method SetEnforcingReconcileStatus (line 49) | func (m *TemplatedEnforcingCRD) SetEnforcingReconcileStatus(reconcileS...
type TemplatedEnforcingCRDList (line 68) | type TemplatedEnforcingCRDList struct
function init (line 74) | func init() {
FILE: api/v1alpha1/zz_generated.deepcopy.go
method DeepCopyInto (line 30) | func (in ConditionMap) DeepCopyInto(out *ConditionMap) {
method DeepCopy (line 51) | func (in ConditionMap) DeepCopy() ConditionMap {
method DeepCopyInto (line 61) | func (in Conditions) DeepCopyInto(out *Conditions) {
method DeepCopy (line 72) | func (in Conditions) DeepCopy() Conditions {
method DeepCopyInto (line 82) | func (in *EnforcingCRD) DeepCopyInto(out *EnforcingCRD) {
method DeepCopy (line 91) | func (in *EnforcingCRD) DeepCopy() *EnforcingCRD {
method DeepCopyObject (line 101) | func (in *EnforcingCRD) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 109) | func (in *EnforcingCRDList) DeepCopyInto(out *EnforcingCRDList) {
method DeepCopy (line 123) | func (in *EnforcingCRDList) DeepCopy() *EnforcingCRDList {
method DeepCopyObject (line 133) | func (in *EnforcingCRDList) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 141) | func (in *EnforcingCRDSpec) DeepCopyInto(out *EnforcingCRDSpec) {
method DeepCopy (line 153) | func (in *EnforcingCRDSpec) DeepCopy() *EnforcingCRDSpec {
method DeepCopyInto (line 163) | func (in *EnforcingCRDStatus) DeepCopyInto(out *EnforcingCRDStatus) {
method DeepCopy (line 169) | func (in *EnforcingCRDStatus) DeepCopy() *EnforcingCRDStatus {
method DeepCopyInto (line 179) | func (in *EnforcingPatch) DeepCopyInto(out *EnforcingPatch) {
method DeepCopy (line 188) | func (in *EnforcingPatch) DeepCopy() *EnforcingPatch {
method DeepCopyObject (line 198) | func (in *EnforcingPatch) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 206) | func (in *EnforcingPatchList) DeepCopyInto(out *EnforcingPatchList) {
method DeepCopy (line 220) | func (in *EnforcingPatchList) DeepCopy() *EnforcingPatchList {
method DeepCopyObject (line 230) | func (in *EnforcingPatchList) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 238) | func (in *EnforcingPatchSpec) DeepCopyInto(out *EnforcingPatchSpec) {
method DeepCopy (line 250) | func (in *EnforcingPatchSpec) DeepCopy() *EnforcingPatchSpec {
method DeepCopyInto (line 260) | func (in *EnforcingPatchStatus) DeepCopyInto(out *EnforcingPatchStatus) {
method DeepCopy (line 266) | func (in *EnforcingPatchStatus) DeepCopy() *EnforcingPatchStatus {
method DeepCopyInto (line 276) | func (in *EnforcingReconcileStatus) DeepCopyInto(out *EnforcingReconcile...
method DeepCopy (line 332) | func (in *EnforcingReconcileStatus) DeepCopy() *EnforcingReconcileStatus {
method DeepCopyInto (line 342) | func (in *LockedResource) DeepCopyInto(out *LockedResource) {
method DeepCopy (line 353) | func (in *LockedResource) DeepCopy() *LockedResource {
method DeepCopyInto (line 363) | func (in *LockedResourceTemplate) DeepCopyInto(out *LockedResourceTempla...
method DeepCopy (line 373) | func (in *LockedResourceTemplate) DeepCopy() *LockedResourceTemplate {
method DeepCopyInto (line 383) | func (in *MyCRD) DeepCopyInto(out *MyCRD) {
method DeepCopy (line 392) | func (in *MyCRD) DeepCopy() *MyCRD {
method DeepCopyObject (line 402) | func (in *MyCRD) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 410) | func (in *MyCRDList) DeepCopyInto(out *MyCRDList) {
method DeepCopy (line 424) | func (in *MyCRDList) DeepCopy() *MyCRDList {
method DeepCopyObject (line 434) | func (in *MyCRDList) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 442) | func (in *MyCRDSpec) DeepCopyInto(out *MyCRDSpec) {
method DeepCopy (line 447) | func (in *MyCRDSpec) DeepCopy() *MyCRDSpec {
method DeepCopyInto (line 457) | func (in *MyCRDStatus) DeepCopyInto(out *MyCRDStatus) {
method DeepCopy (line 469) | func (in *MyCRDStatus) DeepCopy() *MyCRDStatus {
method DeepCopyInto (line 479) | func (in *PatchSpec) DeepCopyInto(out *PatchSpec) {
method DeepCopy (line 492) | func (in *PatchSpec) DeepCopy() *PatchSpec {
method DeepCopyInto (line 502) | func (in *SourceObjectReference) DeepCopyInto(out *SourceObjectReference) {
method DeepCopy (line 512) | func (in *SourceObjectReference) DeepCopy() *SourceObjectReference {
method DeepCopyInto (line 522) | func (in *TargetObjectReference) DeepCopyInto(out *TargetObjectReference) {
method DeepCopy (line 542) | func (in *TargetObjectReference) DeepCopy() *TargetObjectReference {
method DeepCopyInto (line 552) | func (in *TemplatedEnforcingCRD) DeepCopyInto(out *TemplatedEnforcingCRD) {
method DeepCopy (line 561) | func (in *TemplatedEnforcingCRD) DeepCopy() *TemplatedEnforcingCRD {
method DeepCopyObject (line 571) | func (in *TemplatedEnforcingCRD) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 579) | func (in *TemplatedEnforcingCRDList) DeepCopyInto(out *TemplatedEnforcin...
method DeepCopy (line 593) | func (in *TemplatedEnforcingCRDList) DeepCopy() *TemplatedEnforcingCRDLi...
method DeepCopyObject (line 603) | func (in *TemplatedEnforcingCRDList) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 611) | func (in *TemplatedEnforcingCRDSpec) DeepCopyInto(out *TemplatedEnforcin...
method DeepCopy (line 623) | func (in *TemplatedEnforcingCRDSpec) DeepCopy() *TemplatedEnforcingCRDSp...
method DeepCopyInto (line 633) | func (in *TemplatedEnforcingCRDStatus) DeepCopyInto(out *TemplatedEnforc...
method DeepCopy (line 639) | func (in *TemplatedEnforcingCRDStatus) DeepCopy() *TemplatedEnforcingCRD...
FILE: controllers/enforcingcrd_controller.go
type EnforcingCRDReconciler (line 39) | type EnforcingCRDReconciler struct
method Reconcile (line 48) | func (r *EnforcingCRDReconciler) Reconcile(context context.Context, re...
method manageCleanUpLogic (line 105) | func (r *EnforcingCRDReconciler) manageCleanUpLogic(instance *v1alpha1...
method IsInitialized (line 116) | func (r *EnforcingCRDReconciler) IsInitialized(instance *v1alpha1.Enfo...
method SetupWithManager (line 136) | func (r *EnforcingCRDReconciler) SetupWithManager(mgr ctrl.Manager) er...
FILE: controllers/enforcingpatch_controller.go
type EnforcingPatchReconciler (line 37) | type EnforcingPatchReconciler struct
method Reconcile (line 46) | func (r *EnforcingPatchReconciler) Reconcile(context context.Context, ...
method IsInitialized (line 88) | func (r *EnforcingPatchReconciler) IsInitialized(instance *v1alpha1.En...
method SetupWithManager (line 100) | func (r *EnforcingPatchReconciler) SetupWithManager(mgr ctrl.Manager) ...
FILE: controllers/mycrd_controller.go
constant controllerName (line 33) | controllerName = "MyCRD_controller"
type MyCRDReconciler (line 38) | type MyCRDReconciler struct
method Reconcile (line 46) | func (r *MyCRDReconciler) Reconcile(context context.Context, req ctrl....
method SetupWithManager (line 101) | func (r *MyCRDReconciler) SetupWithManager(mgr ctrl.Manager) error {
method IsInitialized (line 107) | func (r *MyCRDReconciler) IsInitialized(obj metav1.Object) bool {
method IsValid (line 121) | func (r *MyCRDReconciler) IsValid(obj metav1.Object) (bool, error) {
method manageCleanUpLogic (line 132) | func (r *MyCRDReconciler) manageCleanUpLogic(mycrd *v1alpha1.MyCRD) er...
method manageOperatorLogic (line 136) | func (r *MyCRDReconciler) manageOperatorLogic(mycrd *v1alpha1.MyCRD) e...
FILE: controllers/suite_test.go
function TestAPIs (line 41) | func TestAPIs(t *testing.T) {
FILE: controllers/templatedenforcingcrd_controller.go
type TemplatedEnforcingCRDReconciler (line 39) | type TemplatedEnforcingCRDReconciler struct
method Reconcile (line 48) | func (r *TemplatedEnforcingCRDReconciler) Reconcile(context context.Co...
method IsInitialized (line 108) | func (r *TemplatedEnforcingCRDReconciler) IsInitialized(instance *v1al...
method manageCleanUpLogic (line 128) | func (r *TemplatedEnforcingCRDReconciler) manageCleanUpLogic(instance ...
method SetupWithManager (line 137) | func (r *TemplatedEnforcingCRDReconciler) SetupWithManager(mgr ctrl.Ma...
FILE: main.go
function init (line 42) | func init() {
function main (line 49) | func main() {
FILE: pkg/util/apis/conditions.go
constant ReconcileError (line 9) | ReconcileError = "ReconcileError"
constant ReconcileErrorReason (line 10) | ReconcileErrorReason = "LastReconcileCycleFailed"
constant ReconcileSuccess (line 11) | ReconcileSuccess = "ReconcileSuccess"
constant ReconcileSuccessReason (line 12) | ReconcileSuccessReason = "LastReconcileCycleSucceded"
type ConditionsAware (line 15) | type ConditionsAware interface
function AddOrReplaceCondition (line 21) | func AddOrReplaceCondition(c metav1.Condition, conditions []metav1.Condi...
function GetCondition (line 33) | func GetCondition(conditionType string, conditions []metav1.Condition) (...
function GetLastCondition (line 43) | func GetLastCondition(conditions []metav1.Condition) (metav1.Condition, ...
function IsErrorCondition (line 59) | func IsErrorCondition(condition metav1.Condition) bool {
FILE: pkg/util/apis/key.go
function GetKeyLong (line 15) | func GetKeyLong(obj metav1.Object) string {
function GetKeyShort (line 27) | func GetKeyShort(obj metav1.Object) string {
FILE: pkg/util/crud/crudutils.go
function CreateOrUpdateResource (line 20) | func CreateOrUpdateResource(context context.Context, owner client.Object...
function CreateOrUpdateResources (line 62) | func CreateOrUpdateResources(context context.Context, owner client.Objec...
function CreateOrUpdateUnstructuredResources (line 74) | func CreateOrUpdateUnstructuredResources(context context.Context, owner ...
function DeleteResourceIfExists (line 86) | func DeleteResourceIfExists(context context.Context, obj client.Object) ...
function DeleteResourcesIfExist (line 99) | func DeleteResourcesIfExist(context context.Context, objs []client.Objec...
function DeleteUnstructuredResources (line 111) | func DeleteUnstructuredResources(context context.Context, objs []unstruc...
function CreateResourceIfNotExists (line 125) | func CreateResourceIfNotExists(context context.Context, owner client.Obj...
function CreateResourcesIfNotExist (line 145) | func CreateResourcesIfNotExist(context context.Context, owner client.Obj...
function CreateUnstructuredResourcesIfNotExist (line 157) | func CreateUnstructuredResourcesIfNotExist(context context.Context, owne...
function CreateOrUpdateTemplatedResources (line 169) | func CreateOrUpdateTemplatedResources(context context.Context, owner cli...
function CreateIfNotExistTemplatedResources (line 187) | func CreateIfNotExistTemplatedResources(context context.Context, owner c...
function DeleteTemplatedResources (line 205) | func DeleteTemplatedResources(context context.Context, data interface{},...
FILE: pkg/util/discoveryclient/discoveryclientutils.go
function GetDiscoveryClient (line 17) | func GetDiscoveryClient(context context.Context) (*discovery.DiscoveryCl...
function IsGVKDefined (line 24) | func IsGVKDefined(context context.Context, GVK schema.GroupVersionKind) ...
function GetAPIResourceForGVK (line 29) | func GetAPIResourceForGVK(context context.Context, GVK schema.GroupVersi...
function IsGVKNamespaced (line 55) | func IsGVKNamespaced(context context.Context, GVK schema.GroupVersionKin...
function IsUnstructuredDefined (line 65) | func IsUnstructuredDefined(context context.Context, obj *unstructured.Un...
function IsUnstructuredNamespaced (line 71) | func IsUnstructuredNamespaced(context context.Context, obj *unstructured...
FILE: pkg/util/dynamicclient/dynamicclientutils.go
function GetDynamicClientOnUnstructured (line 23) | func GetDynamicClientOnUnstructured(context context.Context, obj *unstru...
function GetDynamicClientForAPIResource (line 43) | func GetDynamicClientForAPIResource(context context.Context, resource *m...
function getDynamicClientForGVR (line 51) | func getDynamicClientForGVR(context context.Context, gvr schema.GroupVer...
function GetDynamicClientForGVK (line 65) | func GetDynamicClientForGVK(context context.Context, gvk schema.GroupVer...
function getAPIReourceForGVK (line 80) | func getAPIReourceForGVK(context context.Context, gvk schema.GroupVersio...
function SetIndexField (line 107) | func SetIndexField(context context.Context, cache cache.Cache, obj clien...
FILE: pkg/util/finalizer.go
function IsBeingDeleted (line 25) | func IsBeingDeleted(obj client.Object) bool {
function HasFinalizer (line 31) | func HasFinalizer(obj client.Object, finalizer string) bool {
function AddFinalizer (line 37) | func AddFinalizer(obj client.Object, finalizer string) {
function RemoveFinalizer (line 43) | func RemoveFinalizer(obj client.Object, finalizer string) {
FILE: pkg/util/lockedresourcecontroller/enforcing-reconciler.go
type EnforcingReconciler (line 28) | type EnforcingReconciler struct
method GetStatusChangeChannel (line 59) | func (er *EnforcingReconciler) GetStatusChangeChannel() <-chan event.G...
method removeLockedResourceManager (line 63) | func (er *EnforcingReconciler) removeLockedResourceManager(instance cl...
method getLockedResourceManager (line 69) | func (er *EnforcingReconciler) getLockedResourceManager(instance clien...
method UpdateLockedResources (line 90) | func (er *EnforcingReconciler) UpdateLockedResources(context context.C...
method UpdateLockedResourcesWithRestConfig (line 101) | func (er *EnforcingReconciler) UpdateLockedResourcesWithRestConfig(con...
method ManageError (line 146) | func (er *EnforcingReconciler) ManageError(context context.Context, in...
method ManageSuccess (line 179) | func (er *EnforcingReconciler) ManageSuccess(context context.Context, ...
method GetLockedResourceStatuses (line 210) | func (er *EnforcingReconciler) GetLockedResourceStatuses(instance clie...
method GetLockedPatchStatuses (line 231) | func (er *EnforcingReconciler) GetLockedPatchStatuses(instance client....
method Terminate (line 258) | func (er *EnforcingReconciler) Terminate(instance client.Object, delet...
function NewEnforcingReconciler (line 42) | func NewEnforcingReconciler(client client.Client, scheme *runtime.Scheme...
function NewFromManager (line 54) | func NewFromManager(mgr manager.Manager, recorderName string, clusterWat...
function getToBeDeletdResources (line 126) | func getToBeDeletdResources(neededResources []lockedresource.LockedResou...
FILE: pkg/util/lockedresourcecontroller/locked-resource-manager.go
type LockedResourceManager (line 36) | type LockedResourceManager struct
method GetResources (line 68) | func (lrm *LockedResourceManager) GetResources() []lockedresource.Lock...
method GetPatches (line 73) | func (lrm *LockedResourceManager) GetPatches() []lockedpatch.LockedPat...
method SetResources (line 78) | func (lrm *LockedResourceManager) SetResources(resources []lockedresou...
method SetPatches (line 92) | func (lrm *LockedResourceManager) SetPatches(patches []lockedpatch.Loc...
method IsStarted (line 117) | func (lrm *LockedResourceManager) IsStarted() bool {
method Start (line 122) | func (lrm *LockedResourceManager) Start(ctx context.Context, config *r...
method Stop (line 175) | func (lrm *LockedResourceManager) Stop(deleteResources bool) error {
method scanNamespaces (line 187) | func (lrm *LockedResourceManager) scanNamespaces() []string {
method Restart (line 213) | func (lrm *LockedResourceManager) Restart(ctx context.Context, resourc...
method IsSameResources (line 240) | func (lrm *LockedResourceManager) IsSameResources(resources []lockedre...
method IsSamePatches (line 255) | func (lrm *LockedResourceManager) IsSamePatches(patches []lockedpatch....
method deleteResources (line 284) | func (lrm *LockedResourceManager) deleteResources(context context.Cont...
method GetResourceReconcilers (line 300) | func (lrm *LockedResourceManager) GetResourceReconcilers() []*LockedRe...
method validateLockedResources (line 307) | func (lrm *LockedResourceManager) validateLockedResources(lockedResour...
method GetPatchReconcilers (line 368) | func (lrm *LockedResourceManager) GetPatchReconcilers() []*LockedPatch...
method validateLockedPatches (line 375) | func (lrm *LockedResourceManager) validateLockedPatches(patches []lock...
function NewLockedResourceManager (line 55) | func NewLockedResourceManager(config *rest.Config, options manager.Optio...
FILE: pkg/util/lockedresourcecontroller/lockedpatch/lockedpatch.go
type LockedPatch (line 18) | type LockedPatch struct
method GetKey (line 28) | func (lp *LockedPatch) GetKey() string {
function GetLockedPatchMap (line 33) | func GetLockedPatchMap(lockedPatches []LockedPatch) (map[string]LockedPa...
function GetLockedPatchesFromLockedPatcheSet (line 43) | func GetLockedPatchesFromLockedPatcheSet(lockedPatchSet *strset.Set, loc...
function GetLockedPatches (line 52) | func GetLockedPatches(patches map[string]utilsapi.PatchSpec, config *res...
FILE: pkg/util/lockedresourcecontroller/lockedresource/lockedresource.go
type LockedResource (line 23) | type LockedResource struct
method GetKey (line 40) | func (lr *LockedResource) GetKey() string {
function AsListOfUnstructured (line 31) | func AsListOfUnstructured(lockedResources []LockedResource) []unstructur...
function GetLockedResources (line 50) | func GetLockedResources(resources []utilsapi.LockedResource) ([]LockedRe...
function GetLockedResourcesFromTemplates (line 75) | func GetLockedResourcesFromTemplates(resources []utilsapi.LockedResource...
function GetLockedResourcesFromTemplatesWithRestConfig (line 81) | func GetLockedResourcesFromTemplatesWithRestConfig(resources []utilsapi....
function getTemplate (line 107) | func getTemplate(resource *utilsapi.LockedResourceTemplate, config *rest...
function GetResources (line 128) | func GetResources(lockedResources []LockedResource) []client.Object {
FILE: pkg/util/lockedresourcecontroller/lockedresource/lockedresourceset/lockedresourceset.go
type Set (line 22) | type Set struct
method Add (line 40) | func (s *Set) Add(items ...lockedresource.LockedResource) {
method Remove (line 48) | func (s *Set) Remove(items ...lockedresource.LockedResource) {
method Pop (line 56) | func (s *Set) Pop() lockedresource.LockedResource {
method Pop2 (line 68) | func (s *Set) Pop2() (lockedresource.LockedResource, bool) {
method Has (line 78) | func (s *Set) Has(items ...lockedresource.LockedResource) bool {
method HasAny (line 91) | func (s *Set) HasAny(items ...lockedresource.LockedResource) bool {
method Size (line 102) | func (s *Set) Size() int {
method Clear (line 107) | func (s *Set) Clear() {
method IsEmpty (line 112) | func (s *Set) IsEmpty() bool {
method IsEqual (line 117) | func (s *Set) IsEqual(t *Set) bool {
method IsSubset (line 133) | func (s *Set) IsSubset(t *Set) bool {
method IsSuperset (line 149) | func (s *Set) IsSuperset(t *Set) bool {
method Each (line 156) | func (s *Set) Each(f func(item lockedresource.LockedResource) bool) {
method Copy (line 165) | func (s *Set) Copy() *Set {
method String (line 174) | func (s *Set) String() string {
method List (line 184) | func (s *Set) List() []lockedresource.LockedResource {
method Merge (line 194) | func (s *Set) Merge(t *Set) {
method Separate (line 202) | func (s *Set) Separate(t *Set) {
function New (line 27) | func New(ts ...lockedresource.LockedResource) *Set {
function NewWithSize (line 34) | func NewWithSize(size int) *Set {
function Union (line 210) | func Union(sets ...*Set) *Set {
function Difference (line 237) | func Difference(set1 *Set, sets ...*Set) *Set {
function Intersection (line 247) | func Intersection(sets ...*Set) *Set {
function SymmetricDifference (line 276) | func SymmetricDifference(s *Set, t *Set) *Set {
FILE: pkg/util/lockedresourcecontroller/lockedresource/patch.go
function FilterOutPaths (line 11) | func FilterOutPaths(obj *unstructured.Unstructured, jsonPaths []string) ...
type Patch (line 53) | type Patch struct
function createPatchesFromJSONPaths (line 58) | func createPatchesFromJSONPaths(jsonPaths []string) ([][]byte, error) {
function getMergePathFromJSONPath (line 77) | func getMergePathFromJSONPath(jsonPath string) string {
FILE: pkg/util/lockedresourcecontroller/patch-reconciler.go
type LockedPatchReconciler (line 39) | type LockedPatchReconciler struct
method Reconcile (line 438) | func (lpr *LockedPatchReconciler) Reconcile(ctx context.Context, reque...
method GetKey (line 492) | func (lpr *LockedPatchReconciler) GetKey() string {
method manageError (line 522) | func (lpr *LockedPatchReconciler) manageError(target client.Object, er...
method manageErrorNoTarget (line 535) | func (lpr *LockedPatchReconciler) manageErrorNoTarget(err error) (reco...
method manageSuccess (line 548) | func (lpr *LockedPatchReconciler) manageSuccess(target client.Object) ...
method setStatus (line 560) | func (lpr *LockedPatchReconciler) setStatus(key string, conditions []m...
method GetStatus (line 572) | func (lpr *LockedPatchReconciler) GetStatus() map[string][]metav1.Cond...
function NewLockedPatchReconciler (line 50) | func NewLockedPatchReconciler(mgr manager.Manager, patch lockedpatch.Loc...
function sourceObjectRefToRuntimeType (line 117) | func sourceObjectRefToRuntimeType(objref *utilsapi.SourceObjectReference...
function targetObjectRefToRuntimeType (line 124) | func targetObjectRefToRuntimeType(objref *utilsapi.TargetObjectReference...
type enqueueRequestForPatch (line 131) | type enqueueRequestForPatch struct
method Create (line 139) | func (e *enqueueRequestForPatch) Create(ctx context.Context, evt event...
method Update (line 212) | func (e *enqueueRequestForPatch) Update(ctx context.Context, evt event...
method Delete (line 275) | func (e *enqueueRequestForPatch) Delete(ctx context.Context, evt event...
method Generic (line 279) | func (e *enqueueRequestForPatch) Generic(ctx context.Context, evt even...
type sourceReferenceModifiedPredicate (line 282) | type sourceReferenceModifiedPredicate struct
method Update (line 290) | func (p *sourceReferenceModifiedPredicate) Update(e event.UpdateEvent)...
method Create (line 298) | func (p *sourceReferenceModifiedPredicate) Create(e event.CreateEvent)...
method isRelevant (line 304) | func (p *sourceReferenceModifiedPredicate) isRelevant(obj client.Objec...
method Delete (line 335) | func (p *sourceReferenceModifiedPredicate) Delete(e event.DeleteEvent)...
method Generic (line 340) | func (p *sourceReferenceModifiedPredicate) Generic(e event.GenericEven...
type targetReferenceModifiedPredicate (line 345) | type targetReferenceModifiedPredicate struct
method Update (line 352) | func (p *targetReferenceModifiedPredicate) Update(e event.UpdateEvent)...
method Create (line 369) | func (p *targetReferenceModifiedPredicate) Create(e event.CreateEvent)...
method Delete (line 382) | func (p *targetReferenceModifiedPredicate) Delete(e event.DeleteEvent)...
method Generic (line 387) | func (p *targetReferenceModifiedPredicate) Generic(e event.GenericEven...
function compareObjectsWithoutIgnoredFields (line 393) | func compareObjectsWithoutIgnoredFields(changedObjSrc runtime.Object, or...
function compareSourceObjects (line 408) | func compareSourceObjects(ctx context.Context, sourceObjectReference *ut...
function getSubMapFromObject (line 496) | func getSubMapFromObject(ctx context.Context, obj *unstructured.Unstruct...
FILE: pkg/util/lockedresourcecontroller/resource-reconciler.go
type LockedResourceReconciler (line 38) | type LockedResourceReconciler struct
method Reconcile (line 112) | func (lor *LockedResourceReconciler) Reconcile(ctx context.Context, re...
method isEqual (line 168) | func (lor *LockedResourceReconciler) isEqual(instance *unstructured.Un...
method logDiff (line 180) | func (lor *LockedResourceReconciler) logDiff(instance *unstructured.Un...
method manageError (line 250) | func (lor *LockedResourceReconciler) manageError(instance *unstructure...
method manageErrorNoInstance (line 269) | func (lor *LockedResourceReconciler) manageErrorNoInstance(err error) ...
method manageSuccess (line 282) | func (lor *LockedResourceReconciler) manageSuccess(instance *unstructu...
method manageSuccessNoInstance (line 294) | func (lor *LockedResourceReconciler) manageSuccessNoInstance() (reconc...
method setStatus (line 306) | func (lor *LockedResourceReconciler) setStatus(status []metav1.Conditi...
method GetStatus (line 318) | func (lor *LockedResourceReconciler) GetStatus() []metav1.Condition {
function NewLockedObjectReconciler (line 51) | func NewLockedObjectReconciler(mgr manager.Manager, object unstructured....
type resourceModifiedPredicate (line 205) | type resourceModifiedPredicate struct
method Update (line 213) | func (p *resourceModifiedPredicate) Update(e event.UpdateEvent) bool {
method Create (line 220) | func (p *resourceModifiedPredicate) Create(e event.CreateEvent) bool {
method Delete (line 227) | func (p *resourceModifiedPredicate) Delete(e event.DeleteEvent) bool {
FILE: pkg/util/owner.go
function IsOwner (line 24) | func IsOwner(owner, owned metav1.Object) bool {
FILE: pkg/util/predicates.go
type ResourceGenerationOrFinalizerChangedPredicate (line 27) | type ResourceGenerationOrFinalizerChangedPredicate struct
method Update (line 32) | func (ResourceGenerationOrFinalizerChangedPredicate) Update(e event.Up...
FILE: pkg/util/reconciler.go
type ReconcilerBase (line 52) | type ReconcilerBase struct
method IsValid (line 76) | func (r *ReconcilerBase) IsValid(obj metav1.Object) (bool, error) {
method IsInitialized (line 81) | func (r *ReconcilerBase) IsInitialized(obj metav1.Object) bool {
method Reconcile (line 86) | func (r *ReconcilerBase) Reconcile(request reconcile.Request) (reconci...
method GetClient (line 91) | func (r *ReconcilerBase) GetClient() client.Client {
method GetRestConfig (line 96) | func (r *ReconcilerBase) GetRestConfig() *rest.Config {
method GetRecorder (line 101) | func (r *ReconcilerBase) GetRecorder() record.EventRecorder {
method GetScheme (line 106) | func (r *ReconcilerBase) GetScheme() *runtime.Scheme {
method GetDiscoveryClient (line 111) | func (r *ReconcilerBase) GetDiscoveryClient() (*discovery.DiscoveryCli...
method CreateOrUpdateResource (line 118) | func (r *ReconcilerBase) CreateOrUpdateResource(context context.Contex...
method CreateOrUpdateResources (line 158) | func (r *ReconcilerBase) CreateOrUpdateResources(context context.Conte...
method CreateOrUpdateUnstructuredResources (line 169) | func (r *ReconcilerBase) CreateOrUpdateUnstructuredResources(context c...
method DeleteResourceIfExists (line 180) | func (r *ReconcilerBase) DeleteResourceIfExists(context context.Contex...
method DeleteResourcesIfExist (line 191) | func (r *ReconcilerBase) DeleteResourcesIfExist(context context.Contex...
method DeleteUnstructuredResources (line 202) | func (r *ReconcilerBase) DeleteUnstructuredResources(context context.C...
method CreateResourceIfNotExists (line 215) | func (r *ReconcilerBase) CreateResourceIfNotExists(context context.Con...
method CreateResourcesIfNotExist (line 233) | func (r *ReconcilerBase) CreateResourcesIfNotExist(context context.Con...
method CreateUnstructuredResourcesIfNotExist (line 244) | func (r *ReconcilerBase) CreateUnstructuredResourcesIfNotExist(context...
method CreateOrUpdateTemplatedResources (line 255) | func (r *ReconcilerBase) CreateOrUpdateTemplatedResources(context cont...
method CreateIfNotExistTemplatedResources (line 272) | func (r *ReconcilerBase) CreateIfNotExistTemplatedResources(context co...
method DeleteTemplatedResources (line 289) | func (r *ReconcilerBase) DeleteTemplatedResources(context context.Cont...
method ManageOutcomeWithRequeue (line 306) | func (r *ReconcilerBase) ManageOutcomeWithRequeue(context context.Cont...
method ManageErrorWithRequeue (line 317) | func (r *ReconcilerBase) ManageErrorWithRequeue(context context.Contex...
method ManageError (line 345) | func (r *ReconcilerBase) ManageError(context context.Context, obj clie...
method ManageSuccessWithRequeue (line 350) | func (r *ReconcilerBase) ManageSuccessWithRequeue(context context.Cont...
method ManageSuccess (line 373) | func (r *ReconcilerBase) ManageSuccess(context context.Context, obj cl...
method GetDirectClient (line 378) | func (r *ReconcilerBase) GetDirectClient() (client.Client, error) {
method GetDirectClientWithSchemeBuilders (line 383) | func (r *ReconcilerBase) GetDirectClientWithSchemeBuilders(addToScheme...
method GetAPIReader (line 397) | func (r *ReconcilerBase) GetAPIReader() client.Reader {
method GetOperatorNamespace (line 403) | func (r *ReconcilerBase) GetOperatorNamespace() (string, error) {
function NewReconcilerBase (line 60) | func NewReconcilerBase(client client.Client, scheme *runtime.Scheme, res...
function NewFromManager (line 71) | func NewFromManager(mgr manager.Manager, recorder record.EventRecorder) ...
FILE: pkg/util/stoppablemanager/stoppable-manager.go
type StoppableManager (line 15) | type StoppableManager struct
method Stop (line 22) | func (sm *StoppableManager) Stop() {
method Start (line 33) | func (sm *StoppableManager) Start(parentCtx context.Context) {
method IsStarted (line 61) | func (sm *StoppableManager) IsStarted() bool {
function NewStoppableManager (line 50) | func NewStoppableManager(config *rest.Config, options manager.Options) (...
FILE: pkg/util/templates/advanced-funcmap.go
function AdvancedTemplateFuncMap (line 43) | func AdvancedTemplateFuncMap(config *rest.Config, logger logr.Logger) te...
function toYAML (line 90) | func toYAML(v interface{}) string {
function fromYAML (line 105) | func fromYAML(str string) map[string]interface{} {
function fromYAMLArray (line 120) | func fromYAMLArray(str string) []interface{} {
function toTOML (line 133) | func toTOML(v interface{}) string {
function toJSON (line 147) | func toJSON(v interface{}) string {
function fromJSON (line 162) | func fromJSON(str string) map[string]interface{} {
function fromJSONArray (line 177) | func fromJSONArray(str string) []interface{} {
function NewLookupFunction (line 189) | func NewLookupFunction(config *rest.Config, logger logr.Logger) lookupFu...
FILE: pkg/util/templates/templates.go
function ProcessTemplate (line 33) | func ProcessTemplate(context context.Context, data interface{}, template...
function ProcessTemplateArray (line 59) | func ProcessTemplateArray(context context.Context, data interface{}, tem...
function ValidateUnstructured (line 110) | func ValidateUnstructured(context context.Context, obj *unstructured.Uns...
function IsJSONArray (line 126) | func IsJSONArray(data []byte) bool {
Condensed preview — 121 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (531K chars).
[
{
"path": ".github/workflows/pr.yaml",
"chars": 6236,
"preview": "name: pull request\non:\n pull_request:\n branches:\n - master\n - main\n\njobs:\n setup:\n runs-on: ubuntu-lat"
},
{
"path": ".github/workflows/push.yaml",
"chars": 6780,
"preview": "name: push\non:\n push:\n branches:\n - main\n - master\n tags:\n - v*\n\njobs:\n setup:\n runs-on: ubunt"
},
{
"path": ".gitignore",
"chars": 383,
"preview": "\n# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\nbin\n\n# Test binary, build with `go test -c`\n*.test\n"
},
{
"path": "Dockerfile",
"chars": 802,
"preview": "# Build the manager binary\nFROM golang:1.18 as builder\n\nWORKDIR /workspace\n# Copy the Go Modules manifests\nCOPY go.mod g"
},
{
"path": "LICENSE",
"chars": 11358,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "Makefile",
"chars": 11150,
"preview": "# VERSION defines the project version for the bundle.\n# Update this value when you upgrade the version of your project.\n"
},
{
"path": "PROJECT",
"chars": 822,
"preview": "domain: example.io\nlayout:\n- go.kubebuilder.io/v3\nprojectName: operator-utils\nrepo: github.com/redhat-cop/operator-utils"
},
{
"path": "README.md",
"chars": 19747,
"preview": "# Operator Utility Library\n\n\n[![Go"
},
{
"path": "api/v1alpha1/enforcingcrd_types.go",
"chars": 2778,
"preview": "/*\n\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with"
},
{
"path": "api/v1alpha1/enforcingpatch_types.go",
"chars": 2729,
"preview": "/*\n\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with"
},
{
"path": "api/v1alpha1/enforcingreconcilerstatus.go",
"chars": 1480,
"preview": "package v1alpha1\n\nimport metav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\n// +listType=map\n// +listMapKey=type\ntype Condit"
},
{
"path": "api/v1alpha1/groupversion_info.go",
"chars": 1238,
"preview": "/*\n\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with"
},
{
"path": "api/v1alpha1/lockedpatch.go",
"chars": 17910,
"preview": "package v1alpha1\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"text/template\"\n\n\t\"github.com/redhat-cop/operator-utils/pkg/ut"
},
{
"path": "api/v1alpha1/lockedresource.go",
"chars": 1234,
"preview": "package v1alpha1\n\nimport (\n\t\"k8s.io/apimachinery/pkg/runtime\"\n)\n\n// LockedResource represents a resource to be enforced "
},
{
"path": "api/v1alpha1/mycrd_types.go",
"chars": 2809,
"preview": "/*\n\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with"
},
{
"path": "api/v1alpha1/templatedenforcingcrd_types.go",
"chars": 2856,
"preview": "/*\n\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with"
},
{
"path": "api/v1alpha1/zz_generated.deepcopy.go",
"chars": 19052,
"preview": "//go:build !ignore_autogenerated\n// +build !ignore_autogenerated\n\n/*\n\n\nLicensed under the Apache License, Version 2.0 (t"
},
{
"path": "ci.Dockerfile",
"chars": 119,
"preview": "FROM registry.access.redhat.com/ubi8/ubi-minimal\nWORKDIR /\nCOPY bin/manager .\nUSER 65532:65532\n\nENTRYPOINT [\"/manager\"]"
},
{
"path": "config/certmanager/certificate.yaml",
"chars": 943,
"preview": "# The following manifests contain a self-signed issuer CR and a certificate CR.\n# More document can be found at https://"
},
{
"path": "config/certmanager/kustomization.yaml",
"chars": 70,
"preview": "resources:\n- certificate.yaml\n\nconfigurations:\n- kustomizeconfig.yaml\n"
},
{
"path": "config/certmanager/kustomizeconfig.yaml",
"chars": 391,
"preview": "# This configuration is for teaching kustomize how to update name ref and var substitution \nnameReference:\n- kind: Issue"
},
{
"path": "config/crd/bases/operator-utils.example.io_enforcingcrds.yaml",
"chars": 16585,
"preview": "---\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n annotations:\n controller-gen.kubeb"
},
{
"path": "config/crd/bases/operator-utils.example.io_enforcingpatches.yaml",
"chars": 27198,
"preview": "---\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n annotations:\n controller-gen.kubeb"
},
{
"path": "config/crd/bases/operator-utils.example.io_mycrds.yaml",
"chars": 6454,
"preview": "---\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n annotations:\n controller-gen.kubeb"
},
{
"path": "config/crd/bases/operator-utils.example.io_templatedenforcingcrds.yaml",
"chars": 16729,
"preview": "---\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n annotations:\n controller-gen.kubeb"
},
{
"path": "config/crd/kustomization.yaml",
"chars": 1366,
"preview": "# This kustomization.yaml is not intended to be run by itself,\n# since it depends on service name and namespace that are"
},
{
"path": "config/crd/kustomizeconfig.yaml",
"chars": 474,
"preview": "# This file is for teaching kustomize how to substitute name and namespace reference in CRD\nnameReference:\n- kind: Servi"
},
{
"path": "config/crd/patches/cainjection_in_enforcingcrds.yaml",
"chars": 352,
"preview": "# The following patch adds a directive for certmanager to inject CA into the CRD\n# CRD conversion requires k8s 1.13 or l"
},
{
"path": "config/crd/patches/cainjection_in_enforcingpatches.yaml",
"chars": 355,
"preview": "# The following patch adds a directive for certmanager to inject CA into the CRD\n# CRD conversion requires k8s 1.13 or l"
},
{
"path": "config/crd/patches/cainjection_in_mycrds.yaml",
"chars": 345,
"preview": "# The following patch adds a directive for certmanager to inject CA into the CRD\n# CRD conversion requires k8s 1.13 or l"
},
{
"path": "config/crd/patches/cainjection_in_templatedenforcingcrds.yaml",
"chars": 361,
"preview": "# The following patch adds a directive for certmanager to inject CA into the CRD\n# CRD conversion requires k8s 1.13 or l"
},
{
"path": "config/crd/patches/webhook_in_enforcingcrds.yaml",
"chars": 636,
"preview": "# The following patch enables conversion webhook for CRD\n# CRD conversion requires k8s 1.13 or later.\napiVersion: apiext"
},
{
"path": "config/crd/patches/webhook_in_enforcingpatches.yaml",
"chars": 639,
"preview": "# The following patch enables conversion webhook for CRD\n# CRD conversion requires k8s 1.13 or later.\napiVersion: apiext"
},
{
"path": "config/crd/patches/webhook_in_mycrds.yaml",
"chars": 629,
"preview": "# The following patch enables conversion webhook for CRD\n# CRD conversion requires k8s 1.13 or later.\napiVersion: apiext"
},
{
"path": "config/crd/patches/webhook_in_templatedenforcingcrds.yaml",
"chars": 645,
"preview": "# The following patch enables conversion webhook for CRD\n# CRD conversion requires k8s 1.13 or later.\napiVersion: apiext"
},
{
"path": "config/default/kustomization.yaml",
"chars": 2700,
"preview": "# Adds namespace to all resources.\nnamespace: operator-utils\n\n# Value of this field is prepended to the\n# names of all r"
},
{
"path": "config/default/manager_auth_proxy_patch.yaml",
"chars": 1241,
"preview": "# This patch inject a sidecar container which is a HTTP proxy for the \n# controller manager, it performs RBAC authorizat"
},
{
"path": "config/default/manager_webhook_patch.yaml",
"chars": 508,
"preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: controller-manager\n namespace: system\nspec:\n template:\n spec"
},
{
"path": "config/default/webhookcainjection_patch.yaml",
"chars": 625,
"preview": "# This patch add annotation to admission webhook config and\n# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_N"
},
{
"path": "config/helmchart/.helmignore",
"chars": 349,
"preview": "# Patterns to ignore when building packages.\n# This supports shell glob matching, relative path matching, and\n# negation"
},
{
"path": "config/helmchart/Chart.yaml.tpl",
"chars": 272,
"preview": "apiVersion: v1\nname: operator-utils\nversion: ${version}\nappVersion: ${version}\ndescription: Helm chart that deploys oper"
},
{
"path": "config/helmchart/kustomization.yaml",
"chars": 725,
"preview": "# Adds namespace to all resources.\nnamespace: release-namespace\n\n# Value of this field is prepended to the\n# names of al"
},
{
"path": "config/helmchart/templates/_helpers.tpl",
"chars": 1890,
"preview": "{{/* vim: set filetype=mustache: */}}\n{{/*\nExpand the name of the chart.\n*/}}\n{{- define \"operator-utils.name\" -}}\n{{- d"
},
{
"path": "config/helmchart/templates/manager.yaml",
"chars": 2287,
"preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: {{ include \"operator-utils.fullname\" . }}\n labels:\n {{- inclu"
},
{
"path": "config/helmchart/values.yaml.tpl",
"chars": 982,
"preview": "# Default values for helm-try.\n# This is a YAML-formatted file.\n# Declare variables to be passed into your templates.\n\nr"
},
{
"path": "config/local-development/kustomization.yaml",
"chars": 451,
"preview": "# Adds namespace to all resources.\nnamespace: operator-utils-operator-local\n\n# Value of this field is prepended to the\n#"
},
{
"path": "config/manager/kustomization.yaml",
"chars": 185,
"preview": "resources:\n- manager.yaml\napiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\nimages:\n- name: controller\n n"
},
{
"path": "config/manager/manager.yaml",
"chars": 825,
"preview": "apiVersion: v1\nkind: Namespace\nmetadata:\n labels:\n operator: operator-utils-operator\n name: system\n---\napiVersion: "
},
{
"path": "config/manifests/bases/operator-utils.clusterserviceversion.yaml",
"chars": 68410,
"preview": "apiVersion: operators.coreos.com/v1alpha1\nkind: ClusterServiceVersion\nmetadata:\n annotations:\n alm-examples: '[]'\n "
},
{
"path": "config/manifests/kustomization.yaml",
"chars": 52,
"preview": "resources:\n- ../default\n- ../samples\n- ../scorecard\n"
},
{
"path": "config/prometheus/kustomization.yaml",
"chars": 66,
"preview": "resources:\n- monitor.yaml\n\nconfigurations:\n- kustomizeconfig.yaml\n"
},
{
"path": "config/prometheus/kustomizeconfig.yaml",
"chars": 85,
"preview": "---\nvarReference:\n- path: spec/endpoints/tlsConfig/serverName\n kind: ServiceMonitor "
},
{
"path": "config/prometheus/monitor.yaml",
"chars": 616,
"preview": "\n# Prometheus Monitor Service (Metrics)\napiVersion: monitoring.coreos.com/v1\nkind: ServiceMonitor\nmetadata:\n labels:\n "
},
{
"path": "config/rbac/auth_proxy_client_clusterrole.yaml",
"chars": 148,
"preview": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n name: metrics-reader\nrules:\n- nonResourceURLs: [\""
},
{
"path": "config/rbac/auth_proxy_role.yaml",
"chars": 280,
"preview": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n name: proxy-role\nrules:\n- apiGroups: [\"authentica"
},
{
"path": "config/rbac/auth_proxy_role_binding.yaml",
"chars": 268,
"preview": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRoleBinding\nmetadata:\n name: proxy-rolebinding\nroleRef:\n apiGrou"
},
{
"path": "config/rbac/auth_proxy_service.yaml",
"chars": 366,
"preview": "apiVersion: v1\nkind: Service\nmetadata:\n labels:\n operator: operator-utils-operator\n annotations:\n service.alpha."
},
{
"path": "config/rbac/enforcingcrd_editor_role.yaml",
"chars": 411,
"preview": "# permissions for end users to edit enforcingcrds.\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n"
},
{
"path": "config/rbac/enforcingcrd_viewer_role.yaml",
"chars": 368,
"preview": "# permissions for end users to view enforcingcrds.\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n"
},
{
"path": "config/rbac/enforcingpatch_editor_role.yaml",
"chars": 422,
"preview": "# permissions for end users to edit enforcingpatches.\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadat"
},
{
"path": "config/rbac/enforcingpatch_viewer_role.yaml",
"chars": 379,
"preview": "# permissions for end users to view enforcingpatches.\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadat"
},
{
"path": "config/rbac/kustomization.yaml",
"chars": 381,
"preview": "resources:\n- role.yaml\n- role_binding.yaml\n- leader_election_role.yaml\n- leader_election_role_binding.yaml\n# Comment the"
},
{
"path": "config/rbac/leader_election_role.yaml",
"chars": 571,
"preview": "# permissions to do leader election.\napiVersion: rbac.authorization.k8s.io/v1\nkind: Role\nmetadata:\n name: leader-electi"
},
{
"path": "config/rbac/leader_election_role_binding.yaml",
"chars": 274,
"preview": "apiVersion: rbac.authorization.k8s.io/v1\nkind: RoleBinding\nmetadata:\n name: leader-election-rolebinding\nroleRef:\n apiG"
},
{
"path": "config/rbac/mycrd_editor_role.yaml",
"chars": 383,
"preview": "# permissions for end users to edit mycrds.\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n name:"
},
{
"path": "config/rbac/mycrd_viewer_role.yaml",
"chars": 340,
"preview": "# permissions for end users to view mycrds.\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n name:"
},
{
"path": "config/rbac/role.yaml",
"chars": 1284,
"preview": "---\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n creationTimestamp: null\n name: manager-role\n"
},
{
"path": "config/rbac/role_binding.yaml",
"chars": 272,
"preview": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRoleBinding\nmetadata:\n name: manager-rolebinding\nroleRef:\n apiGr"
},
{
"path": "config/rbac/templatedenforcingcrd_editor_role.yaml",
"chars": 447,
"preview": "# permissions for end users to edit templatedenforcingcrds.\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nm"
},
{
"path": "config/rbac/templatedenforcingcrd_viewer_role.yaml",
"chars": 404,
"preview": "# permissions for end users to view templatedenforcingcrds.\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nm"
},
{
"path": "config/samples/kustomization.yaml",
"chars": 309,
"preview": "## Append samples you want in your CSV to this file as resources ##\nresources:\n- operator-utils_v1alpha1_mycrd.yaml\n- op"
},
{
"path": "config/samples/operator-utils_v1alpha1_enforcingcrd.yaml",
"chars": 788,
"preview": "apiVersion: operator-utils.example.io/v1alpha1\nkind: EnforcingCRD\nmetadata:\n name: example-enforcingcrd\nspec:\n resourc"
},
{
"path": "config/samples/operator-utils_v1alpha1_enforcingpatch.yaml",
"chars": 660,
"preview": "apiVersion: operator-utils.example.io/v1alpha1\nkind: EnforcingPatch\nmetadata:\n name: test-field-patch\nspec:\n patches:\n"
},
{
"path": "config/samples/operator-utils_v1alpha1_mycrd.yaml",
"chars": 167,
"preview": "apiVersion: operator-utils.example.io/v1alpha1\nkind: MyCRD\nmetadata:\n name: example-mycrd\nspec:\n # Add fields here\n i"
},
{
"path": "config/samples/operator-utils_v1alpha1_templatedenforcingcrd.yaml",
"chars": 810,
"preview": "apiVersion: operator-utils.example.io/v1alpha1\nkind: TemplatedEnforcingCRD\nmetadata:\n name: example-enforcingcrd\nspec:\n"
},
{
"path": "config/scorecard/bases/config.yaml",
"chars": 134,
"preview": "apiVersion: scorecard.operatorframework.io/v1alpha3\nkind: Configuration\nmetadata:\n name: config\nstages:\n- parallel: tru"
},
{
"path": "config/scorecard/kustomization.yaml",
"chars": 384,
"preview": "resources:\n- bases/config.yaml\npatchesJson6902:\n- path: patches/basic.config.yaml\n target:\n group: scorecard.operato"
},
{
"path": "config/scorecard/patches/basic.config.yaml",
"chars": 230,
"preview": "- op: add\n path: /stages/0/tests/-\n value:\n entrypoint:\n - scorecard-test\n - basic-check-spec\n image: quay"
},
{
"path": "config/scorecard/patches/olm.config.yaml",
"chars": 1200,
"preview": "- op: add\n path: /stages/0/tests/-\n value:\n entrypoint:\n - scorecard-test\n - olm-bundle-validation\n image:"
},
{
"path": "config/webhook/kustomization.yaml",
"chars": 83,
"preview": "resources:\n- manifests.yaml\n- service.yaml\n\nconfigurations:\n- kustomizeconfig.yaml\n"
},
{
"path": "config/webhook/kustomizeconfig.yaml",
"chars": 791,
"preview": "# the following config is for teaching kustomize where to look at when substituting vars.\n# It requires kustomize v2.1.0"
},
{
"path": "config/webhook/service.yaml",
"chars": 189,
"preview": "\napiVersion: v1\nkind: Service\nmetadata:\n name: webhook-service\n namespace: system\nspec:\n ports:\n - port: 443\n "
},
{
"path": "controllers/enforcingcrd_controller.go",
"chars": 5204,
"preview": "/*\n\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with"
},
{
"path": "controllers/enforcingpatch_controller.go",
"chars": 3849,
"preview": "/*\n\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with"
},
{
"path": "controllers/mycrd_controller.go",
"chars": 4127,
"preview": "/*\n\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with"
},
{
"path": "controllers/suite_test.go",
"chars": 2034,
"preview": "/*\n\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with"
},
{
"path": "controllers/templatedenforcingcrd_controller.go",
"chars": 5397,
"preview": "/*\n\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with"
},
{
"path": "go.mod",
"chars": 4376,
"preview": "module github.com/redhat-cop/operator-utils\n\ngo 1.21\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.3.2\n\tgithub.com/Mastermin"
},
{
"path": "go.sum",
"chars": 35327,
"preview": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ngithub.com/BurntSushi/toml v0.3.1/go."
},
{
"path": "hack/boilerplate.go.txt",
"chars": 531,
"preview": "/*\n\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with"
},
{
"path": "main.go",
"chars": 3939,
"preview": "/*\n\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with"
},
{
"path": "pkg/util/apis/conditions.go",
"chars": 2130,
"preview": "package apis\n\nimport (\n\t\"sort\"\n\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nconst ReconcileError = \"ReconcileError"
},
{
"path": "pkg/util/apis/key.go",
"chars": 992,
"preview": "package apis\n\nimport (\n\t\"errors\"\n\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\tctr"
},
{
"path": "pkg/util/crud/crudutils.go",
"chars": 7603,
"preview": "package crud\n\nimport (\n\t\"context\"\n\t\"text/template\"\n\n\t\"github.com/redhat-cop/operator-utils/pkg/util/templates\"\n\tapierror"
},
{
"path": "pkg/util/discoveryclient/discoveryclientutils.go",
"chars": 2737,
"preview": "package discoveryclient\n\nimport (\n\t\"context\"\n\n\tapierrors \"k8s.io/apimachinery/pkg/api/errors\"\n\tv1 \"k8s.io/apimachinery/p"
},
{
"path": "pkg/util/dynamicclient/dynamicclientutils.go",
"chars": 5147,
"preview": "package dynamicclient\n\nimport (\n\t\"context\"\n\t\"strings\"\n\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachin"
},
{
"path": "pkg/util/finalizer.go",
"chars": 1531,
"preview": "/*\nCopyright 2019 Red Hat, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this fi"
},
{
"path": "pkg/util/lockedresourcecontroller/enforcing-reconciler.go",
"chars": 13470,
"preview": "package lockedresourcecontroller\n\nimport (\n\t\"context\"\n\t\"sync\"\n\n\t\"github.com/go-logr/logr\"\n\t\"github.com/redhat-cop/operat"
},
{
"path": "pkg/util/lockedresourcecontroller/locked-resource-manager.go",
"chars": 17189,
"preview": "package lockedresourcecontroller\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/go-logr/logr\"\n\tmultierror"
},
{
"path": "pkg/util/lockedresourcecontroller/lockedpatch/lockedpatch.go",
"chars": 2735,
"preview": "package lockedpatch\n\nimport (\n\t\"text/template\"\n\n\t\"github.com/go-logr/logr\"\n\tutilsapi \"github.com/redhat-cop/operator-uti"
},
{
"path": "pkg/util/lockedresourcecontroller/lockedresource/lockedresource.go",
"chars": 5251,
"preview": "package lockedresource\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"text/template\"\n\n\t\"github.com/go-logr/logr\"\n\tutilsapi \"git"
},
{
"path": "pkg/util/lockedresourcecontroller/lockedresource/lockedresourceset/lockedresourceset.go",
"chars": 6828,
"preview": "// Copyright (C) 2017 ScyllaDB\n// Use of this source code is governed by a ALv2-style\n// license that can be found at ht"
},
{
"path": "pkg/util/lockedresourcecontroller/lockedresource/lockedresourceset/lockedresourceset_bench_test.go",
"chars": 3371,
"preview": "// Copyright (C) 2017 ScyllaDB\n// Use of this source code is governed by a ALv2-style\n// license that can be found at ht"
},
{
"path": "pkg/util/lockedresourcecontroller/lockedresource/lockedresourceset/lockedresourceset_test.go",
"chars": 14238,
"preview": "// Copyright (C) 2017 ScyllaDB\n// Use of this source code is governed by a ALv2-style\n// license that can be found at ht"
},
{
"path": "pkg/util/lockedresourcecontroller/lockedresource/patch.go",
"chars": 2461,
"preview": "package lockedresource\n\nimport (\n\t\"encoding/json\"\n\t\"strings\"\n\n\tjsonpatch \"github.com/evanphx/json-patch\"\n\t\"k8s.io/apimac"
},
{
"path": "pkg/util/lockedresourcecontroller/patch-reconciler.go",
"chars": 21307,
"preview": "package lockedresourcecontroller\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n"
},
{
"path": "pkg/util/lockedresourcecontroller/resource-reconciler.go",
"chars": 11580,
"preview": "package lockedresourcecontroller\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"sync\"\n\n\t\"encoding/json\"\n\n\t\"github.com/go-logr/logr\"\n\n"
},
{
"path": "pkg/util/owner.go",
"chars": 1023,
"preview": "/*\nCopyright 2019 Red Hat, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this fi"
},
{
"path": "pkg/util/predicates.go",
"chars": 1405,
"preview": "/*\nCopyright 2019 Red Hat, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this fi"
},
{
"path": "pkg/util/reconciler.go",
"chars": 15749,
"preview": "/*\nCopyright 2019 Red Hat, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this fi"
},
{
"path": "pkg/util/stoppablemanager/stoppable-manager.go",
"chars": 1675,
"preview": "package stoppablemanager\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"k8s.io/client-go/rest\"\n\tlogf \"sigs.k8s.io/controller-runtime/"
},
{
"path": "pkg/util/templates/advanced-funcmap.go",
"chars": 7453,
"preview": "/*\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with t"
},
{
"path": "pkg/util/templates/templates.go",
"chars": 4256,
"preview": "/*\nCopyright 2019 Red Hat, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this fi"
},
{
"path": "test/enforcing-patch-multiple-cluster-level.yaml",
"chars": 585,
"preview": "# test multiple instances cluster\napiVersion: operator-utils.example.io/v1alpha1\nkind: EnforcingPatch\nmetadata:\n name: "
},
{
"path": "test/enforcing-patch-multiple.yaml",
"chars": 606,
"preview": "#test multiple instances namespaced\napiVersion: operator-utils.example.io/v1alpha1\nkind: EnforcingPatch\nmetadata:\n name"
},
{
"path": "test/enforcing-patch.yaml",
"chars": 736,
"preview": "\n# # test single instance namespaced\napiVersion: operator-utils.example.io/v1alpha1\nkind: EnforcingPatch\nmetadata:\n nam"
},
{
"path": "test/enforcing_cr.yaml",
"chars": 788,
"preview": "apiVersion: operator-utils.example.io/v1alpha1\nkind: EnforcingCRD\nmetadata:\n name: example-enforcingcrd\nspec:\n resourc"
},
{
"path": "test/failing-enforcing_cr.yaml",
"chars": 806,
"preview": "apiVersion: operator-utils.example.io/v1alpha1\nkind: EnforcingCRD\nmetadata:\n name: example-enforcingcrd2\nspec:\n resour"
},
{
"path": "test/mycrd_cr.yaml",
"chars": 167,
"preview": "apiVersion: operator-utils.example.io/v1alpha1\nkind: MyCRD\nmetadata:\n name: example-mycrd\nspec:\n # Add fields here\n i"
},
{
"path": "test/templatedenforcing_cr.yaml",
"chars": 810,
"preview": "apiVersion: operator-utils.example.io/v1alpha1\nkind: TemplatedEnforcingCRD\nmetadata:\n name: example-enforcingcrd\nspec:\n"
},
{
"path": "testbin/setup-envtest.sh",
"chars": 2856,
"preview": "#!/usr/bin/env bash\n\n# Copyright 2020 The Kubernetes Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the "
}
]
About this extraction
This page contains the full source code of the redhat-cop/operator-utils GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 121 files (489.2 KB), approximately 163.9k tokens, and a symbol index with 342 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.