parent
ee05f9cf24
commit
1252a4b491
@ -0,0 +1,28 @@ |
||||
.clj-kondo/ |
||||
.lsp/ |
||||
|
||||
# pybuilder |
||||
.pybuilder/ |
||||
__pycache__/ |
||||
|
||||
# lein |
||||
target/ |
||||
.lein-repl-history |
||||
.lein-failures |
||||
pom.* |
||||
|
||||
# cljs |
||||
.shadow-cljs |
||||
.nrepl-* |
||||
package-lock.json |
||||
node_modules/ |
||||
public/js/ |
||||
|
||||
# ide |
||||
.calva |
||||
|
||||
*.iml |
||||
.idea/ |
||||
|
||||
auth.edn |
||||
config.edn |
@ -0,0 +1,116 @@ |
||||
stages: |
||||
- build_and_test |
||||
- package |
||||
- security |
||||
- upload |
||||
- image |
||||
|
||||
services: |
||||
- docker:19.03.12-dind |
||||
|
||||
.cljs-job: &cljs |
||||
image: domaindrivenarchitecture/shadow-cljs |
||||
cache: |
||||
key: ${CI_COMMIT_REF_SLUG} |
||||
paths: |
||||
- node_modules/ |
||||
- .shadow-cljs/ |
||||
- .m2 |
||||
before_script: |
||||
- echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc |
||||
- npm install |
||||
|
||||
.clj-uploadjob: &clj |
||||
image: domaindrivenarchitecture/lein |
||||
cache: |
||||
key: ${CI_COMMIT_REF_SLUG} |
||||
paths: |
||||
- .m2 |
||||
before_script: |
||||
- 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 |
||||
- node target/node-tests.js |
||||
|
||||
test-clj: |
||||
<<: *clj |
||||
stage: build_and_test |
||||
script: |
||||
- lein test |
||||
|
||||
test-schema: |
||||
<<: *clj |
||||
stage: build_and_test |
||||
script: |
||||
- lein uberjar |
||||
- java -jar target/uberjar/c4k-website-standalone.jar valid-config.edn valid-auth.edn | kubeconform --kubernetes-version 1.19.0 --strict --skip Certificate - |
||||
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 |
||||
artifacts: |
||||
paths: |
||||
- target/frontend-build/build-report.html |
||||
|
||||
package-frontend: |
||||
<<: *cljs |
||||
stage: package |
||||
script: |
||||
- mkdir -p target/frontend-build |
||||
- shadow-cljs release frontend |
||||
- cp public/js/main.js target/frontend-build/c4k-website.js |
||||
- sha256sum target/frontend-build/c4k-website.js > target/frontend-build/c4k-website.js.sha256 |
||||
- sha512sum target/frontend-build/c4k-website.js > target/frontend-build/c4k-website.js.sha512 |
||||
artifacts: |
||||
paths: |
||||
- target/frontend-build |
||||
|
||||
package-uberjar: |
||||
<<: *clj |
||||
stage: package |
||||
script: |
||||
- lein uberjar |
||||
- sha256sum target/uberjar/c4k-website-standalone.jar > target/uberjar/c4k-website-standalone.jar.sha256 |
||||
- sha512sum target/uberjar/c4k-website-standalone.jar > target/uberjar/c4k-website-standalone.jar.sha512 |
||||
artifacts: |
||||
paths: |
||||
- target/uberjar |
||||
|
||||
upload-clj-release: |
||||
<<: *clj |
||||
stage: upload |
||||
rules: |
||||
- if: '$CI_COMMIT_TAG != null' |
||||
script: |
||||
- lein deploy |
||||
|
||||
release: |
||||
image: registry.gitlab.com/gitlab-org/release-cli:latest |
||||
stage: upload |
||||
rules: |
||||
- if: '$CI_COMMIT_TAG != null' |
||||
artifacts: |
||||
paths: |
||||
- target/uberjar |
||||
- target/frontend-build |
||||
script: |
||||
- apk --no-cache add curl |
||||
- | |
||||
release-cli create --name "Release $CI_COMMIT_TAG" --tag-name $CI_COMMIT_TAG \ |
||||
--assets-link "{\"name\":\"c4k-website-standalone.jar\",\"url\":\"https://gitlab.com/domaindrivenarchitecture/c4k-website/-/jobs/${CI_JOB_ID}/artifacts/file/target/uberjar/c4k-website-standalone.jar\"}" \ |
||||
--assets-link "{\"name\":\"c4k-website-standalone.jar.sha256\",\"url\":\"https://gitlab.com/domaindrivenarchitecture/c4k-website/-/jobs/${CI_JOB_ID}/artifacts/file/target/uberjar/c4k-website-standalone.jar.sha256\"}" \ |
||||
--assets-link "{\"name\":\"c4k-website-standalone.jar.sha512\",\"url\":\"https://gitlab.com/domaindrivenarchitecture/c4k-website/-/jobs/${CI_JOB_ID}/artifacts/file/target/uberjar/c4k-website-standalone.jar.sha512\"}" \ |
||||
--assets-link "{\"name\":\"c4k-website.js\",\"url\":\"https://gitlab.com/domaindrivenarchitecture/c4k-website/-/jobs/${CI_JOB_ID}/artifacts/file/target/frontend-build/c4k-website.js\"}" \ |
||||
--assets-link "{\"name\":\"c4k-website.js.sha256\",\"url\":\"https://gitlab.com/domaindrivenarchitecture/c4k-website/-/jobs/${CI_JOB_ID}/artifacts/file/target/frontend-build/c4k-website.js.sha256\"}" \ |
||||
--assets-link "{\"name\":\"c4k-website.js.sha512\",\"url\":\"https://gitlab.com/domaindrivenarchitecture/c4k-website/-/jobs/${CI_JOB_ID}/artifacts/file/target/frontend-build/c4k-website.js.sha512\"}" \ |
@ -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 [yyyy] [name of copyright owner] |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
@ -0,0 +1,14 @@ |
||||
# stable release (should be done from master) |
||||
|
||||
``` |
||||
#adjust [version] |
||||
vi package.json |
||||
|
||||
lein release |
||||
git push --follow-tags |
||||
|
||||
# bump version - increase version and add -SNAPSHOT |
||||
vi package.json |
||||
git commit -am "version bump" |
||||
git push |
||||
``` |
After Width: | Height: | Size: 99 KiB |
@ -0,0 +1,33 @@ |
||||
{ |
||||
"name": "c4k-website", |
||||
"description": "Generate c4k yaml for a website deployment.", |
||||
"author": "meissa GmbH", |
||||
"version": "0.1.1-SNAPSHOT", |
||||
"homepage": "https://gitlab.com/domaindrivenarchitecture/c4k-website#readme", |
||||
"repository": "https://www.npmjs.com/package/c4k-website", |
||||
"license": "APACHE2", |
||||
"main": "c4k-website.js", |
||||
"bin": { |
||||
"c4k-website": "./c4k-website.js" |
||||
}, |
||||
"keywords": [ |
||||
"cljs", |
||||
"website", |
||||
"k8s", |
||||
"c4k", |
||||
"deployment", |
||||
"yaml", |
||||
"convention4kubernetes" |
||||
], |
||||
"bugs": { |
||||
"url": "https://gitlab.com/domaindrivenarchitecture/c4k-website/issues" |
||||
}, |
||||
"dependencies": { |
||||
"js-base64": "^3.6.1", |
||||
"js-yaml": "^4.0.0" |
||||
}, |
||||
"devDependencies": { |
||||
"shadow-cljs": "^2.11.18", |
||||
"source-map-support": "^0.5.19" |
||||
} |
||||
} |
@ -0,0 +1,46 @@ |
||||
(defproject org.domaindrivenarchitecture/c4k-website "0.1.1-SNAPSHOT" |
||||
:description "website 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.6"] |
||||
[org.domaindrivenarchitecture/c4k-common-clj "3.0.1"] |
||||
[hickory "0.7.1"]] |
||||
: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" {: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.c4k-website.uberjar |
||||
:uberjar-name "c4k-website-standalone.jar" |
||||
:dependencies [[org.clojure/tools.cli "1.0.206"] |
||||
[ch.qos.logback/logback-classic "1.3.0-alpha4" |
||||
:exclusions [com.sun.mail/javax.mail]] |
||||
[org.slf4j/jcl-over-slf4j "2.0.0-alpha1"]]}} |
||||
:release-tasks [["test"] |
||||
["vcs" "assert-committed"] |
||||
["change" "version" "leiningen.release/bump-version" "release"] |
||||
["vcs" "commit"] |
||||
["vcs" "tag" "v" "--no-sign"] |
||||
["change" "version" "leiningen.release/bump-version"]] |
||||
:aliases {"native" ["shell" |
||||
"native-image" |
||||
"--report-unsupported-elements-at-runtime" |
||||
"--initialize-at-build-time" |
||||
"-jar" "target/uberjar/c4k-website-standalone.jar" |
||||
"-H:ResourceConfigurationFiles=graalvm-resource-config.json" |
||||
"-H:Log=registerResource" |
||||
"-H:Name=target/graalvm/${:name}"] |
||||
"inst" ["shell" |
||||
"sh" |
||||
"-c" |
||||
"lein uberjar && sudo install -m=755 target/uberjar/c4k-website-standalone.jar /usr/local/bin/c4k-website-standalone.jar"]}) |
@ -0,0 +1,17 @@ |
||||
<!doctype html> |
||||
<html> |
||||
|
||||
<head> |
||||
<meta charset="utf-8" /> |
||||
<title>c4k-website</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/custom.css" rel="stylesheet" type="text/css" /> |
||||
</head> |
||||
|
||||
<body> |
||||
<div id="c4k-content"></div> |
||||
<script src="js/main.js"></script> |
||||
</body> |
||||
|
||||
</html> |
@ -0,0 +1,9 @@ |
||||
(ns dda.c4k-website.uberjar |
||||
(:gen-class) |
||||
(:require |
||||
[dda.c4k-website.core :as core] |
||||
[dda.c4k-website.website :as website] |
||||
[dda.c4k-common.uberjar :as uberjar])) |
||||
|
||||
(defn -main [& cmd-args] |
||||
(uberjar/main-common "c4k-website" website/config? website/auth? website/config-defaults core/k8s-objects cmd-args)) |
@ -0,0 +1,29 @@ |
||||
(ns dda.c4k-website.core |
||||
(:require |
||||
[dda.c4k-common.yaml :as yaml] |
||||
[dda.c4k-common.common :as cm] |
||||
[dda.c4k-website.website :as website] |
||||
[dda.c4k-common.postgres :as postgres])) |
||||
|
||||
(defn k8s-objects [config] |
||||
(let [storage-class (if (contains? config :postgres-data-volume-path) :manual :local-path)] |
||||
(cm/concat-vec |
||||
(map yaml/to-string |
||||
(filter #(not (nil? %)) |
||||
[(postgres/generate-config {:postgres-size :2gb :db-name "website"}) |
||||
(postgres/generate-secret config) |
||||
(when (contains? config :postgres-data-volume-path) |
||||
(postgres/generate-persistent-volume (select-keys config [:postgres-data-volume-path :pv-storage-size-gb]))) |
||||
(postgres/generate-pvc {:pv-storage-size-gb 5 |
||||
:pvc-storage-class-name storage-class}) |
||||
(postgres/generate-deployment {:postgres-image "postgres:14" |
||||
:postgres-size :2gb}) |
||||
(postgres/generate-service) |
||||
(website/generate-deployment) |
||||
(website/generate-service) |
||||
(website/generate-service-ssh) |
||||
(website/generate-data-volume config) |
||||
(website/generate-appini-env config) |
||||
(website/generate-secrets config) |
||||
(website/generate-ingress config) |
||||
(website/generate-certificate config)]))))) |
@ -0,0 +1,138 @@ |
||||
(ns dda.c4k-website.website |
||||
(:require |
||||
[clojure.spec.alpha :as s] |
||||
[clojure.string :as st] |
||||
#?(:cljs [shadow.resource :as rc]) |
||||
#?(:clj [orchestra.core :refer [defn-spec]] |
||||
:cljs [orchestra.core :refer-macros [defn-spec]]) |
||||
#?(:clj [clojure.edn :as edn] |
||||
:cljs [cljs.reader :as edn]) |
||||
[dda.c4k-common.yaml :as yaml] |
||||
[dda.c4k-common.common :as cm] |
||||
[dda.c4k-common.base64 :as b64] |
||||
[dda.c4k-common.predicate :as pred] |
||||
[dda.c4k-common.postgres :as postgres])) |
||||
|
||||
(defn domain-list? |
||||
[input] |
||||
(or |
||||
(st/blank? input) |
||||
(pred/string-of-separated-by? pred/fqdn-string? #"," input))) |
||||
|
||||
(s/def ::default-app-name string?) |
||||
(s/def ::fqdn pred/fqdn-string?) |
||||
(s/def ::mailer-from pred/bash-env-string?) |
||||
(s/def ::mailer-host-port pred/host-and-port-string?) |
||||
(s/def ::service-domain-whitelist domain-list?) |
||||
(s/def ::service-noreply-address string?) |
||||
(s/def ::mailer-user pred/bash-env-string?) |
||||
(s/def ::mailer-pw pred/bash-env-string?) |
||||
(s/def ::issuer pred/letsencrypt-issuer?) |
||||
(s/def ::volume-total-storage-size (partial pred/int-gt-n? 5)) |
||||
|
||||
(def config-defaults {:issuer "staging"}) |
||||
|
||||
(def config? (s/keys :req-un [::fqdn |
||||
::mailer-from |
||||
::mailer-host-port |
||||
::service-noreply-address] |
||||
:opt-un [::issuer |
||||
::default-app-name |
||||
::service-domain-whitelist])) |
||||
|
||||
(def auth? (s/keys :req-un [::postgres/postgres-db-user ::postgres/postgres-db-password ::mailer-user ::mailer-pw])) |
||||
|
||||
(def vol? (s/keys :req-un [::volume-total-storage-size])) |
||||
|
||||
(defn data-storage-by-volume-size |
||||
[total] |
||||
total) |
||||
|
||||
|
||||
#?(:cljs |
||||
(defmethod yaml/load-resource :website [resource-name] |
||||
(case resource-name |
||||
"website/appini-env-configmap.yaml" (rc/inline "website/appini-env-configmap.yaml") |
||||
"website/deployment.yaml" (rc/inline "website/deployment.yaml") |
||||
"website/certificate.yaml" (rc/inline "website/certificate.yaml") |
||||
"website/ingress.yaml" (rc/inline "website/ingress.yaml") |
||||
"website/secrets.yaml" (rc/inline "website/secrets.yaml") |
||||
"website/service.yaml" (rc/inline "website/service.yaml") |
||||
"website/service-ssh.yaml" (rc/inline "website/service-ssh.yaml") |
||||
"website/datavolume.yaml" (rc/inline "website/datavolume.yaml") |
||||
(throw (js/Error. "Undefined Resource!"))))) |
||||
|
||||
#?(:cljs |
||||
(defmethod yaml/load-as-edn :website [resource-name] |
||||
(yaml/from-string (yaml/load-resource resource-name)))) |
||||
|
||||
(defn-spec generate-appini-env pred/map-or-seq? |
||||
[config config?] |
||||
(let [{:keys [default-app-name |
||||
fqdn |
||||
mailer-from |
||||
mailer-host-port |
||||
service-domain-whitelist |
||||
service-noreply-address] |
||||
:or {default-app-name "website instance" |
||||
service-domain-whitelist fqdn}} |
||||
config] |
||||
(-> |
||||
(yaml/load-as-edn "website/appini-env-configmap.yaml") |
||||
(cm/replace-all-matching-values-by-new-value "APPNAME" default-app-name) |
||||
(cm/replace-all-matching-values-by-new-value "FQDN" fqdn) |
||||
(cm/replace-all-matching-values-by-new-value "URL" (str "https://" fqdn)) |
||||
(cm/replace-all-matching-values-by-new-value "FROM" mailer-from) |
||||
(cm/replace-all-matching-values-by-new-value "HOSTANDPORT" mailer-host-port) |
||||
(cm/replace-all-matching-values-by-new-value "WHITELISTDOMAINS" service-domain-whitelist) |
||||
(cm/replace-all-matching-values-by-new-value "NOREPLY" service-noreply-address)))) |
||||
|
||||
(defn-spec generate-secrets pred/map-or-seq? |
||||
[auth auth?] |
||||
(let [{:keys [postgres-db-user |
||||
postgres-db-password |
||||
mailer-user |
||||
mailer-pw]} auth] |
||||
(-> |
||||
(yaml/load-as-edn "website/secrets.yaml") |
||||
(cm/replace-all-matching-values-by-new-value "DBUSER" (b64/encode postgres-db-user)) |
||||
(cm/replace-all-matching-values-by-new-value "DBPW" (b64/encode postgres-db-password)) |
||||
(cm/replace-all-matching-values-by-new-value "MAILERUSER" (b64/encode mailer-user)) |
||||
(cm/replace-all-matching-values-by-new-value "MAILERPW" (b64/encode mailer-pw))))) |
||||
|
||||
(defn-spec generate-ingress pred/map-or-seq? |
||||
[config config?] |
||||
(let [{:keys [fqdn issuer]} config] |
||||
(-> |
||||
(yaml/load-as-edn "website/ingress.yaml") |
||||
(cm/replace-all-matching-values-by-new-value "FQDN" fqdn)))) |
||||
|
||||
(defn-spec generate-certificate pred/map-or-seq? |
||||
[config config?] |
||||
(let [{:keys [fqdn issuer] |
||||
:or {issuer "staging"}} config |
||||
letsencrypt-issuer (name issuer)] |
||||
(-> |
||||
(yaml/load-as-edn "website/certificate.yaml") |
||||
(assoc-in [:spec :issuerRef :name] letsencrypt-issuer) |
||||
(cm/replace-all-matching-values-by-new-value "FQDN" fqdn)))) |
||||
|
||||
(defn-spec generate-data-volume pred/map-or-seq? |
||||
[config vol?] |
||||
(let [{:keys [volume-total-storage-size]} config |
||||
data-storage-size (data-storage-by-volume-size volume-total-storage-size)] |
||||
(-> |
||||
(yaml/load-as-edn "website/datavolume.yaml") |
||||
(cm/replace-all-matching-values-by-new-value "DATASTORAGESIZE" (str (str data-storage-size) "Gi"))))) |
||||
|
||||
(defn-spec generate-deployment pred/map-or-seq? |
||||
[] |
||||
(yaml/load-as-edn "website/deployment.yaml")) |
||||
|
||||
(defn-spec generate-service pred/map-or-seq? |
||||
[] |
||||
(yaml/load-as-edn "website/service.yaml")) |
||||
|
||||
(defn-spec generate-service-ssh pred/map-or-seq? |
||||
[] |
||||
(yaml/load-as-edn "website/service-ssh.yaml")) |
@ -0,0 +1,125 @@ |
||||
(ns dda.c4k-website.browser |
||||
(:require |
||||
[clojure.string :as st] |
||||
[clojure.tools.reader.edn :as edn] |
||||
[dda.c4k-website.core :as core] |
||||
[dda.c4k-website.website :as website] |
||||
[dda.c4k-common.browser :as br] |
||||
[dda.c4k-common.postgres :as pgc] |
||||
[dda.c4k-common.common :as cm])) |
||||
|
||||
(defn generate-group |
||||
[name |
||||
content] |
||||
[{:type :element |
||||
:tag :div |
||||
:attrs {:class "rounded border border-3 m-3 p-2"} |
||||
:content [{:type :element |
||||
:tag :b |
||||
:attrs {:style "z-index: 1; position: relative; top: -1.3rem;"} |
||||
:content name} |
||||
{:type :element |
||||
:tag :fieldset |
||||
:content content}]}]) |
||||
|
||||
(defn generate-content [] |
||||
(cm/concat-vec |
||||
[(assoc |
||||
(br/generate-needs-validation) :content |
||||
(cm/concat-vec |
||||
(generate-group |
||||
"domain" |
||||
(cm/concat-vec |
||||
(br/generate-input-field "fqdn" "Your fqdn:" "repo.test.de") |
||||
(br/generate-input-field "mailer-from" "Your mailer email address:" "test@test.de") |
||||
(br/generate-input-field "mailer-host-port" "Your mailer host with port:" "test.de:123") |
||||
(br/generate-input-field "service-noreply-address" "Your noreply domain:" "test.de") |
||||
(br/generate-input-field "issuer" "(Optional) Your issuer prod/staging:" "") |
||||
(br/generate-input-field "app-name" "(Optional) Your app name:" "") |
||||
(br/generate-input-field "domain-whitelist" "(Optional) Domain whitelist for registration email-addresses:" ""))) |
||||
(generate-group |
||||
"provider" |
||||
(cm/concat-vec |
||||
(br/generate-input-field "volume-total-storage-size" "Your website volume-total-storage-size:" "20") |
||||
(br/generate-input-field "postgres-data-volume-path" "(Optional) Your postgres-data-volume-path if Persistent Volumes are not generated by an Operator:" ""))) |
||||
(generate-group |
||||
"credentials" |
||||
(br/generate-text-area |
||||
"auth" "Your auth.edn:" |
||||
"{:postgres-db-user \"website\" |
||||
:postgres-db-password \"website-db-password\" |
||||
:mailer-user \"test@test.de\" |
||||
:mailer-pw \"mail-test-password\"}" |
||||
"5")) |
||||
[(br/generate-br)] |
||||
(br/generate-button "generate-button" "Generate c4k yaml")))] |
||||
(br/generate-output "c4k-website-output" "Your c4k deployment.yaml:" "25"))) |
||||
|
||||
(defn generate-content-div |
||||
[] |
||||
{:type :element |
||||
:tag :div |
||||
:content |
||||
(generate-content)}) |
||||
|
||||
(defn config-from-document [] |
||||
(let [postgres-data-volume-path (br/get-content-from-element "postgres-data-volume-path" :optional true) |
||||
issuer (br/get-content-from-element "issuer" :optional true) |
||||
app-name (br/get-content-from-element "app-name" :optional true) |
||||
domain-whitelist (br/get-content-from-element "domain-whitelist" :optional true)] |
||||
(merge |
||||
{:fqdn (br/get-content-from-element "fqdn") |
||||
:mailer-from (br/get-content-from-element "mailer-from") |
||||
:mailer-host-port (br/get-content-from-element "mailer-host-port") |
||||
:service-noreply-address (br/get-content-from-element "service-noreply-address") |
||||
:volume-total-storage-size (br/get-content-from-element "volume-total-storage-size" :deserializer js/parseInt)} |
||||
(when (not (st/blank? postgres-data-volume-path)) |
||||
{:postgres-data-volume-path postgres-data-volume-path}) |
||||
(when (not (st/blank? issuer)) |
||||
{:issuer issuer}) |
||||
(when (not (st/blank? app-name)) |
||||
{:default-app-name app-name}) |
||||
(when (not (st/blank? domain-whitelist)) |
||||
{:service-domain-whitelist domain-whitelist}) |
||||
))) |
||||
|
||||
(defn validate-all! [] |
||||
(br/validate! "fqdn" ::website/fqdn) |
||||
(br/validate! "mailer-from" ::website/mailer-from) |
||||
(br/validate! "mailer-host-port" ::website/mailer-host-port) |
||||
(br/validate! "service-noreply-address" ::website/service-noreply-address) |
||||
(br/validate! "issuer" ::website/issuer :optional true) |
||||
(br/validate! "app-name" ::website/default-app-name :optional true) |
||||
(br/validate! "domain-whitelist" ::website/service-domain-whitelist :optional true) |
||||
(br/validate! "postgres-data-volume-path" ::pgc/postgres-data-volume-path :optional true) |
||||
(br/validate! "volume-total-storage-size" ::website/volume-total-storage-size :deserializer js/parseInt) |
||||
(br/validate! "auth" website/auth? :deserializer edn/read-string) |
||||
(br/set-form-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) |
||||
website/config-defaults |
||||
core/k8s-objects) |
||||
(br/set-output!))))) |
||||
(add-validate-listener "fqdn") |
||||
(add-validate-listener "mailer-from") |
||||
(add-validate-listener "mailer-host-port") |
||||
(add-validate-listener "service-noreply-address") |
||||
(add-validate-listener "app-name") |
||||
(add-validate-listener "domain-whitelist") |
||||
(add-validate-listener "postgres-data-volume-path") |
||||
(add-validate-listener "volume-total-storage-size") |
||||
(add-validate-listener "issuer") |
||||
(add-validate-listener "auth")) |
@ -0,0 +1,15 @@ |
||||
apiVersion: cert-manager.io/v1 |
||||
kind: Certificate |
||||
metadata: |
||||
name: website-cert |
||||
namespace: default |
||||
spec: |
||||
secretName: website-cert |
||||
commonName: FQDN |
||||
duration: 2160h # 90d |
||||
renewBefore: 360h # 15d |
||||
dnsNames: |
||||
- FQDN |
||||
issuerRef: |
||||
name: staging |
||||
kind: ClusterIssuer |
@ -0,0 +1,15 @@ |
||||
apiVersion: v1 |
||||
kind: PersistentVolumeClaim |
||||
metadata: |
||||
name: website-data-pvc |
||||
namespace: default |
||||
labels: |
||||
app: website |
||||
spec: |
||||
storageClassName: local-path |
||||
accessModes: |
||||
- ReadWriteOnce |
||||
resources: |
||||
requests: |
||||
storage: DATASTORAGESIZE |
||||
|
@ -0,0 +1,24 @@ |
||||
apiVersion: networking.k8s.io/v1 |
||||
kind: Ingress |
||||
metadata: |
||||
name: ingress-website |
||||
namespace: default |
||||
annotations: |
||||
ingress.kubernetes.io/ssl-redirect: "true" |
||||
traefik.ingress.kubernetes.io/router.middlewares: default-redirect-https@kubernetescrd |
||||
spec: |
||||
tls: |
||||
- hosts: |
||||
- FQDN |
||||
secretName: website-cert |
||||
rules: |
||||
- host: FQDN |
||||
http: |
||||
paths: |
||||
- pathType: Prefix |
||||
path: "/" |
||||
backend: |
||||
service: |
||||
name: website-service |
||||
port: |
||||
number: 3000 |
@ -0,0 +1,56 @@ |
||||
# ToDo: |
||||
# content-pfad für nginx server definieren |
||||
# sinnvolle security policies konfigurieren |
||||
|
||||
apiVersion: v1 |
||||
kind: ConfigMap |
||||
metadata: |
||||
name: nginx-env |
||||
namespace: default |
||||
data: |
||||
nginx.conv: | |
||||
user nginx; |
||||
worker_processes 3; |
||||
error_log /var/log/nginx/error.log; |
||||
events { |
||||
worker_connections 10240; |
||||
} |
||||
http { |
||||
log_format main |
||||
'remote_addr:$remote_addr\t' |
||||
'time_local:$time_local\t' |
||||
'method:$request_method\t' |
||||
'uri:$request_uri\t' |
||||
'host:$host\t' |
||||
'status:$status\t' |
||||
'bytes_sent:$body_bytes_sent\t' |
||||
'referer:$http_referer\t' |
||||
'useragent:$http_user_agent\t' |
||||
'forwardedfor:$http_x_forwarded_for\t' |
||||
'request_time:$request_time'; |
||||
access_log /var/log/nginx/access.log main; |
||||
server { |
||||
listen 80; |
||||
server_name _; |
||||
location / { |
||||
root html; |
||||
index index.html index.htm; |
||||
} |
||||
} |
||||
include /etc/nginx/virtualhost/virtualhost.conf; |
||||
} |
||||
virtualhost.conf: | |
||||
upstream app { |
||||
server localhost:8080; |
||||
keepalive 1024; |
||||
} |
||||
server { |
||||
listen 80 default_server; |
||||
root /usr/local/app; |
||||
access_log /var/log/nginx/app.access_log main; |
||||
error_log /var/log/nginx/app.error_log; |
||||
location / { |
||||
proxy_pass http://app/; |
||||
proxy_http_version 1.1; |
||||
} |
||||
} |
@ -0,0 +1,36 @@ |
||||
apiVersion: apps/v1 |
||||
kind: Deployment |
||||
metadata: |
||||
name: nginx |
||||
namespace: default |
||||
labels: |
||||
app: nginx |
||||
spec: |
||||
replicas: 1 |
||||
selector: |
||||
matchLabels: |
||||
app: nginx |
||||
template: |
||||
metadata: |
||||
name: nginx |
||||
labels: |
||||
app: nginx |
||||
spec: |
||||
containers: |
||||
- name: nginx |
||||
image: nginx:latest |
||||
imagePullPolicy: IfNotPresent |
||||
ports: |
||||
- containerPort: 80 |
||||
protocol: TCP |
||||
envFrom: |
||||
- configMapRef: |
||||
name: nginx-env |
||||
volumeMounts: |
||||
- name: website-data-volume |
||||
mountPath: "/data" |
||||
volumes: |
||||
- name: website-data-volume |
||||
persistentVolumeClaim: |
||||
claimName: website-data-pvc |
||||
|
@ -0,0 +1,16 @@ |
||||
kind: Service |
||||
apiVersion: v1 |
||||
metadata: |
||||
name: website-server-service |
||||
namespace: default |
||||
annotations: |
||||
metallb.universe.tf/allow-shared-ip: "shared-ip-service-group" |
||||
spec: |
||||
type: LoadBalancer |
||||
selector: |
||||
app: website-server |
||||
ports: |
||||
- port: 80 |
||||
targetPort: 80 |
||||
protocol: TCP |
||||
|
@ -0,0 +1,50 @@ |
||||
<configuration scan="true" scanPeriod="1 seconds" debug="false"> |
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> |
||||
<encoder> |
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> |
||||
</encoder> |
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> |
||||
<level>INFO</level> |
||||
</filter> |
||||
</appender> |
||||
|
||||
<appender name="PALLETFILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
||||
<file>logs/pallet.log</file> |
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
||||
<fileNamePattern>logs/old/pallet.%d{yyyy-MM-dd}.log</fileNamePattern> |
||||
<maxHistory>3</maxHistory> |
||||
</rollingPolicy> |
||||
<encoder> |
||||
<pattern>%date %level [%thread] %logger{10} %msg%n</pattern> |
||||
</encoder> |
||||
</appender> |
||||
|
||||
<logger name="clj-ssh.ssh" level="ERROR"> |
||||
<appender-ref ref="PALLETFILE" /> |
||||
</logger> |
||||
|
||||
<logger name="pallet" level="DEBUG"> |
||||
<appender-ref ref="PALLETFILE" /> |
||||
</logger> |
||||
|
||||
<logger name="pallet.ssh" level="ERROR"> |
||||
<appender-ref ref="PALLETFILE" /> |
||||
</logger> |
||||
|
||||
<logger name="pallet.algo" level="ERROR"> |
||||
<appender-ref ref="PALLETFILE" /> |
||||
</logger> |
||||
|
||||
<logger name="dda" level="DEBUG"> |
||||
<appender-ref ref="PALLETFILE" /> |
||||
</logger> |
||||
|
||||
<logger name="meissa" level="DEBUG"> |
||||
<appender-ref ref="PALLETFILE" /> |
||||
</logger> |
||||
|
||||
<root level="DEBUG"> |
||||
<appender-ref ref="CONSOLE" /> |
||||
</root> |
||||
|
||||
</configuration> |
@ -0,0 +1,73 @@ |
||||
(ns dda.c4k-website.website-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-common.test-helper :as th] |
||||
[dda.c4k-common.base64 :as b64] |
||||
[dda.c4k-website.website :as cut])) |
||||
|
||||
(st/instrument `cut/generate-appini-env) |
||||
(st/instrument `cut/generate-ingress) |
||||
(st/instrument `cut/generate-secrets) |
||||
|
||||
(deftest should-generate-appini-env |
||||
(is (= {:APP_NAME-c1 "", |
||||
:APP_NAME-c2 "test website", |
||||
:website__mailer__FROM-c1 "", |
||||
:website__mailer__FROM-c2 "test@test.com", |
||||
:website__mailer__HOST-c1 "m.t.de:123", |
||||
:website__mailer__HOST-c2 "mail.test.com:123", |
||||
:website__server__DOMAIN-c1 "test.de", |
||||
:website__server__DOMAIN-c2 "test.com", |
||||
:website__server__ROOT_URL-c1 "https://test.de", |
||||
:website__server__ROOT_URL-c2 "https://test.com", |
||||
:website__server__SSH_DOMAIN-c1 "test.de", |
||||
:website__server__SSH_DOMAIN-c2 "test.com", |
||||
:website__service__EMAIL_DOMAIN_WHITELIST-c1 "adb.de", |
||||
:website__service__EMAIL_DOMAIN_WHITELIST-c2 "test.com,test.net", |
||||
:website__service__NO_REPLY_ADDRESS-c1 "", |
||||
:website__service__NO_REPLY_ADDRESS-c2 "noreply@test.com"} |
||||
(th/map-diff (cut/generate-appini-env {:default-app-name "" |
||||
:fqdn "test.de" |
||||
:mailer-from "" |
||||
:mailer-host-port "m.t.de:123" |
||||
:service-domain-whitelist "adb.de" |
||||
:service-noreply-address "" |
||||
}) |
||||
(cut/generate-appini-env {:default-app-name "test website" |
||||
:fqdn "test.com" |
||||
:mailer-from "test@test.com" |
||||
:mailer-host-port "mail.test.com:123" |
||||
:service-domain-whitelist "test.com,test.net" |
||||
:service-noreply-address "noreply@test.com" |
||||
}))))) |
||||
|
||||
(deftest should-generate-certificate |
||||
(is (= {:name-c2 "prod", :name-c1 "staging"} |
||||
(th/map-diff (cut/generate-certificate {}) |
||||
(cut/generate-certificate {:issuer "prod"}))))) |
||||
|
||||
(deftest should-generate-secret |
||||
(is (= {:website__database__USER-c1 "", |
||||
:website__database__USER-c2 (b64/encode "pg-user"), |
||||
:website__database__PASSWD-c1 "", |
||||
:website__database__PASSWD-c2 (b64/encode "pg-pw"), |
||||
:website__mailer__USER-c1 "", |
||||
:website__mailer__USER-c2 (b64/encode "maileruser"), |
||||
:website__mailer__PASSWD-c1 "", |
||||
:website__mailer__PASSWD-c2 (b64/encode "mailerpw")} |
||||
(th/map-diff (cut/generate-secrets {:postgres-db-user "" |
||||
:postgres-db-password "" |
||||
:mailer-user "" |
||||
:mailer-pw ""}) |
||||
(cut/generate-secrets {:postgres-db-user "pg-user" |
||||
:postgres-db-password "pg-pw" |
||||
:mailer-user "maileruser" |
||||
:mailer-pw "mailerpw"}))))) |
||||
|
||||
(deftest should-generate-data-volume |
||||
(is (= {:storage-c1 "1Gi", |
||||
:storage-c2 "15Gi"} |
||||
(th/map-diff (cut/generate-data-volume {:volume-total-storage-size 1}) |
||||
(cut/generate-data-volume {:volume-total-storage-size 15}))))) |
Loading…
Reference in new issue