Compare commits

...

133 commits

Author SHA1 Message Date
d8ea3da5fd bump version to: 1.3.2-SNAPSHOT 2024-08-22 17:02:39 +02:00
cd10a5673c release: 1.3.1 2024-08-22 17:02:39 +02:00
f646ac81b7 Revert spec 2024-08-22 16:41:44 +02:00
e974711923 Update spec 2024-08-09 15:44:05 +02:00
7c474c6d89 bump version to: 1.3.1-SNAPSHOT 2024-08-09 15:19:35 +02:00
12e6f97519 release: 1.3.0 2024-08-09 15:19:35 +02:00
ad82277e96 Fix monitoring not receiving config 2024-08-09 15:06:38 +02:00
05039420bd Update deps 2024-08-09 15:06:21 +02:00
d59f40a961 Don't use deprecated func 2024-08-08 14:37:43 +02:00
74beafdcfe Update build 2024-08-08 11:38:34 +02:00
fd8b1facb8 Update deps 2024-08-08 11:38:26 +02:00
9f2ee663bd Split Config and Auth 2024-08-08 11:38:16 +02:00
7ff6f20fce Merge pull request 'proper-namespace-implementation' (#2) from proper-namespace-implementation into master
Reviewed-on: #2
2024-08-07 13:15:31 +00:00
d2ca5b5442 Fix tests 2024-08-07 14:57:54 +02:00
2fba9ce1de Remove unnecessary keys 2024-08-07 14:57:47 +02:00
b7a284a2dc Dont hardcode namespace 2024-08-07 14:48:19 +02:00
f64f4f3ab5 Update functions to replace NAMESPACE 2024-08-07 14:48:04 +02:00
1429ff0058 Move default config values to config defaults
Also add spec for namespace
2024-08-07 14:30:11 +02:00
cfc679cd82 [Skip-CI] Add website to contact info 2024-08-06 13:07:34 +02:00
432d46e3e7 [Skip-CI] Remove unnecessary secondary build files 2024-07-09 10:58:17 +02:00
6199978a05 bump version to: 1.2.1-SNAPSHOT 2024-07-04 16:16:46 +02:00
f249c82fa1 release: 1.2.0 2024-07-04 16:16:46 +02:00
3ea6331339 update deps, fix build.py 2024-07-04 16:15:35 +02:00
1b1f759c69 fix function args 2024-07-04 16:06:08 +02:00
e8eea1e646 Merge pull request 'Add the inline macro, native build and namespaces refactorings' (#1) from refactorings into master
Reviewed-on: #1
2024-07-04 14:02:43 +00:00
85f44ea8d1 Add test task 2024-07-04 14:43:46 +02:00
b5afdc0959 Update deps 2024-07-04 14:43:33 +02:00
3639f3d5e6 Add namespaces
Also Update postgres usage
2024-07-04 14:33:53 +02:00
f4bf41d374 Add tag_only 2024-07-04 12:24:57 +02:00
e838fdda7a Unlock experimental and update gitignore 2024-07-04 12:13:41 +02:00
94192a3e42 Update deps and docs for native image 2024-07-04 12:13:07 +02:00
0293fea409 Update CI for native-image 2024-07-04 12:11:39 +02:00
af1b305bd5 Add build.py 2024-07-04 11:22:30 +02:00
dda4ecb1fb Implement inline resources 2024-07-04 10:47:18 +02:00
8bcea1ef8f [Skip-CI] Add Development and mirrors section 2023-07-28 14:00:58 +02:00
3c8a9168d2 update mirrors 2023-05-17 09:00:06 +02:00
Clemens
398fa5475b upgraded non-c4k deps 2023-02-10 13:09:05 +01:00
6ff50bca4c [Skip-CI] version bump 2023-01-31 12:18:45 +01:00
700875de53 Version 1.0.1 2023-01-31 12:17:20 +01:00
6f5d51b6a1 Release 1.0.1 2023-01-31 12:17:02 +01:00
bfc4661278 Version 0.2.4 2023-01-31 12:11:46 +01:00
3d15863be9 Release 1.0.0 2023-01-31 12:11:36 +01:00
088d4e3d0f Remove service port 443 2023-01-31 12:01:59 +01:00
ab51472eba Format 2023-01-31 12:01:46 +01:00
2b28a02e17 Fix cljs tests
Now testing for invalid config and auth.
Bumped dependency for c4k-common-cljs.
2023-01-31 11:58:44 +01:00
c7560de0db Repair and test monitoring auth spec 2023-01-31 11:08:25 +01:00
bom
acb2a2155e Remove resolved review comment 2023-01-27 14:15:49 +01:00
bom
af17850b06 Make core reject invalid monitoring configs 2023-01-27 14:14:48 +01:00
938ba80275 [Skip-CI] [WIP] Update tests for config and auth
Currently specs opt-un seems to validate invalid specs.
This results in invalid configs not failing our tests.
2023-01-27 13:06:25 +01:00
d362d440dd Update gitignore 2023-01-27 13:02:43 +01:00
138bb8f38a Bump c4k-common version 2023-01-27 13:02:19 +01:00
1dc56bb77e review 2023-01-27 09:39:13 +01:00
7193a34365 Fix deployment test 2023-01-25 13:10:16 +01:00
83817bdb2c [Skip-CI] Update resources 2023-01-25 12:56:42 +01:00
bom
9c236b954b Repair lein test 2023-01-25 10:00:38 +01:00
9175c46f48 [Skip-CI] Update Env-Vars 2023-01-25 09:54:07 +01:00
49e96a579d [Skip-CI] Update deployment for keycloak 20 2023-01-24 15:36:47 +01:00
4641a6592b fix valid config 2023-01-20 18:05:25 +01:00
4e9c1c1d8c update deps 2023-01-20 18:05:08 +01:00
bom
d5d8caea3c Update ci to use correct config and auth 2023-01-20 16:13:17 +01:00
bom
8293f9a122 Verify validity of example config and auth 2023-01-20 16:03:12 +01:00
bom
d369fdcd91 Update core for common uberjar 2023-01-20 15:39:04 +01:00
bom
4a6030f053 Remove loadbalancer type from service 2023-01-20 15:38:40 +01:00
bom
ccdc8c9cc0 Update keycloak test 2023-01-20 15:19:59 +01:00
bom
bdaee2eaf6 Remove yaml test 2023-01-20 15:19:46 +01:00
bom
3d69076767 Include postgres credentials in browser text area 2023-01-20 15:16:01 +01:00
bom
079f9be04a Fix indentation 2023-01-20 15:15:17 +01:00
bom
0882afd5d1 Split merged config 2023-01-20 15:14:25 +01:00
bom
f8348f704a Remove merged generate-certificate 2023-01-20 14:36:17 +01:00
bom
6e02e57a8f Use common uberjar 2023-01-20 14:34:36 +01:00
bom
45507aade6 Use common ingress
update the rest of keycloak.cljc
2023-01-20 14:32:44 +01:00
bom
c67aab1b56 Update Browser build 2023-01-20 14:17:55 +01:00
bom
4640a2e762 Use common monitoring 2023-01-20 14:09:42 +01:00
bom
1f459b1cde Update frontend script 2023-01-20 14:06:21 +01:00
bom
349eb9dfc5 Use common yaml 2023-01-20 13:53:35 +01:00
bom
337fa68592 Use common postgres 2023-01-20 13:51:49 +01:00
bom
2fc3b8f5a7 Bump c4k-common version 2023-01-20 13:38:47 +01:00
f05a3731fe Update CI to show failing tests 2022-09-02 10:11:39 +02:00
f1aa04b94b fix gpgless deploy 2022-02-25 11:44:43 +01:00
f8ab5317ef version bump 2022-02-25 10:49:03 +01:00
d30496b659 Version 0.2.3 2022-02-25 10:46:46 +01:00
3dd01f16f8 version bump 2022-02-25 10:40:31 +01:00
61d6312792 Version 0.2.2 2022-02-25 10:39:24 +01:00
1c6e7d9a14 remove automated prerelease upload 2022-02-25 10:38:59 +01:00
jem
33559b7982 Merge branch 'master' of gitlab.com:domaindrivenarchitecture/c4k-keycloak 2021-07-05 11:28:10 +02:00
jem
5b1c68973d removed unused image 2021-07-05 11:28:06 +02:00
344d84d118 Updated license 2021-06-29 09:21:22 +02:00
jem
a792e468e9 Merge branch 'master' of gitlab.com:domaindrivenarchitecture/c4k-keycloak 2021-06-23 07:52:22 +02:00
jem
9f731f6966 format 2021-06-23 07:52:18 +02:00
bom
a66259efbc refactored browser to use c4k-common 2021-06-21 11:49:05 +02:00
jem
590241c211 add doc 2021-06-21 08:29:15 +02:00
jem
4f9e7e889d use c4k-common 2021-06-18 17:36:57 +02:00
jem
f542121386 add license 2021-06-18 16:04:12 +02:00
jem
7201ded302 version bump 2021-06-18 12:12:58 +02:00
jem
5cd774f4dd Version 0.2.1 2021-06-18 11:59:02 +02:00
jem
7f20cb7052 fix badge 2021-06-18 11:58:01 +02:00
jem
7d290e89df add try out image 2021-06-18 11:55:35 +02:00
jem
8572d1aaad activate frontend build 2021-06-18 11:50:10 +02:00
jem
d8ffd9919f format html 2021-06-18 11:21:05 +02:00
jem
55f7b6d898 version bump 2021-06-18 11:20:23 +02:00
jem
f20ac99512 Version 0.2.0 2021-06-18 11:18:13 +02:00
jem
4390a4127b finished browser refactoring 2021-06-18 09:54:53 +02:00
jem
e2bf9a25cd start refactoring for issuer 2021-06-15 21:37:59 +02:00
jem
a694acb1fa refactor useful functions out 2021-06-15 21:23:39 +02:00
jem
3c4be8b8ff validation based on value 2021-06-15 21:06:12 +02:00
bom
2466dbc85f correct config and auth generate output 2021-06-14 13:01:33 +02:00
bom
8def2ec408 Changed from textarea to text field, added issuer 2021-06-14 12:08:20 +02:00
bom
30ddfd295c Changed common regex to be used by js 2021-06-14 09:46:53 +02:00
jem
43d90ea8eb first try for frontend build 2021-06-05 17:49:05 +02:00
jem
a900551a58 more refactoring 2021-06-05 17:27:42 +02:00
jem
af2f16714e fix ci 2021-06-05 17:25:03 +02:00
jem
c935cce986 doc 2021-06-05 17:23:46 +02:00
jem
1dc813f937 apply c4k renaming to readme 2021-06-05 17:20:15 +02:00
jem
fd81f414d0 refactored k8s -> c4k & fixed cljs tests 2021-06-05 17:12:01 +02:00
jem
444cb32554 add secret & config refs to keycloak 2021-06-01 21:32:07 +02:00
jem
2d3f50d2a9 remove unused blanks 2021-06-01 20:53:10 +02:00
jem
04f08ef0d9 symetric coding config 2021-05-28 20:34:29 +02:00
jem
a4e034a529 add secrets to postgrs 2021-05-28 20:20:00 +02:00
jem
ded683b8ee fix tests 2021-05-28 18:04:37 +02:00
jem
f2c70d0f86 #1 2021-05-28 18:00:18 +02:00
jem
e5cf4f55d9 fix new image 2021-05-28 17:57:41 +02:00
jem
5424096db2 test schema 2021-05-28 17:54:59 +02:00
jem
388ca2e50f use config ref 2021-05-28 17:06:39 +02:00
jem
48be734cfc use kubeconform instead of kubeval 2021-05-28 17:03:40 +02:00
jem
c00c5a272e local config? 2021-05-28 16:42:10 +02:00
jem
aab4521260 refactor out common, postgres 2021-05-28 14:46:35 +02:00
jem
1d00f271d1 ignore my-auth 2021-05-28 13:18:02 +02:00
jem
62cb477e5b refactored yaml location 2021-05-28 13:17:06 +02:00
bom
7df0ec866b use replace named value function instead of index
* updated gitignore
* fixed tests
2021-05-28 12:18:05 +02:00
leo
e56d261d91 mob 2021-05-28 10:18:34 +02:00
19e4254ad4 mob changed keywords 2021-05-28 10:00:34 +02:00
jem
9a35ea346a not used credentials 2021-05-28 09:49:38 +02:00
jem
1d635a1068 [skip-ci] mob 2021-05-28 09:48:28 +02:00
46 changed files with 1062 additions and 925 deletions

11
.gitignore vendored
View file

@ -7,6 +7,7 @@ target/
.lein-repl-history
.lein-failures
pom.*
reports/*
# cljs
.shadow-cljs
@ -21,5 +22,11 @@ public/js/
*.iml
.idea/
myauth.edn
myconfig.edn
valid-auth.edn
valid-config.edn
my-auth.edn
.clj-kondo/
.lsp/
build-and-move-frontend.sh

View file

@ -1,12 +1,11 @@
stages:
- first
- build_and_test
- package
- security
- upload
.cljs-job: &cljs
image: domaindrivenarchitecture/shadow-cljs
image: "domaindrivenarchitecture/ddadevops-clj-cljs:4.11.3"
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
@ -14,49 +13,62 @@ stages:
- .shadow-cljs/
- .m2
before_script:
- export RELEASE_ARTIFACT_TOKEN=$MEISSA_REPO_BUERO_RW
- echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
- npm install
.clj-uploadjob: &clj
image: clojure:lein-2.7.1-alpine
image: "domaindrivenarchitecture/ddadevops-clj:4.11.3"
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .m2
before_script:
- export RELEASE_ARTIFACT_TOKEN=$MEISSA_REPO_BUERO_RW
- mkdir -p /root/.lein
- echo "{:auth {:repository-auth {#\"clojars\" {:username \"${CLOJARS_USER}\" :password \"${CLOJARS_TOKEN_DOMAINDRIVENARCHITECTURE}\" }}}}" > ~/.lein/profiles.clj
.test-cljs:
<<: *cljs
stage: build_and_test
script:
- shadow-cljs compile test
.tag_only: &tag_only
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: never
- if: '$CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+$/'
test-clj:
<<: *clj
stage: build_and_test
script:
- lein test
- pyb test_clj
.report-frontend:
test-cljs:
<<: *cljs
stage: build_and_test
script:
- pyb test_cljs
test-schema:
<<: *clj
stage: build_and_test
script:
- pyb test_schema
artifacts:
paths:
- target/uberjar
report-frontend:
<<: *cljs
stage: package
script:
- mkdir -p target/frontend-build
- shadow-cljs run shadow.cljs.build-report frontend target/frontend-build/build-report.html
- pyb report_frontend
artifacts:
paths:
- target/frontend-build/build-report.html
.package-frontend:
package-frontend:
<<: *cljs
stage: package
script:
- mkdir -p target/frontend-build
- shadow-cljs release frontend
- cp public/js/main.js target/frontend-build/k8s-keycloak.js
- sha256sum target/frontend-build/k8s-keycloak.js > target/frontend-build/k8s-keycloak.js.sha256
- sha512sum target/frontend-build/k8s-keycloak.js > target/frontend-build/k8s-keycloak.js.sha512
- pyb package_frontend
artifacts:
paths:
- target/frontend-build
@ -65,48 +77,30 @@ package-uberjar:
<<: *clj
stage: package
script:
- lein uberjar
- sha256sum target/uberjar/k8s-keycloak-standalone.jar > target/uberjar/k8s-keycloak-standalone.jar.sha256
- sha512sum target/uberjar/k8s-keycloak-standalone.jar > target/uberjar/k8s-keycloak-standalone.jar.sha512
- pyb package_uberjar
artifacts:
paths:
- target/uberjar
sast:
variables:
SAST_EXCLUDED_ANALYZERS:
bandit, brakeman, flawfinder, gosec, kubesec, phpcs-security-audit,
pmd-apex, security-code-scan, sobelow, spotbugs
stage: security
before_script:
- mkdir -p builds && cp -r target/ builds/
include:
- template: Security/SAST.gitlab-ci.yml
upload-clj-prerelease:
package-native:
<<: *clj
stage: upload
rules:
- if: '$CI_COMMIT_BRANCH == "master" && $CI_COMMIT_TAG == null'
stage: package
script:
- lein deploy clojars
release:
image: registry.gitlab.com/gitlab-org/release-cli:latest
stage: upload
rules:
- if: '$CI_COMMIT_TAG != null'
- pyb package_native
artifacts:
paths:
- target/uberjar
- target/frontend-build
- target/graalvm
release-to-clojars:
<<: *clj
<<: *tag_only
stage: upload
script:
- apk --no-cache add curl
- |
release-cli create --name "Release $CI_COMMIT_TAG" --tag-name $CI_COMMIT_TAG \
--assets-link "{\"name\":\"k8s-keycloak-standalone.jar\",\"url\":\"https://gitlab.com/domaindrivenarchitecture/k8s-keycloak/-/jobs/${CI_JOB_ID}/artifacts/file/target/uberjar/k8s-keycloak-standalone.jar\"}" \
--assets-link "{\"name\":\"k8s-keycloak-standalone.jar.sha256\",\"url\":\"https://gitlab.com/domaindrivenarchitecture/k8s-keycloak/-/jobs/${CI_JOB_ID}/artifacts/file/target/uberjar/k8s-keycloak-standalone.jar.sha256\"}" \
--assets-link "{\"name\":\"k8s-keycloak-standalone.jar.sha512\",\"url\":\"https://gitlab.com/domaindrivenarchitecture/k8s-keycloak/-/jobs/${CI_JOB_ID}/artifacts/file/target/uberjar/k8s-keycloak-standalone.jar.sha512\"}" \
--assets-link "{\"name\":\"k8s-keycloak.js\",\"url\":\"https://gitlab.com/domaindrivenarchitecture/k8s-keycloak/-/jobs/${CI_JOB_ID}/artifacts/file/target/frontend-build/k8s-keycloak.js\"}" \
--assets-link "{\"name\":\"k8s-keycloak.js.sha256\",\"url\":\"https://gitlab.com/domaindrivenarchitecture/k8s-keycloak/-/jobs/${CI_JOB_ID}/artifacts/file/target/frontend-build/k8s-keycloak.js.sha256\"}" \
--assets-link "{\"name\":\"k8s-keycloak.js.sha512\",\"url\":\"https://gitlab.com/domaindrivenarchitecture/k8s-keycloak/-/jobs/${CI_JOB_ID}/artifacts/file/target/frontend-build/k8s-keycloak.js.sha512\"}" \
- pyb upload_clj
release-to-forgejo:
<<: *clj
<<: *tag_only
stage: upload
script:
- pyb publish_artifacts

201
LICENSE Normal file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2021 meissa GmbH
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,40 +1,48 @@
# k8s-keycloak
[![Clojars Project](https://img.shields.io/clojars/v/dda/k8s-keycloak.svg)](https://clojars.org/dda/k8s-keycloak) [![pipeline status](https://gitlab.com/domaindrivenarchitecture/k8s-keycloak/badges/master/pipeline.svg)](https://gitlab.com/domaindrivenarchitecture/k8s-keycloak/-/commits/master)
# convention 4 kubernetes: c4k-keycloak
[![Clojars Project](https://img.shields.io/clojars/v/org.domaindrivenarchitecture/c4k-keycloak.svg)](https://clojars.org/org.domaindrivenarchitecture/c4k-keycloak) [![pipeline status](https://gitlab.com/domaindrivenarchitecture/c4k-keycloak/badges/master/pipeline.svg)](https://gitlab.com/domaindrivenarchitecture/c4k-keycloak/-/commits/master)
[<img src="https://domaindrivenarchitecture.org/img/delta-chat.svg" width=20 alt="DeltaChat"> chat over e-mail](mailto:buero@meissa-gmbh.de?subject=community-chat) | [<img src="https://meissa-gmbh.de/img/community/Mastodon_Logotype.svg" width=20 alt="team@social.meissa-gmbh.de"> team@social.meissa-gmbh.de](https://social.meissa-gmbh.de/@team) | [Website & Blog](https://domaindrivenarchitecture.org)
[<img src="https://domaindrivenarchitecture.org/img/delta-chat.svg" width=20 alt="DeltaChat"> chat over e-mail](mailto:buero@meissa-gmbh.de?subject=community-chat) | [<img src="https://meissa.de/images/parts/contact/mastodon36_hue9b2464f10b18e134322af482b9c915e_5501_filter_14705073121015236177.png" width=20 alt="M"> meissa@social.meissa-gmbh.de](https://social.meissa-gmbh.de/@meissa) | [Blog](https://domaindrivenarchitecture.org) | [Website](https://meissa.de)
## Purpose
k8s-keycloak ....
c4k-keycloak provides a k8s deployment for keycloak containing:
* keycloak
* ingress having a letsencrypt managed certificate
* postgres database
## Rational
The package aims to a low load sceanrio.
There are many comparable solutions for creating k8s deployments like helm or kustomize. Why do we need another one?
* We like the simplicity of kustomize. Yaml in, yaml out, the ability to lint the result and the option to split large yaml files into objects. But a simple overwriting per environment may not be enough ...
* We like helm packages. A package encapsulates the setup for an application. On the one hand, but on the other hand we don't like the idea of having to program and debug in a template language. We can program much better in real programming languages.
## Status
Our k8s-* tools combine the advantages of both approaches:
* Packages for one application
* Programming in clojure
* yaml / edn as input and output, no more magic
* good validation, integration as api, cli or in the browser
This is just a POC, database is stored volatile, there is no backup implemented.
## Try out
Click on the image to try out live in your browser:
[![Try it out](/doc/tryItOut.png "Try out yourself")](https://domaindrivenarchitecture.org/pages/dda-provision/k8s-keycloak/)
[![Try it out](/doc/tryItOut.png "Try out yourself")](https://domaindrivenarchitecture.org/pages/dda-provision/c4k-keycloak/)
Your input will stay in your browser. No server interaction is required.
You will also be able to try out on cli:
```
target/graalvm/k8s-keycloak src/test/resources/valid-config.edn src/test/resources/valid-auth.edn | kubeval -
target/graalvm/k8s-keycloak src/test/resources/valid-config.edn src/test/resources/valid-auth.edn | kubectl apply -f -
target/graalvm/c4k-keycloak src/test/resources/keycloak-test/valid-config.edn src/test/resourceskeycloak-test/valid-auth.edn | kubeval -
target/graalvm/c4k-keycloak src/test/resources/keycloak-test/valid-config.edn src/test/resources/keycloak-test/valid-auth.edn | kubectl apply -f -
```
## Development & mirrors
Development happens at: https://repo.prod.meissa.de/meissa/c4k-keycloak
Mirrors are:
* https://gitlab.com/domaindrivenarchitecture/c4k-keycloak (issues and PR, CI)
* https://github.com/DomainDrivenArchitecture/c4k-keycloak
For more details about our repository model see: https://repo.prod.meissa.de/meissa/federate-your-repos
## License
Copyright © 2021 meissa GmbH
Copyright © 2024 meissa GmbH
Licensed under the [Apache License, Version 2.0](LICENSE) (the "License")
Pls. find licenses of our subcomponents [here](doc/SUBCOMPONENT_LICENSE)

234
build.py Normal file
View file

@ -0,0 +1,234 @@
from os import environ
from subprocess import run
from pybuilder.core import init, task
from ddadevops import *
default_task = "dev"
name = 'c4k-keycloak'
MODULE = 'not-used'
PROJECT_ROOT_PATH = '.'
@init
def initialize(project):
project.build_depends_on("ddadevops>=4.7.0")
input = {
"name": name,
"module": MODULE,
"stage": "notused",
"project_root_path": PROJECT_ROOT_PATH,
"build_types": [],
"release_main_branch": "master",
"mixin_types": ["RELEASE"],
"release_primary_build_file": "project.clj",
"release_secondary_build_files": [
"package.json",
],
"release_artifact_server_url": "https://repo.prod.meissa.de",
"release_organisation": "meissa",
"release_repository_name": name,
"release_artifacts": [
"target/graalvm/" + name,
"target/uberjar/" + name + "-standalone.jar",
"target/frontend-build/" + name + ".js",
],
}
build = ReleaseMixin(project, input)
build.initialize_build_dir()
@task
def test_clj():
run("lein test", shell=True, check=True)
@task
def test_cljs():
run("shadow-cljs compile test", shell=True, check=True)
run("node target/node-tests.js", shell=True, check=True)
@task
def test_schema():
run("lein uberjar", shell=True, check=True)
run(
"java -jar target/uberjar/c4k-keycloak-standalone.jar "
+ "src/test/resources/keycloak-test/valid-config.yaml "
+ "src/test/resources/keycloak-test/valid-auth.yaml | "
+ """kubeconform --kubernetes-version 1.23.0 --strict --skip "Certificate,Middleware" -""",
shell=True,
check=True,
)
@task
def test():
test_clj()
test_cljs()
test_schema()
@task
def report_frontend(project):
run("mkdir -p target/frontend-build", shell=True, check=True)
run(
"shadow-cljs run shadow.cljs.build-report frontend target/frontend-build/build-report.html",
shell=True,
check=True,
)
@task
def package_frontend(project):
run("mkdir -p target/frontend-build", shell=True, check=True)
run("shadow-cljs release frontend", shell=True, check=True)
run(
"cp public/js/main.js target/frontend-build/" + project.name + ".js",
shell=True,
check=True,
)
run(
"sha256sum target/frontend-build/c4k-keycloak.js > target/frontend-build/" + project.name + ".js.sha256",
shell=True,
check=True,
)
run(
"sha512sum target/frontend-build/c4k-keycloak.js > target/frontend-build/" + project.name + ".js.sha512",
shell=True,
check=True,
)
@task
def package_uberjar(project):
run("mkdir -p target/uberjar", shell=True, check=True)
run(
"lein uberjar",
shell=True,
check=True,
)
run(
"sha256sum target/uberjar/c4k-keycloak-standalone.jar > target/uberjar/" + project.name + "-standalone.jar.sha256",
shell=True,
check=True,
)
run(
"sha512sum target/uberjar/c4k-keycloak-standalone.jar > target/uberjar/" + project.name + "-standalone.jar.sha512",
shell=True,
check=True,
)
@task
def package_native(project):
run(
"mkdir -p target/graalvm",
shell=True,
check=True,
)
run(
"native-image " +
"--native-image-info " +
"--report-unsupported-elements-at-runtime " +
"--no-server " +
"--no-fallback " +
"--features=clj_easy.graal_build_time.InitClojureClasses " +
f"-jar target/uberjar/{project.name}-standalone.jar " +
"-H:IncludeResources=.*.yaml " +
"-H:Log=registerResource:verbose " +
f"-H:Name=target/graalvm/{project.name}",
shell=True,
check=True,
)
run(
"sha256sum target/graalvm/c4k-keycloak > target/graalvm/" + project.name + ".sha256",
shell=True,
check=True,
)
run(
"sha512sum target/graalvm/c4k-keycloak > target/graalvm/" + project.name + ".sha512",
shell=True,
check=True,
)
@task
def inst(project):
package_uberjar(project)
package_native(project)
run(
"sudo install -m=755 target/uberjar/" + project.name + "-standalone.jar /usr/local/bin/" + project.name + "-standalone.jar",
shell=True,
check=True,
)
run(
"sudo install -m=755 target/graalvm/" + project.name + " /usr/local/bin/" + project.name + "",
shell=True,
check=True,
)
@task
def upload_clj(project):
run("lein deploy", shell=True, check=True)
@task
def lint(project):
run(
"lein ancient check",
shell=True,
check=True,
)
@task
def patch(project):
linttest(project, "PATCH")
release(project)
@task
def minor(project):
linttest(project, "MINOR")
release(project)
@task
def major(project):
linttest(project, "MAJOR")
release(project)
@task
def dev(project):
linttest(project, "NONE")
@task
def prepare(project):
build = get_devops_build(project)
build.prepare_release()
@task
def tag(project):
build = get_devops_build(project)
build.tag_bump_and_push_release()
@task
def publish_artifacts(project):
build = get_devops_build(project)
build.publish_artifacts()
def release(project):
prepare(project)
tag(project)
def linttest(project, release_type):
build = get_devops_build(project)
build.update_release_type(release_type)
test_clj()
test_cljs()
test_schema()
lint(project)

View file

@ -3,13 +3,13 @@
## clj setup
### install leiningen
```
```bash
sudo apt install leiningen
```
or manually using Instructions on https://leiningen.org/#install
### install vscode + extensions
```
```bash
sudo snap install code
```
or with packages from https://code.visualstudio.com/Download
@ -18,7 +18,7 @@ install extension "Calva: Clojure & ClojureScript Interactive Programming"
## cljs / js-dev setup
```
```bash
sudo apt install npm
sudo npm install -g npx
@ -32,47 +32,44 @@ npx shadow-cljs compile test
### create frontend script
```
```bash
npx shadow-cljs release frontend
```
## graalvm-setup
```
curl -LO https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0.2/graalvm-ce-java11-linux-amd64-21.0.0.2.tar.gz
```bash
curl -LO https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-21.0.2/graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz
# unpack
tar -xzf graalvm-ce-java11-linux-amd64-21.0.0.2.tar.gz
tar -xzf graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz
sudo mv graalvm-ce-java11-21.0.0.2 /usr/lib/jvm/
sudo ln -s /usr/lib/jvm/graalvm-ce-java11-21.0.0.2 /usr/lib/jvm/graalvm
sudo ln -s /usr/lib/jvm/graalvm/bin/gu /usr/local/bin
sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/graalvm/bin/java 2
sudo mv graalvm-community-openjdk-21.0.2+13.1 /usr/lib/jvm/
sudo ln -s /usr/lib/jvm/graalvm-community-openjdk-21.0.2+13.1 /usr/lib/jvm/graalvm-21
sudo ln -s /usr/lib/jvm/graalvm-21/bin/gu /usr/local/bin
sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/graalvm-21/bin/java 2
sudo update-alternatives --config java
# install native-image in graalvm-ce-java11-linux-amd64-21.0.0.2/bin
sudo gu install native-image
sudo ln -s /usr/lib/jvm/graalvm/bin/native-image /usr/local/bin
sudo ln -s /usr/lib/jvm/graalvm-21/bin/native-image /usr/local/bin
# deps
sudo apt-get install build-essential libz-dev zlib1g-dev
# build
cd ~/repo/dda/k8s-keycloak
cd ~/repo/c4k/c4k-keycloak
lein uberjar
mkdir -p target/graalvm
lein native
# execute
./target/graalvm/k8s-keycloak -h
./target/graalvm/k8s-keycloak src/test/resources/valid-config.edn src/test/resources/valid-auth.edn
./target/graalvm/k8s-keycloak src/test/resources/invalid-config.edn src/test/resources/invalid-auth.edn
./target/graalvm/c4k-cloud -h
./target/graalvm/c4k-cloud src/test/resources/valid-config.edn src/test/resources/valid-auth.edn
./target/graalvm/c4k-cloud src/test/resources/invalid-config.edn src/test/resources/invalid-auth.edn
```
## k8s-setup
## c4k-setup
### install kubectl
```
```bash
sudo -i
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" \
@ -81,20 +78,18 @@ apt update && apt install kubectl
kubectl completion bash >> /etc/bash_completion.d/kubernetes
```
### install kubeval
### install kubeconform
```
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz
tar xf kubeval-linux-amd64.tar.gz
sudo cp kubeval /usr/local/bin
## remote access to k8s
```bash
curl -Lo /tmp/kubeconform.tar.gz https://github.com/yannh/kubeconform/releases/download/v0.4.7/kubeconform-linux-amd64.tar.gz
tar -xf /tmp/kubeconform.tar.gz
sudo cp kubeconform /usr/local/bin
```
### remote access to k8s
### remote access to c4k
```
scp -r root@devops.test.meissa-gmbh.de:/home/k8s/.kube ~/
```bash
scp -r root@devops.test.meissa-gmbh.de:/home/c4k/.kube ~/
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@devops.test.meissa-gmbh.de -L 8002:localhost:8002 -L 6443:192.168.5.1:6443
# add in /etc/hosts "127.0.0.1 kubernetes"
@ -106,7 +101,7 @@ kubectl get pods
### deploy keycloak
```
java -jar target/uberjar/k8s-keycloak-standalone.jar myconfig.edn myauth.edn | kubeval -
java -jar target/uberjar/k8s-keycloak-standalone.jar myconfig.edn myauth.edn | kubectl apply -f -
```bash
java -jar target/uberjar/c4k-keycloak-standalone.jar valid-config.edn valid-auth.edn | kubeconform --kubernetes-version 1.19.0 --strict --skip Certificate -
java -jar target/uberjar/c4k-keycloak-standalone.jar valid-config.edn my-auth.edn | kubectl apply -f -
```

BIN
doc/tryItOut.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View file

@ -1,7 +0,0 @@
FROM node:lts-slim
RUN npm install -g keycloak
RUN mkdir /app
ENTRYPOINT ["keycloak"]
CMD [ "/app/config.edn" ]

View file

@ -1,26 +1,29 @@
{
"name": "k8s-keycloak",
"description": "Generate k8s yaml for a keycloak deployment.",
"name": "c4k-keycloak",
"description": "Generate c4k yaml for a keycloak deployment.",
"author": "meissa GmbH",
"version": "0.1.5-SNAPSHOT",
"homepage": "https://gitlab.com/domaindrivenarchitecture/k8s-keycloak#readme",
"repository": "https://www.npmjs.com/package/k8s-keycloak",
"version": "1.3.2-SNAPSHOT",
"homepage": "https://gitlab.com/domaindrivenarchitecture/c4k-keycloak#readme",
"repository": "https://www.npmjs.com/package/c4k-keycloak",
"license": "APACHE2",
"main": "k8s-keycloak.js",
"main": "c4k-keycloak.js",
"bin": {
"k8s-keycloak": "./k8s-keycloak.js"
"c4k-keycloak": "./c4k-keycloak.js"
},
"keywords": [
"cljs",
"keycloak",
"k8s",
"deplyoment",
"yaml"
"c4k",
"deployment",
"yaml",
"convention4kubernetes"
],
"bugs": {
"url": "https://gitlab.com/domaindrivenarchitecture/k8s-keycloak/issues"
"url": "https://gitlab.com/domaindrivenarchitecture/c4k-keycloak/issues"
},
"dependencies": {
"js-base64": "^3.6.1",
"js-yaml": "^4.0.0"
},
"devDependencies": {

View file

@ -1,44 +1,34 @@
(defproject org.domaindrivenarchitecture/k8s-keycloak "0.1.0-SNAPSHOT"
:description "keycloak k8s-installation package"
(defproject org.domaindrivenarchitecture/c4k-keycloak "1.3.2-SNAPSHOT"
:description "keycloak c4k-installation package"
:url "https://domaindrivenarchitecture.org"
:license {:name "Apache License, Version 2.0"
:url "https://www.apache.org/licenses/LICENSE-2.0.html"}
:dependencies [[org.clojure/clojure "1.10.3"]
[org.clojure/tools.reader "1.3.4"]
[aero "1.1.6"]
[orchestra "2021.01.01-1"]
[expound "0.8.9"]
[clj-commons/clj-yaml "0.7.106"]]
:dependencies [[org.clojure/clojure "1.11.4"]
[org.clojure/tools.reader "1.5.0"]
[org.domaindrivenarchitecture/c4k-common-clj "8.0.0"]]
:target-path "target/%s/"
:source-paths ["src/main/cljc"
"src/main/clj"]
:resource-paths ["src/main/resources"]
:repositories [["snapshots" :clojars]
["releases" :clojars]]
:deploy-repositories [["snapshots" :clojars]
["releases" :clojars]]
:deploy-repositories [["snapshots" {:sign-releases false :url "https://clojars.org/repo"}]
["releases" {:sign-releases false :url "https://clojars.org/repo"}]]
:profiles {:test {:test-paths ["src/test/cljc"]
:resource-paths ["src/test/resources"]
:dependencies [[dda/data-test "0.1.1"]]}
:dev {:plugins [[lein-shell "0.5.0"]]}
:uberjar {:aot :all
:main dda.k8s-keycloak.uberjar
:uberjar-name "k8s-keycloak-standalone.jar"
:dependencies [[org.clojure/tools.cli "1.0.206"]
[ch.qos.logback/logback-classic "1.3.0-alpha4"
:main dda.c4k-keycloak.uberjar
:uberjar-name "c4k-keycloak-standalone.jar"
:dependencies [[org.clojure/tools.cli "1.1.230"]
[ch.qos.logback/logback-classic "1.5.7"
:exclusions [com.sun.mail/javax.mail]]
[org.slf4j/jcl-over-slf4j "2.0.0-alpha1"]]}}
:release-tasks [["vcs" "assert-committed"]
[org.slf4j/jcl-over-slf4j "2.0.16"]
[com.github.clj-easy/graal-build-time "1.0.5"]]}}
:release-tasks [["test"]
["vcs" "assert-committed"]
["change" "version" "leiningen.release/bump-version" "release"]
["vcs" "commit"]
["vcs" "tag"]
["deploy"]
["change" "version" "leiningen.release/bump-version"]]
:aliases {"native" ["shell"
"native-image"
"--report-unsupported-elements-at-runtime"
"--initialize-at-build-time"
"-jar" "target/uberjar/k8s-keycloak-standalone.jar"
"-H:ResourceConfigurationFiles=graalvm-resource-config.json"
"-H:Log=registerResource"
"-H:Name=target/graalvm/${:name}"]})
["vcs" "tag" "--no-sign"]
["change" "version" "leiningen.release/bump-version"]])

View file

@ -3,77 +3,15 @@
<head>
<meta charset="utf-8" />
<title>k8s-mastodon-bot</title>
<title>c4k-keycloak</title>
<link href="https://domaindrivenarchitecture.org/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="https://domaindrivenarchitecture.org/css/fonts/fontawesome/fontawesome.css" rel="stylesheet" type="text/css" />
<link href="https://domaindrivenarchitecture.org/css/fonts/fontawesome/fontawesome.css" rel="stylesheet"
type="text/css" />
<link href="https://domaindrivenarchitecture.org/css/custom.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="container jumbotron">
<form class="needs-validation" id="form">
<label for="config" class="form-label">Your config.edn:</label>
<textarea name="config" id="config" class="form-control" rows="15">
{:transform [{:source {:source-type :twitter
;; optional, defaults to false
:include-replies? false
;; optional, defaults to false
:include-rts? false
;; Replace Twitter links by Nitter
:nitter-urls? false
;; accounts you wish to mirror
:accounts ["arstechnica" "WIRED"]}
:target {:target-type :mastodon
;; optional flag specifying wether the name of the account
;; will be appended in the post, defaults to false
:append-screen-name? false
;; optional visibility flag: direct, private, unlisted, public
;; defaults to public
:visibility "unlisted"
;; optional boolean to mark content as sensitive. Defaults to true.
:sensitive? true
;; optional boolean defaults to false
;; only sources containing media will be posted when set to true
:media-only? true
;; optional limit for the post length. Defaults to 300.
:max-post-length 300
;; optional signature for posts. Defaults to "not present".
:signature "#newsbot"}
}]
:auth {}}}
</textarea>
<div class="invalid-feedback"><pre id="config-validation"></pre></div>
<br><br>
<label for="auth" class="form-label">Your auth.edn:</label>
<textarea name="auth" id="auth" class="form-control" rows="15">
{:auth {;; add Twitter config to mirror Twitter accounts
:twitter {:consumer_key "XXXX"
:consumer_secret "XXXX"
:access_token_key "XXXX"
:access_token_secret "XXXX"}
:mastodon {:access_token "XXXX"
;; account number you see when you log in and go to your profile
;; e.g: https://mastodon.social/web/accounts/294795
:account-id "XXXX"
:api_url "https://botsin.space/api/v1/"}
:tumblr {:consumer_key "XXXX"
:consumer_secret "XXXX"
:token "XXXX"
:token_secret "XXXX"}}}
</textarea>
<div class="invalid-feedback"><pre id="auth-validation"></pre></div>
<br><br>
<button type="button" id="generate-button" class="btn btn-primary">
Generate k8s yaml
</button></form><br><br>
<div id="k8s-mastodon-bot-output">
<label for="output" class="form-label">Your k8s deployment.yaml:</label>
<textarea name="output" id="output" class="form-control" rows="15">
</textarea>
</div>
</div>
<div id="c4k-content"></div>
<script src="js/main.js"></script>
</body>

View file

@ -1,16 +1,15 @@
{:source-paths ["src/main/cljc"
"src/main/cljs"
"src/main/resources"
"src/test/cljc"]
:dependencies [[aero "1.1.6"]
[orchestra "2021.01.01-1"]
[expound "0.8.9"]]
:dev-http {8080 "public"}
"src/test/cljc"
"src/test/cljs"
"src/test/resources"]
:dependencies [[org.domaindrivenarchitecture/c4k-common-cljs "6.3.1"]
[hickory "0.7.1"]]
:builds {:frontend {:target :browser
:modules {:main {:init-fn dda.k8s-keycloak.browser/init}}
:modules {:main {:init-fn dda.c4k-keycloak.browser/init}}
:release {}
:compiler-options {:optimizations :advanced}}
:test {:target :node-test
:output-to "target/node-tests.js"
:autorun true
:repl-pprint true}}}

View file

@ -0,0 +1,17 @@
(ns dda.c4k-keycloak.uberjar
(:gen-class)
(:require
[dda.c4k-common.uberjar :as uberjar]
[dda.c4k-keycloak.core :as core]))
(set! *warn-on-reflection* true)
(defn -main [& cmd-args]
(uberjar/main-cm
"c4k-keycloak"
core/config?
core/auth?
core/config-defaults
core/config-objects
core/auth-objects
cmd-args))

View file

@ -1,56 +0,0 @@
(ns dda.k8s-keycloak.uberjar
(:gen-class)
(:require
[clojure.spec.alpha :as s]
[clojure.string :as cs]
[clojure.tools.reader.edn :as edn]
[expound.alpha :as expound]
[dda.k8s-keycloak.core :as core]))
(def usage
"usage:
k8s-keycloak {your configuraton file} {your authorization file}")
(s/def ::options (s/* #{"-h"}))
(s/def ::filename (s/and string?
#(not (cs/starts-with? % "-"))))
(s/def ::cmd-args (s/cat :options ::options
:args (s/?
(s/cat :config ::filename
:auth ::filename))))
(defn expound-config
[config]
(expound/expound ::core/config config))
(defn invalid-args-msg
[spec args]
(s/explain spec args)
(println (str "Bad commandline arguments\n" usage)))
(defn -main [& cmd-args]
(let [parsed-args-cmd (s/conform ::cmd-args cmd-args)]
(if (= ::s/invalid parsed-args-cmd)
(invalid-args-msg ::cmd-args cmd-args)
(let [{:keys [options args]} parsed-args-cmd
{:keys [config auth]} args]
(cond
(some #(= "-h" %) options)
(println usage)
:default
(let [config-str (slurp config)
auth-str (slurp auth)
config-edn (edn/read-string config-str)
auth-edn (edn/read-string auth-str)
config-valid? (s/valid? core/config? config-edn)
auth-valid? (s/valid? core/auth? auth-edn)]
(if (and config-valid? auth-valid?)
(println (core/generate config-edn auth-edn))
(do
(when (not config-valid?)
(println
(expound/expound-str core/config? config-edn {:print-specs? false})))
(when (not auth-valid?)
(println
(expound/expound-str core/auth? auth-edn {:print-specs? false})))))))))))

View file

@ -1,20 +0,0 @@
(ns dda.k8s-keycloak.yaml
(:require
[clojure.java.io :as io]
[clj-yaml.core :as yaml]
[clojure.walk]))
(defn cast-lazy-seq-to-vec
[lazy-seq]
(clojure.walk/postwalk #(if (instance? clojure.lang.LazySeq %)
(into [] %)
%) lazy-seq))
(defn load-resource [resource-name]
(slurp (io/resource resource-name)))
(defn from-string [input]
(cast-lazy-seq-to-vec (yaml/parse-string input)))
(defn to-string [edn]
(yaml/generate-string edn :dumper-options {:flow-style :block}))

View file

@ -0,0 +1,57 @@
(ns dda.c4k-keycloak.core
(:require
[clojure.spec.alpha :as s]
#?(:clj [orchestra.core :refer [defn-spec]]
:cljs [orchestra.core :refer-macros [defn-spec]])
[dda.c4k-common.common :as cm]
[dda.c4k-common.predicate :as cp]
[dda.c4k-common.monitoring :as mon]
[dda.c4k-common.yaml :as yaml]
[dda.c4k-common.postgres :as postgres]
[dda.c4k-keycloak.keycloak :as kc]
[dda.c4k-common.namespace :as ns]))
(def default-storage-class :local-path)
(def config-defaults {:issuer "staging",
:namespace "keycloak"
:postgres-image "postgres:14"
:postgres-size :2gb
:db-name "keycloak"
:pv-storage-size-gb 30
:pvc-storage-class-name default-storage-class})
(def config? (s/keys :req-un [::kc/fqdn]
:opt-un [::kc/issuer
::mon/mon-cfg
::kc/namespace]))
(def auth? (s/keys :req-un [::kc/keycloak-admin-user ::kc/keycloak-admin-password
::postgres/postgres-db-user ::postgres/postgres-db-password]
:opt-un [::mon/mon-auth]))
(defn-spec config-objects cp/map-or-seq?
[config config?]
(map yaml/to-string
(filter
#(not (nil? %))
(cm/concat-vec
(ns/generate config)
(postgres/generate-config config)
[(kc/generate-service config)
(kc/generate-deployment config)]
(kc/generate-ingress config)
(when (contains? config :mon-cfg)
(mon/generate-config))))))
(defn-spec auth-objects cp/map-or-seq?
[config config?
auth auth?]
(map yaml/to-string
(filter
#(not (nil? %))
(cm/concat-vec
(postgres/generate-auth config auth)
[(kc/generate-secret config auth)]
(when (and (contains? auth :mon-auth) (contains? config :mon-cfg))
(mon/generate-auth (:mon-cfg config) (:mon-auth auth)))))))

View file

@ -0,0 +1,64 @@
(ns dda.c4k-keycloak.keycloak
(:require
[clojure.spec.alpha :as s]
#?(:cljs [dda.c4k-common.macros :refer-macros [inline-resources]])
#?(:clj [orchestra.core :refer [defn-spec]]
:cljs [orchestra.core :refer-macros [defn-spec]])
[dda.c4k-common.yaml :as yaml]
[dda.c4k-common.common :as cm]
[dda.c4k-common.base64 :as b64]
[dda.c4k-common.ingress :as ing]
[dda.c4k-common.predicate :as cp]))
(s/def ::fqdn cp/fqdn-string?)
(s/def ::namespace string?)
(s/def ::issuer cp/letsencrypt-issuer?)
(s/def ::keycloak-admin-user cp/bash-env-string?)
(s/def ::keycloak-admin-password cp/bash-env-string?)
(def config? (s/keys :req-un [::fqdn]
:opt-un [::issuer
::namespace]))
(def auth? (s/keys :req-un [::keycloak-admin-user
::keycloak-admin-password]))
#?(:cljs
(defmethod yaml/load-resource :keycloak [resource-name]
(get (inline-resources "keycloak") resource-name)))
(defn-spec generate-ingress cp/map-or-seq?
[config config?]
(ing/generate-ingress-and-cert
(merge
{:service-name "keycloak"
:service-port 80
:fqdns [(:fqdn config)]}
config)))
(defn-spec generate-secret cp/map-or-seq?
[config config?
auth auth?]
(let [{:keys [namespace]} config
{:keys [keycloak-admin-user keycloak-admin-password]} auth]
(->
(yaml/load-as-edn "keycloak/secret.yaml")
(cm/replace-all-matching "NAMESPACE" namespace)
(cm/replace-key-value :keycloak-user (b64/encode keycloak-admin-user))
(cm/replace-key-value :keycloak-password (b64/encode keycloak-admin-password)))))
(defn-spec generate-service cp/map-or-seq?
[config config?]
(let [{:keys [namespace]} config]
(->
(yaml/load-as-edn "keycloak/service.yaml")
(cm/replace-all-matching "NAMESPACE" namespace))))
(defn-spec generate-deployment cp/map-or-seq?
[config config?]
(let [{:keys [fqdn namespace]} config]
(->
(yaml/load-as-edn "keycloak/deployment.yaml")
(cm/replace-all-matching "NAMESPACE" namespace)
(cm/replace-all-matching "FQDN" fqdn))))

View file

@ -1,104 +0,0 @@
(ns dda.k8s-keycloak.core
(:require
[clojure.string :as cs]
[clojure.spec.alpha :as s]
#?(:clj [orchestra.core :refer [defn-spec]]
:cljs [orchestra.core :refer-macros [defn-spec]])
[dda.k8s-keycloak.yaml :as yaml]
[clojure.walk]))
(defn bash-env-string?
[input]
(and (string? input)
(not (re-matches #".*['\"\$]+.*" input))))
(defn fqdn-string?
[input]
(and (string? input)
(not (nil? (re-matches #"(?=^.{4,253}\.?$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}\.?$)" input)))))
(s/def ::user-name bash-env-string?)
(s/def ::user-password string?)
(s/def ::fqdn fqdn-string?)
(s/def ::issuer #{:prod :staging})
(def config? (s/keys :req-un [::fqdn]
:opt-un [::issuer]))
(def auth? (s/keys :req-un [::user-name ::user-password]))
(defn replace-all-matching-values-by-new-value
[coll value-to-match value-to-replace]
(clojure.walk/postwalk #(if (and (= (type value-to-match) (type %))
(= value-to-match %))
value-to-replace
%) coll))
(defn generate-config [my-config my-auth]
(->
(yaml/from-string (yaml/load-resource "config.yaml"))
(assoc-in [:data :config.edn] (str my-config))
(assoc-in [:data :credentials.edn] (str my-auth))))
(defn generate-postgres-config []
(yaml/from-string (yaml/load-resource "postgres/postgres-config.yaml")))
(defn generate-deployment [my-auth]
(let [{:keys [user-name user-password]} my-auth]
(->
(yaml/from-string (yaml/load-resource "deployment.yaml"))
(assoc-in [:spec :template :spec :containers 0 :env 0 :value] user-name)
(assoc-in [:spec :template :spec :containers 0 :env 1 :value] user-password))))
(defn generate-postgres-deployment [my-auth]
(let [{:keys [postgres-user postgres-password postgres-db]} my-auth]
(->
(yaml/from-string (yaml/load-resource "postgres/postgres-deployment.yaml"))
(assoc-in [:spec :template :spec :containers 0 :env 0 :value] postgres-user)
(assoc-in [:spec :template :spec :containers 0 :env 1 :value] postgres-db)
(assoc-in [:spec :template :spec :containers 0 :env 2 :value] postgres-password))))
(defn generate-certificate [config]
(let [{:keys [fqdn issuer]
:or {issuer :staging}} config
letsencrypt-issuer (str "letsencrypt-" (name issuer) "-issuer")]
(->
(yaml/from-string (yaml/load-resource "certificate.yaml"))
(assoc-in [:spec :commonName] fqdn)
(assoc-in [:spec :dnsNames] [fqdn])
(assoc-in [:spec :issuerRef :name] letsencrypt-issuer))))
(defn generate-ingress [config]
(let [{:keys [fqdn issuer]
:or {issuer :staging}} config
letsencrypt-issuer (str "letsencrypt-" (name issuer) "-issuer")]
(->
(yaml/from-string (yaml/load-resource "ingress.yaml"))
(assoc-in [:metadata :annotations :cert-manager.io/cluster-issuer] letsencrypt-issuer)
(replace-all-matching-values-by-new-value "fqdn" fqdn))))
(defn generate-service []
(yaml/from-string (yaml/load-resource "service.yaml")))
(defn generate-postgres-service []
(yaml/from-string (yaml/load-resource "postgres/postgres-service.yaml")))
(defn-spec generate any?
[my-config config?
my-auth auth?]
(cs/join "\n"
[(yaml/to-string (generate-postgres-config))
"---"
(yaml/to-string (generate-postgres-service))
"---"
(yaml/to-string (generate-postgres-deployment my-auth))
"---"
(yaml/to-string (generate-config my-config my-auth))
"---"
(yaml/to-string (generate-certificate my-config))
"---"
(yaml/to-string (generate-ingress my-config))
"---"
(yaml/to-string (generate-service))
"---"
(yaml/to-string (generate-deployment my-auth))]))

View file

@ -0,0 +1,88 @@
(ns dda.c4k-keycloak.browser
(:require
[clojure.tools.reader.edn :as edn]
[dda.c4k-common.monitoring :as mon]
[dda.c4k-common.common :as cm]
[dda.c4k-common.browser :as br]
[dda.c4k-keycloak.core :as core]
[dda.c4k-keycloak.keycloak :as kc]))
(defn generate-content []
(cm/concat-vec
[(assoc
(br/generate-needs-validation) :content
(cm/concat-vec
(br/generate-group
"domain"
(cm/concat-vec
(br/generate-input-field "fqdn" "Your fqdn:" "keycloak.prod.meissa.de")
(br/generate-input-field "issuer" "(Optional) Your issuer prod/staging:" "")
(br/generate-input-field "mon-cluster-name" "(Optional) monitoring cluster name:" "keycloak")
(br/generate-input-field "mon-cluster-stage" "(Optional) monitoring cluster stage:" "test")
(br/generate-input-field "mon-cloud-url" "(Optional) grafana cloud url:" "https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push")))
(br/generate-group
"credentials"
(br/generate-text-area "auth" "Your auth.edn:"
"{:keycloak-admin-user \"keycloak\"
:keycloak-admin-password \"adminpassword\"
:postgres-db-user \"keycloakuser\"
:postgres-db-password \"testdbpassword\"
:mon-auth {:grafana-cloud-user \"your-user-id\"
:grafana-cloud-password \"your-cloud-password\"}}"
"5"))
[(br/generate-br)]
(br/generate-button "generate-button" "Generate c4k yaml")))]
(br/generate-output "c4k-keycloak-output" "Your c4k deployment.yaml:" "25")))
(defn generate-content-div
[]
{:type :element
:tag :div
:content
(generate-content)})
(defn config-from-document []
(let [issuer (br/get-content-from-element "issuer" :optional true)
mon-cluster-name (br/get-content-from-element "mon-cluster-name" :optional true)
mon-cluster-stage (br/get-content-from-element "mon-cluster-stage" :optional true :deserializer keyword)
mon-cloud-url (br/get-content-from-element "mon-cloud-url" :optional true)]
(merge
{:fqdn (br/get-content-from-element "fqdn")}
(when (some? issuer)
{:issuer issuer})
(when (some? mon-cluster-name)
{:mon-cfg {:cluster-name mon-cluster-name
:cluster-stage (keyword mon-cluster-stage)
:grafana-cloud-url mon-cloud-url}}))))
(defn validate-all! []
(br/validate! "fqdn" ::kc/fqdn)
(br/validate! "issuer" ::kc/issuer :optional true)
(br/validate! "mon-cluster-name" ::mon/cluster-name :optional true)
(br/validate! "mon-cluster-stage" ::mon/cluster-stage :optional true :deserializer keyword)
(br/validate! "mon-cloud-url" ::mon/grafana-cloud-url :optional true)
(br/validate! "auth" core/auth? :deserializer edn/read-string)
(br/set-validated!))
(defn add-validate-listener [name]
(-> (br/get-element-by-id name)
(.addEventListener "blur" #(do (validate-all!)))))
(defn init []
(br/append-hickory (generate-content-div))
(-> js/document
(.getElementById "generate-button")
(.addEventListener "click"
#(do (validate-all!)
(-> (cm/generate-common
(config-from-document)
(br/get-content-from-element "auth" :deserializer edn/read-string)
{}
core/k8s-objects)
(br/set-output!)))))
(add-validate-listener "fqdn")
(add-validate-listener "issuer")
(add-validate-listener "mon-cluster-name")
(add-validate-listener "mon-cluster-stage")
(add-validate-listener "mon-cloud-url")
(add-validate-listener "auth"))

View file

@ -1,99 +0,0 @@
(ns dda.k8s-keycloak.browser
(:require
[clojure.spec.alpha :as s]
[clojure.tools.reader.edn :as edn]
[expound.alpha :as expound]
[dda.k8s-keycloak.core :as core]))
(defn print-debug [sth]
(print "debug " sth)
sth)
(defn config []
(-> js/document
(.getElementById "config")))
(defn auth []
(-> js/document
(.getElementById "auth")))
(defn form []
(-> js/document
(.getElementById "form")))
(defn config-from-document []
(-> (config)
(.-value)))
(defn auth-from-document []
(-> (auth)
(.-value)))
(defn set-output!
[input]
(-> js/document
(.getElementById "output")
(.-value)
(set! input)))
(defn set-config-validation-result!
[validation-result]
(-> js/document
(.getElementById "config-validation")
(.-innerHTML)
(set! validation-result))
(-> (config)
(.setCustomValidity validation-result))
validation-result)
(defn validate-config! []
(let [config-str (config-from-document)
config-map (edn/read-string config-str)]
(if (s/valid? core/config? config-map)
(set-config-validation-result! "")
(set-config-validation-result!
(expound/expound-str core/config? config-map {:print-specs? false})))))
(defn set-validated! []
(-> (form)
(.-classList)
(.add "was-validated")))
(defn set-auth-validation-result!
[validation-result]
(-> js/document
(.getElementById "auth-validation")
(.-innerHTML)
(set! validation-result))
(-> (auth)
(.setCustomValidity validation-result))
validation-result)
(defn validate-auth! []
(let [auth-str (auth-from-document)
auth-map (edn/read-string auth-str)]
(print-debug (s/valid? core/auth? auth-map))
(if (s/valid? core/auth? auth-map)
(set-auth-validation-result! "")
(set-auth-validation-result!
(expound/expound-str core/auth? auth-map {:print-specs? false})))))
(defn init []
(-> js/document
(.getElementById "generate-button")
(.addEventListener "click"
#(do (validate-config!)
(validate-auth!)
(set-validated!)
(-> (core/generate (config-from-document) (auth-from-document))
(set-output!)))))
(-> (config)
(.addEventListener "blur"
#(do (validate-config!)
(validate-auth!)
(set-validated!))))
(-> (auth)
(.addEventListener "blur"
#(do (validate-config!)
(validate-auth!)
(set-validated!)))))

View file

@ -1,24 +0,0 @@
(ns dda.k8s-keycloak.yaml
(:require
["js-yaml" :as yaml]
[shadow.resource :as rc]
))
(def config (rc/inline "config.yaml"))
(def cron (rc/inline "cron.yaml"))
(def deployment (rc/inline "deployment.yaml"))
(defn load-resource [resource-name]
(case resource-name
"config.yaml" config
"cron.yaml" cron
"deployment.yaml" deployment))
(defn from-string [input]
(js->clj (yaml/load input)
:keywordize-keys true))
(defn to-string [edn]
(yaml/dump (clj->js edn)))

View file

@ -1,13 +0,0 @@
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: keycloak-cert
namespace: default
spec:
secretName: keycloak-secret
commonName: fqdn
dnsNames:
- fqdn
issuerRef:
name: letsencrypt-staging-issuer
kind: ClusterIssuer

View file

@ -1,11 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: keycloak
labels:
app.kubernetes.io/name: k8s-keycloak
data:
config.edn: |
some-config-value
credentials.edn: |
some-credentials-value

View file

@ -1,34 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
namespace: default
labels:
app: keycloak
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:13.0.0
env:
- name: KEYCLOAK_USER
value: "admin"
- name: KEYCLOAK_PASSWORD
value: "admin"
- name: PROXY_ADDRESS_FORWARDING
value: "true"
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /auth/realms/master
port: 8080

View file

@ -1,25 +0,0 @@
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-cloud
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging-issuer
nginx.ingress.kubernetes.io/proxy-body-size: "256m"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/proxy-connect-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
namespace: default
spec:
tls:
- hosts:
- fqdn
secretName: keycloak-secret
rules:
- host: fqdn
http:
paths:
- backend:
serviceName: keycloak
servicePort: 8080

View file

@ -0,0 +1,80 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
namespace: NAMESPACE
labels:
app: keycloak
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:20.0.3
imagePullPolicy: IfNotPresent
args:
- start
volumeMounts:
- name: keycloak-cert
mountPath: /etc/certs
readOnly: true
env:
- name: KC_HTTPS_CERTIFICATE_FILE
value: /etc/certs/tls.crt
- name: KC_HTTPS_CERTIFICATE_KEY_FILE
value: /etc/certs/tls.key
- name: KC_HOSTNAME
value: FQDN
- name: KC_PROXY
value: edge
- name: DB_VENDOR
value: POSTGRES
- name: DB_ADDR
value: postgresql-service
- name: DB_SCHEMA
value: public
- name: DB_DATABASE
valueFrom:
configMapKeyRef:
name: postgres-config
key: postgres-db
- name: DB_USER
valueFrom:
secretKeyRef:
name: postgres-secret
key: postgres-user
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: postgres-password
- name: KEYCLOAK_ADMIN
valueFrom:
secretKeyRef:
name: keycloak-secret
key: keycloak-user
- name: KEYCLOAK_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: keycloak-secret
key: keycloak-password
ports:
- name: http
containerPort: 8080
volumes:
- name: keycloak-cert
secret:
secretName: keycloak
items:
- key: tls.crt
path: tls.crt
- key: tls.key
path: tls.key

View file

@ -0,0 +1,9 @@
apiVersion: v1
kind: Secret
metadata:
name: keycloak-secret
namespace: NAMESPACE
type: Opaque
data:
keycloak-user: admin
keycloak-password: admin

View file

@ -3,12 +3,12 @@ kind: Service
metadata:
name: keycloak
labels:
app: keycloak
service: keycloak
namespace: NAMESPACE
spec:
ports:
- name: http
port: 8080
- name: "http"
port: 80
targetPort: 8080
selector:
app: keycloak
type: LoadBalancer

View file

@ -1,10 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: postgres-config
labels:
app: postgres
data:
postgresql.conf: |
max_connections = 1000
shared_buffers = 512MB

View file

@ -1,38 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgresql
spec:
selector:
matchLabels:
app: postgresql
strategy:
type: Recreate
template:
metadata:
labels:
app: postgresql
spec:
containers:
- image: postgres
name: postgresql
env:
- name: POSTGRES_USER
value: "psql-user"
- name: POSTGRES_DB
value: "psql-db"
- name: POSTGRES_PASSWORD
value: "psql-pw"
ports:
- containerPort: 5432
name: postgresql
cmd:
volumeMounts:
- name: postgres-config-volume
mountPath: /etc/postgresql/postgresql.conf
subPath: postgresql.conf
readOnly: true
volumes:
- name: postgres-config-volume
configMap:
name: postgres-config

View file

@ -1,9 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: postgresql-service
spec:
selector:
app: postgresql
ports:
- port: 5432

View file

@ -0,0 +1,23 @@
(ns dda.c4k-keycloak.core-test
(:require
#?(:clj [clojure.test :refer [deftest is are testing run-tests]]
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
#?(:cljs [shadow.resource :as rc])
[clojure.spec.alpha :as s]
[dda.c4k-common.yaml :as yaml]
[dda.c4k-keycloak.core :as cut]))
#?(:cljs
(defmethod yaml/load-resource :keycloak-test [resource-name]
(case resource-name
"keycloak-test/valid-auth.yaml" (rc/inline "keycloak-test/valid-auth.yaml")
"keycloak-test/valid-config.yaml" (rc/inline "keycloak-test/valid-config.yaml")
"keycloak-test/invalid-config.yaml" (rc/inline "keycloak-test/invalid-config.yaml")
"keycloak-test/invalid-auth.yaml" (rc/inline "keycloak-test/invalid-auth.yaml")
(throw (js/Error. "Undefined Resource!")))))
(deftest validate-valid-resources
(is (s/valid? cut/config? (yaml/load-as-edn "keycloak-test/valid-config.yaml")))
(is (s/valid? cut/auth? (yaml/load-as-edn "keycloak-test/valid-auth.yaml")))
(is (not (s/valid? cut/config? (yaml/load-as-edn "keycloak-test/invalid-config.yaml"))))
(is (not (s/valid? cut/auth? (yaml/load-as-edn "keycloak-test/invalid-auth.yaml")))))

View file

@ -0,0 +1,78 @@
(ns dda.c4k-keycloak.keycloak-test
(:require
#?(:clj [clojure.test :refer [deftest is are testing run-tests]]
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
[clojure.spec.test.alpha :as st]
[dda.c4k-keycloak.keycloak :as cut]))
(st/instrument)
(deftest should-generate-secret
(is (= {:apiVersion "v1"
:kind "Secret"
:metadata {:name "keycloak-secret", :namespace "keycloak"}
:type "Opaque"
:data
{:keycloak-user "dXNlcg=="
:keycloak-password "cGFzc3dvcmQ="}}
(cut/generate-secret {:namespace "keycloak" :fqdn "test.de"} {:keycloak-admin-user "user" :keycloak-admin-password "password"}))))
(deftest should-generate-deployment
(is (= {:apiVersion "apps/v1",
:kind "Deployment",
:metadata
{:name "keycloak", :namespace "keycloak", :labels {:app "keycloak"}},
:spec
{:replicas 1,
:selector {:matchLabels {:app "keycloak"}},
:template
{:metadata {:labels {:app "keycloak"}},
:spec
{:containers
[{:name "keycloak",
:image "quay.io/keycloak/keycloak:20.0.3",
:imagePullPolicy "IfNotPresent",
:args ["start"],
:volumeMounts
[{:name "keycloak-cert",
:mountPath "/etc/certs",
:readOnly true}],
:env
[{:name "KC_HTTPS_CERTIFICATE_FILE",
:value "/etc/certs/tls.crt"}
{:name "KC_HTTPS_CERTIFICATE_KEY_FILE",
:value "/etc/certs/tls.key"}
{:name "KC_HOSTNAME", :value "test.de"}
{:name "KC_PROXY", :value "edge"}
{:name "DB_VENDOR", :value "POSTGRES"}
{:name "DB_ADDR", :value "postgresql-service"}
{:name "DB_SCHEMA", :value "public"}
{:name "DB_DATABASE",
:valueFrom
{:configMapKeyRef
{:name "postgres-config", :key "postgres-db"}}}
{:name "DB_USER",
:valueFrom
{:secretKeyRef
{:name "postgres-secret", :key "postgres-user"}}}
{:name "DB_PASSWORD",
:valueFrom
{:secretKeyRef
{:name "postgres-secret", :key "postgres-password"}}}
{:name "KEYCLOAK_ADMIN",
:valueFrom
{:secretKeyRef
{:name "keycloak-secret", :key "keycloak-user"}}}
{:name "KEYCLOAK_ADMIN_PASSWORD",
:valueFrom
{:secretKeyRef
{:name "keycloak-secret", :key "keycloak-password"}}}],
:ports [{:name "http", :containerPort 8080}]}],
:volumes
[{:name "keycloak-cert",
:secret
{:secretName "keycloak",
:items
[{:key "tls.crt", :path "tls.crt"}
{:key "tls.key", :path "tls.key"}]}}]}}}}
(cut/generate-deployment {:fqdn "test.de" :namespace "keycloak"}))))

View file

@ -1,112 +0,0 @@
(ns dda.k8s-keycloak.core-test
(:require
#?(:clj [clojure.test :refer [deftest is are testing run-tests]]
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
[dda.k8s-keycloak.core :as cut]
[dda.k8s-keycloak.yaml :as yaml]))
(deftest should-generate-yaml
(is (= {:apiVersion "v1", :kind "ConfigMap"
:metadata {:name "keycloak",
:labels {:app.kubernetes.io/name "k8s-keycloak"}},
:data {:config.edn "some-config-value\n",
:credentials.edn "some-credentials-value\n"}}
(cut/generate-config "some-config-value\n" "some-credentials-value\n"))))
(deftest should-generate-certificate
(is (= {:apiVersion "cert-manager.io/v1alpha2"
:kind "Certificate"
:metadata {:name "keycloak-cert", :namespace "default"}
:spec
{:secretName "keycloak-secret"
:commonName "test.de"
:dnsNames ["test.de"]
:issuerRef {:name "letsencrypt-prod-issuer", :kind "ClusterIssuer"}}}
(cut/generate-certificate {:fqdn "test.de" :issuer :prod} ))))
(deftest should-generate-ingress-yaml-with-default-issuer
(is (= {:apiVersion "networking.k8s.io/v1beta1"
:kind "Ingress"
:metadata
{:name "ingress-cloud"
:annotations
{:cert-manager.io/cluster-issuer "letsencrypt-staging-issuer"
:nginx.ingress.kubernetes.io/proxy-body-size "256m"
:nginx.ingress.kubernetes.io/ssl-redirect "true"
:nginx.ingress.kubernetes.io/rewrite-target "/"
:nginx.ingress.kubernetes.io/proxy-connect-timeout "300"
:nginx.ingress.kubernetes.io/proxy-send-timeout "300"
:nginx.ingress.kubernetes.io/proxy-read-timeout "300"}
:namespace "default"}
:spec
{:tls [{:hosts ["test.de"] :secretName "keycloak-secret"}]
:rules [{:host "test.de", :http {:paths [{:backend {:serviceName "keycloak", :servicePort 8080}}]}}]}}
(cut/generate-ingress {:fqdn "test.de"}))))
(deftest should-generate-ingress-yaml-with-prod-issuer
(is (= {:apiVersion "networking.k8s.io/v1beta1"
:kind "Ingress"
:metadata
{:name "ingress-cloud"
:annotations
{:cert-manager.io/cluster-issuer "letsencrypt-prod-issuer"
:nginx.ingress.kubernetes.io/proxy-body-size "256m"
:nginx.ingress.kubernetes.io/ssl-redirect "true"
:nginx.ingress.kubernetes.io/rewrite-target "/"
:nginx.ingress.kubernetes.io/proxy-connect-timeout "300"
:nginx.ingress.kubernetes.io/proxy-send-timeout "300"
:nginx.ingress.kubernetes.io/proxy-read-timeout "300"}
:namespace "default"}
:spec
{:tls [{:hosts ["test.de"], :secretName "keycloak-secret"}]
:rules '({:host "test.de", :http {:paths [{:backend {:serviceName "keycloak", :servicePort 8080}}]}})}}
(cut/generate-ingress {:fqdn "test.de"
:issuer :prod}))))
(deftest should-generate-deployment
(is (= {:apiVersion "apps/v1"
:kind "Deployment"
:metadata {:name "keycloak", :namespace "default", :labels {:app "keycloak"}}
:spec
{:replicas 1
:selector {:matchLabels {:app "keycloak"}}
:template
{:metadata {:labels {:app "keycloak"}}
:spec
{:containers
[{:name "keycloak"
:image "quay.io/keycloak/keycloak:13.0.0"
:env
[{:name "KEYCLOAK_USER", :value "testuser"}
{:name "KEYCLOAK_PASSWORD", :value "test1234"}
{:name "PROXY_ADDRESS_FORWARDING", :value "true"}]
:ports [{:name "http", :containerPort 8080}]
:readinessProbe {:httpGet {:path "/auth/realms/master", :port 8080}}}]}}}}
(cut/generate-deployment {:user-name "testuser" :user-password "test1234"}))))
(deftest should-generate-postgres-deployment
(is (= {:apiVersion "apps/v1"
:kind "Deployment"
:metadata {:name "postgresql"}
:spec
{:selector {:matchLabels {:app "postgresql"}}
:strategy {:type "Recreate"}
:template
{:metadata {:labels {:app "postgresql"}}
:spec
{:containers
[{:image "postgres"
:name "postgresql"
:env
[{:name "POSTGRES_USER", :value "psqluser"}
{:name "POSTGRES_DB", :value "keycloak"}
{:name "POSTGRES_PASSWORD", :value "test1234"}]
:ports [{:containerPort 5432, :name "postgresql"}]
:cmd nil
:volumeMounts
[{:name "postgres-config-volume"
:mountPath "/etc/postgresql/postgresql.conf"
:subPath "postgresql.conf"
:readOnly true}]}]
:volumes [{:name "postgres-config-volume", :configMap {:name "postgres-config"}}]}}}}
(cut/generate-postgres-deployment {:postgres-user "psqluser" :postgres-db "keycloak" :postgres-password "test1234"}))))

View file

@ -1,22 +0,0 @@
(ns dda.k8s-keycloak.yaml-test
(:require
#?(:clj [clojure.test :refer [deftest is are testing run-tests]]
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
[dda.k8s-keycloak.yaml :as cut]))
(deftest should-parse-yaml-string
(is (= {:hallo "welt"}
(cut/from-string "hallo: welt"))))
(deftest should-generate-yaml-string
(is (= "hallo: welt
"
(cut/to-string {:hallo "welt"}))))
(deftest should-convert-config-yml-to-map
(is (= {:apiVersion "v1", :kind "ConfigMap"
:metadata {:name "keycloak",
:labels {:app.kubernetes.io/name "k8s-keycloak"}},
:data {:config.edn "some-config-value\n",
:credentials.edn "some-credentials-value\n"}}
(cut/from-string (cut/load-resource "config.yaml")))))

View file

@ -1,31 +0,0 @@
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-cloud
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging-issuer
nginx.ingress.kubernetes.io/proxy-body-size: "256m"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/proxy-connect-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
namespace: default
spec:
tls:
- hosts:
- fqdn
secretName: keycloak-secret
rules:
- host: fqdn
http:
paths:
- backend:
serviceName: keycloak
servicePort: 8080
- host: fqdn
http:
paths:
- backend:
serviceName: another_keycloak
servicePort: 8081

View file

@ -1 +0,0 @@
{:xx {}}

View file

@ -1,27 +0,0 @@
{:transform [{:source {:source-type :twitter
;; optional, defaults to false
:include-replies? false
;; optional, defaults to false
:include-rts? false
;; Replace Twitter links by Nitter
:nitter-urls? 42
;; accounts you wish to mirror
:accounts ["arstechnica" "WIRED"]}
:target {:target-type :mastodon
;; optional flag specifying wether the name of the account
;; will be appended in the post, defaults to false
:append-screen-name? false
;; optional visibility flag: direct, private, unlisted, public
;; defaults to public
:visibility "unlisted"
;; optional boolean to mark content as sensitive. Defaults to true.
:sensitive? true
;; optional boolean defaults to false
;; only sources containing media will be posted when set to true
:media-only? true
;; optional limit for the post length. Defaults to 300.
:max-post-length 300
;; optional signature for posts. Defaults to "not present".
:signature "#newsbot"}
}]
:auth {}}

View file

@ -0,0 +1,7 @@
keycloak-admin-user: "testuser"
keycloak-admin-password: "testpassword"
postgres-db-user: "keycloakuser"
postgres-db-password: "testdbpassword"
mon-auth:
grafana-clod-user: "user"
grafana-cloud-password: "password"

View file

@ -0,0 +1,6 @@
fqdn: "keycloak.test.meissa-gmbh.de"
issuer: "staging"
mon-cfg:
grafana-clod-url: "url-for-your-prom-remote-write-endpoint"
cluster-nam: "keycloak"
cluster-stage: "none"

View file

@ -0,0 +1,7 @@
keycloak-admin-user: "testuser"
keycloak-admin-password: "testpassword"
postgres-db-user: "keycloakuser"
postgres-db-password: "testdbpassword"
mon-auth:
grafana-cloud-user: "user"
grafana-cloud-password: "password"

View file

@ -0,0 +1,6 @@
fqdn: "keycloak.test.meissa-gmbh.de"
issuer: "staging"
mon-cfg:
grafana-cloud-url: "url-for-your-prom-remote-write-endpoint"
cluster-name: "keycloak"
cluster-stage: "test"

View file

@ -1 +0,0 @@
{:auth {}}

View file

@ -1,27 +0,0 @@
{:transform [{:source {:source-type :twitter
;; optional, defaults to false
:include-replies? false
;; optional, defaults to false
:include-rts? false
;; Replace Twitter links by Nitter
:nitter-urls? false
;; accounts you wish to mirror
:accounts ["arstechnica" "WIRED"]}
:target {:target-type :mastodon
;; optional flag specifying wether the name of the account
;; will be appended in the post, defaults to false
:append-screen-name? false
;; optional visibility flag: direct, private, unlisted, public
;; defaults to public
:visibility "unlisted"
;; optional boolean to mark content as sensitive. Defaults to true.
:sensitive? true
;; optional boolean defaults to false
;; only sources containing media will be posted when set to true
:media-only? true
;; optional limit for the post length. Defaults to 300.
:max-post-length 300
;; optional signature for posts. Defaults to "not present".
:signature "#newsbot"}
}]
:auth {}}

View file

@ -1,2 +0,0 @@
{:user-name "testuser"
:user-password "test1234"}

View file

@ -1 +0,0 @@
{:fqdn "keycloak.test.meissa-gmbh.de"}