├── .bumpversion.cfg
├── .dockerignore
├── .drone.yml
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── dependabot.yml
└── pull_request_template.md
├── .gitignore
├── .rules
└── .htmlhintrc
├── CHANGELOG.md
├── Dockerfile
├── LICENSE
├── MAINTENANCE.md
├── README.md
├── app
├── app.py
├── constraints.txt
├── requirements.txt
├── static-content
│ ├── logo.svg
│ └── semantic.min.css
├── templates
│ └── constraints-report.html
└── web-client
│ ├── .env
│ ├── .gitignore
│ ├── README.md
│ ├── fork-ts-checker.config.js
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ ├── robots.txt
│ └── touch-icon.png
│ ├── src
│ ├── App.scss
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── AppContext.ts
│ ├── AppContextProvider.tsx
│ ├── assets
│ │ ├── fonts
│ │ │ ├── JetBrainsMono-Bold.ttf
│ │ │ ├── JetBrainsMono-Regular.ttf
│ │ │ ├── Poppins-Bold.ttf
│ │ │ ├── Poppins-Light.ttf
│ │ │ ├── Poppins-Medium.ttf
│ │ │ ├── Poppins-Regular.ttf
│ │ │ └── Poppins-SemiBold.ttf
│ │ ├── github-logo.svg
│ │ ├── gpm-logo.svg
│ │ ├── shield-active.svg
│ │ └── shield-inactive.svg
│ ├── components
│ │ ├── Footer
│ │ │ ├── Component.tsx
│ │ │ ├── Style.scss
│ │ │ └── index.ts
│ │ └── Header
│ │ │ ├── Component.tsx
│ │ │ ├── Style.scss
│ │ │ └── index.ts
│ ├── hooks
│ │ ├── useCurrentElementInView.tsx
│ │ └── useScrollToHash.tsx
│ ├── index.scss
│ ├── index.tsx
│ ├── pages
│ │ ├── Configurations
│ │ │ ├── Component.tsx
│ │ │ ├── Style.scss
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ ├── ConstraintTemplates
│ │ │ ├── Component.tsx
│ │ │ ├── Style.scss
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ ├── Constraints
│ │ │ ├── Component.tsx
│ │ │ ├── Style.scss
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ ├── Error
│ │ │ ├── Component.tsx
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ ├── Home
│ │ │ ├── Component.tsx
│ │ │ └── index.ts
│ │ ├── Logout
│ │ │ ├── Component.tsx
│ │ │ └── index.ts
│ │ ├── NotFound
│ │ │ ├── Component.tsx
│ │ │ └── index.ts
│ │ ├── theme.ts
│ │ └── types.ts
│ ├── react-app-env.d.ts
│ ├── reportWebVitals.ts
│ ├── setupTests.ts
│ ├── theme.ts
│ └── utils.tsx
│ ├── tsconfig.json
│ └── yarn.lock
├── chart
├── .frigate
├── Chart.yaml
├── LICENSE
├── README.md
├── templates
│ ├── NOTES.txt
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── rbac.yaml
│ ├── secret-multicluster.yaml
│ ├── secret-oidc.yaml
│ ├── secret.yaml
│ ├── service.yaml
│ └── serviceaccount.yaml
└── values.yaml
├── docs
└── releases
│ ├── v0.1.md
│ ├── v0.2.md
│ ├── v0.3.0.md
│ ├── v0.4.0.md
│ ├── v0.4.1.md
│ ├── v0.4.2.md
│ ├── v0.5.0.md
│ ├── v0.5.1.md
│ ├── v1.0.0.md
│ ├── v1.0.1.md
│ ├── v1.0.10.md
│ ├── v1.0.11.md
│ ├── v1.0.12.md
│ ├── v1.0.13.md
│ ├── v1.0.2.md
│ ├── v1.0.3.md
│ ├── v1.0.4.md
│ ├── v1.0.5.md
│ ├── v1.0.6.md
│ ├── v1.0.7.md
│ ├── v1.0.8.md
│ └── v1.0.9.md
├── kustomization.yaml
├── manifests
├── deployment.yaml
├── enable-oidc.yaml
├── ingress.yaml
├── multi-cluster.yaml
├── rbac.yaml
└── service.yaml
├── renovate.json
├── screenshots
├── 01-home.png
├── 02-constrainttemplates.png
├── 03-constrainttemplates.png
├── 04-constraints.png
├── 05-constraints.png
├── 06-constraints.png
├── 07-configs.png
└── 08-multicluster.png
└── tests
├── deploy-patch.yaml
├── e2e-tests.yaml
├── e2e
├── .gitignore
├── README.md
├── package.json
├── playwright.config.js
├── tests
│ ├── configurations.spec.ts
│ ├── configurations.spec.ts-snapshots
│ │ └── page-configurations-snapshot-1-linux.png
│ ├── constraints.spec.ts
│ ├── constraints.spec.ts-snapshots
│ │ └── page-constraints-snapshot-1-linux.png
│ ├── constrainttemplates.spec.ts
│ ├── constrainttemplates.spec.ts-snapshots
│ │ └── page-constrainttemplates-snapshot-1-linux.png
│ ├── home.spec.ts
│ └── home.spec.ts-snapshots
│ │ └── page-home-snapshot-1-linux.png
└── yarn.lock
├── helper.bash
├── kustomization.yaml
└── tests.sh
/.bumpversion.cfg:
--------------------------------------------------------------------------------
1 | [bumpversion]
2 | commit = True
3 | tag = True
4 | current_version = 1.0.13
5 | parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-rc(?P\d+))?
6 | serialize =
7 | {major}.{minor}.{patch}-rc{rc}
8 | {major}.{minor}.{patch}
9 |
10 | [bumpversion:file:README.md]
11 |
12 | [bumpversion:file:kustomization.yaml]
13 |
14 | [bumpversion:file:app/web-client/src/components/Footer/Component.tsx]
15 |
16 | [bumpversion:file:chart/Chart.yaml]
17 |
18 | [bumpversion:file:chart/README.md]
19 |
20 | [bumpversion:file:chart/values.yaml]
21 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/node_modules
2 | **/build
3 | **/chart
4 | **/manifests
5 | **/screenshots
6 | **/tests
7 |
--------------------------------------------------------------------------------
/.drone.yml:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2022-2024 SIGHUP s.r.l All rights reserved.
2 | # Use of this source code is governed by a BSD-style
3 | # license that can be found in the LICENSE file.
4 |
5 | name: QA
6 | kind: pipeline
7 | type: docker
8 |
9 | trigger:
10 | ref:
11 | exclude:
12 | # this tag is created by Chart Releaser after the release.
13 | - refs/tags/gatekeeper-policy-manager-**
14 |
15 | steps:
16 | - name: Check license headers presence
17 | image: docker.io/library/golang:1.23.2
18 | depends_on:
19 | - clone
20 | commands:
21 | - go install github.com/google/addlicense@latest
22 | - addlicense -c "SIGHUP s.r.l" -v -l bsd -check -ignore 'chart/**' -ignore 'gpm.yaml' -ignore 'rendered_chart.yaml' .
23 |
24 | - name: Lint
25 | image: quay.io/sighup/policeman
26 | pull: always
27 | depends_on:
28 | - clone
29 | environment:
30 | FILTER_REGEX_EXCLUDE: (app/static-content/semantic.min.css|chart/|tests/e2e/|\.github)
31 | # Identifies false positives like missing 'selector'.
32 | # Doing this is valid for Kustomize patches
33 | VALIDATE_KUBERNETES_KUBECONFORM: "false"
34 | VALIDATE_KUBERNETES_KUBEVAL: "false"
35 | # Some duplicated code is intended.
36 | VALIDATE_JSCPD: "false"
37 | TYPESCRIPT_DEFAULT_STYLE: "prettier"
38 | # We ignore the command not found error, because in the linter image we don't have all the binaries being called.
39 | SHELLCHECK_OPTS: "-e SC2317"
40 |
41 | - name: Render manifests
42 | image: quay.io/sighup/e2e-testing:1.1.0_0.11.0_3.12.0_1.9.4_1.29.1_3.5.3_4.33.3
43 | depends_on:
44 | - clone
45 | commands:
46 | - kustomize build . > gpm.yaml
47 | - helm template --set config.secretKey=e2e chart > rendered_chart.yaml
48 |
49 | - name: Check deprecated APIs
50 | image: us-docker.pkg.dev/fairwinds-ops/oss/pluto:v5
51 | pull: always
52 | depends_on:
53 | - "Render manifests"
54 | commands:
55 | # we use --ignore-deprecations because we don't want the CI to fail when the API has not been removed yet.
56 | - /pluto detect gpm.yaml --ignore-deprecations --target-versions=k8s=v1.31.0
57 | - /pluto detect rendered_chart.yaml --ignore-deprecations --target-versions=k8s=v1.31.0
58 |
59 | ---
60 | name: Build test and release
61 | kind: pipeline
62 | type: docker
63 |
64 | depends_on:
65 | - QA
66 |
67 | trigger:
68 | ref:
69 | include:
70 | - refs/tags/**
71 | - refs/heads/main
72 | - refs/heads/dependabot/**
73 | exclude:
74 | # this tag is created by Chart Releaser after the release.
75 | - refs/tags/gatekeeper-policy-manager-**
76 | # exclude this expensive pipeline when releasing only a new version of the chart
77 | - refs/tags/helm-chart-**
78 |
79 | steps:
80 | - name: Build container image
81 | image: docker:dind
82 | pull: always
83 | environment:
84 | CONTAINER_IMAGE_NAME: gatekeeper-policy-manager
85 | CONTAINER_IMAGE_TAG: test-${DRONE_BUILD_NUMBER}
86 | DOCKERFILE: Dockerfile
87 | BUILD_CONTEXT: "."
88 | volumes:
89 | - name: dockerconfig
90 | path: /root/.docker/config.json
91 | - name: dockersock
92 | path: /var/run/docker.sock
93 | commands:
94 | - "apk add git"
95 | - "docker buildx create --name sighup-builder --use"
96 | - "docker buildx build --platform linux/amd64,linux/arm64 --pull -f $${DOCKERFILE} -t $${CONTAINER_IMAGE_NAME}:$${CONTAINER_IMAGE_TAG} $${BUILD_CONTEXT}"
97 | - "docker buildx build --load -t $${CONTAINER_IMAGE_NAME}:$${CONTAINER_IMAGE_TAG} $${BUILD_CONTEXT}"
98 |
99 | - name: Create Kind cluster
100 | image: docker:dind
101 | pull: always
102 | environment:
103 | KIND_VERSION: v0.24.0
104 | CLUSTER_VERSION: v1.31.0
105 | LOAD_IMAGE: gatekeeper-policy-manager:test-${DRONE_BUILD_NUMBER}
106 | CLUSTER_NAME: ${DRONE_REPO_NAME}-${DRONE_BUILD_NUMBER}
107 | volumes:
108 | - name: dockerconfig
109 | path: /root/.docker/config.json
110 | - name: dockersock
111 | path: /var/run/docker.sock
112 | commands:
113 | - wget -qO /usr/local/bin/kind "https://kind.sigs.k8s.io/dl/$${KIND_VERSION}/kind-$(uname)-amd64"
114 | - wget -qO /usr/local/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/$${CLUSTER_VERSION}/bin/linux/amd64/kubectl"
115 | - chmod +x /usr/local/bin/kind /usr/local/bin/kubectl
116 | - kind create cluster --name $${CLUSTER_NAME} --image registry.sighup.io/fury/kindest/node:$${CLUSTER_VERSION}
117 | - kind load docker-image $${LOAD_IMAGE} --name $${CLUSTER_NAME}
118 | - kind get kubeconfig --name $${CLUSTER_NAME} > kubeconfig.yml
119 |
120 | - name: E2E tests
121 | image: quay.io/sighup/e2e-testing:1.1.0_1.31.1_3.10.0_4.33.3
122 | network_mode: host
123 | environment:
124 | LOAD_IMAGE: gatekeeper-policy-manager:test-${DRONE_BUILD_NUMBER}
125 | commands:
126 | - export KUBECONFIG=$PWD/kubeconfig.yml
127 | - cd tests
128 | - kustomize edit set image quay.io/sighup/gatekeeper-policy-manager=$${LOAD_IMAGE}
129 | - cd -
130 | - bats -t tests/tests.sh
131 |
132 | - name: GPM port-forward
133 | image: quay.io/sighup/e2e-testing:1.1.0_1.31.1_3.10.0_4.33.3
134 | network_mode: host
135 | detach: true
136 | commands:
137 | - export KUBECONFIG=$PWD/kubeconfig.yml
138 | - kubectl port-forward svc/gatekeeper-policy-manager 8080:80 -n gatekeeper-system
139 |
140 | - name: UI tests
141 | # This must match the version in the package.json file
142 | image: mcr.microsoft.com/playwright:v1.30.0-focal
143 | network_mode: host
144 | commands:
145 | - cd tests/e2e && yarn install && yarn test
146 |
147 | - name: Print images diff on fail
148 | # This must match the version in the package.json file
149 | image: mcr.microsoft.com/playwright:v1.30.0-focal
150 | commands:
151 | - for f in tests/e2e/test-results/*/*diff.png; do echo $f; base64 -w 0 $f; echo; done;
152 | - "echo 'Use base64 to decode the images and see the diff'"
153 | when:
154 | status:
155 | - failure
156 |
157 | - name: Destroy Kind cluster
158 | image: docker:dind
159 | pull: always
160 | environment:
161 | KIND_VERSION: v0.24.0
162 | CLUSTER_NAME: ${DRONE_REPO_NAME}-${DRONE_BUILD_NUMBER}
163 | volumes:
164 | - name: dockerconfig
165 | path: /root/.docker/config.json
166 | - name: dockersock
167 | path: /var/run/docker.sock
168 | commands:
169 | - wget -qO /usr/local/bin/kind "https://kind.sigs.k8s.io/dl/$${KIND_VERSION}/kind-$(uname)-amd64"
170 | - chmod +x /usr/local/bin/kind
171 | - kind delete cluster --name $${CLUSTER_NAME}
172 | - rm kubeconfig.yml
173 | when:
174 | status:
175 | - success
176 | - failure
177 |
178 | - name: Push unstable image
179 | image: docker:dind
180 | pull: always
181 | environment:
182 | username:
183 | from_secret: quay_username
184 | password:
185 | from_secret: quay_password
186 | REGISTRY: quay.io
187 | IMAGE_NAME: sighup/gatekeeper-policy-manager
188 | DOCKERFILE: Dockerfile
189 | BUILD_CONTEXT: "."
190 | volumes:
191 | - name: dockersock
192 | path: /var/run/docker.sock
193 | commands:
194 | - "apk add git"
195 | - "docker login $${REGISTRY} -u $${username} -p $${password}"
196 | - "docker buildx create --name sighup-builder --use"
197 | - "docker buildx build --platform linux/amd64,linux/arm64 --pull --push -f $${DOCKERFILE} -t $${REGISTRY}/$${IMAGE_NAME}:${DRONE_COMMIT_SHA} -t $${REGISTRY}/$${IMAGE_NAME}:unstable $${BUILD_CONTEXT}"
198 | when:
199 | ref:
200 | include:
201 | - refs/heads/main
202 |
203 | - name: Push stable image
204 | image: docker:dind
205 | pull: always
206 | environment:
207 | username:
208 | from_secret: quay_username
209 | password:
210 | from_secret: quay_password
211 | REGISTRY: quay.io
212 | IMAGE_NAME: sighup/gatekeeper-policy-manager
213 | DOCKERFILE: Dockerfile
214 | BUILD_CONTEXT: "."
215 | volumes:
216 | - name: dockersock
217 | path: /var/run/docker.sock
218 | commands:
219 | - "apk add git"
220 | - "docker login $${REGISTRY} -u $${username} -p $${password}"
221 | - "docker buildx create --name sighup-builder --use"
222 | - "docker buildx build --platform linux/amd64,linux/arm64 --pull --push -f $${DOCKERFILE} -t $${REGISTRY}/$${IMAGE_NAME}:latest -t $${REGISTRY}/$${IMAGE_NAME}:${DRONE_TAG} $${BUILD_CONTEXT}"
223 | when:
224 | event:
225 | - tag
226 |
227 | - name: Package tar.gz
228 | image: alpine:latest
229 | pull: always
230 | commands:
231 | - tar -zcvf gatekeeper-policy-manager-${DRONE_TAG}.tar.gz manifests/ kustomization.yaml docs/releases/${DRONE_TAG}.md LICENSE README.md
232 | when:
233 | event:
234 | - tag
235 |
236 | - name: Prepare release notes
237 | image: quay.io/sighup/fury-release-notes-plugin:3.7_2.8.4
238 | settings:
239 | release_notes_file_path: release-notes.md
240 | when:
241 | event:
242 | - tag
243 |
244 | - name: Publish Preview Release
245 | image: plugins/github-release
246 | pull: always
247 | settings:
248 | api_key:
249 | from_secret: c3p0
250 | file_exists: overwrite
251 | files:
252 | - gatekeeper-policy-manager-${DRONE_TAG}.tar.gz
253 | prerelease: true
254 | overwrite: true
255 | title: "Release Candidate ${DRONE_TAG}"
256 | note: release-notes.md
257 | checksum:
258 | - md5
259 | - sha256
260 | when:
261 | ref:
262 | include:
263 | - refs/tags/v**-rc**
264 |
265 | - name: Publish Final Release
266 | image: plugins/github-release
267 | pull: always
268 | settings:
269 | api_key:
270 | from_secret: c3p0
271 | file_exists: overwrite
272 | files:
273 | - gatekeeper-policy-manager-${DRONE_TAG}.tar.gz
274 | prerelease: false
275 | overwrite: true
276 | title: "Release ${DRONE_TAG}"
277 | note: release-notes.md
278 | checksum:
279 | - md5
280 | - sha256
281 | when:
282 | ref:
283 | exclude:
284 | - refs/tags/v**-rc**
285 | include:
286 | - refs/tags/v**
287 |
288 | volumes:
289 | - name: dockerconfig
290 | host:
291 | path: /root/.docker/config.json
292 | - name: dockersock
293 | host:
294 | path: /var/run/docker.sock
295 |
296 | ---
297 | name: Release Helm Chart
298 | kind: pipeline
299 | type: docker
300 |
301 | depends_on:
302 | - QA
303 | - "Build test and release"
304 |
305 | trigger:
306 | ref:
307 | include:
308 | # Trigger the Helm Chart Releaser only when tagging
309 | - refs/tags/**
310 | exclude:
311 | # Don't release the chart when tagging Release Candidates
312 | - refs/tags/v**-rc.**
313 | # These kind of tags are generated by `cr` in this pipeline
314 | # we exclude them to avoid a loop.
315 | - refs/tags/gatekeeper-policy-manager-**
316 |
317 | steps:
318 | # We need to manually fetch the gh-pages branch because Drone by default
319 | # only checksout the current branch with a minimal depth
320 | # see: https://docs.drone.io/pipeline/docker/syntax/cloning/
321 | - name: fetch-gh-pages-branch
322 | image: alpine/git
323 | commands:
324 | - git fetch origin gh-pages
325 |
326 | # We use GitHub Pages as Helm Repository and `cr` (Chart Releaser)
327 | # to help us publish the chart.
328 | - name: chart-releaser
329 | image: quay.io/helmpack/chart-releaser:v1.4.0
330 | environment:
331 | CR_OWNER: sighupio
332 | CR_GIT_REPO: gatekeeper-policy-manager
333 | CR_TOKEN:
334 | from_secret: c3p0
335 | commands:
336 | # For some reason we need to manually create the folder each time
337 | # see: https://github.com/helm/chart-releaser/issues/187
338 | - mkdir -p .cr-index
339 | # Package the chart into a tar.gz
340 | - cr package ./chart
341 | # Upload the tar.gz to a GitHub release
342 | - cr upload --skip-existing
343 | # Update the index.yaml and push it to GitHub Pages
344 | - cr index --push
345 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Kubernetes (please complete the following information):**
32 | - Kubernetes version: [e.g. 1.30.0]
33 | - OPA Gatekeeper version: [e.g. 3.18.0]
34 |
35 | **Additional context**
36 | Add any other context about the problem here.
37 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2022 SIGHUP s.r.l All rights reserved.
2 | # Use of this source code is governed by a BSD-style
3 | # license that can be found in the LICENSE file.
4 |
5 | version: 2
6 | updates:
7 | - package-ecosystem: "pip" # See documentation for possible values
8 | directory: "/app" # Location of package manifests
9 | schedule:
10 | interval: "weekly"
11 |
12 | - package-ecosystem: "npm" # See documentation for possible values
13 | directory: "/app/web-client" # Location of package manifests
14 | schedule:
15 | interval: "weekly"
16 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 |
15 |
16 | ### Summary 💡
17 |
18 |
19 |
20 |
21 | Closes:
22 |
23 |
27 |
28 | ### Description 📝
29 |
30 |
37 |
38 | ### Breaking Changes 💔
39 |
40 |
46 |
47 | ### Tests performed 🧪
48 |
49 |
58 |
59 | ### Future work 🔧
60 |
61 |
65 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | env
2 | .venv
3 | .vscode
4 | __pycache__
5 | launch.json
6 | app/static-content/*
7 | !app/static-content/logo.svg
8 | !app/static-content/semantic.min.css
9 | tests/e2e/test-results
10 | build/
11 | node_modules/
12 | static-content/
13 | *.tgz
14 | pyrightconfig.json
15 | .mise.toml
--------------------------------------------------------------------------------
/.rules/.htmlhintrc:
--------------------------------------------------------------------------------
1 | {
2 | "tagname-lowercase": true,
3 | "attr-lowercase": true,
4 | "attr-value-double-quotes": true,
5 | "attr-value-not-empty": false,
6 | "attr-no-duplication": true,
7 | "tag-pair": true,
8 | "tag-self-close": false,
9 | "id-unique": true,
10 | "src-not-empty": true,
11 | "title-require": true,
12 | "alt-require": true,
13 | "doctype-html5": true,
14 | "style-disabled": false,
15 | "inline-style-disabled": false,
16 | "inline-script-disabled": false,
17 | "space-tab-mixed-disabled": "space",
18 | "id-class-ad-disabled": false,
19 | "href-abs-or-rel": false,
20 | "attr-unsafe-chars": true,
21 | "head-script-disabled": true,
22 |
23 | "__COMMENT__": "From here, custom rules. Jinja2 requires special characters. Also there is some HTML templates.",
24 |
25 | "spec-char-escape": false,
26 | "id-class-value": false,
27 | "doctype-first": false
28 | }
29 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Gatekeeper Policy Manager Changelog
2 |
3 | Changelog has been moved to [docs/releases](docs/releases). There you'll find a file for each version.
4 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2022 SIGHUP s.r.l All rights reserved.
2 | # Use of this source code is governed by a BSD-style
3 | # license that can be found in the LICENSE file.
4 | FROM --platform=$BUILDPLATFORM node:lts-alpine AS node
5 | WORKDIR /web-client
6 | COPY app/web-client/package.json app/web-client/yarn.lock ./
7 | RUN yarn install && yarn cache clean
8 | COPY app/web-client /web-client
9 | RUN yarn build
10 |
11 |
12 | FROM python:3.12-slim
13 | LABEL org.opencontainers.vendor="SIGHUP.io"
14 | LABEL org.opencontainers.image.authors="SIGHUP https://sighup.io"
15 | LABEL org.opencontainers.image.source="https://github.com/sighupio/gatekeeper-policy-manager"
16 |
17 | RUN groupadd -r gpm && useradd --no-log-init -r -g gpm gpm
18 |
19 | WORKDIR /app
20 | COPY --chown=gpm ./app /app
21 | COPY --from=node --chown=gpm /web-client/build/ /app/static-content/
22 | # hadolint ignore=DL3013
23 | RUN pip install --no-cache-dir uv && uv pip install --system --no-cache-dir -r /app/requirements.txt
24 | USER 999
25 | EXPOSE 8080
26 | CMD ["gunicorn", "--bind=:8080", "--workers=2", "--threads=4", "--worker-class=gthread", "app:app"]
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2020, SIGHUP
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/MAINTENANCE.md:
--------------------------------------------------------------------------------
1 | # Gatekeeper Policy Manager Maintenance Guide
2 |
3 | This document is for GPM's maintainers. Here you will find intructions on how to perform maintenance-related activities in the project, like releasing a new version of the app, releasing a new version of the helm chart, etc.
4 |
5 | ## Releasing a new version of GPM
6 |
7 | Releasing a new version of GPM is done automatically with our CI, to trigger the release process follow the next steps:
8 |
9 | 1. Be sure that the current state of `main` branch is ready to be released.
10 | 2. Be sure that you don't have any local modifications to the files.
11 | 3. [Bump the chart version](#releasing-a-new-helm-chart-version).
12 | 4. Create release notes.
13 | 5. Commit all changes.
14 | 6. Run [`bumpversion`](https://github.com/c4urself/bump2version/#installation) to update the version strings automatically everywhere.
15 |
16 | For example, assuming the latest version is 1.0.0, to release a new patch version run:
17 |
18 | ```bash
19 | bumpversion --dry-run --verbose --new-version 1.0.1 bugfix
20 | ```
21 |
22 | or to release a new minor:
23 |
24 | ```bash
25 | bumpversion --dry-run --verbose --new-version 1.1.0 minor
26 | ```
27 |
28 | > Notice that the command includes a `--dry-run` flag, drop it to actually perform the change. You can drop the `--verbose` flag too.
29 |
30 | 5. `bumpversion` will create some commits and tags, you'll need to push the commits and then the tags
31 |
32 | ```bash
33 | git push
34 | git push origin
35 | ```
36 |
37 | ## Releasing a new Helm Chart version
38 |
39 | You should update the version in `/chart/Chart.yaml` file each time you do a release of GPM and/or when the chart content gets updated.
40 |
41 | To release a new Helm Chart version:
42 |
43 | 1. Update the `/chart/Chart.yaml` file accordingly (i.e. bumping the version of the Chart)
44 |
45 | 2. Update the `/chart/README.md` file if you made changes to the chart. You can use [`frigate`](https://frigate.readthedocs.io/) to do it automatically:
46 |
47 | ```bash
48 | cd chart
49 | frigate gen . > README.md
50 | ```
51 |
52 | > Notice that `frigate` will use the template in the file `/chart/.frigate` for formatting.
53 |
54 | 3. Tag and push the commit. This can be done as part of the release of a version of GPM or independently.
55 |
56 | > If you want to release just a new version of the chart, notice that the pipeline by default executes the Helm Release step only if the GPM release has been successful. You might need to disable the dependency between the pipeline steps.
57 | >
58 | > This is to avoid publishing a chart that references a failed build of GPM.
59 | > You can use a tag like `helm-chart-`.
60 | >
61 | > ⚠️ Notice that the tags `gatekeeper-policy-manager-` are used by helm/chart-releaser.
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Gatekeeper Policy Manager (GPM)
5 |
6 |
7 |
8 | [](https://ci.sighup.io/sighupio/gatekeeper-policy-manager)
9 | 
10 | 
11 | 
12 | 
13 |
14 | **Gatekeeper Policy Manager** is a simple *read-only* web UI for viewing OPA Gatekeeper policies' status in a Kubernetes Cluster.
15 |
16 | The target Kubernetes Cluster can be the same where GPM is running or some other [remote cluster(s) using a `kubeconfig` file](#multi-cluster-support). You can also run GPM [locally in a client machine](#running-locally) and connect to a remote cluster.
17 |
18 | GPM can display all the defined **Constraint Templates** with their rego code, all the Gatekeeper Configuration CRDs, and all the **Constraints** with their current status, violations, enforcement action, matches definitions, etc.
19 |
20 | [You can see some screenshots below](#screenshots).
21 |
22 | ## Requirements
23 |
24 | You'll need OPA Gatekeeper running in your cluster and at least some constraint templates and constraints defined to take advantage of this tool.
25 |
26 | ℹ You can easily deploy Gatekeeper to your cluster using the (also open source) [Kubernetes Fury OPA](https://github.com/sighupio/fury-kubernetes-opa) module.
27 |
28 | ## Deploying GPM
29 |
30 | ### Deploy using Kustomize
31 |
32 | To deploy Gatekeeper Policy Manager to your cluster, apply the provided [`kustomization`](kustomization.yaml) file running the following command:
33 |
34 | ```shell
35 | kubectl apply -k .
36 | ```
37 |
38 | By default, this will create a deployment and a service both with the name `gatekeper-policy-manager` in the `gatekeeper-system` namespace. We invite you to take a look into the `kustomization.yaml` file to do further configuration.
39 |
40 | > The app can be run as a POD in a Kubernetes cluster or locally with a `kubeconfig` file. It will try its best to autodetect the correct configuration.
41 |
42 | Once you've deployed the application, if you haven't set up an ingress, you can access the web-UI using port-forward:
43 |
44 | ```bash
45 | kubectl -n gatekeeper-system port-forward svc/gatekeeper-policy-manager 8080:80
46 | ```
47 |
48 | Then access it with your browser on: [http://127.0.0.1:8080](http://127.0.0.1:8080)
49 |
50 | ### Deploy using Helm
51 |
52 | It is also possible to deploy GPM using the [provided Helm Chart](./chart).
53 |
54 | First create a values file, for example `my-values.yaml`, with your custom values for the release. See the [chart's readme](./chart/README.md) and the [default values.yaml](./chart/values.yaml) for more information.
55 |
56 | Then, execute:
57 |
58 | ```bash
59 | helm repo add gpm https://sighupio.github.io/gatekeeper-policy-manager
60 | helm upgrade --install --namespace gatekeeper-system --set image.tag=v1.0.13 --values my-values.yaml gatekeeper-policy-manager gpm/gatekeeper-policy-manager
61 | ```
62 |
63 | > don't forget to replace `my-values.yaml` with the path to your values file.
64 |
65 | ## Running locally
66 |
67 | GPM can also be run locally using docker and a `kubeconfig`, assuming that the `kubeconfig` file you want to use is located at `~/.kube/config` the command to run GPM locally would be:
68 |
69 | ```bash
70 | docker run -v ~/.kube/config:/home/gpm/.kube/config -p 8080:8080 quay.io/sighup/gatekeeper-policy-manager:v1.0.13
71 | ```
72 |
73 | Then access it with your browser on: [http://127.0.0.1:8080](http://127.0.0.1:8080)
74 |
75 | > You can also run the flask app directly, see the [development section](#development) for further information.
76 |
77 | ## Configuration
78 |
79 | GPM is a stateless application, but it can be configured using environment variables. The possible configurations are:
80 |
81 | | Environment Variable Name | Description | Default |
82 | | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- |
83 | | `GPM_SECRET_KEY` | The secret key used to generate tokens. **Change this value in production**. | `g8k1p3rp0l1c7m4n4g3r` |
84 | | `KUBECONFIG` | Path to a [kubeconfig](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) file, if provided while running inside a cluster this configuration file will be used instead of the cluster's API. |
85 | | `GPM_LOG_LEVEL` | Log level (see [python logging docs](https://docs.python.org/2/library/logging.html#levels) for available levels) | `INFO` |
86 | | `GPM_AUTH_ENABLED` | Enable Authentication current options: "Anonymous", "OIDC" | Anonymous |
87 | | `GPM_PREFERRED_URL_SCHEME` | URL scheme to be used while generating links. | `http` |
88 | | `GPM_OIDC_REDIRECT_DOMAIN` | The domain where GPM is being running. This is where the client will be redirected after authenticating | |
89 | | `GPM_OIDC_CLIENT_ID` | The Client ID used to authenticate against the OIDC Provider | |
90 | | `GPM_OIDC_CLIENT_SECRET` | The Client Secret used to authenticate against the OIDC Provider (optional) | |
91 | | `GPM_OIDC_ISSUER` | OIDC Issuer hostname (required if OIDC Auth is enabled) | |
92 | | `GPM_OIDC_AUTHORIZATION_ENDPOINT` | OIDC Authorization Endpoint (optional, setting this parameter disables the discovery of the rest of the provider configuration, set all the other values also if setting this one) | |
93 | | `GPM_OIDC_JWKS_URI` | OIDC JWKS URI (optional, setting this parameter disables the discovery of the rest of the provider configuration, set all the other values also if setting this one) | |
94 | | `GPM_OIDC_TOKEN_ENDPOINT` | OIDC TOKEN Endpoint (optional, setting this parameter disables the discovery of the rest of the provider configuration, set all the other values also if setting this one) | |
95 | | `GPM_OIDC_INTROSPECTION_ENDPOINT` | OIDC Introspection Endpoint (optional, setting this parameter disables the discovery of the rest of the provider configuration, set all the other values also if setting this one) | |
96 | | `GPM_OIDC_USERINFO_ENDPOINT` | OIDC Userinfo Endpoint (optional, setting this parameter disables the discovery of the rest of the provider configuration, set all the other values also if setting this one) | |
97 | | `GPM_OIDC_END_SESSION_ENDPOINT` | OIDC End Session Endpoint (optional, setting this parameter disables the discovery of the rest of the provider configuration, set all the other values also if setting this one) | |
98 |
99 | > ⚠️ Please notice that OIDC Authentication is in beta state. It has been tested to work with Keycloak as a provider.
100 | >
101 | > These environment variables are already provided and ready to be set in the [`manifests/enable-oidc.yaml`](manifests/enable-oidc.yaml) file.
102 |
103 | ### Multi-cluster support
104 |
105 | Since `v1.0.13` GPM has basic multi-cluster support when using a `kubeconfig` with more than one context. GPM will let you chose the context right from the UI.
106 |
107 | If you want to run GPM in a cluster but with multi-cluster support, it's as easy as mounting a `kubeconfig` file in GPM's pod(s) with the cluster access configuration and set the environment variable `KUBECONFIG` with the path to the mounted `kubeconfig` file. Or you can simply mount it in `/home/gpm/.kube/config` and GPM will detect it automatically.
108 |
109 | > Please remember that the user for the clusters should have the right permissions. You can use the [`manifests/rabc.yaml`](manifests/rbac.yaml) file as reference.
110 | >
111 | > Also note that the cluster where GPM is running should be able to reach the other clusters.
112 |
113 | When you run GPM locally, you are already using a `kubeconfig` file to connect to the clusters, now you should see all your defined contexts and you can switch between them easily from the UI.
114 |
115 | #### AWS IAM Authentication
116 |
117 | If you want to use a Kubeconfig with IAM Authentication, you'll need to customize GPM's container image because the IAM authentication uses external AWS binaries that are not included by default in the image.
118 |
119 | You can customize the container image with a `Dockerfile` like the following:
120 |
121 | ```Dockerfile
122 | FROM curlimages/curl:7.81.0 as downloader
123 | RUN curl https://github.com/kubernetes-sigs/aws-iam-authenticator/releases/download/v0.5.5/aws-iam-authenticator_0.5.5_linux_amd64 --output /tmp/aws-iam-authenticator
124 | RUN chmod +x /tmp/aws-iam-authenticator
125 | FROM quay.io/sighup/gatekeeper-policy-manager:v1.0.13
126 | COPY --from=downloader --chown=root:root /tmp/aws-iam-authenticator /usr/local/bin/
127 | ```
128 |
129 | You may need to add also the `aws` CLI, you can use the same approach as before.
130 |
131 | Make sure that your `kubeconfig` has the `apiVersion` set as `client.authentication.k8s.io/v1beta1`
132 |
133 | You can read more [in this issue](https://github.com/sighupio/gatekeeper-policy-manager/issues/330).
134 |
135 | ## Screenshots
136 |
137 | 
138 |
139 | 
140 |
141 | 
142 |
143 | 
144 |
145 | 
146 |
147 | 
148 |
149 | 
150 |
151 | 
152 |
153 | ## Development
154 |
155 | GPM is written in Python using the Flask framework for the backend and React with Elastic UI and the Fury theme for the frontend.
156 |
157 | To develop GPM, you'll need to create a Python 3 virtual environment, install all the dependencies specified in the provided `requirements.txt`, build the react frontend and you are good to start hacking.
158 |
159 | The following commands should get you up and running:
160 |
161 | ```bash
162 | # Build frontend and copy over to static folder
163 | $ pushd app/web-client
164 | $ yarn install && yarn build
165 | $ cp -r build/* ../static-content/
166 | $ popd
167 | # Create a virtualenv
168 | $ python3 -m venv env
169 | # Activate it
170 | $ source ./env/bin/activate
171 | # Install all the dependencies
172 | $ pip install -r app/requirements.txt
173 | # Run the development server
174 | $ FLASK_APP=app/app.py flask run
175 | ```
176 |
177 | > Access to a Kubernetes cluster with OPA Gatekeeper deployed is recommended to debug the application.
178 | >
179 | > You'll need an OIDC provider to test the OIDC authentication. You can use our [fury-kubernetes-keycloak](https://github.com/sighupio/fury-kubernetes-keycloak) module.
180 |
181 | ## Roadmap
182 |
183 | The following is a wishlist of features that we would like to add to GPM (in no particular order):
184 |
185 | - [x] List the constraints that are currently using a `ConstraintTemplate`
186 | - [ ] Polished OIDC authentication
187 | - [ ] LDAP authentication
188 | - [x] Better syntax highlighting for the rego code snippets
189 | - [x] Root-less docker image
190 | - [x] Multi-cluster view
191 | - [ ] Minimal write capabilities?
192 | - [ ] Rewrite app in Golang?
193 |
194 | Please, let us know if you are using GPM and what features would you like to have by creating an issue here on GitHub 💪🏻
195 |
--------------------------------------------------------------------------------
/app/constraints.txt:
--------------------------------------------------------------------------------
1 | Beaker==1.13.0
2 | cachetools==5.5.0
3 | certifi==2024.8.30
4 | cffi==1.17.1
5 | charset-normalizer==3.4.0
6 | click==8.1.7
7 | cryptography==43.0.3
8 | defusedxml==0.7.1
9 | future==1.0.0
10 | google-auth==2.35.0
11 | idna==3.10
12 | importlib-resources==6.4.5
13 | itsdangerous==2.2.0
14 | Jinja2==3.1.4
15 | Mako==1.3.5
16 | MarkupSafe==2.1.5
17 | oauthlib==3.2.2
18 | oic==1.6.1
19 | pyasn1==0.6.1
20 | pyasn1-modules==0.4.0
21 | pycparser==2.22
22 | pycryptodomex==3.21.0
23 | pydantic==2.9.2
24 | pyjwkest==1.4.2
25 | python-dateutil==2.9.0.post0
26 | PyYAML==6.0.2
27 | requests==2.32.3
28 | requests-oauthlib==2.0.0
29 | rsa==4.9
30 | six==1.16.0
31 | typing-extensions==4.12.2
32 | urllib3==2.2.3
33 | websocket-client==1.8.0
34 | Werkzeug==3.0.6
35 | zipp==3.20.2
36 |
--------------------------------------------------------------------------------
/app/requirements.txt:
--------------------------------------------------------------------------------
1 | -c constraints.txt
2 | Flask==3.0.3
3 | Flask-pyoidc==3.14.3
4 | flask-cors==5.0.0
5 | gunicorn==23.0.0
6 | kubernetes==31.0.0
7 | setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
8 |
--------------------------------------------------------------------------------
/app/static-content/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/templates/constraints-report.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 | Gatekeeper Policy Manager - {{ title }} Violations Report
12 |
13 |
14 |
20 |
21 |
22 |
23 | {% if constraints|length == 0 %}
24 |
25 |
26 | There are no Constraint defined
27 |
28 |
29 | We didn't find any Constraint CRDs defined in the cluster.
30 |
48 |
49 |
50 | {% for constraint in constraints %}
51 | {% if constraint.status.totalViolations is defined and constraint.status.totalViolations > 0 %}
52 | {% if constraint.status.totalViolations > constraint.status.violations|length %}
53 | {{ limited_view.append(constraint.metadata.name) or '' }}
54 | {% endif %}
55 | {% for violation in constraint.status.violations %}
56 |
57 |
{{ constraint.metadata.name }}
58 |
{{
60 | violation.enforcementAction }}
61 |
{{ violation.kind }}
62 |
{{ violation.namespace }}
63 |
{{ violation.name }}
64 |
{{ violation.message }}
65 |
66 | {% endfor %}
67 | {% elif constraint.status.totalViolations is not defined %}
68 |
Violations for Constraint
69 | {{constraint.metadata.name}} are unkown. This probably means that the Constraint has not
70 | been processed by Gatekeeper yet. Please, try refreshing the page.
71 | {% endif %}
72 | {% endfor %}
73 |
74 |
75 |
76 | {% if limited_view %}
77 |
78 |
79 |
80 |
Not all violations are being shown
81 | Gatekeeper's configuration is limiting the amount of audit violations being shown for Constraints
82 | {{ limited_view|join(', ') }}.
83 | See Gatekeeper's --constraint-violations-limit
84 | audit configuration flag.
85 |
86 |
87 | {% endif %}
88 |
89 |
violations report generated by GPM running at {{ url_for('index', _external=True) }} on {{ timestamp
90 | }}
91 |
92 | {% endif %}
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/app/web-client/.env:
--------------------------------------------------------------------------------
1 | REACT_APP_LOCAL_GPM_SERVER_URL=http://localhost:5000/
2 |
--------------------------------------------------------------------------------
/app/web-client/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/app/web-client/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `yarn start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `yarn test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `yarn build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `yarn eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/app/web-client/fork-ts-checker.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present SIGHUP s.r.l All rights reserved.
3 | * Use of this source code is governed by a BSD-style
4 | * license that can be found in the LICENSE file.
5 | */
6 |
7 | module.exports = {
8 | logger: {
9 | log: (message) => console.log(message),
10 | error: (message) => console.error(message)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/web-client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatekeeper-policy-manager-frontend",
3 | "version": "1.0.0",
4 | "description": "Gatekeeper Policy Manager Frontend",
5 | "author": "Ramiro Algozino ",
6 | "license": "BSD-2-Clause",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/sighupio/gatekeeper-policy-manager"
10 | },
11 | "dependencies": {
12 | "@elastic/eui": "^97.2.0",
13 | "@emotion/css": "^11.13.4",
14 | "@emotion/react": "^11.13.3",
15 | "@testing-library/jest-dom": "^6.6.2",
16 | "@testing-library/react": "^12.1.5",
17 | "@testing-library/user-event": "^14.5.2",
18 | "@types/jest": "^29.5.14",
19 | "@types/node": "^22.8.1",
20 | "@types/react": "^17.0.20",
21 | "@types/react-dom": "^18.3.1",
22 | "lodash.clonedeep": "^4.5.0",
23 | "prop-types": "^15.8.1",
24 | "react": "^17.0.2",
25 | "react-dom": "^18.3.1",
26 | "react-json-tree": "^0.19.0",
27 | "react-router-dom": "^6.27.0",
28 | "react-scripts": "5.0.1",
29 | "sass": "^1.80.4",
30 | "typescript": "^5.6.3",
31 | "web-vitals": "^4.2.4"
32 | },
33 | "resolutions": {
34 | "fork-ts-checker-webpack-plugin": "^8.0.0",
35 | "@typescript-eslint/parser": "^6.7.0",
36 | "@typescript-eslint/eslint-plugin": "^6.7.0",
37 | "eslint": "^8.49.0"
38 | },
39 | "scripts": {
40 | "start": "react-scripts start",
41 | "build": "react-scripts build",
42 | "test": "react-scripts test",
43 | "eject": "react-scripts eject"
44 | },
45 | "eslintConfig": {
46 | "extends": [
47 | "react-app",
48 | "react-app/jest"
49 | ]
50 | },
51 | "browserslist": {
52 | "production": [
53 | ">0.2%",
54 | "not dead",
55 | "not op_mini all"
56 | ],
57 | "development": [
58 | "last 1 chrome version",
59 | "last 1 firefox version",
60 | "last 1 safari version"
61 | ]
62 | },
63 | "devDependencies": {
64 | "@elastic/datemath": "^5.0.3",
65 | "@types/lodash.clonedeep": "^4.5.9",
66 | "@types/react-router-dom": "^5.3.3",
67 | "moment": "^2.30.1"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/web-client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sighupio/gatekeeper-policy-manager/d045289c36afbd2632f75dec38bc690130fd4360/app/web-client/public/favicon.ico
--------------------------------------------------------------------------------
/app/web-client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
19 |
20 |
24 |
25 |
34 | Gatekeeper Policy Manager
35 |
36 |
37 |
38 |
39 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/app/web-client/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sighupio/gatekeeper-policy-manager/d045289c36afbd2632f75dec38bc690130fd4360/app/web-client/public/logo192.png
--------------------------------------------------------------------------------
/app/web-client/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sighupio/gatekeeper-policy-manager/d045289c36afbd2632f75dec38bc690130fd4360/app/web-client/public/logo512.png
--------------------------------------------------------------------------------
/app/web-client/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "GPM",
3 | "name": "Gatekeeper Policy Manager",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/app/web-client/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/app/web-client/public/touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sighupio/gatekeeper-policy-manager/d045289c36afbd2632f75dec38bc690130fd4360/app/web-client/public/touch-icon.png
--------------------------------------------------------------------------------
/app/web-client/src/App.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present SIGHUP s.r.l All rights reserved.
3 | * Use of this source code is governed by a BSD-style
4 | * license that can be found in the LICENSE file.
5 | */
6 |
7 | /* stylelint-disable selector-class-pattern, max-line-length */
8 | .gpm-page {
9 | width: 100%;
10 | }
11 |
12 | .gpm-page .euiPanel.euiPanel--plain {
13 | transition: 0.75s ease-out all;
14 | }
15 |
16 | .gpm-page .euiPanel.euiPanel--plain.highlighted {
17 | background-color: rgb(104 40 140 / 7%);
18 | box-shadow:
19 | 0 0.9px 4px -1px rgb(104 40 140 / 18%),
20 | 0 2.6px 8px -1px rgb(104 40 140 / 16%),
21 | 0 5.7px 12px -1px rgb(104 40 140 / 15%),
22 | 0 15px 15px -1px rgb(104 40 140 / 14%);
23 | }
24 |
25 | .gpm-page .euiCodeBlock {
26 | filter: invert(1);
27 | background: white;
28 | }
29 |
30 | .gpm-page .euiCodeBlock .token.comment,
31 | .gpm-page .euiCodeBlock .token.block-comment,
32 | .gpm-page .euiCodeBlock .token.prolog,
33 | .gpm-page .euiCodeBlock .token.doctype,
34 | .gpm-page .euiCodeBlock .token.cdata {
35 | color: #666;
36 | }
37 |
38 | .gpm-page .euiCodeBlock .token.punctuation {
39 | color: #333;
40 | }
41 |
42 | .gpm-page .euiCodeBlock .token.tag,
43 | .gpm-page .euiCodeBlock .token.attr-name,
44 | .gpm-page .euiCodeBlock .token.namespace,
45 | .gpm-page .euiCodeBlock .token.deleted {
46 | color: #1d8885;
47 | }
48 |
49 | .gpm-page .euiCodeBlock .token.function-name {
50 | color: #9e6933;
51 | }
52 |
53 | .gpm-page .euiCodeBlock .token.boolean,
54 | .gpm-page .euiCodeBlock .token.number,
55 | .gpm-page .euiCodeBlock .token.function {
56 | color: #0f72b6;
57 | }
58 |
59 | .gpm-page .euiCodeBlock .token.property,
60 | .gpm-page .euiCodeBlock .token.class-name,
61 | .gpm-page .euiCodeBlock .token.constant,
62 | .gpm-page .euiCodeBlock .token.symbol {
63 | color: #073aaa;
64 | }
65 |
66 | .gpm-page .euiCodeBlock .token.selector,
67 | .gpm-page .euiCodeBlock .token.important,
68 | .gpm-page .euiCodeBlock .token.atrule,
69 | .gpm-page .euiCodeBlock .token.keyword,
70 | .gpm-page .euiCodeBlock .token.builtin {
71 | color: #336632;
72 | }
73 |
74 | .gpm-page .euiCodeBlock .token.string,
75 | .gpm-page .euiCodeBlock .token.char,
76 | .gpm-page .euiCodeBlock .token.attr-value,
77 | .gpm-page .euiCodeBlock .token.regex,
78 | .gpm-page .euiCodeBlock .token.variable {
79 | color: #813966;
80 | }
81 |
82 | .gpm-page .euiCodeBlock .token.operator,
83 | .gpm-page .euiCodeBlock .token.entity,
84 | .gpm-page .euiCodeBlock .token.url {
85 | color: #983233;
86 | }
87 |
88 | .gpm-page .euiCodeBlock .token.important,
89 | .gpm-page .euiCodeBlock .token.bold {
90 | font-weight: bold;
91 | }
92 |
93 | .gpm-page .euiCodeBlock .token.italic {
94 | font-style: italic;
95 | }
96 |
97 | .gpm-page .euiCodeBlock .token.entity {
98 | cursor: help;
99 | }
100 |
101 | .gpm-page .euiCodeBlock .token.inserted {
102 | color: #ff7fff;
103 | }
104 |
--------------------------------------------------------------------------------
/app/web-client/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present SIGHUP s.r.l All rights reserved.
3 | * Use of this source code is governed by a BSD-style
4 | * license that can be found in the LICENSE file.
5 | */
6 |
7 | import React from "react";
8 | import { render, screen } from "@testing-library/react";
9 | import App from "./App";
10 |
11 | test("renders learn react link", () => {
12 | render();
13 | const linkElement = screen.getByText(/learn react/i);
14 | expect(linkElement).toBeInTheDocument();
15 | });
16 |
--------------------------------------------------------------------------------
/app/web-client/src/App.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present SIGHUP s.r.l All rights reserved.
3 | * Use of this source code is governed by a BSD-style
4 | * license that can be found in the LICENSE file.
5 | */
6 |
7 | import React from "react";
8 | import ContextProvider from "./AppContextProvider";
9 | import { EuiProvider } from "@elastic/eui";
10 | import { Routes, Route, useLocation } from "react-router-dom";
11 | import { Home } from "./pages/Home";
12 | import { Header } from "./components/Header";
13 | import { Footer } from "./components/Footer";
14 | import { ConstraintTemplates } from "./pages/ConstraintTemplates";
15 | import { Constraints } from "./pages/Constraints";
16 | import { Configurations } from "./pages/Configurations";
17 | import { Error } from "./pages/Error";
18 | import { Logout } from "./pages/Logout";
19 | import { NotFound } from "./pages/NotFound";
20 | import { theme } from "./theme";
21 | import "./App.scss";
22 |
23 | function App() {
24 | const { pathname } = useLocation();
25 |
26 | return (
27 |
31 |
32 | {pathname === "/logout" ? null : }
33 |
34 |
35 | } />
36 | } />
37 |
38 |
39 | } />
40 | } />
41 |
42 |
43 | } />
44 | } />
45 |
46 |
47 | } />
48 | } />
49 |
50 | } />
51 | } />
52 | } />
53 |
54 |
55 |
56 |
57 | );
58 | }
59 |
60 | export default App;
61 |
--------------------------------------------------------------------------------
/app/web-client/src/AppContext.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present SIGHUP s.r.l All rights reserved.
3 | * Use of this source code is governed by a BSD-style
4 | * license that can be found in the LICENSE file.
5 | */
6 |
7 | import { createContext } from "react";
8 |
9 | export interface IApplicationContextData {
10 | apiUrl: string;
11 | k8sContexts: string[];
12 | currentK8sContext: string;
13 | authEnabled: boolean;
14 | }
15 |
16 | export interface IApplicationContext {
17 | context: IApplicationContextData;
18 | setContext?: (context: Partial) => void;
19 | }
20 |
21 | export const ApplicationContext = createContext({
22 | context: {
23 | apiUrl: "",
24 | k8sContexts: [],
25 | currentK8sContext: "",
26 | authEnabled: false,
27 | },
28 | });
29 |
--------------------------------------------------------------------------------
/app/web-client/src/AppContextProvider.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present SIGHUP s.r.l All rights reserved.
3 | * Use of this source code is governed by a BSD-style
4 | * license that can be found in the LICENSE file.
5 | */
6 |
7 | import React, { useCallback, useEffect, useMemo, useState } from "react";
8 | import { ApplicationContext, IApplicationContextData } from "./AppContext";
9 |
10 | interface ContextProviderProps {
11 | children: React.ReactNode;
12 | }
13 |
14 | interface IK8sContext {
15 | context: {
16 | cluster: string;
17 | user: string;
18 | };
19 | name: string;
20 | }
21 |
22 | interface IAuthResponse {
23 | auth_enabled: boolean;
24 | }
25 |
26 | type K8sContextsResponse = IK8sContext[][] | IK8sContext[];
27 |
28 | const getDefaultContext = (): IApplicationContextData => {
29 | return {
30 | apiUrl:
31 | process.env.NODE_ENV !== "production" ? (process.env?.REACT_APP_LOCAL_GPM_SERVER_URL ?? "missing local url") : "/",
32 | authEnabled:
33 | JSON.parse(localStorage.getItem("authEnabled") ?? "false") || false,
34 | currentK8sContext: localStorage.getItem("currentK8sContext") || "",
35 | k8sContexts: localStorage.getItem("k8sContexts")
36 | ? JSON.parse(localStorage.getItem("k8sContexts") || "[]")
37 | : [],
38 | };
39 | };
40 |
41 | const ContextProvider = ({ children }: ContextProviderProps) => {
42 | const [appContext, setAppContext] = useState(
43 | getDefaultContext()
44 | );
45 |
46 | const setCurrentContext = useCallback(
47 | (updates: Partial) => {
48 | setAppContext({
49 | ...appContext,
50 | ...updates,
51 | });
52 |
53 | if (updates.currentK8sContext) {
54 | localStorage.setItem("currentK8sContext", updates.currentK8sContext);
55 | }
56 |
57 | if (updates.k8sContexts) {
58 | localStorage.setItem(
59 | "k8sContexts",
60 | JSON.stringify(updates.k8sContexts)
61 | );
62 | }
63 |
64 | if (updates.authEnabled) {
65 | localStorage.setItem(
66 | "authEnabled",
67 | JSON.stringify(updates.authEnabled)
68 | );
69 | }
70 | },
71 | [appContext, setAppContext]
72 | );
73 |
74 | const contextValue = useMemo(
75 | () => ({
76 | context: appContext,
77 | setContext: setCurrentContext,
78 | }),
79 | [appContext, setCurrentContext]
80 | );
81 |
82 | useEffect(() => {
83 | Promise.allSettled([
84 | fetch(`${appContext.apiUrl}api/v1/contexts/`)
85 | .then((res) => res.json())
86 | .then((body) => {
87 | if ((body?.length ?? 1) > 1) {
88 | const newContexts = (body[0] as IK8sContext[]).map((c) => c.name);
89 | const newCurrentContext =
90 | localStorage.getItem("currentK8sContext") ||
91 | (body[1] as IK8sContext).name;
92 |
93 | localStorage.setItem("k8sContexts", JSON.stringify(newContexts));
94 | localStorage.setItem("currentK8sContext", newCurrentContext);
95 |
96 | setAppContext({
97 | ...appContext,
98 | k8sContexts: newContexts,
99 | currentK8sContext: newCurrentContext,
100 | });
101 | } else {
102 | localStorage.removeItem("k8sContexts");
103 | localStorage.removeItem("currentK8sContext");
104 |
105 | setAppContext({
106 | ...appContext,
107 | k8sContexts: [],
108 | currentK8sContext: "",
109 | });
110 | }
111 | })
112 | .catch((err) => {
113 | localStorage.removeItem("k8sContexts");
114 | localStorage.removeItem("currentK8sContext");
115 | console.error(err);
116 | }),
117 | fetch(`${appContext.apiUrl}api/v1/auth/`)
118 | .then((res) => res.json())
119 | .then((body) => {
120 | localStorage.setItem(
121 | "authEnabled",
122 | JSON.stringify(body.auth_enabled)
123 | );
124 |
125 | setAppContext({
126 | ...appContext,
127 | authEnabled: body.auth_enabled,
128 | });
129 | })
130 | .catch((err) => {
131 | localStorage.setItem("authEnabled", "false");
132 | console.error(err);
133 | }),
134 | ]);
135 | }, []);
136 |
137 | return (
138 |
139 | {children}
140 |
141 | );
142 | };
143 |
144 | export default ContextProvider;
145 |
--------------------------------------------------------------------------------
/app/web-client/src/assets/fonts/JetBrainsMono-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sighupio/gatekeeper-policy-manager/d045289c36afbd2632f75dec38bc690130fd4360/app/web-client/src/assets/fonts/JetBrainsMono-Bold.ttf
--------------------------------------------------------------------------------
/app/web-client/src/assets/fonts/JetBrainsMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sighupio/gatekeeper-policy-manager/d045289c36afbd2632f75dec38bc690130fd4360/app/web-client/src/assets/fonts/JetBrainsMono-Regular.ttf
--------------------------------------------------------------------------------
/app/web-client/src/assets/fonts/Poppins-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sighupio/gatekeeper-policy-manager/d045289c36afbd2632f75dec38bc690130fd4360/app/web-client/src/assets/fonts/Poppins-Bold.ttf
--------------------------------------------------------------------------------
/app/web-client/src/assets/fonts/Poppins-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sighupio/gatekeeper-policy-manager/d045289c36afbd2632f75dec38bc690130fd4360/app/web-client/src/assets/fonts/Poppins-Light.ttf
--------------------------------------------------------------------------------
/app/web-client/src/assets/fonts/Poppins-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sighupio/gatekeeper-policy-manager/d045289c36afbd2632f75dec38bc690130fd4360/app/web-client/src/assets/fonts/Poppins-Medium.ttf
--------------------------------------------------------------------------------
/app/web-client/src/assets/fonts/Poppins-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sighupio/gatekeeper-policy-manager/d045289c36afbd2632f75dec38bc690130fd4360/app/web-client/src/assets/fonts/Poppins-Regular.ttf
--------------------------------------------------------------------------------
/app/web-client/src/assets/fonts/Poppins-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sighupio/gatekeeper-policy-manager/d045289c36afbd2632f75dec38bc690130fd4360/app/web-client/src/assets/fonts/Poppins-SemiBold.ttf
--------------------------------------------------------------------------------
/app/web-client/src/assets/github-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/app/web-client/src/assets/gpm-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/web-client/src/assets/shield-active.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/web-client/src/assets/shield-inactive.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/web-client/src/components/Footer/Component.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present SIGHUP s.r.l All rights reserved.
3 | * Use of this source code is governed by a BSD-style
4 | * license that can be found in the LICENSE file.
5 | */
6 |
7 | import {
8 | EuiFlexGroup,
9 | EuiFlexItem,
10 | EuiIcon,
11 | EuiLink,
12 | EuiSpacer,
13 | EuiText,
14 | } from "@elastic/eui";
15 | import githubLogo from "../../assets/github-logo.svg";
16 | import "./Style.scss";
17 |
18 | function FooterComponent() {
19 | return (
20 |
63 | );
64 | }
65 |
66 | export default FooterComponent;
67 |
--------------------------------------------------------------------------------
/app/web-client/src/components/Footer/Style.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present SIGHUP s.r.l All rights reserved.
3 | * Use of this source code is governed by a BSD-style
4 | * license that can be found in the LICENSE file.
5 | */
6 |
7 | /* stylelint-disable-next-line selector-class-pattern */
8 | .gpm-footer > .euiFlexGroup {
9 | margin-left: auto;
10 | margin-right: auto;
11 | z-index: 1002;
12 | padding: 0 13%;
13 | }
14 |
--------------------------------------------------------------------------------
/app/web-client/src/components/Footer/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present SIGHUP s.r.l All rights reserved.
3 | * Use of this source code is governed by a BSD-style
4 | * license that can be found in the LICENSE file.
5 | */
6 |
7 | import FooterComponent from "./Component";
8 |
9 | export { FooterComponent as Footer };
10 |
--------------------------------------------------------------------------------
/app/web-client/src/components/Header/Component.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present SIGHUP s.r.l All rights reserved.
3 | * Use of this source code is governed by a BSD-style
4 | * license that can be found in the LICENSE file.
5 | */
6 |
7 | import {
8 | EuiButton,
9 | EuiButtonEmpty,
10 | EuiHeader,
11 | EuiHeaderSection,
12 | EuiHeaderSectionItem,
13 | EuiHideFor,
14 | EuiSuperSelect,
15 | EuiText,
16 | } from "@elastic/eui";
17 | import {
18 | MouseEventHandler,
19 | MouseEvent,
20 | useContext,
21 | useEffect,
22 | useState,
23 | } from "react";
24 | import { ApplicationContext } from "../../AppContext";
25 | import { EuiSuperSelectOption } from "@elastic/eui/src/components/form/super_select/super_select_item";
26 | import { useLocation, useNavigate } from "react-router-dom";
27 | import "./Style.scss";
28 |
29 | function HeaderComponent() {
30 | const [optionsFromContexts, setOptionsFromContexts] = useState<
31 | EuiSuperSelectOption[]
32 | >([]);
33 | const { context, setContext } = useContext(ApplicationContext);
34 | const { pathname, hash } = useLocation();
35 | const navigate = useNavigate();
36 |
37 | const pathSplit = pathname.match(/\/([^/]*)/gi);
38 |
39 | const routes = [
40 | {
41 | path: "/",
42 | name: "Home",
43 | },
44 | {
45 | path: "/constrainttemplates",
46 | name: "Constraint Templates",
47 | },
48 | {
49 | path: "/constraints",
50 | name: "Constraints",
51 | },
52 | {
53 | path: "/configurations",
54 | name: "Configurations",
55 | },
56 | ];
57 |
58 | useEffect(() => {
59 | const optionsFromContexts = context.k8sContexts.map((k8sContext) => {
60 | return {
61 | value: k8sContext,
62 | inputDisplay: k8sContext,
63 | dropdownDisplay: (
64 |
73 | {k8sContext}
74 |
75 | ),
76 | };
77 | });
78 | setOptionsFromContexts(optionsFromContexts);
79 |
80 | if (
81 | pathSplit &&
82 | pathSplit.length > 1 &&
83 | pathname !== "/" &&
84 | pathname !== "/error" &&
85 | context.k8sContexts.length > 0
86 | ) {
87 | if (setContext) {
88 | setContext({
89 | currentK8sContext: pathSplit[1].slice(1),
90 | });
91 | }
92 | }
93 | }, [context.k8sContexts]);
94 |
95 | const doLogout: MouseEventHandler = (
96 | event: MouseEvent,
97 | ): void => {
98 | event.preventDefault();
99 | window.location.replace("/logout");
100 | };
101 |
102 | const onChangeContext = (value: string) => {
103 | if (setContext) {
104 | setContext({
105 | currentK8sContext: value,
106 | });
107 |
108 | if (
109 | pathSplit &&
110 | pathSplit.length > 0 &&
111 | pathname !== "/" &&
112 | pathname !== "/error"
113 | ) {
114 | navigate(pathSplit[0] + "/" + encodeURIComponent(value) + hash, {
115 | replace: true,
116 | });
117 | navigate(0);
118 | }
119 | }
120 | };
121 |
122 | return (
123 |