├── .chglog
├── CHANGELOG.tpl.md
└── config.yml
├── .dockerignore
├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ ├── bug-report.yml
│ ├── documentation-question.yml
│ └── feature-request.yml
├── dependabot.yml
└── workflows
│ └── ci.yml
├── .gitignore
├── .golangci.yml
├── .goreleaser.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Dockerfile
├── LICENSE
├── Makefile
├── PROJECT
├── README.md
├── SECURITY.md
├── apis
└── grafana
│ └── v1alpha1
│ ├── grafanauser_types.go
│ ├── grafanauser_webhook.go
│ ├── groupversion_info.go
│ ├── webhook_suite_test.go
│ └── zz_generated.deepcopy.go
├── config
├── certmanager
│ ├── certificate.yaml
│ ├── kustomization.yaml
│ └── kustomizeconfig.yaml
├── crd
│ ├── bases
│ │ └── grafana.snappcloud.io_grafanausers.yaml
│ ├── kustomization.yaml
│ ├── kustomizeconfig.yaml
│ └── patches
│ │ ├── cainjection_in_grafana_grafanausers.yaml
│ │ └── webhook_in_grafana_grafanausers.yaml
├── default
│ ├── kustomization.yaml
│ ├── manager_auth_proxy_patch.yaml
│ ├── manager_config_patch.yaml
│ ├── manager_webhook_patch.yaml
│ └── webhookcainjection_patch.yaml
├── manager
│ ├── controller_manager_config.yaml
│ ├── kustomization.yaml
│ └── manager.yaml
├── manifests
│ └── kustomization.yaml
├── prometheus
│ ├── kustomization.yaml
│ └── monitor.yaml
├── rbac
│ ├── auth_proxy_client_clusterrole.yaml
│ ├── auth_proxy_role.yaml
│ ├── auth_proxy_role_binding.yaml
│ ├── auth_proxy_service.yaml
│ ├── grafana_grafanauser_editor_role.yaml
│ ├── grafana_grafanauser_viewer_role.yaml
│ ├── kustomization.yaml
│ ├── leader_election_role.yaml
│ ├── leader_election_role_binding.yaml
│ ├── role.yaml
│ ├── role_binding.yaml
│ └── service_account.yaml
├── samples
│ ├── grafana_v1alpha1_grafanauser.yaml
│ └── kustomization.yaml
├── scorecard
│ ├── bases
│ │ └── config.yaml
│ ├── kustomization.yaml
│ └── patches
│ │ ├── basic.config.yaml
│ │ └── olm.config.yaml
└── webhook
│ ├── kustomization.yaml
│ ├── kustomizeconfig.yaml
│ ├── manifests.yaml
│ └── service.yaml
├── controllers
├── grafanauser
│ ├── grafanauser_controller.go
│ └── suite_test.go
└── namespace
│ ├── namespace_controller.go
│ └── suite_test.go
├── go.mod
├── go.sum
├── hack
└── boilerplate.go.txt
└── main.go
/.chglog/CHANGELOG.tpl.md:
--------------------------------------------------------------------------------
1 | {{ range .Versions }}
2 |
3 | ## {{ if .Tag.Previous }}[{{ .Tag.Name }}]({{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}){{ else }}{{ .Tag.Name }}{{ end }} ({{ datetime "2006-01-02" .Tag.Date }})
4 |
5 | {{ range .CommitGroups -}}
6 | ### {{ .Title }}
7 |
8 | {{ range .Commits -}}
9 | * {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
10 | {{ end }}
11 | {{ end -}}
12 |
13 | {{- if .RevertCommits -}}
14 | ### Reverts
15 |
16 | {{ range .RevertCommits -}}
17 | * {{ .Revert.Header }}
18 | {{ end }}
19 | {{ end -}}
20 |
21 | {{- if .MergeCommits -}}
22 | ### Pull Requests
23 |
24 | {{ range .MergeCommits -}}
25 | * {{ .Header }}
26 | {{ end }}
27 | {{ end -}}
28 |
29 | {{- if .NoteGroups -}}
30 | {{ range .NoteGroups -}}
31 | ### {{ .Title }}
32 |
33 | {{ range .Notes }}
34 | {{ .Body }}
35 | {{ end }}
36 | {{ end -}}
37 | {{ end -}}
38 | {{ end -}}
--------------------------------------------------------------------------------
/.chglog/config.yml:
--------------------------------------------------------------------------------
1 | style: github
2 | template: CHANGELOG.tpl.md
3 | info:
4 | title: CHANGELOG
5 | repository_url: https://github.com/snapp-incubator/event-exporter
6 | options:
7 | commits:
8 | # filters:
9 | # Type:
10 | # - feat
11 | # - fix
12 | # - perf
13 | # - refactor
14 | commit_groups:
15 | # title_maps:
16 | # feat: Features
17 | # fix: Bug Fixes
18 | # perf: Performance Improvements
19 | # refactor: Code Refactoring
20 | header:
21 | pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$"
22 | pattern_maps:
23 | - Type
24 | - Scope
25 | - Subject
26 | notes:
27 | keywords:
28 | - BREAKING CHANGE
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
2 | # Ignore build and test binaries.
3 | bin/
4 | testbin/
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 | # Uses editorconfig to maintain consistent coding styles
3 |
4 | # top-most EditorConfig file
5 | root = true
6 |
7 | # Unix-style newlines with a newline ending every file
8 | [*]
9 | charset = utf-8
10 | end_of_line = lf
11 | indent_size = 2
12 | indent_style = space
13 | insert_final_newline = true
14 | max_line_length = 120
15 | trim_trailing_whitespace = true
16 | tab_width = 8
17 |
18 | [*.{tf,tfvars}]
19 | indent_size = 2
20 | indent_style = space
21 |
22 | [*.md]
23 | max_line_length = 0
24 | trim_trailing_whitespace = false
25 |
26 | [*.py]
27 | max_line_length = 120
28 | indent_style = space
29 | indent_size = 4
30 | ignore_frosted_errors = E103
31 | skip = runtests.py,build
32 | balanced_wrapping = true
33 | not_skip = __init__.py
34 |
35 | [Makefile]
36 | tab_width = 2
37 | indent_style = tab
38 |
39 | [COMMIT_EDITMSG]
40 | max_line_length = 0
41 |
42 | [*.yml]
43 | indent_style = space
44 | indent_size = 2
45 |
46 | [*.go]
47 | indent_style = tab
48 | indent_size = 8
49 |
50 | [*.{rst,ini}]
51 | indent_style = space
52 | indent_size = 4
53 |
54 | [*.j2]
55 | end_of_line = lf
56 |
57 | [{Dockerfile,*.json,*.sh}]
58 | indent_style = space
59 | indent_size = 2
60 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.yml:
--------------------------------------------------------------------------------
1 | name: 🐛 Bug Report
2 | description: File a bug report
3 | title: "[Bug]: "
4 | labels: ["bug", "triage"]
5 | assignees:
6 | - m-yosefpor # TODO
7 | body:
8 | - type: markdown
9 | attributes:
10 | value: |
11 | Thanks for taking the time to fill out this bug report!
12 | - type: textarea
13 | id: what-happened
14 | attributes:
15 | label: What happened?
16 | description: Also tell us, what did you expect to happen?
17 | placeholder: Tell us what you see!
18 | validations:
19 | required: true
20 | - type: input
21 | id: version
22 | attributes:
23 | label: App Version
24 | description: The version of the app you experience the bug in.
25 | placeholder: ex. v0.1.0
26 | validations:
27 | required: true
28 | - type: textarea
29 | id: logs
30 | attributes:
31 | label: Relevant log output
32 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
33 | render: shell
34 | - type: checkboxes
35 | id: terms
36 | attributes:
37 | label: Code of Conduct
38 | description: By submitting this issue, you agree to follow our [Code of Conduct](./CODE_OF_CONDUCT.md).
39 | options:
40 | - label: I agree to follow this project's Code of Conduct
41 | required: true
42 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/documentation-question.yml:
--------------------------------------------------------------------------------
1 | name: 📝 Question
2 | description: Question
3 | title: "[Question]: "
4 | labels: ["question"]
5 | assignees:
6 | - m-yosefpor # TODO
7 | body:
8 | - type: markdown
9 | attributes:
10 | value: |
11 | Thanks for your question. We try respond ASAP!
12 | - type: textarea
13 | id: question
14 | attributes:
15 | label: What's your question?
16 | description: What's your question?
17 | placeholder: Tell us your question!
18 | validations:
19 | required: true
20 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.yml:
--------------------------------------------------------------------------------
1 | name: ✨ Enhancement
2 | description: Feature Request or Enhancement Proposal
3 | title: "[Enhancement]: "
4 | labels: ["enhancement"]
5 | assignees:
6 | - m-yosefpor # TODO
7 | body:
8 | - type: markdown
9 | attributes:
10 | value: |
11 | Thanks for your suggestion. We try our best to prioritize it!
12 | - type: textarea
13 | id: proposal
14 | attributes:
15 | label: What's your proposal?
16 | description: Also tell us, how do you think it should be implemented?
17 | placeholder: Tell us your proposal!
18 | validations:
19 | required: true
20 | - type: checkboxes
21 | id: terms
22 | attributes:
23 | label: Code of Conduct
24 | description: By submitting this issue, you agree to follow our [Code of Conduct](./CODE_OF_CONDUCT.md).
25 | options:
26 | - label: I agree to follow this project's Code of Conduct
27 | required: true
28 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: gomod
4 | directory: "/"
5 | schedule:
6 | interval: monthly
7 | labels:
8 | - dependencies
9 | groups:
10 | dev-dependencies:
11 | patterns:
12 | - "*"
13 | - package-ecosystem: "docker"
14 | directory: "/"
15 | schedule:
16 | interval: "monthly"
17 | labels:
18 | - dependencies
19 | groups:
20 | dev-dependencies:
21 | patterns:
22 | - "*"
23 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: ci
3 | on:
4 | push:
5 | branches: [ main ]
6 | tags: [ v* ]
7 | pull_request:
8 | branches: [ main ]
9 |
10 | jobs:
11 | lint:
12 | name: lint
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: golangci-lint
17 | uses: golangci/golangci-lint-action@v2
18 | with:
19 | version: latest
20 | # test:
21 | # name: test
22 | # runs-on: ubuntu-latest
23 | # steps:
24 | # - uses: actions/checkout@v2
25 | # - uses: actions/setup-go@v2
26 | # with:
27 | # go-version: '^1.16'
28 | # - uses: RyanSiu1995/kubebuilder-action@v1.2.1
29 | # - run: make test
30 | # - uses: codecov/codecov-action@v1
31 | # with:
32 | # files: coverage.out
33 | docker:
34 | name: docker
35 | runs-on: ubuntu-latest
36 | needs:
37 | - lint
38 | # - test
39 | steps:
40 | - uses: actions/checkout@v2
41 | - uses: docker/setup-qemu-action@v1
42 | - uses: docker/setup-buildx-action@v1
43 | - uses: docker/login-action@v1
44 | with:
45 | registry: ghcr.io
46 | username: ${{ github.repository_owner }}
47 | password: ${{ secrets.GITHUB_TOKEN }}
48 | - uses: docker/metadata-action@v3
49 | id: meta
50 | with:
51 | images: ghcr.io/${{ github.repository }}
52 | tags: |
53 | type=ref,event=branch
54 | type=ref,event=pr
55 | type=semver,pattern={{version}}
56 | type=semver,pattern={{major}}.{{minor}}
57 | - uses: docker/build-push-action@v2
58 | with:
59 | file: "Dockerfile"
60 | context: .
61 | platforms: linux/amd64
62 | push: true
63 | tags: ${{ steps.meta.outputs.tags }}
64 | labels: ${{ steps.meta.outputs.labels }}
65 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | grafana-complementary-operator
2 | # Binaries for programs and plugins
3 | *.exe
4 | *.exe~
5 | *.dll
6 | *.so
7 | *.dylib
8 | bin
9 | testbin/*
10 |
11 | # Test binary, build with `go test -c`
12 | *.test
13 |
14 | # Output of the go coverage tool, specifically when used with LiteIDE
15 | *.out
16 |
17 | # Kubernetes Generated files - skip generated files, except for vendored files
18 |
19 | !vendor/**/zz_generated.*
20 |
21 | # editor and IDE paraphernalia
22 | .idea
23 | *.swp
24 | *.swo
25 | *~
26 |
--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
1 | # https://golangci-lint.run/usage/configuration/
2 | run:
3 | timeout: 10m
4 | deadline: 5m
5 |
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | # This is an example .goreleaser.yml file with some sane defaults.
2 | # Make sure to check the documentation at https://goreleaser.com
3 | before:
4 | hooks:
5 | # You may remove this if you don't use go modules.
6 | - go mod tidy
7 | # you may remove this if you don't need go generate
8 | - go generate ./...
9 | builds:
10 | - env:
11 | - CGO_ENABLED=0
12 | goos:
13 | - linux
14 | goarch:
15 | - amd64
16 | archives:
17 | - replacements:
18 | linux: Linux
19 | amd64: x86_64
20 | checksum:
21 | name_template: 'checksums.txt'
22 | snapshot:
23 | name_template: "{{ incpatch .Version }}-next"
24 | changelog:
25 | sort: asc
26 | filters:
27 | exclude:
28 | - '^docs:'
29 | - '^test:'
30 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## [v0.2.1](https://github.com/snapp-incubator/event-exporter/compare/v0.2.0...v0.2.1) (2022-03-05)
4 |
5 | ### Chore
6 |
7 | * bump kustomize to 0.2.0
8 |
9 | ### Fix
10 |
11 | * change ns label
12 |
13 |
14 |
15 | ## [v0.2.0](https://github.com/snapp-incubator/event-exporter/compare/0.0.1...v0.2.0) (2022-03-04)
16 |
17 | ### Chore
18 |
19 | * update changelog for v0.2.0
20 |
21 | ### Feat
22 |
23 | * add releaser
24 |
25 | ### Fix
26 |
27 | * add svu version
28 | * trigger on changes on owned object
29 | * handle object already exits
30 |
31 |
32 |
33 | ## [0.0.1](https://github.com/snapp-incubator/event-exporter/compare/v0.1.0...0.0.1) (2022-03-04)
34 |
35 | ### Chore
36 |
37 | * update changelog for 0.0.1
38 | * add release files
39 |
40 |
41 |
42 | ## v0.1.0 (2022-02-28)
43 |
44 | ### Chore
45 |
46 | * fix lint
47 | * add golangci timeout
48 | * add ci
49 |
50 | ### Fix
51 |
52 | * add grafana crd to scheme
53 | * role
54 | * add rbac
55 | * issues
56 | * **kustomize:** manager image
57 |
58 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 |
2 | # Contributor Covenant Code of Conduct
3 |
4 | ## Our Pledge
5 |
6 | We as members, contributors, and leaders pledge to make participation in our
7 | community a harassment-free experience for everyone, regardless of age, body
8 | size, visible or invisible disability, ethnicity, sex characteristics, gender
9 | identity and expression, level of experience, education, socio-economic status,
10 | nationality, personal appearance, race, caste, color, religion, or sexual identity
11 | and orientation.
12 |
13 | We pledge to act and interact in ways that contribute to an open, welcoming,
14 | diverse, inclusive, and healthy community.
15 |
16 | ## Our Standards
17 |
18 | Examples of behavior that contributes to a positive environment for our
19 | community include:
20 |
21 | * Demonstrating empathy and kindness toward other people
22 | * Being respectful of differing opinions, viewpoints, and experiences
23 | * Giving and gracefully accepting constructive feedback
24 | * Accepting responsibility and apologizing to those affected by our mistakes,
25 | and learning from the experience
26 | * Focusing on what is best not just for us as individuals, but for the
27 | overall community
28 |
29 | Examples of unacceptable behavior include:
30 |
31 | * The use of sexualized language or imagery, and sexual attention or
32 | advances of any kind
33 | * Trolling, insulting or derogatory comments, and personal or political attacks
34 | * Public or private harassment
35 | * Publishing others' private information, such as a physical or email
36 | address, without their explicit permission
37 | * Other conduct which could reasonably be considered inappropriate in a
38 | professional setting
39 |
40 | ## Enforcement Responsibilities
41 |
42 | Community leaders are responsible for clarifying and enforcing our standards of
43 | acceptable behavior and will take appropriate and fair corrective action in
44 | response to any behavior that they deem inappropriate, threatening, offensive,
45 | or harmful.
46 |
47 | Community leaders have the right and responsibility to remove, edit, or reject
48 | comments, commits, code, wiki edits, issues, and other contributions that are
49 | not aligned to this Code of Conduct, and will communicate reasons for moderation
50 | decisions when appropriate.
51 |
52 | ## Scope
53 |
54 | This Code of Conduct applies within all community spaces, and also applies when
55 | an individual is officially representing the community in public spaces.
56 | Examples of representing our community include using an official e-mail address,
57 | posting via an official social media account, or acting as an appointed
58 | representative at an online or offline event.
59 |
60 | ## Enforcement
61 |
62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
63 | reported to the community leaders responsible for enforcement at
64 | [INSERT CONTACT METHOD].
65 | All complaints will be reviewed and investigated promptly and fairly.
66 |
67 | All community leaders are obligated to respect the privacy and security of the
68 | reporter of any incident.
69 |
70 | ## Enforcement Guidelines
71 |
72 | Community leaders will follow these Community Impact Guidelines in determining
73 | the consequences for any action they deem in violation of this Code of Conduct:
74 |
75 | ### 1. Correction
76 |
77 | **Community Impact**: Use of inappropriate language or other behavior deemed
78 | unprofessional or unwelcome in the community.
79 |
80 | **Consequence**: A private, written warning from community leaders, providing
81 | clarity around the nature of the violation and an explanation of why the
82 | behavior was inappropriate. A public apology may be requested.
83 |
84 | ### 2. Warning
85 |
86 | **Community Impact**: A violation through a single incident or series
87 | of actions.
88 |
89 | **Consequence**: A warning with consequences for continued behavior. No
90 | interaction with the people involved, including unsolicited interaction with
91 | those enforcing the Code of Conduct, for a specified period of time. This
92 | includes avoiding interactions in community spaces as well as external channels
93 | like social media. Violating these terms may lead to a temporary or
94 | permanent ban.
95 |
96 | ### 3. Temporary Ban
97 |
98 | **Community Impact**: A serious violation of community standards, including
99 | sustained inappropriate behavior.
100 |
101 | **Consequence**: A temporary ban from any sort of interaction or public
102 | communication with the community for a specified period of time. No public or
103 | private interaction with the people involved, including unsolicited interaction
104 | with those enforcing the Code of Conduct, is allowed during this period.
105 | Violating these terms may lead to a permanent ban.
106 |
107 | ### 4. Permanent Ban
108 |
109 | **Community Impact**: Demonstrating a pattern of violation of community
110 | standards, including sustained inappropriate behavior, harassment of an
111 | individual, or aggression toward or disparagement of classes of individuals.
112 |
113 | **Consequence**: A permanent ban from any sort of public interaction within
114 | the community.
115 |
116 | ## Attribution
117 |
118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
119 | version 2.1, available at
120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
121 |
122 | Community Impact Guidelines were inspired by
123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
124 |
125 | For answers to common questions about this code of conduct, see the FAQ at
126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available
127 | at [https://www.contributor-covenant.org/translations][translations].
128 |
129 | [homepage]: https://www.contributor-covenant.org
130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
131 | [Mozilla CoC]: https://github.com/mozilla/diversity
132 | [FAQ]: https://www.contributor-covenant.org/faq
133 | [translations]: https://www.contributor-covenant.org/translations
134 |
135 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Build the manager binary
2 | FROM golang:1.21 as builder
3 |
4 | WORKDIR /workspace
5 | # Copy the Go Modules manifests
6 | COPY go.mod go.mod
7 | COPY go.sum go.sum
8 | # cache deps before building and copying source so that we don't need to re-download as much
9 | # and so that source changes don't invalidate our downloaded layer
10 | RUN go mod download
11 |
12 | # Copy the go source
13 | COPY apis/ apis/
14 | COPY main.go main.go
15 | COPY controllers/ controllers/
16 |
17 | # Build
18 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go
19 |
20 | # Use distroless as minimal base image to package the manager binary
21 | # Refer to https://github.com/GoogleContainerTools/distroless for more details
22 | FROM gcr.io/distroless/static:nonroot
23 | WORKDIR /
24 | COPY --from=builder /workspace/manager .
25 | USER 65532:65532
26 |
27 | ENTRYPOINT ["/manager"]
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # VERSION defines the project version for the bundle.
2 | # Update this value when you upgrade the version of your project.
3 | # To re-generate a bundle for another specific version without changing the standard setup, you can:
4 | # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2)
5 | # - use environment variables to overwrite this value (e.g export VERSION=0.0.2)
6 | VERSION ?= $(shell svu next)
7 |
8 | # CHANNELS define the bundle channels used in the bundle.
9 | # Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable")
10 | # To re-generate a bundle for other specific channels without changing the standard setup, you can:
11 | # - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable)
12 | # - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable")
13 | ifneq ($(origin CHANNELS), undefined)
14 | BUNDLE_CHANNELS := --channels=$(CHANNELS)
15 | endif
16 |
17 | # DEFAULT_CHANNEL defines the default channel used in the bundle.
18 | # Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable")
19 | # To re-generate a bundle for any other default channel without changing the default setup, you can:
20 | # - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable)
21 | # - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable")
22 | ifneq ($(origin DEFAULT_CHANNEL), undefined)
23 | BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL)
24 | endif
25 | BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)
26 |
27 | # IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images.
28 | # This variable is used to construct full image tags for bundle and catalog images.
29 | #
30 | # For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both
31 | # snappcloud.io/grafana-complementary-operator-bundle:$VERSION and snappcloud.io/grafana-complementary-operator-catalog:$VERSION.
32 | IMAGE_TAG_BASE ?= snappcloud.io/grafana-complementary-operator
33 |
34 | # BUNDLE_IMG defines the image:tag used for the bundle.
35 | # You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:)
36 | BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
37 |
38 | # BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command
39 | BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
40 |
41 | # USE_IMAGE_DIGESTS defines if images are resolved via tags or digests
42 | # You can enable this value if you would like to use SHA Based Digests
43 | # To enable set flag to true
44 | USE_IMAGE_DIGESTS ?= false
45 | ifeq ($(USE_IMAGE_DIGESTS), true)
46 | BUNDLE_GEN_FLAGS += --use-image-digests
47 | endif
48 |
49 | # Image URL to use all building/pushing image targets
50 | IMG ?= controller:latest
51 | # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
52 | ENVTEST_K8S_VERSION = 1.23
53 |
54 | # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
55 | ifeq (,$(shell go env GOBIN))
56 | GOBIN=$(shell go env GOPATH)/bin
57 | else
58 | GOBIN=$(shell go env GOBIN)
59 | endif
60 |
61 | # Setting SHELL to bash allows bash commands to be executed by recipes.
62 | # This is a requirement for 'setup-envtest.sh' in the test target.
63 | # Options are set to exit when a recipe line exits non-zero or a piped command fails.
64 | SHELL = /usr/bin/env bash -o pipefail
65 | .SHELLFLAGS = -ec
66 |
67 | .PHONY: all
68 | all: build
69 |
70 | ##@ General
71 |
72 | # The help target prints out all targets with their descriptions organized
73 | # beneath their categories. The categories are represented by '##@' and the
74 | # target descriptions by '##'. The awk commands is responsible for reading the
75 | # entire set of makefiles included in this invocation, looking for lines of the
76 | # file as xyz: ## something, and then pretty-format the target and help. Then,
77 | # if there's a line with ##@ something, that gets pretty-printed as a category.
78 | # More info on the usage of ANSI control characters for terminal formatting:
79 | # https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
80 | # More info on the awk command:
81 | # http://linuxcommand.org/lc3_adv_awk.php
82 |
83 | .PHONY: help
84 | help: ## Display this help.
85 | @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\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)
86 |
87 | ##@ Development
88 |
89 | .PHONY: manifests
90 | manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
91 | $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
92 |
93 | .PHONY: generate
94 | generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
95 | $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
96 |
97 | .PHONY: fmt
98 | fmt: ## Run go fmt against code.
99 | go fmt ./...
100 |
101 | .PHONY: vet
102 | vet: ## Run go vet against code.
103 | go vet ./...
104 |
105 | .PHONY: test
106 | test: manifests generate fmt vet envtest ## Run tests.
107 | KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out
108 |
109 | ##@ Build
110 |
111 | .PHONY: build
112 | build: generate fmt vet ## Build manager binary.
113 | go build -o bin/manager main.go
114 |
115 | .PHONY: run
116 | run: manifests generate fmt vet ## Run a controller from your host.
117 | go run ./main.go
118 |
119 | .PHONY: docker-build
120 | docker-build: test ## Build docker image with the manager.
121 | docker build -t ${IMG} .
122 |
123 | .PHONY: docker-push
124 | docker-push: ## Push docker image with the manager.
125 | docker push ${IMG}
126 |
127 | ##@ Deployment
128 |
129 | ifndef ignore-not-found
130 | ignore-not-found = false
131 | endif
132 |
133 | .PHONY: install
134 | install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
135 | $(KUSTOMIZE) build config/crd | kubectl apply -f -
136 |
137 | .PHONY: uninstall
138 | 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.
139 | $(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f -
140 |
141 | .PHONY: deploy
142 | deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
143 | cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
144 | $(KUSTOMIZE) build config/default | kubectl apply -f -
145 |
146 | .PHONY: undeploy
147 | 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.
148 | $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f -
149 |
150 | CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
151 | .PHONY: controller-gen
152 | controller-gen: ## Download controller-gen locally if necessary.
153 | $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0)
154 |
155 | KUSTOMIZE = $(shell pwd)/bin/kustomize
156 | .PHONY: kustomize
157 | kustomize: ## Download kustomize locally if necessary.
158 | $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)
159 |
160 | ENVTEST = $(shell pwd)/bin/setup-envtest
161 | .PHONY: envtest
162 | envtest: ## Download envtest-setup locally if necessary.
163 | $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest)
164 |
165 | # go-get-tool will 'go get' any package $2 and install it to $1.
166 | PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
167 | define go-get-tool
168 | @[ -f $(1) ] || { \
169 | set -e ;\
170 | TMP_DIR=$$(mktemp -d) ;\
171 | cd $$TMP_DIR ;\
172 | go mod init tmp ;\
173 | echo "Downloading $(2)" ;\
174 | GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\
175 | rm -rf $$TMP_DIR ;\
176 | }
177 | endef
178 |
179 | .PHONY: bundle
180 | bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files.
181 | operator-sdk generate kustomize manifests -q
182 | cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
183 | $(KUSTOMIZE) build config/manifests | operator-sdk generate bundle $(BUNDLE_GEN_FLAGS)
184 | operator-sdk bundle validate ./bundle
185 |
186 | .PHONY: bundle-build
187 | bundle-build: ## Build the bundle image.
188 | docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .
189 |
190 | .PHONY: bundle-push
191 | bundle-push: ## Push the bundle image.
192 | $(MAKE) docker-push IMG=$(BUNDLE_IMG)
193 |
194 | .PHONY: opm
195 | OPM = ./bin/opm
196 | opm: ## Download opm locally if necessary.
197 | ifeq (,$(wildcard $(OPM)))
198 | ifeq (,$(shell which opm 2>/dev/null))
199 | @{ \
200 | set -e ;\
201 | mkdir -p $(dir $(OPM)) ;\
202 | OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \
203 | curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.19.1/$${OS}-$${ARCH}-opm ;\
204 | chmod +x $(OPM) ;\
205 | }
206 | else
207 | OPM = $(shell which opm)
208 | endif
209 | endif
210 |
211 | # 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).
212 | # These images MUST exist in a registry and be pull-able.
213 | BUNDLE_IMGS ?= $(BUNDLE_IMG)
214 |
215 | # The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0).
216 | CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION)
217 |
218 | # Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image.
219 | ifneq ($(origin CATALOG_BASE_IMG), undefined)
220 | FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG)
221 | endif
222 |
223 | # Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'.
224 | # This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see:
225 | # https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator
226 | .PHONY: catalog-build
227 | catalog-build: opm ## Build a catalog image.
228 | $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT)
229 |
230 | # Push the catalog image.
231 | .PHONY: catalog-push
232 | catalog-push: ## Push a catalog image.
233 | $(MAKE) docker-push IMG=$(CATALOG_IMG)
234 |
235 |
236 |
237 |
238 | #############
239 | ##@ Release
240 |
241 |
242 | .PHONY: changelog
243 | changelog: build ## Generate changelog
244 | echo $(VERSION)
245 | git-chglog --next-tag $(VERSION) -o CHANGELOG.md
246 |
247 | .PHONY: release
248 | release: changelog ## Release a new tag
249 | git add CHANGELOG.md
250 | git commit -m "chore: update changelog for $(VERSION)"
251 | git tag $(VERSION) -m "$(VERSION)"
252 | git push origin main $(VERSION)
253 |
254 |
--------------------------------------------------------------------------------
/PROJECT:
--------------------------------------------------------------------------------
1 | domain: snappcloud.io
2 | layout:
3 | - go.kubebuilder.io/v3
4 | multigroup: true
5 | plugins:
6 | manifests.sdk.operatorframework.io/v2: {}
7 | scorecard.sdk.operatorframework.io/v2: {}
8 | projectName: grafana-complementary-operator
9 | repo: github.com/snapp-cab/grafana-complementary-operator
10 | resources:
11 | - controller: true
12 | group: core
13 | kind: Namespace
14 | path: k8s.io/api/core/v1
15 | version: v1
16 | - api:
17 | crdVersion: v1
18 | namespaced: true
19 | controller: true
20 | domain: snappcloud.io
21 | group: grafana
22 | kind: GrafanaUser
23 | path: github.com/snapp-cab/grafana-complementary-operator/apis/grafana/v1alpha1
24 | version: v1alpha1
25 | webhooks:
26 | defaulting: true
27 | validation: true
28 | webhookVersion: v1
29 | version: "3"
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Grafana Complementary Operator
2 |
3 | A grafana which complements grafana-operator for custom features which are not feasible to be merged into core operator.
4 |
5 | ## Instructions
6 |
7 | ### Development
8 |
9 | * `make generate` update the generated code for that resource type.
10 | * `make manifests` Generating CRD manifests.
11 | * `make test` Run tests.
12 |
13 | ### Build
14 |
15 | Export your image name:
16 |
17 | ```
18 | export IMG=ghcr.io/your-repo-path/image-name:latest
19 | ```
20 |
21 | * `make build` builds golang app locally.
22 | * `make docker-build` build docker image locally.
23 | * `make docker-push` push container image to registry.
24 |
25 | ### Run, Deploy
26 | * `make run` run app locally
27 | * `make deploy` deploy to k8s.
28 |
29 | ### Clean up
30 |
31 | * `make undeploy` delete resouces in k8s.
32 |
33 | ### Release
34 |
35 | * `make changelog` generate changelog to check before release
36 | * `make release` create a new tag and release it to github
37 |
38 | ## Metrics
39 |
40 | | Metric | Notes
41 | |-----------------------------------------------------|------------------------------------
42 | | controller_runtime_active_workers | Number of currently used workers per controller
43 | | controller_runtime_max_concurrent_reconciles | Maximum number of concurrent reconciles per controller
44 | | controller_runtime_reconcile_errors_total | Total number of reconciliation errors per controller
45 | | controller_runtime_reconcile_time_seconds | Length of time per reconciliation per controller
46 | | controller_runtime_reconcile_total | Total number of reconciliations per controller
47 | | rest_client_request_latency_seconds | Request latency in seconds. Broken down by verb and URL.
48 | | rest_client_requests_total | Number of HTTP requests, partitioned by status code, method, and host.
49 | | workqueue_adds_total | Total number of adds handled by workqueue
50 | | workqueue_depth | Current depth of workqueue
51 | | workqueue_longest_running_processor_seconds | How many seconds has the longest running processor for workqueue been running.
52 | | workqueue_queue_duration_seconds | How long in seconds an item stays in workqueue before being requested
53 | | workqueue_retries_total | Total number of retries handled by workqueue
54 | | workqueue_unfinished_work_seconds | How many seconds of work has been done that is in progress and hasn't been observed by work_duration. Large values indicate stuck threads. One can deduce the number of stuck threads by observing the rate at which this increases.
55 | | workqueue_work_duration_seconds | How long in seconds processing an item from workqueue takes.
56 |
57 |
58 | ## Security
59 |
60 | ### Reporting security vulnerabilities
61 |
62 | If you find a security vulnerability or any security related issues, please DO NOT file a public issue, instead send your report privately to cloud@snapp.cab. Security reports are greatly appreciated and we will publicly thank you for it.
63 |
64 | ## License
65 |
66 | Apache-2.0 License, see [LICENSE](LICENSE).
67 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 |
4 | ## Reporting a Vulnerability
5 |
6 | If you find a security vulnerability or any security related issues, please DO NOT file a public issue. Do not create a Github issue. Instead, send your report privately to cloud@snapp.cab. Security reports are greatly appreciated and we will publicly thank you for it.
7 |
--------------------------------------------------------------------------------
/apis/grafana/v1alpha1/grafanauser_types.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package v1alpha1
18 |
19 | import (
20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21 | )
22 |
23 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
24 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
25 |
26 | // GrafanaUserSpec defines the desired state of GrafanaUser
27 | type GrafanaUserSpec struct {
28 | // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
29 | // Important: Run "make" to regenerate code after modifying this file
30 |
31 | Admin []string `json:"admin,omitempty"`
32 | Edit []string `json:"edit,omitempty"`
33 | View []string `json:"view,omitempty"`
34 | }
35 |
36 | //+kubebuilder:object:root=true
37 | //+kubebuilder:subresource:status
38 |
39 | // GrafanaUser is the Schema for the grafanausers API
40 | type GrafanaUser struct {
41 | metav1.TypeMeta `json:",inline"`
42 | metav1.ObjectMeta `json:"metadata,omitempty"`
43 | Spec GrafanaUserSpec `json:"spec,omitempty"`
44 | }
45 |
46 | //+kubebuilder:object:root=true
47 |
48 | // GrafanaUserList contains a list of GrafanaUser
49 | type GrafanaUserList struct {
50 | metav1.TypeMeta `json:",inline"`
51 | metav1.ListMeta `json:"metadata,omitempty"`
52 | Items []GrafanaUser `json:"items"`
53 | }
54 |
55 | func init() {
56 | SchemeBuilder.Register(&GrafanaUser{}, &GrafanaUserList{})
57 | }
58 |
--------------------------------------------------------------------------------
/apis/grafana/v1alpha1/grafanauser_webhook.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package v1alpha1
18 |
19 | import (
20 | "context"
21 | "fmt"
22 | "os"
23 | "strings"
24 |
25 | "github.com/grafana-tools/sdk"
26 | "k8s.io/apimachinery/pkg/runtime"
27 | ctrl "sigs.k8s.io/controller-runtime"
28 | logf "sigs.k8s.io/controller-runtime/pkg/log"
29 | "sigs.k8s.io/controller-runtime/pkg/webhook"
30 | )
31 |
32 | // log is for logging in this package.
33 | var grafanauserlog = logf.Log.WithName("grafanauser-resource")
34 |
35 | // Get Grafana URL and PassWord as a env.
36 | var grafanaPassword = os.Getenv("GRAFANA_PASSWORD")
37 | var grafanaUsername = os.Getenv("GRAFANA_USERNAME")
38 | var grafanaURL = os.Getenv("GRAFANA_URL")
39 |
40 | // Get Grafana URL and PassWord as a env.
41 |
42 | func (r *GrafanaUser) SetupWebhookWithManager(mgr ctrl.Manager) error {
43 | return ctrl.NewWebhookManagedBy(mgr).
44 | For(r).
45 | Complete()
46 | }
47 |
48 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
49 |
50 | var _ webhook.Defaulter = &GrafanaUser{}
51 |
52 | // Default implements webhook.Defaulter so a webhook will be registered for the type
53 | func (r *GrafanaUser) Default() {
54 | grafanauserlog.Info("default", "name", r.Name)
55 |
56 | // TODO(user): fill in your defaulting logic.
57 | }
58 |
59 | // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
60 | //+kubebuilder:webhook:path=/validate-grafana-snappcloud-io-v1alpha1-grafanauser,mutating=false,failurePolicy=fail,sideEffects=None,groups=grafana.snappcloud.io,resources=grafanausers,verbs=create;update,versions=v1alpha1,name=vgrafana.kb.io,admissionReviewVersions={v1,v1beta1}
61 |
62 | var _ webhook.Validator = &GrafanaUser{}
63 |
64 | // ValidateCreate implements webhook.Validator so a webhook will be registered for the type
65 | func (r *GrafanaUser) ValidateCreate() error {
66 | grafanauserlog.Info("validate create", "name", r.Name)
67 | // TODO(user): fill in your validation logic upon object creation.
68 | var emaillist []string
69 | emaillist = append(r.Spec.Admin, r.Spec.Edit...)
70 | emaillist = append(emaillist, r.Spec.View...)
71 | str2 := strings.Join(emaillist, ", ")
72 | grafanauserlog.Info(str2)
73 | err := r.ValidateEmailExist(context.Background(), emaillist)
74 | if err != nil {
75 | return err
76 | }
77 | return nil
78 | }
79 |
80 | // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
81 | func (r *GrafanaUser) ValidateUpdate(old runtime.Object) error {
82 | grafanauserlog.Info("validate update", "name", r.Name)
83 | var emaillist []string
84 | emaillist = append(r.Spec.Admin, r.Spec.Edit...)
85 | emaillist = append(emaillist, r.Spec.View...)
86 | err := r.ValidateEmailExist(context.Background(), emaillist)
87 | if err != nil {
88 | return err
89 | }
90 | // TODO(user): fill in your validation logic upon object update.
91 | return nil
92 | }
93 |
94 | // ValidateDelete implements webhook.Validator so a webhook will be registered for the type
95 | func (r *GrafanaUser) ValidateDelete() error {
96 | grafanauserlog.Info("validate delete", "name", r.Name)
97 | // TODO(user): fill in your validation logic upon object deletion.
98 | return nil
99 | }
100 |
101 | func Find(slice []sdk.User, val string) bool {
102 | for _, item := range slice {
103 | if item.Email == val {
104 | return true
105 | }
106 | }
107 | return false
108 | }
109 |
110 | func (r *GrafanaUser) ValidateEmailExist(ctx context.Context, emails []string) error {
111 | client, _ := sdk.NewClient(grafanaURL, fmt.Sprintf("%s:%s", grafanaUsername, grafanaPassword), sdk.DefaultHTTPClient)
112 | grafanalUsers, _ := client.GetAllUsers(ctx)
113 | var Users []string
114 | for _, email := range emails {
115 | found := Find(grafanalUsers, email)
116 | if !found {
117 | Users = append(Users, email)
118 | }
119 | }
120 | userlist := strings.Join(Users, ", ")
121 | if len(Users) > 0 {
122 | return fmt.Errorf("%q do NOT exist in grafana, please make sure the user name is correct, or they have login at least one time in grafana and then try again", userlist)
123 |
124 | }
125 | return nil
126 | }
127 |
--------------------------------------------------------------------------------
/apis/grafana/v1alpha1/groupversion_info.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package v1alpha1 contains API Schema definitions for the grafana v1alpha1 API group
18 | //+kubebuilder:object:generate=true
19 | //+groupName=grafana.snappcloud.io
20 | package v1alpha1
21 |
22 | import (
23 | "k8s.io/apimachinery/pkg/runtime/schema"
24 | "sigs.k8s.io/controller-runtime/pkg/scheme"
25 | )
26 |
27 | var (
28 | // GroupVersion is group version used to register these objects
29 | GroupVersion = schema.GroupVersion{Group: "grafana.snappcloud.io", Version: "v1alpha1"}
30 |
31 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme
32 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
33 |
34 | // AddToScheme adds the types in this group-version to the given scheme.
35 | AddToScheme = SchemeBuilder.AddToScheme
36 | )
37 |
--------------------------------------------------------------------------------
/apis/grafana/v1alpha1/webhook_suite_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package v1alpha1
18 |
19 | import (
20 | "context"
21 | "crypto/tls"
22 | "fmt"
23 | "net"
24 | "path/filepath"
25 | "testing"
26 | "time"
27 |
28 | . "github.com/onsi/ginkgo"
29 | . "github.com/onsi/gomega"
30 |
31 | admissionv1beta1 "k8s.io/api/admission/v1beta1"
32 | //+kubebuilder:scaffold:imports
33 | "k8s.io/apimachinery/pkg/runtime"
34 | "k8s.io/client-go/rest"
35 | ctrl "sigs.k8s.io/controller-runtime"
36 | "sigs.k8s.io/controller-runtime/pkg/client"
37 | "sigs.k8s.io/controller-runtime/pkg/envtest"
38 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
39 | logf "sigs.k8s.io/controller-runtime/pkg/log"
40 | "sigs.k8s.io/controller-runtime/pkg/log/zap"
41 | )
42 |
43 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to
44 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
45 |
46 | var cfg *rest.Config
47 | var k8sClient client.Client
48 | var testEnv *envtest.Environment
49 | var ctx context.Context
50 | var cancel context.CancelFunc
51 |
52 | func TestAPIs(t *testing.T) {
53 | RegisterFailHandler(Fail)
54 |
55 | RunSpecsWithDefaultAndCustomReporters(t,
56 | "Webhook Suite",
57 | []Reporter{printer.NewlineReporter{}})
58 | }
59 |
60 | var _ = BeforeSuite(func() {
61 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
62 |
63 | ctx, cancel = context.WithCancel(context.TODO())
64 |
65 | By("bootstrapping test environment")
66 | testEnv = &envtest.Environment{
67 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")},
68 | ErrorIfCRDPathMissing: false,
69 | WebhookInstallOptions: envtest.WebhookInstallOptions{
70 | Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")},
71 | },
72 | }
73 |
74 | var err error
75 | // cfg is defined in this file globally.
76 | cfg, err = testEnv.Start()
77 | Expect(err).NotTo(HaveOccurred())
78 | Expect(cfg).NotTo(BeNil())
79 |
80 | scheme := runtime.NewScheme()
81 | err = AddToScheme(scheme)
82 | Expect(err).NotTo(HaveOccurred())
83 |
84 | err = admissionv1beta1.AddToScheme(scheme)
85 | Expect(err).NotTo(HaveOccurred())
86 |
87 | //+kubebuilder:scaffold:scheme
88 |
89 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
90 | Expect(err).NotTo(HaveOccurred())
91 | Expect(k8sClient).NotTo(BeNil())
92 |
93 | // start webhook server using Manager
94 | webhookInstallOptions := &testEnv.WebhookInstallOptions
95 | mgr, err := ctrl.NewManager(cfg, ctrl.Options{
96 | Scheme: scheme,
97 | Host: webhookInstallOptions.LocalServingHost,
98 | Port: webhookInstallOptions.LocalServingPort,
99 | CertDir: webhookInstallOptions.LocalServingCertDir,
100 | LeaderElection: false,
101 | MetricsBindAddress: "0",
102 | })
103 | Expect(err).NotTo(HaveOccurred())
104 |
105 | err = (&GrafanaUser{}).SetupWebhookWithManager(mgr)
106 | Expect(err).NotTo(HaveOccurred())
107 |
108 | //+kubebuilder:scaffold:webhook
109 |
110 | go func() {
111 | defer GinkgoRecover()
112 | err = mgr.Start(ctx)
113 | Expect(err).NotTo(HaveOccurred())
114 | }()
115 |
116 | // wait for the webhook server to get ready
117 | dialer := &net.Dialer{Timeout: time.Second}
118 | addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
119 | Eventually(func() error {
120 | conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true})
121 | if err != nil {
122 | return err
123 | }
124 | conn.Close()
125 | return nil
126 | }).Should(Succeed())
127 |
128 | }, 60)
129 |
130 | var _ = AfterSuite(func() {
131 | cancel()
132 | By("tearing down the test environment")
133 | err := testEnv.Stop()
134 | Expect(err).NotTo(HaveOccurred())
135 | })
136 |
--------------------------------------------------------------------------------
/apis/grafana/v1alpha1/zz_generated.deepcopy.go:
--------------------------------------------------------------------------------
1 | //go:build !ignore_autogenerated
2 | // +build !ignore_autogenerated
3 |
4 | /*
5 | Copyright 2022.
6 |
7 | Licensed under the Apache License, Version 2.0 (the "License");
8 | you may not use this file except in compliance with the License.
9 | You may obtain a copy of the License at
10 |
11 | http://www.apache.org/licenses/LICENSE-2.0
12 |
13 | Unless required by applicable law or agreed to in writing, software
14 | distributed under the License is distributed on an "AS IS" BASIS,
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | See the License for the specific language governing permissions and
17 | limitations under the License.
18 | */
19 |
20 | // Code generated by controller-gen. DO NOT EDIT.
21 |
22 | package v1alpha1
23 |
24 | import (
25 | "k8s.io/apimachinery/pkg/runtime"
26 | )
27 |
28 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
29 | func (in *GrafanaUser) DeepCopyInto(out *GrafanaUser) {
30 | *out = *in
31 | out.TypeMeta = in.TypeMeta
32 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
33 | in.Spec.DeepCopyInto(&out.Spec)
34 | }
35 |
36 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GrafanaUser.
37 | func (in *GrafanaUser) DeepCopy() *GrafanaUser {
38 | if in == nil {
39 | return nil
40 | }
41 | out := new(GrafanaUser)
42 | in.DeepCopyInto(out)
43 | return out
44 | }
45 |
46 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
47 | func (in *GrafanaUser) DeepCopyObject() runtime.Object {
48 | if c := in.DeepCopy(); c != nil {
49 | return c
50 | }
51 | return nil
52 | }
53 |
54 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
55 | func (in *GrafanaUserList) DeepCopyInto(out *GrafanaUserList) {
56 | *out = *in
57 | out.TypeMeta = in.TypeMeta
58 | in.ListMeta.DeepCopyInto(&out.ListMeta)
59 | if in.Items != nil {
60 | in, out := &in.Items, &out.Items
61 | *out = make([]GrafanaUser, len(*in))
62 | for i := range *in {
63 | (*in)[i].DeepCopyInto(&(*out)[i])
64 | }
65 | }
66 | }
67 |
68 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GrafanaUserList.
69 | func (in *GrafanaUserList) DeepCopy() *GrafanaUserList {
70 | if in == nil {
71 | return nil
72 | }
73 | out := new(GrafanaUserList)
74 | in.DeepCopyInto(out)
75 | return out
76 | }
77 |
78 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
79 | func (in *GrafanaUserList) DeepCopyObject() runtime.Object {
80 | if c := in.DeepCopy(); c != nil {
81 | return c
82 | }
83 | return nil
84 | }
85 |
86 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
87 | func (in *GrafanaUserSpec) DeepCopyInto(out *GrafanaUserSpec) {
88 | *out = *in
89 | if in.Admin != nil {
90 | in, out := &in.Admin, &out.Admin
91 | *out = make([]string, len(*in))
92 | copy(*out, *in)
93 | }
94 | if in.Edit != nil {
95 | in, out := &in.Edit, &out.Edit
96 | *out = make([]string, len(*in))
97 | copy(*out, *in)
98 | }
99 | if in.View != nil {
100 | in, out := &in.View, &out.View
101 | *out = make([]string, len(*in))
102 | copy(*out, *in)
103 | }
104 | }
105 |
106 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GrafanaUserSpec.
107 | func (in *GrafanaUserSpec) DeepCopy() *GrafanaUserSpec {
108 | if in == nil {
109 | return nil
110 | }
111 | out := new(GrafanaUserSpec)
112 | in.DeepCopyInto(out)
113 | return out
114 | }
115 |
--------------------------------------------------------------------------------
/config/certmanager/certificate.yaml:
--------------------------------------------------------------------------------
1 | # The following manifests contain a self-signed issuer CR and a certificate CR.
2 | # More document can be found at https://docs.cert-manager.io
3 | # WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes.
4 | apiVersion: cert-manager.io/v1
5 | kind: Issuer
6 | metadata:
7 | name: selfsigned-issuer
8 | namespace: system
9 | spec:
10 | selfSigned: {}
11 | ---
12 | apiVersion: cert-manager.io/v1
13 | kind: Certificate
14 | metadata:
15 | name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml
16 | namespace: system
17 | spec:
18 | # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize
19 | dnsNames:
20 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc
21 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local
22 | issuerRef:
23 | kind: Issuer
24 | name: selfsigned-issuer
25 | secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize
26 |
--------------------------------------------------------------------------------
/config/certmanager/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - certificate.yaml
3 |
4 | configurations:
5 | - kustomizeconfig.yaml
6 |
--------------------------------------------------------------------------------
/config/certmanager/kustomizeconfig.yaml:
--------------------------------------------------------------------------------
1 | # This configuration is for teaching kustomize how to update name ref and var substitution
2 | nameReference:
3 | - kind: Issuer
4 | group: cert-manager.io
5 | fieldSpecs:
6 | - kind: Certificate
7 | group: cert-manager.io
8 | path: spec/issuerRef/name
9 |
10 | varReference:
11 | - kind: Certificate
12 | group: cert-manager.io
13 | path: spec/commonName
14 | - kind: Certificate
15 | group: cert-manager.io
16 | path: spec/dnsNames
17 |
--------------------------------------------------------------------------------
/config/crd/bases/grafana.snappcloud.io_grafanausers.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | controller-gen.kubebuilder.io/version: v0.8.0
7 | creationTimestamp: null
8 | name: grafanausers.grafana.snappcloud.io
9 | spec:
10 | group: grafana.snappcloud.io
11 | names:
12 | kind: GrafanaUser
13 | listKind: GrafanaUserList
14 | plural: grafanausers
15 | singular: grafanauser
16 | scope: Namespaced
17 | versions:
18 | - name: v1alpha1
19 | schema:
20 | openAPIV3Schema:
21 | description: GrafanaUser is the Schema for the grafanausers API
22 | properties:
23 | apiVersion:
24 | description: 'APIVersion defines the versioned schema of this representation
25 | of an object. Servers should convert recognized schemas to the latest
26 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
27 | type: string
28 | kind:
29 | description: 'Kind is a string value representing the REST resource this
30 | object represents. Servers may infer this from the endpoint the client
31 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
32 | type: string
33 | metadata:
34 | type: object
35 | spec:
36 | description: GrafanaUserSpec defines the desired state of GrafanaUser
37 | properties:
38 | admin:
39 | items:
40 | type: string
41 | type: array
42 | edit:
43 | items:
44 | type: string
45 | type: array
46 | view:
47 | items:
48 | type: string
49 | type: array
50 | type: object
51 | type: object
52 | served: true
53 | storage: true
54 | subresources:
55 | status: {}
56 | status:
57 | acceptedNames:
58 | kind: ""
59 | plural: ""
60 | conditions: []
61 | storedVersions: []
62 |
--------------------------------------------------------------------------------
/config/crd/kustomization.yaml:
--------------------------------------------------------------------------------
1 | # This kustomization.yaml is not intended to be run by itself,
2 | # since it depends on service name and namespace that are out of this kustomize package.
3 | # It should be run by config/default
4 | resources:
5 | - bases/grafana.snappcloud.io_grafanausers.yaml
6 | #+kubebuilder:scaffold:crdkustomizeresource
7 |
8 | patchesStrategicMerge:
9 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
10 | # patches here are for enabling the conversion webhook for each CRD
11 | - patches/webhook_in_grafana_grafanausers.yaml
12 | #+kubebuilder:scaffold:crdkustomizewebhookpatch
13 |
14 | # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.
15 | # patches here are for enabling the CA injection for each CRD
16 | - patches/cainjection_in_grafana_grafanausers.yaml
17 | #+kubebuilder:scaffold:crdkustomizecainjectionpatch
18 |
19 | # the following config is for teaching kustomize how to do kustomization for CRDs.
20 | configurations:
21 | - kustomizeconfig.yaml
22 |
--------------------------------------------------------------------------------
/config/crd/kustomizeconfig.yaml:
--------------------------------------------------------------------------------
1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD
2 | nameReference:
3 | - kind: Service
4 | version: v1
5 | fieldSpecs:
6 | - kind: CustomResourceDefinition
7 | version: v1
8 | group: apiextensions.k8s.io
9 | path: spec/conversion/webhook/clientConfig/service/name
10 |
11 | namespace:
12 | - kind: CustomResourceDefinition
13 | version: v1
14 | group: apiextensions.k8s.io
15 | path: spec/conversion/webhook/clientConfig/service/namespace
16 | create: false
17 |
18 | varReference:
19 | - path: metadata/annotations
20 |
--------------------------------------------------------------------------------
/config/crd/patches/cainjection_in_grafana_grafanausers.yaml:
--------------------------------------------------------------------------------
1 | # The following patch adds a directive for certmanager to inject CA into the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
7 | name: grafanausers.grafana.snappcloud.io
8 |
--------------------------------------------------------------------------------
/config/crd/patches/webhook_in_grafana_grafanausers.yaml:
--------------------------------------------------------------------------------
1 | # The following patch enables a conversion webhook for the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | name: grafanausers.grafana.snappcloud.io
6 | spec:
7 | conversion:
8 | strategy: Webhook
9 | webhook:
10 | clientConfig:
11 | service:
12 | namespace: system
13 | name: webhook-service
14 | path: /convert
15 | conversionReviewVersions:
16 | - v1
17 |
--------------------------------------------------------------------------------
/config/default/kustomization.yaml:
--------------------------------------------------------------------------------
1 | # Adds namespace to all resources.
2 | namespace: grafana-complementary-operator-system
3 |
4 | # Value of this field is prepended to the
5 | # names of all resources, e.g. a deployment named
6 | # "wordpress" becomes "alices-wordpress".
7 | # Note that it should also match with the prefix (text before '-') of the namespace
8 | # field above.
9 | namePrefix: grafana-complementary-operator-
10 |
11 | # Labels to add to all resources and selectors.
12 | #commonLabels:
13 | # someName: someValue
14 |
15 | bases:
16 | - ../crd
17 | - ../rbac
18 | - ../manager
19 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
20 | # crd/kustomization.yaml
21 | - ../webhook
22 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
23 | - ../certmanager
24 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
25 | - ../prometheus
26 |
27 | patchesStrategicMerge:
28 | # Protect the /metrics endpoint by putting it behind auth.
29 | # If you want your controller-manager to expose the /metrics
30 | # endpoint w/o any authn/z, please comment the following line.
31 | - manager_auth_proxy_patch.yaml
32 |
33 | # Mount the controller config file for loading manager configurations
34 | # through a ComponentConfig type
35 | #- manager_config_patch.yaml
36 |
37 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
38 | # crd/kustomization.yaml
39 | - manager_webhook_patch.yaml
40 |
41 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
42 | # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
43 | # 'CERTMANAGER' needs to be enabled to use ca injection
44 | - webhookcainjection_patch.yaml
45 |
46 | # the following config is for teaching kustomize how to do var substitution
47 | vars:
48 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
49 | - name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
50 | objref:
51 | kind: Certificate
52 | group: cert-manager.io
53 | version: v1
54 | name: serving-cert # this name should match the one in certificate.yaml
55 | fieldref:
56 | fieldpath: metadata.namespace
57 | - name: CERTIFICATE_NAME
58 | objref:
59 | kind: Certificate
60 | group: cert-manager.io
61 | version: v1
62 | name: serving-cert # this name should match the one in certificate.yaml
63 | - name: SERVICE_NAMESPACE # namespace of the service
64 | objref:
65 | kind: Service
66 | version: v1
67 | name: webhook-service
68 | fieldref:
69 | fieldpath: metadata.namespace
70 | - name: SERVICE_NAME
71 | objref:
72 | kind: Service
73 | version: v1
74 | name: webhook-service
75 |
--------------------------------------------------------------------------------
/config/default/manager_auth_proxy_patch.yaml:
--------------------------------------------------------------------------------
1 | # This patch inject a sidecar container which is a HTTP proxy for the
2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
3 | apiVersion: apps/v1
4 | kind: Deployment
5 | metadata:
6 | name: controller-manager
7 | namespace: system
8 | spec:
9 | template:
10 | spec:
11 | containers:
12 | - name: kube-rbac-proxy
13 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0
14 | args:
15 | - "--secure-listen-address=0.0.0.0:8443"
16 | - "--upstream=http://127.0.0.1:8080/"
17 | - "--logtostderr=true"
18 | - "--v=0"
19 | ports:
20 | - containerPort: 8443
21 | protocol: TCP
22 | name: https
23 | resources:
24 | limits:
25 | cpu: 500m
26 | memory: 128Mi
27 | requests:
28 | cpu: 5m
29 | memory: 64Mi
30 | - name: manager
31 | args:
32 | - "--health-probe-bind-address=:8081"
33 | - "--metrics-bind-address=127.0.0.1:8080"
34 | - "--leader-elect"
35 |
--------------------------------------------------------------------------------
/config/default/manager_config_patch.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: controller-manager
5 | namespace: system
6 | spec:
7 | template:
8 | spec:
9 | containers:
10 | - name: manager
11 | args:
12 | - "--config=controller_manager_config.yaml"
13 | volumeMounts:
14 | - name: manager-config
15 | mountPath: /controller_manager_config.yaml
16 | subPath: controller_manager_config.yaml
17 | volumes:
18 | - name: manager-config
19 | configMap:
20 | name: manager-config
21 |
--------------------------------------------------------------------------------
/config/default/manager_webhook_patch.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: controller-manager
5 | namespace: system
6 | spec:
7 | template:
8 | spec:
9 | containers:
10 | - name: manager
11 | ports:
12 | - containerPort: 9443
13 | name: webhook-server
14 | protocol: TCP
15 | volumeMounts:
16 | - mountPath: /tmp/k8s-webhook-server/serving-certs
17 | name: cert
18 | readOnly: true
19 | volumes:
20 | - name: cert
21 | secret:
22 | defaultMode: 420
23 | secretName: webhook-server-cert
24 |
--------------------------------------------------------------------------------
/config/default/webhookcainjection_patch.yaml:
--------------------------------------------------------------------------------
1 | # This patch add annotation to admission webhook config and
2 | # the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize.
3 | apiVersion: admissionregistration.k8s.io/v1
4 | kind: ValidatingWebhookConfiguration
5 | metadata:
6 | name: validating-webhook-configuration
7 | annotations:
8 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
9 |
--------------------------------------------------------------------------------
/config/manager/controller_manager_config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1
2 | kind: ControllerManagerConfig
3 | health:
4 | healthProbeBindAddress: :8081
5 | metrics:
6 | bindAddress: 127.0.0.1:8080
7 | webhook:
8 | port: 9443
9 | leaderElection:
10 | leaderElect: true
11 | resourceName: 5a5bab80.snappcloud.io
12 |
--------------------------------------------------------------------------------
/config/manager/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - manager.yaml
3 |
4 | generatorOptions:
5 | disableNameSuffixHash: true
6 |
7 | configMapGenerator:
8 | - files:
9 | - controller_manager_config.yaml
10 | name: manager-config
11 | apiVersion: kustomize.config.k8s.io/v1beta1
12 | kind: Kustomization
13 | images:
14 | - name: controller
15 | newName: controller
16 | newTag: latest
17 |
--------------------------------------------------------------------------------
/config/manager/manager.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: controller-manager
5 | namespace: system
6 | labels:
7 | control-plane: controller-manager
8 | spec:
9 | selector:
10 | matchLabels:
11 | control-plane: controller-manager
12 | replicas: 1
13 | template:
14 | metadata:
15 | annotations:
16 | kubectl.kubernetes.io/default-container: manager
17 | labels:
18 | control-plane: controller-manager
19 | spec:
20 | securityContext:
21 | runAsNonRoot: true
22 | containers:
23 | - command:
24 | - /manager
25 | args:
26 | - --leader-elect
27 | image: ghcr.io/snapp-incubator/grafana-complementary-operator:0.8.0
28 | env:
29 | - name: GRAFANA_PASSWORD
30 | valueFrom:
31 | secretKeyRef:
32 | name: grafana-operated-dashboard-credentials
33 | key: grafana-password
34 | - name: GRAFANA_USERNAME
35 | valueFrom:
36 | secretKeyRef:
37 | name: grafana-operated-dashboard-credentials
38 | key: grafana-username
39 | - name: GRAFANA_URL
40 | valueFrom:
41 | configMapKeyRef:
42 | name: grafana-complementary-config
43 | key: grafana-url
44 | - name: PROMETHEUS_URL
45 | valueFrom:
46 | configMapKeyRef:
47 | name: grafana-complementary-config
48 | key: prometheus-url
49 | imagePullPolicy: Always
50 | name: manager
51 | securityContext:
52 | allowPrivilegeEscalation: false
53 | livenessProbe:
54 | httpGet:
55 | path: /healthz
56 | port: 8081
57 | initialDelaySeconds: 15
58 | periodSeconds: 20
59 | readinessProbe:
60 | httpGet:
61 | path: /readyz
62 | port: 8081
63 | initialDelaySeconds: 5
64 | periodSeconds: 10
65 | # TODO(user): Configure the resources accordingly based on the project requirements.
66 | # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
67 | resources:
68 | limits:
69 | cpu: 2
70 | memory: 800Mi
71 | requests:
72 | cpu: 1
73 | memory: 800Mi
74 | serviceAccountName: controller-manager
75 | terminationGracePeriodSeconds: 10
76 |
--------------------------------------------------------------------------------
/config/manifests/kustomization.yaml:
--------------------------------------------------------------------------------
1 | # These resources constitute the fully configured set of manifests
2 | # used to generate the 'manifests/' directory in a bundle.
3 | resources:
4 | - bases/grafana-complementary-operator.clusterserviceversion.yaml
5 | - ../default
6 | - ../samples
7 | - ../scorecard
8 |
9 | # [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix.
10 | # Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager.
11 | # These patches remove the unnecessary "cert" volume and its manager container volumeMount.
12 | #patchesJson6902:
13 | #- target:
14 | # group: apps
15 | # version: v1
16 | # kind: Deployment
17 | # name: controller-manager
18 | # namespace: system
19 | # patch: |-
20 | # # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs.
21 | # # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment.
22 | # - op: remove
23 | # path: /spec/template/spec/containers/1/volumeMounts/0
24 | # # Remove the "cert" volume, since OLM will create and mount a set of certs.
25 | # # Update the indices in this path if adding or removing volumes in the manager's Deployment.
26 | # - op: remove
27 | # path: /spec/template/spec/volumes/0
28 |
--------------------------------------------------------------------------------
/config/prometheus/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - monitor.yaml
3 |
--------------------------------------------------------------------------------
/config/prometheus/monitor.yaml:
--------------------------------------------------------------------------------
1 |
2 | # Prometheus Monitor Service (Metrics)
3 | apiVersion: monitoring.coreos.com/v1
4 | kind: ServiceMonitor
5 | metadata:
6 | labels:
7 | control-plane: controller-manager
8 | name: controller-manager-metrics-monitor
9 | namespace: system
10 | spec:
11 | endpoints:
12 | - path: /metrics
13 | port: https
14 | scheme: https
15 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
16 | tlsConfig:
17 | insecureSkipVerify: true
18 | selector:
19 | matchLabels:
20 | control-plane: controller-manager
21 |
--------------------------------------------------------------------------------
/config/rbac/auth_proxy_client_clusterrole.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: metrics-reader
5 | rules:
6 | - nonResourceURLs:
7 | - "/metrics"
8 | verbs:
9 | - get
10 |
--------------------------------------------------------------------------------
/config/rbac/auth_proxy_role.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: proxy-role
5 | rules:
6 | - apiGroups:
7 | - authentication.k8s.io
8 | resources:
9 | - tokenreviews
10 | verbs:
11 | - create
12 | - apiGroups:
13 | - authorization.k8s.io
14 | resources:
15 | - subjectaccessreviews
16 | verbs:
17 | - create
18 |
--------------------------------------------------------------------------------
/config/rbac/auth_proxy_role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | name: proxy-rolebinding
5 | roleRef:
6 | apiGroup: rbac.authorization.k8s.io
7 | kind: ClusterRole
8 | name: proxy-role
9 | subjects:
10 | - kind: ServiceAccount
11 | name: controller-manager
12 | namespace: system
13 |
--------------------------------------------------------------------------------
/config/rbac/auth_proxy_service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | control-plane: controller-manager
6 | name: ctrl-metrics-service
7 | namespace: system
8 | spec:
9 | ports:
10 | - name: https
11 | port: 8443
12 | protocol: TCP
13 | targetPort: https
14 | selector:
15 | control-plane: controller-manager
16 |
--------------------------------------------------------------------------------
/config/rbac/grafana_grafanauser_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit grafanausers.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafanauser-editor-role
6 | rules:
7 | - apiGroups:
8 | - grafana.snappcloud.io
9 | resources:
10 | - grafanausers
11 | verbs:
12 | - create
13 | - delete
14 | - get
15 | - list
16 | - patch
17 | - update
18 | - watch
19 | - apiGroups:
20 | - grafana.snappcloud.io
21 | resources:
22 | - grafanausers/status
23 | verbs:
24 | - get
25 |
--------------------------------------------------------------------------------
/config/rbac/grafana_grafanauser_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view grafanausers.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafanauser-viewer-role
6 | rules:
7 | - apiGroups:
8 | - grafana.snappcloud.io
9 | resources:
10 | - grafanausers
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - apiGroups:
16 | - grafana.snappcloud.io
17 | resources:
18 | - grafanausers/status
19 | verbs:
20 | - get
21 |
--------------------------------------------------------------------------------
/config/rbac/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | # All RBAC will be applied under this service account in
3 | # the deployment namespace. You may comment out this resource
4 | # if your manager will use a service account that exists at
5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding
6 | # subjects if changing service account names.
7 | - service_account.yaml
8 | - role.yaml
9 | - role_binding.yaml
10 | - leader_election_role.yaml
11 | - leader_election_role_binding.yaml
12 | # Comment the following 4 lines if you want to disable
13 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy)
14 | # which protects your /metrics endpoint.
15 | - auth_proxy_service.yaml
16 | - auth_proxy_role.yaml
17 | - auth_proxy_role_binding.yaml
18 | - auth_proxy_client_clusterrole.yaml
19 |
--------------------------------------------------------------------------------
/config/rbac/leader_election_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions to do leader election.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: Role
4 | metadata:
5 | name: leader-election-role
6 | rules:
7 | - apiGroups:
8 | - ""
9 | resources:
10 | - configmaps
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - create
16 | - update
17 | - patch
18 | - delete
19 | - apiGroups:
20 | - coordination.k8s.io
21 | resources:
22 | - leases
23 | verbs:
24 | - get
25 | - list
26 | - watch
27 | - create
28 | - update
29 | - patch
30 | - delete
31 | - apiGroups:
32 | - ""
33 | resources:
34 | - events
35 | verbs:
36 | - create
37 | - patch
38 |
--------------------------------------------------------------------------------
/config/rbac/leader_election_role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: RoleBinding
3 | metadata:
4 | name: leader-election-rolebinding
5 | roleRef:
6 | apiGroup: rbac.authorization.k8s.io
7 | kind: Role
8 | name: leader-election-role
9 | subjects:
10 | - kind: ServiceAccount
11 | name: controller-manager
12 | namespace: system
13 |
--------------------------------------------------------------------------------
/config/rbac/role.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | creationTimestamp: null
6 | name: manager-role
7 | rules:
8 | - apiGroups:
9 | - ""
10 | resources:
11 | - configmaps
12 | verbs:
13 | - create
14 | - delete
15 | - get
16 | - list
17 | - patch
18 | - update
19 | - watch
20 | - apiGroups:
21 | - ""
22 | resources:
23 | - secrets
24 | verbs:
25 | - create
26 | - delete
27 | - get
28 | - list
29 | - patch
30 | - update
31 | - watch
32 | - apiGroups:
33 | - ""
34 | resources:
35 | - namespaces
36 | verbs:
37 | - get
38 | - list
39 | - watch
40 | - apiGroups:
41 | - ""
42 | resources:
43 | - namespaces/finalizers
44 | verbs:
45 | - update
46 | - apiGroups:
47 | - ""
48 | resources:
49 | - namespaces/status
50 | verbs:
51 | - get
52 | - patch
53 | - update
54 | - apiGroups:
55 | - ""
56 | resources:
57 | - secrets
58 | verbs:
59 | - get
60 | - list
61 | - watch
62 | - apiGroups:
63 | - ""
64 | resources:
65 | - serviceaccounts
66 | verbs:
67 | - get
68 | - list
69 | - watch
70 | - apiGroups:
71 | - grafana.snappcloud.io
72 | resources:
73 | - grafanausers
74 | verbs:
75 | - create
76 | - delete
77 | - get
78 | - list
79 | - patch
80 | - update
81 | - watch
82 | - apiGroups:
83 | - grafana.snappcloud.io
84 | resources:
85 | - grafanausers/finalizers
86 | verbs:
87 | - update
88 | - apiGroups:
89 | - grafana.snappcloud.io
90 | resources:
91 | - grafanausers/status
92 | verbs:
93 | - get
94 | - patch
95 | - update
96 | - apiGroups:
97 | - integreatly.org
98 | resources:
99 | - grafanadatasources
100 | verbs:
101 | - create
102 | - delete
103 | - get
104 | - list
105 | - patch
106 | - update
107 | - watch
108 | - apiGroups:
109 | - user.openshift.io
110 | resources:
111 | - '*'
112 | verbs:
113 | - create
114 | - delete
115 | - get
116 | - list
117 | - patch
118 | - update
119 | - watch
120 |
--------------------------------------------------------------------------------
/config/rbac/role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | name: manager-rolebinding
5 | roleRef:
6 | apiGroup: rbac.authorization.k8s.io
7 | kind: ClusterRole
8 | name: manager-role
9 | subjects:
10 | - kind: ServiceAccount
11 | name: controller-manager
12 | namespace: system
13 |
--------------------------------------------------------------------------------
/config/rbac/service_account.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | name: controller-manager
5 | namespace: system
6 |
--------------------------------------------------------------------------------
/config/samples/grafana_v1alpha1_grafanauser.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.snappcloud.io/v1alpha1
2 | kind: GrafanaUser
3 | metadata:
4 | name: grafanauser-sample
5 | namespace: test
6 | spec:
7 | admin:
8 | - user1
9 | view:
10 | - user2
11 | edit:
12 | - user3
13 |
--------------------------------------------------------------------------------
/config/samples/kustomization.yaml:
--------------------------------------------------------------------------------
1 | ## Append samples you want in your CSV to this file as resources ##
2 | resources:
3 | - core_v1_namespace.yaml
4 | - grafana_v1alpha1_grafanauser.yaml
5 | #+kubebuilder:scaffold:manifestskustomizesamples
6 |
--------------------------------------------------------------------------------
/config/scorecard/bases/config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: scorecard.operatorframework.io/v1alpha3
2 | kind: Configuration
3 | metadata:
4 | name: config
5 | stages:
6 | - parallel: true
7 | tests: []
8 |
--------------------------------------------------------------------------------
/config/scorecard/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - bases/config.yaml
3 | patchesJson6902:
4 | - path: patches/basic.config.yaml
5 | target:
6 | group: scorecard.operatorframework.io
7 | version: v1alpha3
8 | kind: Configuration
9 | name: config
10 | - path: patches/olm.config.yaml
11 | target:
12 | group: scorecard.operatorframework.io
13 | version: v1alpha3
14 | kind: Configuration
15 | name: config
16 | #+kubebuilder:scaffold:patchesJson6902
17 |
--------------------------------------------------------------------------------
/config/scorecard/patches/basic.config.yaml:
--------------------------------------------------------------------------------
1 | - op: add
2 | path: /stages/0/tests/-
3 | value:
4 | entrypoint:
5 | - scorecard-test
6 | - basic-check-spec
7 | image: quay.io/operator-framework/scorecard-test:v1.18.0
8 | labels:
9 | suite: basic
10 | test: basic-check-spec-test
11 |
--------------------------------------------------------------------------------
/config/scorecard/patches/olm.config.yaml:
--------------------------------------------------------------------------------
1 | - op: add
2 | path: /stages/0/tests/-
3 | value:
4 | entrypoint:
5 | - scorecard-test
6 | - olm-bundle-validation
7 | image: quay.io/operator-framework/scorecard-test:v1.18.0
8 | labels:
9 | suite: olm
10 | test: olm-bundle-validation-test
11 | - op: add
12 | path: /stages/0/tests/-
13 | value:
14 | entrypoint:
15 | - scorecard-test
16 | - olm-crds-have-validation
17 | image: quay.io/operator-framework/scorecard-test:v1.18.0
18 | labels:
19 | suite: olm
20 | test: olm-crds-have-validation-test
21 | - op: add
22 | path: /stages/0/tests/-
23 | value:
24 | entrypoint:
25 | - scorecard-test
26 | - olm-crds-have-resources
27 | image: quay.io/operator-framework/scorecard-test:v1.18.0
28 | labels:
29 | suite: olm
30 | test: olm-crds-have-resources-test
31 | - op: add
32 | path: /stages/0/tests/-
33 | value:
34 | entrypoint:
35 | - scorecard-test
36 | - olm-spec-descriptors
37 | image: quay.io/operator-framework/scorecard-test:v1.18.0
38 | labels:
39 | suite: olm
40 | test: olm-spec-descriptors-test
41 | - op: add
42 | path: /stages/0/tests/-
43 | value:
44 | entrypoint:
45 | - scorecard-test
46 | - olm-status-descriptors
47 | image: quay.io/operator-framework/scorecard-test:v1.18.0
48 | labels:
49 | suite: olm
50 | test: olm-status-descriptors-test
51 |
--------------------------------------------------------------------------------
/config/webhook/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - manifests.yaml
3 | - service.yaml
4 |
5 | configurations:
6 | - kustomizeconfig.yaml
7 |
--------------------------------------------------------------------------------
/config/webhook/kustomizeconfig.yaml:
--------------------------------------------------------------------------------
1 | # the following config is for teaching kustomize where to look at when substituting vars.
2 | # It requires kustomize v2.1.0 or newer to work properly.
3 | nameReference:
4 | - kind: Service
5 | version: v1
6 | fieldSpecs:
7 | - kind: ValidatingWebhookConfiguration
8 | group: admissionregistration.k8s.io
9 | path: webhooks/clientConfig/service/name
10 |
11 | namespace:
12 | - kind: ValidatingWebhookConfiguration
13 | group: admissionregistration.k8s.io
14 | path: webhooks/clientConfig/service/namespace
15 | create: true
16 |
17 | varReference:
18 | - path: metadata/annotations
19 |
--------------------------------------------------------------------------------
/config/webhook/manifests.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: admissionregistration.k8s.io/v1
3 | kind: ValidatingWebhookConfiguration
4 | metadata:
5 | creationTimestamp: null
6 | name: validating-webhook-configuration
7 | webhooks:
8 | - admissionReviewVersions:
9 | - v1
10 | - v1beta1
11 | clientConfig:
12 | service:
13 | name: webhook-service
14 | namespace: system
15 | path: /validate-grafana-snappcloud-io-v1alpha1-grafanauser
16 | failurePolicy: Fail
17 | name: vgrafana.kb.io
18 | rules:
19 | - apiGroups:
20 | - grafana.snappcloud.io
21 | apiVersions:
22 | - v1alpha1
23 | operations:
24 | - CREATE
25 | - UPDATE
26 | resources:
27 | - grafanausers
28 | sideEffects: None
29 |
--------------------------------------------------------------------------------
/config/webhook/service.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: v1
3 | kind: Service
4 | metadata:
5 | name: webhook-service
6 | namespace: system
7 | spec:
8 | ports:
9 | - port: 443
10 | protocol: TCP
11 | targetPort: 9443
12 | selector:
13 | control-plane: controller-manager
14 |
--------------------------------------------------------------------------------
/controllers/grafanauser/grafanauser_controller.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package grafanauser
18 |
19 | import (
20 | "context"
21 | "fmt"
22 | "os"
23 | "strings"
24 |
25 | "k8s.io/apimachinery/pkg/api/errors"
26 | "k8s.io/apimachinery/pkg/runtime"
27 | "k8s.io/apimachinery/pkg/types"
28 | ctrl "sigs.k8s.io/controller-runtime"
29 | "sigs.k8s.io/controller-runtime/pkg/client"
30 | "sigs.k8s.io/controller-runtime/pkg/log"
31 |
32 | "github.com/grafana-tools/sdk"
33 | grafanauserv1alpha1 "github.com/snapp-cab/grafana-complementary-operator/apis/grafana/v1alpha1"
34 | corev1 "k8s.io/api/core/v1"
35 | )
36 |
37 | const (
38 | teamLabel = "snappcloud.io/team"
39 | )
40 |
41 | // Get Grafana URL and PassWord as a env.
42 | var grafanaPassword = os.Getenv("GRAFANA_PASSWORD")
43 | var grafanaUsername = os.Getenv("GRAFANA_USERNAME")
44 | var grafanaURL = os.Getenv("GRAFANA_URL")
45 |
46 | // GrafanaReconciler reconciles a Grafana object
47 | type GrafanaUserReconciler struct {
48 | client.Client
49 | Scheme *runtime.Scheme
50 | }
51 |
52 | //+kubebuilder:rbac:groups=grafana.snappcloud.io,resources=grafanausers,verbs=get;list;watch;create;update;patch;delete
53 | //+kubebuilder:rbac:groups=grafana.snappcloud.io,resources=grafanausers/status,verbs=get;update;patch
54 | //+kubebuilder:rbac:groups=grafana.snappcloud.io,resources=grafanausers/finalizers,verbs=update
55 | //+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete
56 | //+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete
57 | //+kubebuilder:rbac:groups=user.openshift.io,resources=*,verbs=get;list;watch;create;update;patch;delete
58 |
59 | // Reconcile is part of the main kubernetes reconciliation loop which aims to
60 | // move the current state of the cluster closer to the desired state.
61 | // TODO(user): Modify the Reconcile function to compare the state specified by
62 | // the Grafana object against the actual cluster state, and then
63 | // perform operations to make the cluster state reflect the state specified by
64 | // the user.
65 | //
66 | // For more details, check Reconcile and its Result here:
67 | // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile
68 | func (r *GrafanaUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
69 | log := log.FromContext(ctx)
70 | reqLogger := log.WithValues("Request.Namespace", req.Namespace, "Request.Name", req.Name)
71 | // Getting namespace
72 | ns := &corev1.Namespace{}
73 | err := r.Client.Get(context.TODO(), types.NamespacedName{Name: req.Namespace}, ns)
74 | if err != nil {
75 | log.Error(err, "Failed to get namespace")
76 | return ctrl.Result{}, err
77 | }
78 | // Ignore namespaces which does not have team label
79 | org, ok := ns.Labels[teamLabel]
80 | if !ok {
81 | reqLogger.Info("Namespace does not have team label. Ignoring", "namespace", ns.Name, "team name ", org)
82 | return ctrl.Result{}, nil
83 | }
84 | //Connecting to the Grafana API
85 | grafanaclient, err := sdk.NewClient(grafanaURL, fmt.Sprintf("%s:%s", grafanaUsername, grafanaPassword), sdk.DefaultHTTPClient)
86 | if err != nil {
87 | reqLogger.Error(err, "Unable to create Grafana client")
88 | return ctrl.Result{}, err
89 | }
90 | //Retrieving the Organization Info
91 | retrievedOrg, err := grafanaclient.GetOrgByOrgName(ctx, org)
92 | if err != nil {
93 | if strings.Contains(err.Error(), "Organization not found") {
94 | reqLogger.Error(err, "Unable to get organization")
95 | return ctrl.Result{}, err
96 | }
97 | }
98 | reqLogger.Info("Reconciling grafana")
99 | grafana := &grafanauserv1alpha1.GrafanaUser{}
100 | err = r.Client.Get(context.TODO(), req.NamespacedName, grafana)
101 | if err != nil {
102 | if errors.IsNotFound(err) {
103 | // Request object not found, could have been deleted after reconcile request.
104 | // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
105 | // Return and don't requeue
106 | return ctrl.Result{}, nil
107 | }
108 | // Error reading the object - requeue the request.
109 | return ctrl.Result{}, err
110 | } else {
111 | log.Info("grafana_org is found and orgName is : " + org)
112 |
113 | }
114 |
115 | _, err = r.AddUsersToGrafanaOrgByEmail(ctx, req, org, grafanaclient, retrievedOrg, grafana.Spec.Admin, "admin")
116 | if err != nil {
117 | return ctrl.Result{}, err
118 | }
119 |
120 | _, err = r.AddUsersToGrafanaOrgByEmail(ctx, req, org, grafanaclient, retrievedOrg, grafana.Spec.Edit, "editor")
121 | if err != nil {
122 | return ctrl.Result{}, err
123 | }
124 | _, err = r.AddUsersToGrafanaOrgByEmail(ctx, req, org, grafanaclient, retrievedOrg, grafana.Spec.View, "viewer")
125 | if err != nil {
126 | return ctrl.Result{}, err
127 | }
128 |
129 | return ctrl.Result{}, nil
130 | }
131 | func (r *GrafanaUserReconciler) AddUsersToGrafanaOrgByEmail(ctx context.Context, req ctrl.Request, org string, client *sdk.Client, retrievedOrg sdk.Org, emails []string, role string) (ctrl.Result, error) {
132 | log := log.FromContext(ctx)
133 | reqLogger := log.WithValues("Request.Namespace", req.Namespace, "Request.Name", req.Name)
134 | orgID := retrievedOrg.ID
135 | orgName := retrievedOrg.Name
136 | getallUser, _ := client.GetAllUsers(ctx)
137 | getuserOrg, _ := client.GetOrgUsers(ctx, orgID)
138 | for _, email := range emails {
139 | var orguserfound bool
140 | for _, orguser := range getuserOrg {
141 | UserOrg := orguser.Email
142 | if email == UserOrg {
143 | orguserfound = true
144 | reqLogger.Info(orguser.Email, "is already in", orgName)
145 | break
146 | }
147 | }
148 | if orguserfound {
149 | continue
150 | }
151 | for _, user := range getallUser {
152 | UserEmail := user.Email
153 | if email == UserEmail {
154 | newuser := sdk.UserRole{LoginOrEmail: email, Role: role}
155 | _, err := client.AddOrgUser(ctx, newuser, orgID)
156 | if err != nil {
157 | return ctrl.Result{}, err
158 | } else {
159 | log.Info(UserEmail, "is added to", orgName)
160 | }
161 | break
162 | }
163 | }
164 | }
165 | return ctrl.Result{}, nil
166 | }
167 |
168 | // SetupWithManager sets up the controller with the Manager.
169 | func (r *GrafanaUserReconciler) SetupWithManager(mgr ctrl.Manager) error {
170 | return ctrl.NewControllerManagedBy(mgr).
171 | For(&grafanauserv1alpha1.GrafanaUser{}).
172 | Complete(r)
173 | }
174 |
--------------------------------------------------------------------------------
/controllers/grafanauser/suite_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package grafanauser
18 |
19 | import (
20 | "path/filepath"
21 | "testing"
22 |
23 | . "github.com/onsi/ginkgo"
24 | . "github.com/onsi/gomega"
25 | "k8s.io/client-go/kubernetes/scheme"
26 | "sigs.k8s.io/controller-runtime/pkg/client"
27 | "sigs.k8s.io/controller-runtime/pkg/envtest"
28 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
29 | logf "sigs.k8s.io/controller-runtime/pkg/log"
30 | "sigs.k8s.io/controller-runtime/pkg/log/zap"
31 |
32 | grafanauserv1alpha1 "github.com/snapp-cab/grafana-complementary-operator/apis/grafana/v1alpha1"
33 | //+kubebuilder:scaffold:imports
34 | )
35 |
36 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to
37 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
38 |
39 | var k8sClient client.Client
40 | var testEnv *envtest.Environment
41 |
42 | func TestAPIs(t *testing.T) {
43 | RegisterFailHandler(Fail)
44 |
45 | RunSpecsWithDefaultAndCustomReporters(t,
46 | "Controller Suite",
47 | []Reporter{printer.NewlineReporter{}})
48 | }
49 |
50 | var _ = BeforeSuite(func() {
51 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
52 |
53 | By("bootstrapping test environment")
54 | testEnv = &envtest.Environment{
55 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
56 | ErrorIfCRDPathMissing: true,
57 | }
58 |
59 | cfg, err := testEnv.Start()
60 | Expect(err).NotTo(HaveOccurred())
61 | Expect(cfg).NotTo(BeNil())
62 |
63 | err = grafanauserv1alpha1.AddToScheme(scheme.Scheme)
64 | Expect(err).NotTo(HaveOccurred())
65 |
66 | //+kubebuilder:scaffold:scheme
67 |
68 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
69 | Expect(err).NotTo(HaveOccurred())
70 | Expect(k8sClient).NotTo(BeNil())
71 |
72 | }, 60)
73 |
74 | var _ = AfterSuite(func() {
75 | By("tearing down the test environment")
76 | err := testEnv.Stop()
77 | Expect(err).NotTo(HaveOccurred())
78 | })
79 |
--------------------------------------------------------------------------------
/controllers/namespace/namespace_controller.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package controllers
18 |
19 | import (
20 | "context"
21 | "fmt"
22 | "os"
23 | "reflect"
24 | "strings"
25 |
26 | grafanav1alpha1 "github.com/grafana-operator/grafana-operator/v4/api/integreatly/v1alpha1"
27 | "github.com/grafana-tools/sdk"
28 | corev1 "k8s.io/api/core/v1"
29 | "k8s.io/apimachinery/pkg/api/errors"
30 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31 | "k8s.io/apimachinery/pkg/runtime"
32 | "k8s.io/apimachinery/pkg/types"
33 | ctrl "sigs.k8s.io/controller-runtime"
34 | "sigs.k8s.io/controller-runtime/pkg/client"
35 | "sigs.k8s.io/controller-runtime/pkg/log"
36 | )
37 |
38 | const (
39 | baseNs = "snappcloud-monitoring"
40 | baseSa = "monitoring-datasource"
41 | nsMonitoringLabel = "monitoring.snappcloud.io/grafana-datasource"
42 | teamLabel = "snappcloud.io/team"
43 | )
44 |
45 | // Get Grafana URL and PassWord as a env.
46 | var grafanaPassword = os.Getenv("GRAFANA_PASSWORD")
47 | var grafanaUsername = os.Getenv("GRAFANA_USERNAME")
48 | var grafanaURL = os.Getenv("GRAFANA_URL")
49 | var prometheusURL = os.Getenv("PROMETHEUS_URL")
50 |
51 | // NamespaceReconciler reconciles a Namespace object
52 | type NamespaceReconciler struct {
53 | client.Client
54 | Scheme *runtime.Scheme
55 | }
56 |
57 | //+kubebuilder:rbac:groups=core,resources=namespaces,verbs=get;list;watch
58 | //+kubebuilder:rbac:groups=core,resources=namespaces/status,verbs=get;update;patch
59 | //+kubebuilder:rbac:groups=core,resources=namespaces/finalizers,verbs=update
60 | //+kubebuilder:rbac:groups=core,resources=serviceaccounts,verbs=get;list;watch
61 | //+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch
62 |
63 | //+kubebuilder:rbac:groups=integreatly.org,resources=grafanadatasources,verbs=get;list;watch;create;update;patch;delete
64 |
65 | // Reconcile is part of the main kubernetes reconciliation loop which aims to
66 | // move the current state of the cluster closer to the desired state.
67 | // TODO(user): Modify the Reconcile function to compare the state specified by
68 | // the Namespace object against the actual cluster state, and then
69 | // perform operations to make the cluster state reflect the state specified by
70 | // the user.
71 | //
72 | // For more details, check Reconcile and its Result here:
73 | // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.11.0/pkg/reconcile
74 | func (r *NamespaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
75 | logger := log.FromContext(ctx)
76 | ns := &corev1.Namespace{}
77 | err := r.Get(ctx, req.NamespacedName, ns)
78 | if err != nil {
79 | if errors.IsNotFound(err) {
80 | // Request object not found, could have been deleted after reconcile request.
81 | // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
82 | // Return and don't requeue
83 | logger.Info("Resource not found. Ignoring since object must be deleted")
84 | return ctrl.Result{}, nil
85 | }
86 | // Error reading the object - requeue the request.
87 | logger.Error(err, "Failed to get Namespace")
88 | return ctrl.Result{}, err
89 | }
90 |
91 | // Ignore namespaces which does not have special label
92 | if _, ok := ns.Labels[nsMonitoringLabel]; !ok {
93 | logger.Info("Namespace does not have monitoring label. Ignoring", "namespace", ns.Name)
94 | return ctrl.Result{}, nil
95 | }
96 |
97 | // Ignore namespaces which does not have team label
98 | team, ok := ns.Labels[teamLabel]
99 | if !ok {
100 | logger.Info("Namespace does not have team label. Ignoring", "namespace", ns.Name, "team name ", team)
101 | return ctrl.Result{}, nil
102 | }
103 |
104 | logger.Info("Reconciling Namespace", "Namespace.Name", req.NamespacedName, "Team", team)
105 |
106 | // Getting serviceAccount
107 | logger.Info("Getting serviceAccount", "serviceAccount.Name", baseSa, "Namespace.Name", req.NamespacedName)
108 | sa := &corev1.ServiceAccount{}
109 | err = r.Get(ctx, types.NamespacedName{Name: baseSa, Namespace: req.Name}, sa)
110 | if err != nil {
111 | logger.Error(err, "Unable to get ServiceAccount")
112 | return ctrl.Result{}, err
113 | }
114 |
115 | // Getting serviceaccount token
116 | secret := &corev1.Secret{}
117 | var token string
118 | for _, ref := range sa.Secrets {
119 |
120 | logger.Info("Getting secret", "secret.Name", ref.Name, "Namespace.Name", req.Name)
121 | // get secret
122 | err = r.Get(ctx, types.NamespacedName{Name: ref.Name, Namespace: req.Name}, secret)
123 | if err != nil {
124 | logger.Error(err, "Unable to get Secret")
125 | return ctrl.Result{}, err
126 | }
127 |
128 | // Check if secret is a token for the serviceaccount
129 | if secret.Type != corev1.SecretTypeServiceAccountToken {
130 | continue
131 | }
132 | name := secret.Annotations[corev1.ServiceAccountNameKey]
133 | uid := secret.Annotations[corev1.ServiceAccountUIDKey]
134 | tokenData := secret.Data[corev1.ServiceAccountTokenKey]
135 | //tmp
136 | logger.Info("Token data", "token", string(tokenData))
137 | logger.Info("Token meta", "name", name, "uid", uid, "ref.Name", ref.Name, "ref.UID", ref.UID)
138 | if name == sa.Name && uid == string(sa.UID) && len(tokenData) > 0 {
139 | // found token, the first token found is used
140 | token = string(tokenData)
141 | logger.Info("Found token", "token", token)
142 | break
143 | }
144 |
145 | }
146 | // if no token found
147 | if token == "" {
148 | logger.Error(fmt.Errorf("did not found service account token for service account %q", sa.Name), "")
149 | return ctrl.Result{}, err
150 | }
151 | gfDs, err := r.generateGfDataSource(ctx, req.Name, team, token, ns)
152 | if err != nil {
153 | logger.Error(err, "Error generating grafanaDatasource manifest")
154 | return ctrl.Result{}, err
155 | }
156 |
157 | // Check if grafanaDatasource does not exist and create a new one
158 | found := &grafanav1alpha1.GrafanaDataSource{}
159 | err = r.Get(ctx, types.NamespacedName{Name: req.Name, Namespace: baseNs}, found)
160 | if err != nil && errors.IsNotFound(err) {
161 | logger.Info("Creating grafana datasource", "grafanaDatasource.Name", gfDs.Name)
162 | err = r.Create(ctx, gfDs)
163 | if err != nil {
164 | logger.Error(err, "Unable to create GrafanaDataSource")
165 | return ctrl.Result{}, err
166 | }
167 | // grafanaDatasource created successfully - return and requeue
168 | return ctrl.Result{Requeue: true}, nil
169 | } else if err != nil {
170 | logger.Error(err, "Failed to get grafanaDatasource")
171 | return ctrl.Result{}, err
172 | }
173 |
174 | // If GrafanaDatasource already exist, check if it is deeply equal with desrired state
175 | if !reflect.DeepEqual(gfDs.Spec, found.Spec) {
176 | logger.Info("Updating grafanaDatasource", "grafanaDatasource.Namespace", found.Namespace, "grafanaDatasource.Name", found.Name)
177 | found.Spec = gfDs.Spec
178 | err := r.Update(ctx, found)
179 | if err != nil {
180 | logger.Error(err, "Failed to update grafanaDatasource", "grafanaDatasource.Namespace", found.Namespace, "grafanaDatasource.Name", found.Name)
181 | return ctrl.Result{}, err
182 | }
183 | }
184 |
185 | return ctrl.Result{}, nil
186 | }
187 |
188 | // SetupWithManager sets up the controller with the Manager.
189 | func (r *NamespaceReconciler) SetupWithManager(mgr ctrl.Manager) error {
190 | return ctrl.NewControllerManagedBy(mgr).
191 | For(&corev1.Namespace{}).
192 | Owns(&grafanav1alpha1.GrafanaDataSource{}).
193 | Complete(r)
194 | }
195 |
196 | func (r *NamespaceReconciler) generateGfDataSource(ctx context.Context, name, team, token string, nsOwner *corev1.Namespace) (*grafanav1alpha1.GrafanaDataSource, error) {
197 | logger := log.FromContext(ctx)
198 |
199 | // Connecting to the Grafana API
200 | client, err := sdk.NewClient(grafanaURL, fmt.Sprintf("%s:%s", grafanaUsername, grafanaPassword), sdk.DefaultHTTPClient)
201 | if err != nil {
202 | logger.Error(err, "Unable to create Grafana client")
203 | return nil, err
204 | }
205 |
206 | // Retrieving the Organization Info
207 | retrievedOrg, err := client.GetOrgByOrgName(ctx, team)
208 | if err != nil {
209 | if strings.Contains(err.Error(), "Organization not found") {
210 | logger.Info("Creating organization", "team name is", team)
211 | // Create the organization
212 |
213 | neworg := sdk.Org{Name: team}
214 | _, err := client.CreateOrg(ctx, neworg)
215 |
216 | if err != nil {
217 | logger.Error(err, "Failed to create organization")
218 | return nil, err
219 | }
220 | retrievedOrg = neworg
221 | logger.Info("Organization created", "for team", team, "organization name is", retrievedOrg.Name)
222 | } else {
223 | logger.Error(err, "Unable to get organization")
224 | return nil, err
225 | }
226 | }
227 | // Generating the datasource
228 | logger.Info("Start creating Datasource", "Team name:", team, "Team ID:", retrievedOrg.ID, "Namespace", name)
229 | grafanaDatasource := &grafanav1alpha1.GrafanaDataSource{
230 | ObjectMeta: metav1.ObjectMeta{
231 | Name: name,
232 | Namespace: baseNs,
233 | },
234 | Spec: grafanav1alpha1.GrafanaDataSourceSpec{
235 | Name: name + ".yaml",
236 | Datasources: []grafanav1alpha1.GrafanaDataSourceFields{{
237 | Access: "proxy",
238 | Editable: false,
239 | IsDefault: false,
240 | Name: name,
241 | OrgId: int(retrievedOrg.ID),
242 | Type: "prometheus",
243 | Url: prometheusURL,
244 | Version: 1,
245 | JsonData: grafanav1alpha1.GrafanaDataSourceJsonData{
246 | HTTPMethod: "POST",
247 | TlsSkipVerify: true,
248 | HTTPHeaderName1: "Authorization",
249 | HTTPHeaderName2: "namespace",
250 | },
251 | SecureJsonData: grafanav1alpha1.GrafanaDataSourceSecureJsonData{
252 | HTTPHeaderValue1: "Bearer " + token,
253 | HTTPHeaderValue2: name,
254 | },
255 | }},
256 | },
257 | }
258 |
259 | // Set target Namespace as the owner of GrafanaDatasource in another Namespace
260 | err = ctrl.SetControllerReference(nsOwner, grafanaDatasource, r.Scheme)
261 | if err != nil {
262 | return nil, err
263 | }
264 |
265 | return grafanaDatasource, nil
266 | }
267 |
--------------------------------------------------------------------------------
/controllers/namespace/suite_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package controllers
18 |
19 | import (
20 | "path/filepath"
21 | "testing"
22 |
23 | . "github.com/onsi/ginkgo"
24 | . "github.com/onsi/gomega"
25 | corev1 "k8s.io/api/core/v1"
26 | "k8s.io/client-go/kubernetes/scheme"
27 | "sigs.k8s.io/controller-runtime/pkg/client"
28 | "sigs.k8s.io/controller-runtime/pkg/envtest"
29 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
30 | logf "sigs.k8s.io/controller-runtime/pkg/log"
31 | "sigs.k8s.io/controller-runtime/pkg/log/zap"
32 | //+kubebuilder:scaffold:imports
33 | )
34 |
35 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to
36 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
37 |
38 | var k8sClient client.Client
39 | var testEnv *envtest.Environment
40 |
41 | func TestAPIs(t *testing.T) {
42 | RegisterFailHandler(Fail)
43 |
44 | RunSpecsWithDefaultAndCustomReporters(t,
45 | "Controller Suite",
46 | []Reporter{printer.NewlineReporter{}})
47 | }
48 |
49 | var _ = BeforeSuite(func() {
50 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
51 |
52 | By("bootstrapping test environment")
53 | testEnv = &envtest.Environment{
54 | CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
55 | ErrorIfCRDPathMissing: false,
56 | }
57 |
58 | cfg, err := testEnv.Start()
59 | Expect(err).NotTo(HaveOccurred())
60 | Expect(cfg).NotTo(BeNil())
61 |
62 | err = corev1.AddToScheme(scheme.Scheme)
63 | Expect(err).NotTo(HaveOccurred())
64 |
65 | //+kubebuilder:scaffold:scheme
66 |
67 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
68 | Expect(err).NotTo(HaveOccurred())
69 | Expect(k8sClient).NotTo(BeNil())
70 |
71 | }, 60)
72 |
73 | var _ = AfterSuite(func() {
74 | By("tearing down the test environment")
75 | err := testEnv.Stop()
76 | Expect(err).NotTo(HaveOccurred())
77 | })
78 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/snapp-cab/grafana-complementary-operator
2 |
3 | go 1.19
4 |
5 | require (
6 | github.com/grafana-operator/grafana-operator/v4 v4.10.1
7 | github.com/grafana-tools/sdk v0.0.0-20220402173226-77f22ba83269
8 | github.com/onsi/ginkgo v1.16.5
9 | github.com/onsi/gomega v1.29.0
10 | k8s.io/api v0.28.3
11 | k8s.io/apimachinery v0.28.3
12 | k8s.io/client-go v0.28.3
13 | sigs.k8s.io/controller-runtime v0.16.3
14 | )
15 |
16 | require (
17 | github.com/beorn7/perks v1.0.1 // indirect
18 | github.com/blang/semver v3.5.1+incompatible // indirect
19 | github.com/cespare/xxhash/v2 v2.2.0 // indirect
20 | github.com/davecgh/go-spew v1.1.1 // indirect
21 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect
22 | github.com/evanphx/json-patch/v5 v5.6.0 // indirect
23 | github.com/fsnotify/fsnotify v1.6.0 // indirect
24 | github.com/go-logr/logr v1.2.4 // indirect
25 | github.com/go-logr/zapr v1.2.4 // indirect
26 | github.com/go-openapi/jsonpointer v0.19.6 // indirect
27 | github.com/go-openapi/jsonreference v0.20.2 // indirect
28 | github.com/go-openapi/swag v0.22.3 // indirect
29 | github.com/gogo/protobuf v1.3.2 // indirect
30 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
31 | github.com/golang/protobuf v1.5.3 // indirect
32 | github.com/google/gnostic-models v0.6.8 // indirect
33 | github.com/google/go-cmp v0.6.0 // indirect
34 | github.com/google/gofuzz v1.2.0 // indirect
35 | github.com/google/uuid v1.3.0 // indirect
36 | github.com/gosimple/slug v1.12.0 // indirect
37 | github.com/gosimple/unidecode v1.0.1 // indirect
38 | github.com/imdario/mergo v0.3.13 // indirect
39 | github.com/josharian/intern v1.0.0 // indirect
40 | github.com/json-iterator/go v1.1.12 // indirect
41 | github.com/mailru/easyjson v0.7.7 // indirect
42 | github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
43 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
44 | github.com/modern-go/reflect2 v1.0.2 // indirect
45 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
46 | github.com/nxadm/tail v1.4.8 // indirect
47 | github.com/openshift/api v3.9.0+incompatible // indirect
48 | github.com/pkg/errors v0.9.1 // indirect
49 | github.com/prometheus/client_golang v1.16.0 // indirect
50 | github.com/prometheus/client_model v0.4.0 // indirect
51 | github.com/prometheus/common v0.44.0 // indirect
52 | github.com/prometheus/procfs v0.10.1 // indirect
53 | github.com/spf13/pflag v1.0.5 // indirect
54 | go.uber.org/multierr v1.11.0 // indirect
55 | go.uber.org/zap v1.25.0 // indirect
56 | golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
57 | golang.org/x/net v0.17.0 // indirect
58 | golang.org/x/oauth2 v0.8.0 // indirect
59 | golang.org/x/sys v0.13.0 // indirect
60 | golang.org/x/term v0.13.0 // indirect
61 | golang.org/x/text v0.13.0 // indirect
62 | golang.org/x/time v0.3.0 // indirect
63 | gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
64 | google.golang.org/appengine v1.6.7 // indirect
65 | google.golang.org/protobuf v1.30.0 // indirect
66 | gopkg.in/inf.v0 v0.9.1 // indirect
67 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
68 | gopkg.in/yaml.v2 v2.4.0 // indirect
69 | gopkg.in/yaml.v3 v3.0.1 // indirect
70 | k8s.io/apiextensions-apiserver v0.28.3 // indirect
71 | k8s.io/component-base v0.28.3 // indirect
72 | k8s.io/klog/v2 v2.100.1 // indirect
73 | k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
74 | k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
75 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
76 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
77 | sigs.k8s.io/yaml v1.3.0 // indirect
78 | )
79 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
2 | github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
3 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
4 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
5 | github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
6 | github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
7 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
8 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
9 | github.com/chromedp/cdproto v0.0.0-20210526005521-9e51b9051fd0/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
10 | github.com/chromedp/cdproto v0.0.0-20210706234513-2bc298e8be7f h1:lg5k1KAxmknil6Z19LaaeiEs5Pje7hPzRfyWSSnWLP0=
11 | github.com/chromedp/cdproto v0.0.0-20210706234513-2bc298e8be7f/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
12 | github.com/chromedp/chromedp v0.7.3 h1:FvgJICfjvXtDX+miuMUY0NHuY8zQvjS/TcEQEG6Ldzs=
13 | github.com/chromedp/chromedp v0.7.3/go.mod h1:9gC521Yzgrk078Ulv6KIgG7hJ2x9aWrxMBBobTFk30A=
14 | github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
15 | github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
16 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
17 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
18 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
19 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
20 | github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
21 | github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
22 | github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
23 | github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
24 | github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
25 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
26 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
27 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
28 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
29 | github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
30 | github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
31 | github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
32 | github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
33 | github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA=
34 | github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
35 | github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
36 | github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
37 | github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
38 | github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
39 | github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
40 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
41 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
42 | github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
43 | github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
44 | github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
45 | github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
46 | github.com/gobwas/ws v1.1.0-rc.5 h1:QOAag7FoBaBYYHRqzqkhhd8fq5RTubvI4v3Ft/gDVVQ=
47 | github.com/gobwas/ws v1.1.0-rc.5/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
48 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
49 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
50 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
51 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
52 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
53 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
54 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
55 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
56 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
57 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
58 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
59 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
60 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
61 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
62 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
63 | github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
64 | github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
65 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
66 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
67 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
68 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
69 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
70 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
71 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
72 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
73 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
74 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
75 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
76 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
77 | github.com/gosimple/slug v1.1.1/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0=
78 | github.com/gosimple/slug v1.12.0 h1:xzuhj7G7cGtd34NXnW/yF0l+AGNfWqwgh/IXgFy7dnc=
79 | github.com/gosimple/slug v1.12.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
80 | github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
81 | github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
82 | github.com/grafana-operator/grafana-operator/v4 v4.10.1 h1:9TSZhuMh6b64frhTa8eb+jBEw0oZp076Bh990Ts2WqU=
83 | github.com/grafana-operator/grafana-operator/v4 v4.10.1/go.mod h1:k69wJcXVrqAcZBoGuh5LSqz0ak8LlVOxxqp0W3f/4V8=
84 | github.com/grafana-tools/sdk v0.0.0-20220402173226-77f22ba83269 h1:730mFhwDnWHkimN3URriW84LJs8k0PJccN1lDn7cxHA=
85 | github.com/grafana-tools/sdk v0.0.0-20220402173226-77f22ba83269/go.mod h1:AHHlOEv1+GGQ3ktHMlhuTUwo3zljV3QJbC0+8o2kn+4=
86 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
87 | github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
88 | github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
89 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
90 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
91 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
92 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
93 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
94 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
95 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
96 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
97 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
98 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
99 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
100 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
101 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
102 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
103 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
104 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
105 | github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
106 | github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
107 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
108 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
109 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
110 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
111 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
112 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
113 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
114 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
115 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
116 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
117 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
118 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
119 | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
120 | github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
121 | github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
122 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
123 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
124 | github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
125 | github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
126 | github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs=
127 | github.com/openshift/api v3.9.0+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY=
128 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
129 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
130 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
131 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
132 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
133 | github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
134 | github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
135 | github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
136 | github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
137 | github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
138 | github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
139 | github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
140 | github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
141 | github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
142 | github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
143 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
144 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
145 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
146 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
147 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
148 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
149 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
150 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
151 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
152 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
153 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
154 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
155 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
156 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
157 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
158 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
159 | go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
160 | go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
161 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
162 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
163 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
164 | go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
165 | go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
166 | go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
167 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
168 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
169 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
170 | golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
171 | golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
172 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
173 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
174 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
175 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
176 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
177 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
178 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
179 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
180 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
181 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
182 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
183 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
184 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
185 | golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
186 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
187 | golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
188 | golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
189 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
190 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
191 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
192 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
193 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
194 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
195 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
196 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
197 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
198 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
199 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
200 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
201 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
202 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
203 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
204 | golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
205 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
206 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
207 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
208 | golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
209 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
210 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
211 | golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
212 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
213 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
214 | golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
215 | golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
216 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
217 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
218 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
219 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
220 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
221 | golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
222 | golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
223 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
224 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
225 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
226 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
227 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
228 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
229 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
230 | golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
231 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
232 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
233 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
234 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
235 | gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
236 | gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
237 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
238 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
239 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
240 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
241 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
242 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
243 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
244 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
245 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
246 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
247 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
248 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
249 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
250 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
251 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
252 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
253 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
254 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
255 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
256 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
257 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
258 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
259 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
260 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
261 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
262 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
263 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
264 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
265 | gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
266 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
267 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
268 | k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
269 | k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
270 | k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08=
271 | k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc=
272 | k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A=
273 | k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8=
274 | k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
275 | k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
276 | k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI=
277 | k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8=
278 | k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
279 | k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
280 | k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ=
281 | k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
282 | k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
283 | k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
284 | sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4=
285 | sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0=
286 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
287 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
288 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
289 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
290 | sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
291 | sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
292 |
--------------------------------------------------------------------------------
/hack/boilerplate.go.txt:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "flag"
21 | "os"
22 |
23 | // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
24 | // to ensure that exec-entrypoint and run can make use of them.
25 | _ "k8s.io/client-go/plugin/pkg/client/auth"
26 |
27 | grafanav1alpha1 "github.com/grafana-operator/grafana-operator/v4/api/integreatly/v1alpha1"
28 | "k8s.io/apimachinery/pkg/runtime"
29 | utilruntime "k8s.io/apimachinery/pkg/util/runtime"
30 | clientgoscheme "k8s.io/client-go/kubernetes/scheme"
31 | ctrl "sigs.k8s.io/controller-runtime"
32 | "sigs.k8s.io/controller-runtime/pkg/healthz"
33 | "sigs.k8s.io/controller-runtime/pkg/log/zap"
34 |
35 | grafanauserv1alpha1 "github.com/snapp-cab/grafana-complementary-operator/apis/grafana/v1alpha1"
36 | grafanausercontrollers "github.com/snapp-cab/grafana-complementary-operator/controllers/grafanauser"
37 | namesapcecontrollers "github.com/snapp-cab/grafana-complementary-operator/controllers/namespace"
38 | //+kubebuilder:scaffold:imports
39 | )
40 |
41 | var (
42 | scheme = runtime.NewScheme()
43 | setupLog = ctrl.Log.WithName("setup")
44 | )
45 |
46 | func init() {
47 | utilruntime.Must(clientgoscheme.AddToScheme(scheme))
48 |
49 | utilruntime.Must(grafanav1alpha1.AddToScheme(scheme))
50 | utilruntime.Must(grafanauserv1alpha1.AddToScheme(scheme))
51 | //+kubebuilder:scaffold:scheme
52 | }
53 |
54 | func main() {
55 | var metricsAddr string
56 | var enableLeaderElection bool
57 | var probeAddr string
58 | flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
59 | flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
60 | flag.BoolVar(&enableLeaderElection, "leader-elect", false,
61 | "Enable leader election for controller manager. "+
62 | "Enabling this will ensure there is only one active controller manager.")
63 | opts := zap.Options{
64 | Development: true,
65 | }
66 | opts.BindFlags(flag.CommandLine)
67 | flag.Parse()
68 |
69 | ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
70 |
71 | mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
72 | Scheme: scheme,
73 | MetricsBindAddress: metricsAddr,
74 | Port: 9443,
75 | HealthProbeBindAddress: probeAddr,
76 | LeaderElection: enableLeaderElection,
77 | LeaderElectionID: "5a5bab80.snappcloud.io",
78 | })
79 | if err != nil {
80 | setupLog.Error(err, "unable to start manager")
81 | os.Exit(1)
82 | }
83 |
84 | if err = (&namesapcecontrollers.NamespaceReconciler{
85 | Client: mgr.GetClient(),
86 | Scheme: mgr.GetScheme(),
87 | }).SetupWithManager(mgr); err != nil {
88 | setupLog.Error(err, "unable to create controller", "controller", "Namespace")
89 | os.Exit(1)
90 | }
91 | if err = (&grafanausercontrollers.GrafanaUserReconciler{
92 | Client: mgr.GetClient(),
93 | Scheme: mgr.GetScheme(),
94 | }).SetupWithManager(mgr); err != nil {
95 | setupLog.Error(err, "unable to create controller", "controller", "GrafanaUser")
96 | os.Exit(1)
97 | }
98 | if err = (&grafanauserv1alpha1.GrafanaUser{}).SetupWebhookWithManager(mgr); err != nil {
99 | setupLog.Error(err, "unable to create webhook", "webhook", "GrafanaUser")
100 | os.Exit(1)
101 | }
102 | //+kubebuilder:scaffold:builder
103 |
104 | if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
105 | setupLog.Error(err, "unable to set up health check")
106 | os.Exit(1)
107 | }
108 | if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
109 | setupLog.Error(err, "unable to set up ready check")
110 | os.Exit(1)
111 | }
112 |
113 | setupLog.Info("starting manager")
114 | if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
115 | setupLog.Error(err, "problem running manager")
116 | os.Exit(1)
117 | }
118 | }
119 |
--------------------------------------------------------------------------------