From 71d0cd2ac60d2950a14fcc064882fef4d288044a Mon Sep 17 00:00:00 2001 From: Stavros Kois <47820033+stavros-k@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:55:59 +0200 Subject: [PATCH] diskoverdata - migration (#2230) * init commit * fixes * more fixes * fix * fiox * fix pre-flight * cron * update questions * add migration * add to_keep_versions * fix typos * remove comment * clean --- library/ix-dev/charts/diskoverdata/Chart.lock | 8 +- library/ix-dev/charts/diskoverdata/Chart.yaml | 8 +- library/ix-dev/charts/diskoverdata/README.md | 15 +- .../ix-dev/charts/diskoverdata/app-readme.md | 4 +- .../diskoverdata/charts/common-1.2.9.tgz | Bin 0 -> 63208 bytes .../diskoverdata/charts/common-2304.0.1.tgz | Bin 4993 -> 0 bytes .../charts/diskoverdata/ci/basic-values.yaml | 19 + .../charts/diskoverdata/ci/test-values.yaml | 30 - .../charts/diskoverdata/migrations/migrate | 106 +++ .../ix-dev/charts/diskoverdata/questions.yaml | 745 ++++++++++++------ .../charts/diskoverdata/templates/NOTES.txt | 1 + .../diskoverdata/templates/_configuration.tpl | 212 +++++ .../diskoverdata/templates/_diskover.tpl | 138 +++- .../charts/diskoverdata/templates/_es.tpl | 53 ++ .../diskoverdata/templates/_migration.tpl | 35 + .../diskoverdata/templates/_persistence.tpl | 68 ++ .../charts/diskoverdata/templates/_portal.tpl | 12 + .../diskoverdata/templates/_service.tpl | 27 + .../charts/diskoverdata/templates/common.yaml | 15 + .../diskoverdata/templates/deployment.yaml | 160 ---- .../templates/diskover-secrets.yaml | 9 - .../templates/elastic_search_deployment.yaml | 46 -- .../templates/elastic_search_service.yaml | 6 - .../templates/elasticsearch-secret.yaml | 9 - .../templates/initial_scripts.yaml | 270 ------- .../templates/pre-install-job.yaml | 31 - .../diskoverdata/templates/service.yaml | 10 - .../templates/tests/deployment-check.yaml | 21 - .../charts/diskoverdata/to_keep_versions.md | 4 + .../charts/diskoverdata/to_keep_versions.yaml | 1 + .../ix-dev/charts/diskoverdata/values.yaml | 50 +- 31 files changed, 1213 insertions(+), 900 deletions(-) create mode 100644 library/ix-dev/charts/diskoverdata/charts/common-1.2.9.tgz delete mode 100644 library/ix-dev/charts/diskoverdata/charts/common-2304.0.1.tgz create mode 100644 library/ix-dev/charts/diskoverdata/ci/basic-values.yaml delete mode 100644 library/ix-dev/charts/diskoverdata/ci/test-values.yaml create mode 100755 library/ix-dev/charts/diskoverdata/migrations/migrate create mode 100644 library/ix-dev/charts/diskoverdata/templates/NOTES.txt create mode 100644 library/ix-dev/charts/diskoverdata/templates/_configuration.tpl create mode 100644 library/ix-dev/charts/diskoverdata/templates/_es.tpl create mode 100644 library/ix-dev/charts/diskoverdata/templates/_migration.tpl create mode 100644 library/ix-dev/charts/diskoverdata/templates/_persistence.tpl create mode 100644 library/ix-dev/charts/diskoverdata/templates/_portal.tpl create mode 100644 library/ix-dev/charts/diskoverdata/templates/_service.tpl create mode 100644 library/ix-dev/charts/diskoverdata/templates/common.yaml delete mode 100644 library/ix-dev/charts/diskoverdata/templates/deployment.yaml delete mode 100644 library/ix-dev/charts/diskoverdata/templates/diskover-secrets.yaml delete mode 100644 library/ix-dev/charts/diskoverdata/templates/elastic_search_deployment.yaml delete mode 100644 library/ix-dev/charts/diskoverdata/templates/elastic_search_service.yaml delete mode 100644 library/ix-dev/charts/diskoverdata/templates/elasticsearch-secret.yaml delete mode 100644 library/ix-dev/charts/diskoverdata/templates/initial_scripts.yaml delete mode 100644 library/ix-dev/charts/diskoverdata/templates/pre-install-job.yaml delete mode 100644 library/ix-dev/charts/diskoverdata/templates/service.yaml delete mode 100644 library/ix-dev/charts/diskoverdata/templates/tests/deployment-check.yaml create mode 100644 library/ix-dev/charts/diskoverdata/to_keep_versions.md create mode 100644 library/ix-dev/charts/diskoverdata/to_keep_versions.yaml diff --git a/library/ix-dev/charts/diskoverdata/Chart.lock b/library/ix-dev/charts/diskoverdata/Chart.lock index 7bfa85e361..00bcc6e479 100644 --- a/library/ix-dev/charts/diskoverdata/Chart.lock +++ b/library/ix-dev/charts/diskoverdata/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: common - repository: file://../../../common/2304.0.1 - version: 2304.0.1 -digest: sha256:1ed155c6760e1166e2cb75b52bc5e81c6bdf0252c16ff5ede001157077c41670 -generated: "2023-04-24T13:39:12.281094547+03:00" + repository: file://../../../common + version: 1.2.9 +digest: sha256:af1a9a1f87e3e48453c9f25f909f5ebcd7fa6e25162b7b425448ba752bcdbc5c +generated: "2024-02-27T20:26:48.651992686+02:00" diff --git a/library/ix-dev/charts/diskoverdata/Chart.yaml b/library/ix-dev/charts/diskoverdata/Chart.yaml index b6e10ed95d..ea71cb8ed7 100644 --- a/library/ix-dev/charts/diskoverdata/Chart.yaml +++ b/library/ix-dev/charts/diskoverdata/Chart.yaml @@ -3,18 +3,18 @@ description: Diskover is used to monitor size/volumes of distributed dataset. annotations: title: Diskover Data type: application -version: 1.0.15 +version: 2.0.0 apiVersion: v2 appVersion: "2.0.1" -kubeVersion: '>=1.16.0-0' +kubeVersion: ">=1.16.0-0" maintainers: - name: truenas url: https://www.truenas.com/ email: dev@ixsystems.com dependencies: - name: common - repository: file://../../../common/2304.0.1 - version: 2304.0.1 + repository: file://../../../common + version: 1.2.9 home: https://github.com/diskoverdata/diskover-community icon: https://media.sys.truenas.net/apps/diskoverdata/icons/icon.png sources: diff --git a/library/ix-dev/charts/diskoverdata/README.md b/library/ix-dev/charts/diskoverdata/README.md index efc6e80942..817612c364 100644 --- a/library/ix-dev/charts/diskoverdata/README.md +++ b/library/ix-dev/charts/diskoverdata/README.md @@ -1,14 +1,3 @@ -# DiskOverData +# Diskover Data -DiskOver App for TrueNAS SCALE - -[Diskover](https://www.diskoverdata.com/) is a sustainable file management solution for your distributed data. - -# Introduction - -This chart is based on [diskoverdata](https://hub.docker.com/r/linuxserver/diskover) and -deployed on kubernetes via helm chart - -## Configuration - -Please refer to questions.yaml for a detailed overview on supported configurable values. +[Diskover Data](https://github.com/diskoverdata/diskover-community) is used to monitor size/volumes of distributed dataset. diff --git a/library/ix-dev/charts/diskoverdata/app-readme.md b/library/ix-dev/charts/diskoverdata/app-readme.md index 189190698d..817612c364 100644 --- a/library/ix-dev/charts/diskoverdata/app-readme.md +++ b/library/ix-dev/charts/diskoverdata/app-readme.md @@ -1 +1,3 @@ -DiskOver App for TrueNAS SCALE +# Diskover Data + +[Diskover Data](https://github.com/diskoverdata/diskover-community) is used to monitor size/volumes of distributed dataset. diff --git a/library/ix-dev/charts/diskoverdata/charts/common-1.2.9.tgz b/library/ix-dev/charts/diskoverdata/charts/common-1.2.9.tgz new file mode 100644 index 0000000000000000000000000000000000000000..797faff0d7d07716e71cb473c442774805865b32 GIT binary patch literal 63208 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 literal 0 HcmV?d00001 diff --git a/library/ix-dev/charts/diskoverdata/charts/common-2304.0.1.tgz b/library/ix-dev/charts/diskoverdata/charts/common-2304.0.1.tgz deleted file mode 100644 index eca4f0b8a159e3e8b484a289d420cd1a0ba7ba9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4993 zcmV-{6MpO;iwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PH<$bKAC-{j6VcmPzMY*_x94-0@6j)@$e9dYU?!IFoxjJ3B5! zLK12cU;t2#Hue7Y9lS{JCCPTIB(3nl7RiHy1K_-PZ~#UuO&J}W%@CL20;S1!pT7); z!{O1vf&M=n4(tDid&A*(ql1IJ(ZS)-Xm9l0aI|-DIQkBTPe4nfs9o~sCPqXd;(Vn@YLcvfDrzG z4>^+E&|87PEI2O=q01H3@ z5R8)?XPi)D?NO3AnFcog9Y<1toFNH_V~99L634w;B4>nxoMGz&IO`vFDiq6ePU?d~ z*q+Z(l4AiUKSF3=UGoKmt)s+VfwilXNuDIsDiK$cOkjq@&v;R^5-5;)4G92^ zCVqq+JsuMXzMt=YKM$&pEvm4@)VB>5io>9sNADJyrr1?co)Y|_h6d${gkZH^ZC9uj zdVJm21Ary^udeWl{*y^Tqk%M!)I*T$SCl68=dEZ||J!gPN|{8;#q<#Gf?}g?81KeO zjh2>P)ClmeM24f2lBgZVVF!OGCnz{eav?FlcoXzt2d9}_6c77-gU|T51z@v_k$Ys| zi%TH3v5X?UO0g{u{o7p|n zoU_@hn0tkB_iX#0$F#3iR@(n-JzJ0R0#?}n(eU7?X8(_m_m8*se-lO9|F@XN*qC=^ zw)Ko_g@EFiC?$)MH~OA-0%|V6+GgF!y=PfH^KO574-FZfg)}#ID@Me>r++(T&Jmqr zcz&%B7$^<2tHI1WgZkwxghYIb<(uMJdF>nJnUq=Js$`r=7O})4vsXU&yR$b1bYY;i zbI^&@86q|O+7g9Lecy*2%A_+#A+`7W&L9D}Ted-`*Gt1)QP|Eksb2j>HR4~ZH1vN$ z#sh8L2iKmH^_2R5I6B_1>;I$CR{w9L`0<}x#%~gaV&A*&RPG*(QR$o|iPdl%(fBmU zX6Q0c;m{An{0So{-*#Ch{}Ia2m?T6J{3r#m%KzKjYsmlcXtJAUT~>i~>&v7aTLpU*k5t|zbzON3>J$3&L`lx1Qt9|es#Ywtc3;w)i{ zRK3&PlfQq6W{Be$P{f%KtEJyXkCo`oaFT{%W*BJ6 z@}3PJhQ=jaKW0~k?n9HYa$4jofDV>t;Y~iqoMMSZNZ6oAI8d*_jVdjUX)Bf#Q8dNz z?s!pMYjlB-YJH<;uPpJ88r8-S?6%UkVlmaNe6-djR7galW%aq~?q!Kaffmar;d{G$ zfI+F{~nC?>iJ(s`$yaL-;ETlt6qN05RJ7@sAH3CVwP{UzU)*{zH(_pA9}#8@u_t9f4f=5JbzR!ApiA$u zl51RAQ)Rc!4xH&_aX`f&O!6dIz=s?qWI~ih$TB_ZVeg;VbWg)e)k!r20>(H(xxgS; zis9!%dz-nM5S+wjp)MgQkvIm)K+cGOo$*4?|IR2Ds_V-VpJkR zh0W~K_lSgBk|Z$3kPAhp0Hlex)GlX>a>e~2tf36Noqva@-~kKL`FshF6!yR_hgP_7 zZ6+*fEQZY>spKG4@vA;pp02~mO)`CE6}!qNRbEO!c&cvhI`=9GyzROsAJi=ZAqk){kzw{m-D}9 z&tZPt7&?339KKcxJTb80x0)zAC!no%C#9?8zS2mUV>w*zk*%BuCCM4b0x#K@;*o}u z(#aQZAGg~PdosLrf_!`n0DV3wDg* z@=`wjV3PHUHQ(WF;KGx(Od}M`heR~mR9{=u0V*vbuP&x zlfYK!|GiP;{FmdS!-K8<-$beEe{bo@?Je%q_+HMegEhLobysq+yS#g2cI}*oNAKkH zmU@Z~a-1b3LSi5!M-opL1BY&%ewsViBEym0^I*|wdW$rMQL%vLo@=AxnmqCM>K2uP zx7h5U4~p7(rVQJatp)~(x8XHKGMc@5+5z)8B4hF2#J%_;WARtU8`YiTZI~+iTJAMe zcHzz&v5F&g4+e|8mnZ)KfVQz(Cy!l^pE)|9Op$L(ylj=}uoA}GdJKQ6(rW)HmbZ-G z5IP-P$B*3nw?_PD|Ddt|XSBb+wf~zaCKKZF3eMKjRV8PHF%^|ML75n=o|W;8;=&|P zSDsZec5O|A)z&l@W>a@@!bb^II}l=<_IuybD*qe{mh%XU!F7Zmd-}ub_|MU>A^-cM zy{-IjqEuZN$#XoIAbZ%1ntL)KIvV=k`ijuknephn)wgPd*ZJJle!)(0I!_&y?$FP8;j%f|mE?p@`(4>)rJLxD>V&hj zSmQM5XX}mRS22Rm?GWg^wTBN>Bii0E7e#ZG_EqCkEbm*_Kb@&Tah8u05|!Yldg#ww zGcm#fWt_~B#5cGR%l|5aZmaaa&MAN784xSi|Bf5^Ux!=&Z!<;fCIEo9cFw_@ai@&B zbx{fIxqmy1m%F>DQf&_TD+Ml!htn*txR5%+Q6N`NpT@733H%J^R-`tyDavx=XIj`O z%Qb*|p5m18MFk|qX#vu3NIOVvo`Kg1*$5$&@qCUyH)-&lhpOlV@=Z&O92tudow!*JFQcHky7T;C(Nx&cK z?YJ|^Lg<5Wid)Y1qUfMlS@OZO7SiXqsX#5i8Q$>K{h_O`55+V#I= z98K}y8fDp)`Td0F{~YW$&VN2W-rwr~jTE~RN^5YGW$#&%r}!6^Q+wLu>dX(zEM?lq zX%)={J##pM&;Np2qq*{WTJMiCI`w%b+RVo6W(9P0|! zVU3Fok-D{cS|*D$ci|PgEms0sf6&jGr^VUNsiN);k~8x_;VN$VIVJju$j9YBOhyS51?>LW57kqL>Ehn1owABM8>>};|q`6j5^ zFq9!XU=;00R42jG$@+jSCbO_X@FZ2k6j?&#D~)v! zEC=1WC341cS=eFrBh@Vy8@J0vb%g1|uNr@1+4AEsoE zDP)W#fWs;LHb|))NJMW2f~8o2{su(Ynzu8|F%kyqH|39&@W1IG9kEp4k0{L&EJA%` zM!gFz(1t(2j?T6(Iy~%~vG2o9B4Nkf{sR(B`>n17 zQudnA>yWbsNh3v)jK|mJq`vDpOIk>~S7XJo`b`4#`Ta8_S_Y;n91k3Nuj%ERZVTB* zSyu5X{w7K_u-c`_vY%=~k82`didzwxtxEHA)zY;)j;Yn%s53}erT!k#K5UMNYRJ~! zY(cj6xI)(}z%q^K8t!oVikZ8-_QZuF-I$bes%cr0fd2>28 zHuUbTqcQ5M!wat6_5tsrwrjG&wQ9GEVRIRxwExTfKl0HIz>|Le;qYLuvH#;}d;a@I zN;NmB^!xNW6Zu#Oz!(V}1EXML3(J$0){7d{;#n&d%yTbr;a*f@;lBK*C|pZ{lQpWM;Z)2tW?;qB`|9y14J^yth<(Ya{k(g7x z&}%qAy>Cz$=Ol>^%d0IG+6=pa3z$NdnV^jm=&pLZ?3f#|e^Tj*T8E z?;{%bo&m*EBhNb-#}o1q$L33q|JDydFAOlMYbuf;!yFPqao7vbudc5o>}loi z&#oXQT=c>zkpunLklzc(|H}vZue&ju4%8oaDdu!gA{wLUCeL6(^l?9-xXpSm!ZEt( zy$EHhelt#{y%+!2dj{_jCoC6masE>D!i=-uaU^>oi7^@&aL#`3g>ws``~8>AlxOhDe$@YL*+>1=Jq^(7y?F7@)3=uwm;X3{ zcRyWR!P!rzZ{NYi6$ zI!|47Vai#aDdpupLCmHkf-#o27@Lou-28|5&lyYYxv?UIUhj&~2m^wI=^&VS028KO z$8{Z{+ykea^byOJB)COFD@>`hD93RgVMr075>up*Moh)Wjr0UHrlju-0+`Pt@bFvEbK9~l+p;a&QZ4@<00960 L*>Hgr0Ez$rii85> diff --git a/library/ix-dev/charts/diskoverdata/ci/basic-values.yaml b/library/ix-dev/charts/diskoverdata/ci/basic-values.yaml new file mode 100644 index 0000000000..910fbce8f8 --- /dev/null +++ b/library/ix-dev/charts/diskoverdata/ci/basic-values.yaml @@ -0,0 +1,19 @@ +diskoverConfig: + cronSchedule: '* * * * *' + username: someuser + password: somepass + +diskoverStorage: + config: + type: pvc + data: + type: pvc + esdata: + type: pvc + additionalStorages: + - type: pvc + diskoverDataIndex: true + mountPath: /data1 + - type: pvc + diskoverDataIndex: true + mountPath: /data2 diff --git a/library/ix-dev/charts/diskoverdata/ci/test-values.yaml b/library/ix-dev/charts/diskoverdata/ci/test-values.yaml deleted file mode 100644 index f0a9f45ca7..0000000000 --- a/library/ix-dev/charts/diskoverdata/ci/test-values.yaml +++ /dev/null @@ -1,30 +0,0 @@ -environmentVariables: [] -extraAppVolumeMounts: [] -extraDataVolumeMounts: [] -web_port: 32000 -dnsConfig: - options: [] -emptyDirVolumes: true -appVolumeMounts: - config: - emptyDir: true - mountPath: /config - data: - emptyDir: true - mountPath: /data -elasticSearchAppVolumeMounts: - esdata: - emptyDir: true - mountPath: /usr/share/elasticsearch/data -ownerUID: 568 -ownerGID: 568 -username: "admin" -password: "admin" -host: "192.169.0.156" -hostNetwork: false -timezone: "America/Los_Angeles" -diskoverCredentials: - username: admin - password: admin -cronjobSchedule: "0 3 * * *" -es_user: elasticsearch diff --git a/library/ix-dev/charts/diskoverdata/migrations/migrate b/library/ix-dev/charts/diskoverdata/migrations/migrate new file mode 100755 index 0000000000..9eea742117 --- /dev/null +++ b/library/ix-dev/charts/diskoverdata/migrations/migrate @@ -0,0 +1,106 @@ +#!/usr/bin/python3 +import json +import os +import sys + + +def migrate_volume(volume): + return { + 'type': 'hostPath', + 'hostPathConfig': { + 'hostPath': volume['hostPath'] + }, + } if volume.get('hostPathEnabled', False) else { + 'type': 'ixVolume', + 'ixVolumeConfig': { + 'datasetName': volume['datasetName'], + }, + } + + +def migrate_common_lib(values): + delete_keys = [ + 'web_port', 'timezone', 'dnsConfig', 'environmentVariables', 'ownerUID', + 'cronjobSchedule', 'diskoverCredentials', 'appVolumeMounts', 'ownerGID', + 'extraAppVolumeMounts', 'extraDataVolumeMounts', 'elasticSearchAppVolumeMounts', + 'elasticsearch', 'enableResourceLimits', 'es_user', 'python', + ] + + additionalVols = [ + { + 'type': 'hostPath', + 'hostPathConfig': {'hostPath': e['hostPath']}, + 'mountPath': e['mountPath'], + } for e in values.get('extraAppVolumeMounts', []) + ] + [ + { + 'type': 'hostPath', + 'hostPathConfig': {'hostPath': e['hostPath']}, + 'mountPath': e['mountPath'], + 'diskoverDataIndex': True + } for e in values.get('extraDataVolumeMounts', []) + ] + + values.update({ + 'TZ': values['timezone'], + # Migrate Network + 'diskoverNetwork': { + 'webPort': values['web_port'], + }, + # Migrate DNS + 'podOptions': { + 'dnsConfig': { + 'options': [ + {'name': opt['name'], 'value': opt['value']} + for opt in values.get('dnsConfig', {}).get('options', []) + ] + } + }, + # Migrate Resources + 'resources': { + 'limits': { + 'cpu': values.get('cpuLimit', '4000m'), + 'memory': values.get('memLimit', '8Gi'), + } + }, + # Migrate ID + 'diskoverID': { + 'user': values['ownerUID'], + 'group': values['ownerGID'], + }, + # Migrate Config + 'diskoverConfig': { + 'cronSchedule': values['cronjobSchedule'], + 'username': values['diskoverCredentials']['username'], + 'password': values['diskoverCredentials']['password'], + 'additionalEnvs': values.get('environmentVariables', []), + }, + # Migrate Storage + 'diskoverStorage': { + 'config': migrate_volume(values['appVolumeMounts']['config']), + 'data': migrate_volume(values['appVolumeMounts']['data']), + 'esdata': migrate_volume(values['elasticSearchAppVolumeMounts']['esdata']), + 'additionalStorages': additionalVols + }, + }) + + for k in delete_keys: + values.pop(k, None) + + return values + +def migrate(values): + # If this missing, we have already migrated + if not 'appVolumeMounts' in values.keys(): + return values + + return migrate_common_lib(values) + + +if __name__ == '__main__': + if len(sys.argv) != 2: + exit(1) + + if os.path.exists(sys.argv[1]): + with open(sys.argv[1], 'r') as f: + print(json.dumps(migrate(json.loads(f.read())))) diff --git a/library/ix-dev/charts/diskoverdata/questions.yaml b/library/ix-dev/charts/diskoverdata/questions.yaml index c30285208f..c29e2361a6 100644 --- a/library/ix-dev/charts/diskoverdata/questions.yaml +++ b/library/ix-dev/charts/diskoverdata/questions.yaml @@ -1,322 +1,585 @@ groups: - - name: "Configuration" - description: "Diskover application configuration" - - name: "Storage" - description: "Configure storage for Diskover" - - name: "Networking" - description: "Networking Configuration for Diskover" - - name: "Advanced DNS Settings" - description: "Configure DNS settings" - - name: "Resource Limits" - description: "Set CPU/memory limits for Kubernetes Pod" + - name: Diskover Data Configuration + description: Configure Diskover Data + - name: User and Group Configuration + description: Configure User and Group for Diskover Data + - name: Advanced Pod Configuration + description: Configure Advanced Pod Options for Diskover Data + - name: Network Configuration + description: Configure Network for Diskover Data + - name: Storage Configuration + description: Configure Storage for Diskover Data + - name: Resources Configuration + description: Configure Resources for Diskover Data portals: web_portal: protocols: - - "http" + - "$kubernetes-resource_configmap_portal_protocol" host: - - "$node_ip" + - "$kubernetes-resource_configmap_portal_host" ports: - - "$variable-web_port" - path: "/" + - "$kubernetes-resource_configmap_portal_port" + path: "$kubernetes-resource_configmap_portal_path" questions: - - variable: web_port - label: "Web Port for Diskover" - group: Networking - schema: - type: int - min: 8000 - max: 65535 - default: 22510 - required: true - - - variable: timezone - label: "Configure timezone" - group: "Configuration" - description: "Configure timezone for Diskover" + - variable: TZ + group: Diskover Data Configuration + label: Timezone schema: type: string + default: Etc/UTC + required: true $ref: - - "definitions/timezone" + - definitions/timezone - - variable: dnsConfig - label: "DNS Configuration" - group: "Advanced DNS Settings" + - variable: discoverConfig + label: "" + group: Diskover Data Configuration schema: type: dict attrs: - - variable: options - label: "DNS Options" + - variable: cronSchedule + label: Cron Schedule + description: The cron schedule for Diskover Data. + schema: + type: string + default: "0 3 * * *" + - variable: username + label: Username + description: The username for Diskover Data. + schema: + type: string + default: "" + required: true + - variable: password + label: Password + description: The password for Diskover Data. + schema: + type: string + default: "" + required: true + private: true + - variable: additionalEnvs + label: Additional Environment Variables + description: Additional environment variables for Diskover Data. schema: type: list items: - - variable: optionsEntry - label: "Option Entry Configuration" + - variable: env + label: Environment Variable schema: type: dict attrs: - variable: name - label: "Option Name" + label: Name schema: type: string required: true - variable: value - label: "Option Value" + label: Value schema: type: string required: true - - variable: ownerUID - label: "Config folder's user id" - description: "Linuxserver uses this user id to configure config's folders permissions" - group: Configuration - schema: - type: int - default: 568 - min: 1 - max: 65535 - - - variable: ownerGID - label: "Config folder's group id" - description: "Linuxserver uses this group id to configure config's folders permissions" - group: Configuration - schema: - type: int - default: 568 - min: 1 - max: 65535 - - - variable: diskoverCredentials - description: "Configure Diskover Initial Password" - label: "Configure Diskover Initial Username and password" - group: "Configuration" + - variable: podOptions + label: "" + group: Advanced Pod Configuration schema: type: dict - required: true - additional_attrs: true attrs: - - variable: username - label: "Username" - description: "UserName for Diskover User" - schema: - type: string - default: "admin" - required: true - - variable: password - label: "Password" - description: "Initial Password for Diskover User" - schema: - type: string - private: true - default: "changeme" - required: true - - - variable: environmentVariables - label: "Diskover Extra Environment" - group: "Configuration" - schema: - type: list - default: [] - items: - - variable: environmentVariable - label: "Environment Variable" + - variable: dnsConfig + label: Advanced DNS Configuration schema: type: dict attrs: - - variable: name - label: "Name" + - variable: options + label: DNS Options schema: - type: string - - variable: value - label: "Value" - schema: - type: string + type: list + items: + - variable: optionsEntry + label: DNS Option Entry + schema: + type: dict + attrs: + - variable: name + label: Option Name + schema: + type: string + required: true + - variable: value + label: Option Value + schema: + type: string + required: true - - variable: cronjobSchedule - description: "Cronjobs Consist on 5 values in this specific format 'Minute Hour Day Month Week'" - label: "Define cronjob schedule for diskover" - group: "Configuration" + - variable: diskoverID + label: "" + group: User and Group Configuration schema: - type: string - default: "0 3 * * *" + type: dict + attrs: + - variable: user + label: User ID + description: The user id that Diskover Data files will be owned by. + schema: + type: int + min: 568 + default: 568 + required: true + - variable: group + label: Group ID + description: The group id that Diskover Data files will be owned by. + schema: + type: int + min: 568 + default: 568 + required: true - - variable: appVolumeMounts - label: "Diskover Storage" - group: "Storage" + - variable: discoverNetwork + label: "" + group: Network Configuration + schema: + type: dict + attrs: + - variable: webPort + label: Web Port + description: The port for the Diskover Data Web UI. + schema: + type: int + default: 22510 + min: 9000 + max: 65535 + required: true + + - variable: discoverStorage + label: "" + group: Storage Configuration schema: type: dict attrs: - variable: config - label: "Storage Volume for Configuration" + label: Diskover Data Config Storage + description: The path to store Diskover Data Configuration. schema: type: dict attrs: - - variable: datasetName - label: "Configuration Storage Volume Dataset Name" + - variable: type + label: Type + description: | + ixVolume: Is dataset created automatically by the system.
+ Host Path: Is a path that already exists on the system. schema: type: string - hidden: true + required: true + immutable: true + default: "ixVolume" + enum: + - value: "hostPath" + description: Host Path (Path that already exists on the system) + - value: "ixVolume" + description: ixVolume (Dataset created automatically by the system) + - variable: ixVolumeConfig + label: ixVolume Configuration + description: The configuration for the ixVolume dataset. + schema: + type: dict + show_if: [["type", "=", "ixVolume"]] $ref: - "normalize/ixVolume" - show_if: [["hostPathEnabled", "=", false]] - default: "ix-config" - editable: false - - variable: mountPath - label: "Configuration Storage Mount Path" - description: "Path where the volume will be mounted inside the pod" + attrs: + - variable: aclEnable + label: Enable ACL + description: Enable ACL for the dataset. + schema: + type: boolean + default: false + - variable: datasetName + label: Dataset Name + description: The name of the dataset to use for storage. + schema: + type: string + required: true + immutable: true + hidden: true + default: "config" + - variable: aclEntries + label: ACL Configuration + schema: + type: dict + show_if: [["aclEnable", "=", true]] + attrs: [] + - variable: hostPathConfig + label: Host Path Configuration schema: - type: path - hidden: true - editable: true - default: "/config" - - variable: hostPathEnabled - label: "Enable Custom Host Path for Diskover Configuration Storage Volume" - schema: - type: boolean - default: false - show_subquestions_if: true - subquestions: + type: dict + show_if: [["type", "=", "hostPath"]] + attrs: + - variable: aclEnable + label: Enable ACL + description: Enable ACL for the dataset. + schema: + type: boolean + default: false + - variable: acl + label: ACL Configuration + schema: + type: dict + show_if: [["aclEnable", "=", true]] + attrs: [] + $ref: + - "normalize/acl" - variable: hostPath - label: "Host Path for Diskover Configuration Storage Volume" + label: Host Path + description: The host path to use for storage. schema: type: hostpath + show_if: [["aclEnable", "=", false]] required: true - variable: data - label: "Storage Volume for Data" + label: Diskover Data Data Storage + description: The path to store Diskover Data Data. schema: type: dict attrs: - - variable: datasetName - label: "Configuration Storage Volume Dataset Name" + - variable: type + label: Type + description: | + ixVolume: Is dataset created automatically by the system.
+ Host Path: Is a path that already exists on the system. schema: type: string - hidden: true + required: true + immutable: true + default: "ixVolume" + enum: + - value: "hostPath" + description: Host Path (Path that already exists on the system) + - value: "ixVolume" + description: ixVolume (Dataset created automatically by the system) + - variable: ixVolumeConfig + label: ixVolume Configuration + description: The configuration for the ixVolume dataset. + schema: + type: dict + show_if: [["type", "=", "ixVolume"]] $ref: - "normalize/ixVolume" - show_if: [["hostPathEnabled", "=", false]] - default: "ix-data" - editable: false - - variable: mountPath - label: "Configuration Storage Mount Path" - description: "Path where the volume will be mounted inside the pod" + attrs: + - variable: aclEnable + label: Enable ACL + description: Enable ACL for the dataset. + schema: + type: boolean + default: false + - variable: datasetName + label: Dataset Name + description: The name of the dataset to use for storage. + schema: + type: string + required: true + immutable: true + hidden: true + default: "data" + - variable: aclEntries + label: ACL Configuration + schema: + type: dict + show_if: [["aclEnable", "=", true]] + attrs: [] + - variable: hostPathConfig + label: Host Path Configuration schema: - type: path - hidden: true - editable: true - default: "/data" - - variable: hostPathEnabled - label: "Enable Custom Host Path for Diskover Data folder to monitor" - schema: - type: boolean - default: false - show_subquestions_if: true - subquestions: + type: dict + show_if: [["type", "=", "hostPath"]] + attrs: + - variable: aclEnable + label: Enable ACL + description: Enable ACL for the dataset. + schema: + type: boolean + default: false + - variable: acl + label: ACL Configuration + schema: + type: dict + show_if: [["aclEnable", "=", true]] + attrs: [] + $ref: + - "normalize/acl" - variable: hostPath - label: "Host Path for Diskover Data folder to monitor" + label: Host Path + description: The host path to use for storage. schema: type: hostpath + show_if: [["aclEnable", "=", false]] required: true - - - variable: extraDataVolumeMounts - label: "Add Extra Host Paths For Diskover To Monitor" - group: "Storage" - schema: - type: list - items: - - variable: dataAppVolume - label: "Host Path Volume" - description: "Add extra Data Volumes for diskover to monitor" - schema: - type: dict - attrs: - - variable: mountPath - label: "Mount Path in Pod" - description: "Path where the volume will be mounted inside the pod" - schema: - type: path - required: true - - variable: hostPath - label: "Host Path" - description: "Host path" - schema: - type: hostpath - required: true - - - variable: extraAppVolumeMounts - label: "Extra Host Path Volumes" - group: "Storage" - schema: - type: list - items: - - variable: extraAppVolume - label: "Host Path Volume" - description: "Add an extra host path volume for Diskover application" - schema: - type: dict - attrs: - - variable: mountPath - label: "Mount Path in Pod" - description: "Path where the volume will be mounted inside the pod" - schema: - type: path - required: true - - variable: hostPath - label: "Host Path" - description: "Host path" - schema: - type: hostpath - required: true - - - variable: elasticSearchAppVolumeMounts - label: "elastic search Storage" - group: "Storage" - schema: - type: dict - hidden: true - attrs: - variable: esdata - label: "Storage Volume for Configuration" + label: Elastic Search Data Storage + description: The path to store Elastic Search Data. schema: type: dict attrs: - - variable: datasetName - label: "Configuration Storage Volume Dataset Name" + - variable: type + label: Type + description: | + ixVolume: Is dataset created automatically by the system.
+ Host Path: Is a path that already exists on the system. schema: type: string + required: true + immutable: true + default: "ixVolume" + enum: + - value: "hostPath" + description: Host Path (Path that already exists on the system) + - value: "ixVolume" + description: ixVolume (Dataset created automatically by the system) + - variable: ixVolumeConfig + label: ixVolume Configuration + description: The configuration for the ixVolume dataset. + schema: + type: dict + show_if: [["type", "=", "ixVolume"]] $ref: - "normalize/ixVolume" - default: "ix-elasticsearch-data" - editable: false - - variable: mountPath - label: "Configuration Storage Mount Path" - description: "Path where the volume will be mounted inside the pod" + attrs: + - variable: aclEnable + label: Enable ACL + description: Enable ACL for the dataset. + schema: + type: boolean + default: false + - variable: datasetName + label: Dataset Name + description: The name of the dataset to use for storage. + schema: + type: string + required: true + immutable: true + hidden: true + default: "esdata" + - variable: aclEntries + label: ACL Configuration + schema: + type: dict + show_if: [["aclEnable", "=", true]] + attrs: [] + - variable: hostPathConfig + label: Host Path Configuration schema: - type: path - editable: false - default: "/usr/share/elasticsearch/data" + type: dict + show_if: [["type", "=", "hostPath"]] + attrs: + - variable: aclEnable + label: Enable ACL + description: Enable ACL for the dataset. + schema: + type: boolean + default: false + - variable: acl + label: ACL Configuration + schema: + type: dict + show_if: [["aclEnable", "=", true]] + attrs: [] + $ref: + - "normalize/acl" + - variable: hostPath + label: Host Path + description: The host path to use for storage. + schema: + type: hostpath + show_if: [["aclEnable", "=", false]] + required: true - - variable: enableResourceLimits - label: "Enable Pod resource limits" - group: "Resource Limits" + - variable: additionalStorages + label: Additional Storage + description: Additional storage for Diskover Data. + schema: + type: list + default: [] + items: + - variable: storageEntry + label: Storage Entry + schema: + type: dict + attrs: + - variable: type + label: Type + description: | + ixVolume: Is dataset created automatically by the system.
+ Host Path: Is a path that already exists on the system.
+ SMB Share: Is a SMB share that is mounted to a persistent volume claim. + schema: + type: string + required: true + default: "ixVolume" + immutable: true + enum: + - value: "hostPath" + description: Host Path (Path that already exists on the system) + - value: "ixVolume" + description: ixVolume (Dataset created automatically by the system) + - value: "smb-pv-pvc" + description: SMB Share (Mounts a persistent volume claim to a SMB share) + - variable: diskoverDataIndex + label: Enable Diskover Data Indexing + description: Enable Diskover Data Indexing + schema: + type: boolean + - variable: readOnly + label: Read Only + description: Mount the volume as read only. + schema: + type: boolean + default: false + - variable: mountPath + label: Mount Path + description: The path inside the container to mount the storage. + schema: + type: path + required: true + - variable: hostPathConfig + label: Host Path Configuration + schema: + type: dict + show_if: [["type", "=", "hostPath"]] + attrs: + - variable: aclEnable + label: Enable ACL + description: Enable ACL for the dataset. + schema: + type: boolean + default: false + - variable: acl + label: ACL Configuration + schema: + type: dict + show_if: [["aclEnable", "=", true]] + attrs: [] + $ref: + - "normalize/acl" + - variable: hostPath + label: Host Path + description: The host path to use for storage. + schema: + type: hostpath + show_if: [["aclEnable", "=", false]] + required: true + - variable: ixVolumeConfig + label: ixVolume Configuration + description: The configuration for the ixVolume dataset. + schema: + type: dict + show_if: [["type", "=", "ixVolume"]] + $ref: + - "normalize/ixVolume" + attrs: + - variable: aclEnable + label: Enable ACL + description: Enable ACL for the dataset. + schema: + type: boolean + default: false + - variable: datasetName + label: Dataset Name + description: The name of the dataset to use for storage. + schema: + type: string + required: true + immutable: true + default: "storage_entry" + - variable: aclEntries + label: ACL Configuration + schema: + type: dict + show_if: [["aclEnable", "=", true]] + attrs: [] + - variable: smbConfig + label: SMB Share Configuration + description: The configuration for the SMB Share. + schema: + type: dict + show_if: [["type", "=", "smb-pv-pvc"]] + attrs: + - variable: server + label: Server + description: The server for the SMB share. + schema: + type: string + required: true + - variable: share + label: Share + description: The share name for the SMB share. + schema: + type: string + required: true + - variable: domain + label: Domain (Optional) + description: The domain for the SMB share. + schema: + type: string + - variable: username + label: Username + description: The username for the SMB share. + schema: + type: string + required: true + - variable: password + label: Password + description: The password for the SMB share. + schema: + type: string + required: true + private: true + - variable: size + label: Size (in Gi) + description: The size of the volume quota. + schema: + type: int + required: true + min: 1 + default: 1 + + - variable: resources + group: Resources Configuration + label: "" schema: - type: boolean - default: false - - variable: cpuLimit - label: "CPU Limit" - description: "CPU resource limit allow plain integer values with suffix m(milli) e.g 1000m, 100." - group: "Resource Limits" - schema: - type: string - show_if: [["enableResourceLimits", "=", true]] - valid_chars: "^\\d+(?:\\.\\d+(?!.*m$)|m?$)" - default: "4000m" - - variable: memLimit - label: "Memory Limit" - group: "Resource Limits" - description: "Memory limits is specified by number of bytes. Followed by quantity suffix like E,P,T,G,M,k and Ei,Pi,Ti,Mi,Gi,Ki can also be used. e.g 129e6, 129M, 128974848000m, 123Mi" - schema: - type: string - show_if: [["enableResourceLimits", "=", true]] - valid_chars: "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$" - default: "8Gi" + type: dict + attrs: + - variable: limits + label: Limits + schema: + type: dict + attrs: + - variable: cpu + label: CPU + description: CPU limit for WG-Easy. + schema: + type: string + max_length: 6 + valid_chars: '^(0\.[1-9]|[1-9][0-9]*)(\.[0-9]|m?)$' + valid_chars_error: | + Valid CPU limit formats are
+ - Plain Integer - eg. 1
+ - Float - eg. 0.5
+ - Milicpu - eg. 500m + default: "4000m" + required: true + - variable: memory + label: Memory + description: Memory limit for WG-Easy. + schema: + type: string + max_length: 12 + valid_chars: '^[1-9][0-9]*([EPTGMK]i?|e[0-9]+)?$' + valid_chars_error: | + Valid Memory limit formats are
+ - Suffixed with E/P/T/G/M/K - eg. 1G
+ - Suffixed with Ei/Pi/Ti/Gi/Mi/Ki - eg. 1Gi
+ - Plain Integer in bytes - eg. 1024
+ - Exponent - eg. 134e6 + default: "8Gi" + required: true diff --git a/library/ix-dev/charts/diskoverdata/templates/NOTES.txt b/library/ix-dev/charts/diskoverdata/templates/NOTES.txt new file mode 100644 index 0000000000..ba4e01146c --- /dev/null +++ b/library/ix-dev/charts/diskoverdata/templates/NOTES.txt @@ -0,0 +1 @@ +{{ include "ix.v1.common.lib.chart.notes" $ }} diff --git a/library/ix-dev/charts/diskoverdata/templates/_configuration.tpl b/library/ix-dev/charts/diskoverdata/templates/_configuration.tpl new file mode 100644 index 0000000000..ceb6dcd7bd --- /dev/null +++ b/library/ix-dev/charts/diskoverdata/templates/_configuration.tpl @@ -0,0 +1,212 @@ +{{- define "diskover.configuration" -}} + {{- $fullname := (include "ix.v1.common.lib.chart.names.fullname" $) }} + {{- $elasticsearch := printf "%s-elasticsearch" $fullname }} + +{{- $esPassword := randAlphaNum 32 }} +secret: + diskover-secret: + enabled: true + data: + es-password: {{ $esPassword }} +configmap: + diskover-config: + enabled: true + data: + .default_crawler.sh: |- + #!/bin/sh + while true; do + # this condition wait for the script to copy into the container . + if test -f "$1"; then + # Empty folders don't generate indices . if folder is empty a default file is generated + if ! [ "$(ls -A $2)" ]; then + echo "Dummy file created as empty dirs are rejected" > $2/diskover_test.txt; + fi + python3 $1 $2/; + break; + fi + sleep 5 + done + Constants.php: | + ['doc', 'docx', 'odt', 'pdf', 'tex', 'wpd', 'wks', 'txt', 'rtf', 'key', 'odp', 'pps', 'ppt', 'pptx', 'ods', 'xls', 'xlsm', 'xlsx'], + 'images' => ['ai', 'bmp', 'gif', 'ico', 'jpeg', 'jpg', 'png', 'ps', 'psd', 'psp', 'svg', 'tif', 'tiff', 'exr', 'tga'], + 'video' => ['3g2', '3gp', 'avi', 'flv', 'h264', 'm4v', 'mkv', 'qt', 'mov', 'mp4', 'mpg', 'mpeg', 'rm', 'swf', 'vob', 'wmv', 'ogg', 'ogv', 'webm'], + 'audio' => ['au', 'aif', 'aiff', 'cda', 'mid', 'midi', 'mp3', 'm4a', 'mpa', 'ogg', 'wav', 'wma', 'wpl'], + 'apps' => ['apk', 'exe', 'bat', 'bin', 'cgi', 'pl', 'gadget', 'com', 'jar', 'msi', 'py', 'wsf'], + 'programming' => ['c', 'cgi', 'pl', 'class', 'cpp', 'cs', 'h', 'java', 'php', 'py', 'sh', 'swift', 'vb'], + 'internet' => ['asp', 'aspx', 'cer', 'cfm', 'cgi', 'pl', 'css', 'htm', 'html', 'js', 'jsp', 'part', 'php', 'py', 'rss', 'xhtml'], + 'system' => ['bak', 'cab', 'cfg', 'cpl', 'cur', 'dll', 'dmp', 'drv', 'icns', 'ico', 'ini', 'lnk', 'msi', 'sys', 'tmp', 'vdi', 'raw'], + 'data' => ['csv', 'dat', 'db', 'dbf', 'log', 'mdb', 'sav', 'sql', 'tar', 'xml'], + 'disc' => ['bin', 'dmg', 'iso', 'toast', 'vcd', 'img'], + 'compressed' => ['7z', 'arj', 'deb', 'pkg', 'rar', 'rpm', 'tar', 'gz', 'z', 'zip'], + 'trash' => ['old', 'trash', 'tmp', 'temp', 'junk', 'recycle', 'delete', 'deleteme', 'clean', 'remove'] + ]; + // extra fields for search results and view file/dir info pages + // key is description for field and value is ES field name + // Example: + //const EXTRA_FIELDS = [ + // 'Date Changed' => 'ctime' + //]; + const EXTRA_FIELDS = []; + // Maximum number of indices to load by default, indices are loaded in order by creation date + // setting this too high can cause slow logins and other timeout issues + // This setting can bo overridden on indices page per user and stored in maxindex cookie + // If MAX_INDEX is set higher than maxindex browser cookie, the cookie will be set to this value + const MAX_INDEX = 250; + // time in seconds for index info to be cached, clicking reload indices forces update + const INDEXINFO_CACHETIME = 600; + // time in seconds to check Elasticsearch for new index info + const NEWINDEX_CHECKTIME = 10; + // sqlite database file path + const DATABASE = '../diskoverdb.sqlite3'; + } + config.yaml: | + # diskover default/sample config file + # + # default search paths for config + # macOS: ~/.config/diskover and ~/Library/Application Support/diskover + # Other Unix: ~/.config/diskover and /etc/diskover + # Windows: %APPDATA%\diskover where the APPDATA environment variable falls back to %HOME%\AppData\Roaming if undefined + # + appName: diskover + logLevel: INFO + logToFile: False + logDirectory: /tmp/ + + diskover: + # max number of crawl threads + # a thread is created up to maxthreads for each directory at level 1 of tree dir arg + # set to a number or leave blank to auto set based on number of cpus + #maxthreads: 20 + maxthreads: + # block size used for du size + blocksize: 512 + excludes: + # directory names and absolute paths you want to exclude from crawl + # directory excludes uses python re.search for string search (regex) + # directory excludes are case-sensitive + # Examples: .* or .backup or .backup* or /dir/dirname + # to exclude none use empty list [] + dirs: [".*", ".snapshot", ".Snapshot", "~snapshot", "~Snapshot", ".zfs"] + #dirs: [] + # files you want to exclude from crawl + # can include wildcards (.*, *.doc or NULLEXT for files with no extension) + # file names are case-sensitive, extensions are not + files: [".*", "Thumbs.db", ".DS_Store", "._.DS_Store", ".localized", "desktop.ini"] + #files: [] + # exclude empty 0 byte files, set to True to exclude empty files or False to not exclude + emptyfiles: True + # exclude empty dirs, set to True to exclude empty dirs or False to not exclude + emptydirs: True + # exclude files smaller than min size in bytes + minfilesize: 1 + #minfilesize: 512 + # exclude files modified less than x days ago + minmtime: 0 + #minmtime: 30 + # exclude files modified more than x days ago + maxmtime: 36500 + # exclude files changed less than x days ago + minctime: 0 + # exclude files changed more than x days ago + maxctime: 36500 + # exclude files accessed less than x days ago + minatime: 0 + # exclude files accessed more than x days ago + maxatime: 36500 + includes: + # directory names and absolute paths you want to include (whitelist), case-sensitive, + # to include none use empty list [] + #dirs: [".recycle"] + dirs: [] + # files you want to include (whitelist), case-sensitive + files: [] + ownersgroups: + # control how owner (username) and group fields are stored for file and directory docs + # store uid and gid's instead of trying to get owner and group names + uidgidonly: False + # owner/group names contain domain name set to True + domain: False + # character separator used on cifs/nfs mounts to separte user/group and domain name, usually \ or @ + domainsep: \ + # if domain name comes first before character separator, set this to True, otherwise False + domainfirst: True + # when indexing owner and group fields, keep the domain name + keepdomain: False + replacepaths: + # translate path names set to True to enable or False to disable. + # Set to True if crawling in Windows to replace drive letters and \ with / + replace: False + #from: /mnt/ + #to: /vols/ + from: + to: + plugins: + # set to True to enable all plugins or False to disable all plugins + enable: False + # list of plugins (by name) to use for directories + dirs: ['unixperms'] + # list of plugins (by name) to use for files + files: ['unixperms'] + other: + # restore atime/mtime for files and dirs during crawl + # set to True or False, default False (useful for cifs which does not work with noatime mount option) + # for nfs, it's preferable to use mount options ro,noatime,nodiratime + restoretimes: False + + databases: + elasticsearch: + host: '{{ $elasticsearch }}' + port: 9200 + user: 'elastic' + password: '{{ $esPassword }}' + # set https to True if using HTTP TLS/SSL or False if using http + # for AWS ES, you will most likely want to set this to True + # override with env var ES_HTTPS + https: False + # compress http data + # for AWS ES, you will most likely want to set this to True + httpcompress: False + # timeout for connection to ES (default is 10) + timeout: 30 + # number of connections kept open to ES when crawling (default is 10) + maxsize: 20 + # max retries for ES operations (default is 0) + maxretries: 10 + # wait for at least yellow status before bulk uploading (default is False), set to True if you want to wait + wait: False + # chunk size for ES bulk operations (default is 500) + chunksize: 1000 + # the below settings are to optimize ES for crawling + # index refresh interval (default is 1s), set to -1 to disable refresh during crawl (fastest performance but no index searches), after crawl is set back to 1s + indexrefresh: 30s + # transaction log flush threshold size (default 512mb) + translogsize: 1gb + # transaction log sync interval time (default 5s) + translogsyncint: 30s + # search scroll size (default 100 docs) + scrollsize: 1000 +{{- end -}} diff --git a/library/ix-dev/charts/diskoverdata/templates/_diskover.tpl b/library/ix-dev/charts/diskoverdata/templates/_diskover.tpl index edafb8c7d3..bc482679d1 100644 --- a/library/ix-dev/charts/diskoverdata/templates/_diskover.tpl +++ b/library/ix-dev/charts/diskoverdata/templates/_diskover.tpl @@ -1,35 +1,105 @@ -{{- define "add.user" -}} - {{- $user := .Values.es_user -}} - {{- printf "adduser %s -D;" $user -}} -{{- end -}} - - -{{- define "change.user.permissions" -}} - {{- $user := .Values.es_user -}} - {{- $mountPath := .Values.elasticSearchAppVolumeMounts.esdata.mountPath -}} - {{- printf "chown -R %s:%s %s;" $user $user $mountPath -}} -{{- end -}} - - -{{- define "elasticsearch.IP" -}} - {{ $envList := (default list) }} - {{ $envList = mustAppend $envList (dict "name" "ES_HOST" "value" (printf "%s-es" (include "common.names.fullname" .))) }} - {{ $envList = mustAppend $envList (dict "name" "ES_PORT" "value" "9200") }} - {{ include "common.containers.environmentVariables" (dict "environmentVariables" $envList) }} -{{- end -}} - - -{{- define "elasticsearch.credentials" -}} - {{ $envList := (default list) }} - {{ $envList = mustAppend $envList (dict "name" "ES_USER" "valueFromSecret" true "secretName" "elastic-search-credentials" "secretKey" "es-username") }} - {{ $envList = mustAppend $envList (dict "name" "ES_PASS" "valueFromSecret" true "secretName" "elastic-search-credentials" "secretKey" "es-password") }} - {{ include "common.containers.environmentVariables" (dict "environmentVariables" $envList) }} -{{- end -}} - - -{{- define "config.file.path" -}} - {{ $envList := (default list) }} - {{ $envList = mustAppend $envList (dict "name" "DEST" "value" .mountPath) }} - {{ $envList = mustAppend $envList (dict "name" "FILE" "value" .configFile) }} - {{ include "common.containers.environmentVariables" (dict "environmentVariables" $envList) }} +{{- define "diskover.workload" -}} + {{- $fullname := (include "ix.v1.common.lib.chart.names.fullname" $) -}} + {{- $elasticsearch := printf "http://%s-elasticsearch:%v/_cluster/health?local=true" $fullname 9200 }} +workload: + diskover: + enabled: true + primary: true + type: Deployment + podSpec: + hostNetwork: false + securityContext: + fsGroup: {{ .Values.diskoverID.group }} + containers: + diskover: + enabled: true + primary: true + imageSelector: image + securityContext: + runAsUser: 0 + runAsGroup: 0 + runAsNonRoot: false + readOnlyRootFilesystem: false + capabilities: + add: + - CHOWN + - DAC_OVERRIDE + - FOWNER + - SETGID + - SETUID + - KILL + fixedEnv: + PUID: {{ .Values.diskoverID.user }} + {{ with .Values.diskoverConfig.additionalEnvs }} + envList: + {{ range $env := . }} + - name: {{ $env.name }} + value: {{ $env.value }} + {{ end }} + {{ end }} + probes: + liveness: + enabled: true + type: http + path: /login.php + port: 80 + readiness: + enabled: true + type: http + path: /login.php + port: 80 + startup: + enabled: true + type: http + path: /login.php + port: 80 + lifecycle: + {{- $sched := .Values.diskoverConfig.cronSchedule }} + postStart: + type: exec + command: + - /bin/sh + - -c + - | + /scripts/.default_crawler.sh /app/diskover/diskover.py /data; + {{- $cron := printf "%s python3 /app/diskover/diskover.py /data" $sched }} + if ! cat /config/crontab | grep -q "{{ $cron }}"; then + echo "{{ $cron }}" >> /config/crontab; + fi + {{- range $item := .Values.diskoverStorage.additionalStorages }} + /scripts/.default_crawler.sh /app/diskover/diskover.py {{ $item.mountPath }}; + {{- end -}} + {{- range $item := .Values.diskoverStorage.additionalStorages }} + {{- if $item.diskoverDataIndex }} + {{- $cron := printf "%s python3 /app/diskover/diskover.py %s" $sched $item.mountPath }} + if ! cat /config/crontab | grep -q "{{ $cron }}"; then + echo "{{ $cron }}" >> /config/crontab; + fi + {{- end }} + {{- end }} + crontab /config/crontab; + + initContainers: + 01-wait-for-elasticsearch: + enabled: true + type: init + imageSelector: bashImage + env: + ELASTIC_PASSWORD: + secretKeyRef: + name: diskover-secret + key: es-password + command: + - bash + - -c + args: + - | + echo "Pinging [{{ $elasticsearch }}] until it is ready..." + head="--header=Authorization: Basic x$(base64 <<< "elastic:$ELASTIC_PASSWORD")" + time="--timeout=3" + until wget "$head" "$time" --spider -qO- "{{ $elasticsearch }}"; do + echo "Waiting for [{{ $elasticsearch }}] to be ready..." + sleep 2 + done + echo "URL [{{ $elasticsearch }}] is ready!" {{- end -}} diff --git a/library/ix-dev/charts/diskoverdata/templates/_es.tpl b/library/ix-dev/charts/diskoverdata/templates/_es.tpl new file mode 100644 index 0000000000..fc4f51393f --- /dev/null +++ b/library/ix-dev/charts/diskoverdata/templates/_es.tpl @@ -0,0 +1,53 @@ +{{- define "es.workload" -}} +workload: + elasticsearch: + enabled: true + type: Deployment + podSpec: + hostNetwork: false + containers: + elasticsearch: + enabled: true + primary: true + imageSelector: elasticSearchImage + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + readOnlyRootFilesystem: false + env: + ELASTIC_PASSWORD: + secretKeyRef: + name: diskover-secret + key: es-password + http.port: 9200 + discovery.type: single-node + node.name: diskoverdata + probes: + liveness: + enabled: true + type: exec + command: + - /bin/bash + - -c + - | + curl -s -H "Authorization: Basic $(base64 <<< "elastic:$ELASTIC_PASSWORD")" \ + http://localhost:9200/_cluster/health?local=true + readiness: + enabled: true + type: exec + command: + - /bin/bash + - -c + - | + curl -s -H "Authorization: Basic $(base64 <<< "elastic:$ELASTIC_PASSWORD")" \ + http://localhost:9200/_cluster/health?local=true + startup: + enabled: true + type: exec + command: + - /bin/bash + - -c + - | + curl -s -H "Authorization: Basic $(base64 <<< "elastic:$ELASTIC_PASSWORD")" \ + http://localhost:9200/_cluster/health?local=true +{{- end -}} diff --git a/library/ix-dev/charts/diskoverdata/templates/_migration.tpl b/library/ix-dev/charts/diskoverdata/templates/_migration.tpl new file mode 100644 index 0000000000..7427253ba0 --- /dev/null +++ b/library/ix-dev/charts/diskoverdata/templates/_migration.tpl @@ -0,0 +1,35 @@ +{{- define "diskover.get-versions" -}} + {{- $oldChartVersion := "" -}} + {{- $newChartVersion := "" -}} + + {{/* Safely access the context, so it wont block CI */}} + {{- if hasKey .Values.global "ixChartContext" -}} + {{- if .Values.global.ixChartContext.upgradeMetadata -}} + + {{- $oldChartVersion = .Values.global.ixChartContext.upgradeMetadata.oldChartVersion -}} + {{- $newChartVersion = .Values.global.ixChartContext.upgradeMetadata.newChartVersion -}} + {{- if and (not $oldChartVersion) (not $newChartVersion) -}} + {{- fail "Upgrade Metadata is missing. Cannot proceed" -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- toYaml (dict "old" $oldChartVersion "new" $newChartVersion) -}} +{{- end -}} + +{{- define "diskover.migration" -}} + {{- $versions := (fromYaml (include "diskover.get-versions" $)) -}} + {{- if and $versions.old $versions.new -}} + {{- $oldV := semver $versions.old -}} + {{- $newV := semver $versions.new -}} + + {{/* If new is v2.x.x */}} + {{- if eq ($newV.Major | int) 2 -}} + {{/* And old is v1.x.x, but lower than 0.15 */}} + {{- if and (eq $oldV.Major 1) (lt ($oldV.Patch | int) 15) -}} + {{/* Block the upgrade */}} + {{- fail "Migration to 2.x.x is only allowed from 1.0.15 or higher" -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/ix-dev/charts/diskoverdata/templates/_persistence.tpl b/library/ix-dev/charts/diskoverdata/templates/_persistence.tpl new file mode 100644 index 0000000000..d623b24ba8 --- /dev/null +++ b/library/ix-dev/charts/diskoverdata/templates/_persistence.tpl @@ -0,0 +1,68 @@ +{{- define "diskover.persistence" -}} +persistence: + config: + enabled: true + {{- include "ix.v1.common.app.storageOptions" (dict "storage" .Values.diskoverStorage.config) | nindent 4 }} + targetSelector: + diskover: + diskover: + mountPath: /config + data: + enabled: true + {{- include "ix.v1.common.app.storageOptions" (dict "storage" .Values.diskoverStorage.data) | nindent 4 }} + targetSelector: + diskover: + diskover: + mountPath: /data + esdata: + enabled: true + {{- include "ix.v1.common.app.storageOptions" (dict "storage" .Values.diskoverStorage.esdata) | nindent 4 }} + targetSelector: + elasticsearch: + elasticsearch: + mountPath: /usr/share/elasticsearch/data + defaultcrawler: + enabled: true + type: configmap + objectName: diskover-config + defaultMode: "0755" + targetSelector: + diskover: + diskover: + mountPath: /scripts/default_crawler.sh + subPath: .default_crawler.sh + phpfile: + enabled: true + type: configmap + objectName: diskover-config + targetSelector: + diskover: + diskover: + mountPath: /config/diskover-web.conf.d/Constants.php + subPath: Constants.php + yamlfile: + enabled: true + type: configmap + objectName: diskover-config + targetSelector: + diskover: + diskover: + mountPath: /config/diskover.conf.d/diskover/config.yaml + subPath: config.yaml + tmp: + enabled: true + type: emptyDir + targetSelector: + diskover: + 01-wait-for-elasticsearch: + mountPath: /tmp + {{- range $idx, $storage := .Values.diskoverStorage.additionalStorages }} + {{ printf "diskover-%v:" (int $idx) }} + enabled: true + {{- include "ix.v1.common.app.storageOptions" (dict "storage" $storage) | nindent 4 }} + targetSelector: + diskover: + diskover: + mountPath: {{ $storage.mountPath }} + {{- end }} +{{- end -}} diff --git a/library/ix-dev/charts/diskoverdata/templates/_portal.tpl b/library/ix-dev/charts/diskoverdata/templates/_portal.tpl new file mode 100644 index 0000000000..61dd8bf69e --- /dev/null +++ b/library/ix-dev/charts/diskoverdata/templates/_portal.tpl @@ -0,0 +1,12 @@ +{{- define "diskover.portal" -}} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: portal +data: + path: "/" + port: {{ .Values.diskoverNetwork.webPort | quote }} + protocol: "http" + host: "$node_ip" +{{- end -}} diff --git a/library/ix-dev/charts/diskoverdata/templates/_service.tpl b/library/ix-dev/charts/diskoverdata/templates/_service.tpl new file mode 100644 index 0000000000..950488ca83 --- /dev/null +++ b/library/ix-dev/charts/diskoverdata/templates/_service.tpl @@ -0,0 +1,27 @@ +{{- define "diskover.service" -}} +service: + diskover: + enabled: true + primary: true + type: NodePort + targetSelector: diskover + ports: + webui: + enabled: true + primary: true + port: {{ .Values.diskoverNetwork.webPort }} + nodePort: {{ .Values.diskoverNetwork.webPort }} + targetPort: 80 + targetSelector: diskover + elasticsearch: + enabled: true + type: ClusterIP + targetSelector: elasticsearch + ports: + elasticsearch: + enabled: true + primary: true + port: 9200 + targetPort: 9200 + targetSelector: elasticsearch +{{- end -}} diff --git a/library/ix-dev/charts/diskoverdata/templates/common.yaml b/library/ix-dev/charts/diskoverdata/templates/common.yaml new file mode 100644 index 0000000000..28349243dd --- /dev/null +++ b/library/ix-dev/charts/diskoverdata/templates/common.yaml @@ -0,0 +1,15 @@ +{{- include "ix.v1.common.loader.init" . -}} + +{{- include "diskover.migration" $ -}} + +{{/* Merge the templates with Values */}} +{{- $_ := mustMergeOverwrite .Values (include "diskover.configuration" $ | fromYaml) -}} +{{- $_ := mustMergeOverwrite .Values (include "diskover.workload" $ | fromYaml) -}} +{{- $_ := mustMergeOverwrite .Values (include "es.workload" $ | fromYaml) -}} +{{- $_ := mustMergeOverwrite .Values (include "diskover.service" $ | fromYaml) -}} +{{- $_ := mustMergeOverwrite .Values (include "diskover.persistence" $ | fromYaml) -}} + +{{/* Create the configmap for portal manually */}} +{{- include "diskover.portal" $ -}} + +{{- include "ix.v1.common.loader.apply" . -}} diff --git a/library/ix-dev/charts/diskoverdata/templates/deployment.yaml b/library/ix-dev/charts/diskoverdata/templates/deployment.yaml deleted file mode 100644 index d1253c0b4e..0000000000 --- a/library/ix-dev/charts/diskoverdata/templates/deployment.yaml +++ /dev/null @@ -1,160 +0,0 @@ -{{ include "common.storage.hostPathValidate" .Values }} -{{ $elastic_search := (. | mustDeepCopy) }} -{{ $_ := set $elastic_search "common" (dict "nameSuffix" "elasticsearch") }} - -apiVersion: {{ template "common.capabilities.deployment.apiVersion" . }} -kind: Deployment -metadata: - name: {{ template "common.names.fullname" . }}-diskover - labels: - app: {{ template "common.names.name" . }} - chart: {{ template "common.names.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} - annotations: - rollme: {{ randAlphaNum 5 | quote }} -spec: - replicas: {{ (default 1 .Values.replicas) }} - strategy: - type: "Recreate" - selector: - matchLabels: - app: {{ template "common.names.name" . }} - release: {{ .Release.Name }} - template: - metadata: - name: {{ template "common.names.fullname" . }} - labels: - app: {{ template "common.names.name" . }} - release: {{ .Release.Name }} - {{- include "common.labels.selectorLabels" . | nindent 8 }} - annotations: {{ include "common.annotations" . | nindent 8 }} - spec: - initContainers: - - name: init-config - {{ include "common.containers.imageConfig" .Values.python.image | nindent 10 }} - command: ["python3", "/init_scripts/init_config.py"] - env: - {{ $envList := (default list .Values.environmentVariables) }} - {{ $envList = mustAppend $envList (dict "name" "TZ" "value" .Values.timezone) }} - {{ $envList = mustAppend $envList (dict "name" "DS_USER" "valueFromSecret" true "secretName" "diskover-credentials" "secretKey" "username") }} - {{ $envList = mustAppend $envList (dict "name" "DS_PASS" "valueFromSecret" true "secretName" "diskover-credentials" "secretKey" "password") }} - {{ include "common.containers.environmentVariables" (dict "environmentVariables" $envList) | nindent 12 }} - {{ include "elasticsearch.IP" $elastic_search | nindent 12 }} - {{ include "elasticsearch.credentials" . | nindent 12 }} - {{ $configPath := (dict "mountPath" (printf "%s/diskover-web.conf.d/" .Values.appVolumeMounts.config.mountPath) "configFile" "Constants.php") }} - {{ include "config.file.path" $configPath | nindent 12 }} - - volumeMounts: {{ include "common.storage.configureAppVolumeMountsInContainer" .Values | nindent 12 }} - - name: diskover-initial-scripts - mountPath: /init_scripts/ - - name: wait-es-search - {{ include "common.containers.imageConfig" .Values.image | nindent 10 }} - env: - {{ include "elasticsearch.IP" $elastic_search | nindent 12 }} - command: ["python3", "/init_scripts/wait_for_elastic_search.py"] - - volumeMounts: - - name: diskover-initial-scripts - mountPath: /init_scripts/ - - name: init-es-config - {{ include "common.containers.imageConfig" .Values.python.image | nindent 10 }} - command: ["python3", "/init_scripts/initial_es_config.py"] - env: - {{ $envListConfig := (default list .Values.environmentVariables) }} - {{ include "elasticsearch.IP" $elastic_search | nindent 12 }} - {{ include "elasticsearch.credentials" . | nindent 12 }} - {{ $configPathES := (dict "mountPath" (printf "%s/diskover.conf.d/diskover/" .Values.appVolumeMounts.config.mountPath) "configFile" "config.yaml") }} - {{ include "config.file.path" $configPathES | nindent 12 }} - {{ include "common.containers.environmentVariables" (dict "environmentVariables" $envListConfig) | nindent 12 }} - - volumeMounts: {{ include "common.storage.configureAppVolumeMountsInContainer" .Values | nindent 12 }} - - name: diskover-initial-scripts - mountPath: /init_scripts/ - - containers: - - name: {{ .Chart.Name }} - {{ include "common.resources.limitation" . | nindent 10 }} - {{ include "common.containers.imageConfig" .Values.image | nindent 10 }} - volumeMounts: {{ include "common.storage.configureAppVolumeMountsInContainer" .Values | nindent 12 }} - - name: diskover-initial-scripts - mountPath: /init_scripts/ - {{ range $index, $hostPathConfiguration := .Values.extraAppVolumeMounts }} - - name: extrappvolume-{{ $index }} - mountPath: {{ $hostPathConfiguration.mountPath }} - {{ end }} - {{ range $index, $hostPathConfiguration := .Values.extraDataVolumeMounts }} - - name: extradatavolume-{{ $index }} - mountPath: {{ $hostPathConfiguration.mountPath }} - {{ end }} - - ports: - - name: web - containerPort: 80 - {{ $cronjobSchedule := .Values.cronjobSchedule }} - lifecycle: - postStart: - exec: - command: - - /bin/sh - - -c - - | - ./init_scripts/.default_crawler.sh /app/diskover/diskover.py /data; - {{ range $index, $hostPathConfiguration := .Values.extraDataVolumeMounts }} - ./init_scripts/.default_crawler.sh /app/diskover/diskover.py {{ $hostPathConfiguration.mountPath }}; - {{ end }} - {{ range $index, $hostPathConfiguration := .Values.extraDataVolumeMounts }} - echo "{{$cronjobSchedule}} python3 /app/diskover/diskover.py {{ $hostPathConfiguration.mountPath }}" >> /config/crontab; - {{ end }} - echo "{{.Values.cronjobSchedule}} python3 /app/diskover/diskover.py /data" >> /config/crontab; - crontab /config/crontab; - env: - {{ $envListDiskover := (default list .Values.environmentVariables) }} - {{ $envListDiskover = mustAppend $envListDiskover (dict "name" "PUID" "value" .Values.ownerUID) }} - {{ $envListDiskover = mustAppend $envListDiskover (dict "name" "PGID" "value" .Values.ownerGID) }} - {{ include "common.containers.environmentVariables" (dict "environmentVariables" $envListDiskover) | nindent 12 }} - livenessProbe: - httpGet: - path: /login.php - port: 80 - initialDelaySeconds: 10 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 5 - successThreshold: 1 - readinessProbe: - httpGet: - path: /login.php - port: 80 - initialDelaySeconds: 10 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 5 - successThreshold: 2 - startupProbe: - httpGet: - path: /login.php - port: 80 - initialDelaySeconds: 10 - periodSeconds: 5 - timeoutSeconds: 2 - failureThreshold: 60 - successThreshold: 1 - -{{ include "common.networking.dnsConfiguration" .Values | nindent 6 }} - volumes: {{ include "common.storage.configureAppVolumes" .Values | nindent 8 }} - {{ range $index, $hostPathConfiguration := .Values.extraAppVolumeMounts }} - - name: extrappvolume-{{ $index }} - hostPath: - path: {{ $hostPathConfiguration.hostPath }} - {{ end }} - {{ range $index, $hostPathConfiguration := .Values.extraDataVolumeMounts }} - - name: extradatavolume-{{ $index }} - hostPath: - path: {{ $hostPathConfiguration.hostPath }} - {{ end }} - - - name: diskover-initial-scripts - configMap: - defaultMode: 0700 - name: "diskover-initial-scripts" diff --git a/library/ix-dev/charts/diskoverdata/templates/diskover-secrets.yaml b/library/ix-dev/charts/diskoverdata/templates/diskover-secrets.yaml deleted file mode 100644 index e1dfb1010b..0000000000 --- a/library/ix-dev/charts/diskoverdata/templates/diskover-secrets.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: diskover-credentials - labels: {{ include "common.labels" . | nindent 4 }} -type: Opaque -data: - username: {{ .Values.diskoverCredentials.username | b64enc | quote }} - password: {{ .Values.diskoverCredentials.password | b64enc | quote }} diff --git a/library/ix-dev/charts/diskoverdata/templates/elastic_search_deployment.yaml b/library/ix-dev/charts/diskoverdata/templates/elastic_search_deployment.yaml deleted file mode 100644 index 8d2042e216..0000000000 --- a/library/ix-dev/charts/diskoverdata/templates/elastic_search_deployment.yaml +++ /dev/null @@ -1,46 +0,0 @@ -{{ $values := (. | mustDeepCopy) }} -{{ $_ := set $values "common" (dict "nameSuffix" "elasticsearch-es") }} -{{ include "common.deployment.common_config" $values | nindent 0 }} -spec: {{ include "common.deployment.common_spec" $values | nindent 2 }} - template: {{ include "common.deployment.pod.metadata" $values | nindent 4 }} - spec: - containers: - - name: {{ .Chart.Name }} - {{ include "common.containers.imageConfig" .Values.elasticsearch.image | nindent 10 }} - volumeMounts: {{ include "common.storage.configureAppVolumeMountsInContainer" (dict "appVolumeMounts" .Values.elasticSearchAppVolumeMounts ) | nindent 12 }} - ports: - - name: es-port - containerPort: 9200 - env: - {{ $envList := (default list .Values.environmentVariables) }} - {{ $envList = mustAppend $envList (dict "name" "discovery.type" "value" "single-node") }} - {{ include "common.containers.environmentVariables" (dict "environmentVariables" $envList) | nindent 12 }} - livenessProbe: - httpGet: - path: / - port: 9200 - initialDelaySeconds: 10 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 5 - successThreshold: 1 - readinessProbe: - httpGet: - path: / - port: 9200 - initialDelaySeconds: 10 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 5 - successThreshold: 2 - startupProbe: - httpGet: - path: / - port: 9200 - initialDelaySeconds: 10 - periodSeconds: 5 - timeoutSeconds: 2 - failureThreshold: 60 - successThreshold: 1 -{{ include "common.networking.dnsConfiguration" .Values | nindent 6 }} - volumes: {{ include "common.storage.configureAppVolumes" (dict "appVolumeMounts" .Values.elasticSearchAppVolumeMounts "emptyDirVolumes" .Values.emptyDirVolumes "ixVolumes" .Values.ixVolumes) | nindent 8 }} diff --git a/library/ix-dev/charts/diskoverdata/templates/elastic_search_service.yaml b/library/ix-dev/charts/diskoverdata/templates/elastic_search_service.yaml deleted file mode 100644 index 7f054f2fe6..0000000000 --- a/library/ix-dev/charts/diskoverdata/templates/elastic_search_service.yaml +++ /dev/null @@ -1,6 +0,0 @@ -{{ $ports := list }} -{{ $ports = mustAppend $ports (dict "name" "es-port" "port" 9200 "targetPort" 9200) }} -{{ $values := (. | mustDeepCopy) }} -{{ $_ := set $values "common" (dict "nameSuffix" "elasticsearch-es") }} -{{ $_1 := set $values "commonService" (dict "type" "ClusterIP" "ports" $ports ) }} -{{ include "common.classes.service" $values }} diff --git a/library/ix-dev/charts/diskoverdata/templates/elasticsearch-secret.yaml b/library/ix-dev/charts/diskoverdata/templates/elasticsearch-secret.yaml deleted file mode 100644 index bb82255889..0000000000 --- a/library/ix-dev/charts/diskoverdata/templates/elasticsearch-secret.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: elastic-search-credentials - labels: {{ include "common.labels" . | nindent 4 }} -type: Opaque -data: - es-username: {{ "elastic" | b64enc | quote }} - es-password: {{ "changeme" | b64enc | quote }} diff --git a/library/ix-dev/charts/diskoverdata/templates/initial_scripts.yaml b/library/ix-dev/charts/diskoverdata/templates/initial_scripts.yaml deleted file mode 100644 index f5edb47ea4..0000000000 --- a/library/ix-dev/charts/diskoverdata/templates/initial_scripts.yaml +++ /dev/null @@ -1,270 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: "diskover-initial-scripts" - annotations: - rollme: {{ randAlphaNum 5 | quote }} - -data: - wait_for_elastic_search.py: |- - # This Script Wait for elastic search to setup completely - import requests - import os - import time - timeout = 100 - while True: - try: - if timeout < 0: - print("timeout") - raise requests.exceptions.ConnectTimeout("Elasticsearch is not responding") - timeout -= 1 - response = requests.get(f"http://{os.environ['ES_HOST']}:{os.environ['ES_PORT']}") - if response.status_code == 200: - break - except requests.exceptions.ConnectTimeout as e: - print(e) - break - except requests.exceptions.ConnectionError: - print("Trying to connect to elastic search") - time.sleep(3) - - - .default_crawler.sh: |- - #!/bin/sh - while : - do - # this condition wait for the script to copy into the container . - if test -f "$1"; then - # Empty folders don't generate indices . if folder is empty a default file is generated - if ! [ "$(ls -A $2)" ]; then - echo "Dummy file created as empty dirs are rejected" > $2/diskover_test.txt; - fi - python3 $1 $2/; - break; - fi - sleep 5 - - done - - init_config.py: |- - import os - Config = f""" ['doc', 'docx', 'odt', 'pdf', 'tex', 'wpd', 'wks', 'txt', 'rtf', 'key', 'odp', 'pps', 'ppt', 'pptx', 'ods', 'xls', 'xlsm', 'xlsx'], - 'images' => ['ai', 'bmp', 'gif', 'ico', 'jpeg', 'jpg', 'png', 'ps', 'psd', 'psp', 'svg', 'tif', 'tiff', 'exr', 'tga'], - 'video' => ['3g2', '3gp', 'avi', 'flv', 'h264', 'm4v', 'mkv', 'qt', 'mov', 'mp4', 'mpg', 'mpeg', 'rm', 'swf', 'vob', 'wmv', 'ogg', 'ogv', 'webm'], - 'audio' => ['au', 'aif', 'aiff', 'cda', 'mid', 'midi', 'mp3', 'm4a', 'mpa', 'ogg', 'wav', 'wma', 'wpl'], - 'apps' => ['apk', 'exe', 'bat', 'bin', 'cgi', 'pl', 'gadget', 'com', 'jar', 'msi', 'py', 'wsf'], - 'programming' => ['c', 'cgi', 'pl', 'class', 'cpp', 'cs', 'h', 'java', 'php', 'py', 'sh', 'swift', 'vb'], - 'internet' => ['asp', 'aspx', 'cer', 'cfm', 'cgi', 'pl', 'css', 'htm', 'html', 'js', 'jsp', 'part', 'php', 'py', 'rss', 'xhtml'], - 'system' => ['bak', 'cab', 'cfg', 'cpl', 'cur', 'dll', 'dmp', 'drv', 'icns', 'ico', 'ini', 'lnk', 'msi', 'sys', 'tmp', 'vdi', 'raw'], - 'data' => ['csv', 'dat', 'db', 'dbf', 'log', 'mdb', 'sav', 'sql', 'tar', 'xml'], - 'disc' => ['bin', 'dmg', 'iso', 'toast', 'vcd', 'img'], - 'compressed' => ['7z', 'arj', 'deb', 'pkg', 'rar', 'rpm', 'tar', 'gz', 'z', 'zip'], - 'trash' => ['old', 'trash', 'tmp', 'temp', 'junk', 'recycle', 'delete', 'deleteme', 'clean', 'remove'] - ]; - - // extra fields for search results and view file/dir info pages - // key is description for field and value is ES field name - // Example: - //const EXTRA_FIELDS = [ - // 'Date Changed' => 'ctime' - //]; - const EXTRA_FIELDS = []; - - // Maximum number of indices to load by default, indices are loaded in order by creation date - // setting this too high can cause slow logins and other timeout issues - // This setting can bo overridden on indices page per user and stored in maxindex cookie - // If MAX_INDEX is set higher than maxindex browser cookie, the cookie will be set to this value - const MAX_INDEX = 250; - - // time in seconds for index info to be cached, clicking reload indices forces update - const INDEXINFO_CACHETIME = 600; - - // time in seconds to check Elasticsearch for new index info - const NEWINDEX_CHECKTIME = 10; - - // sqlite database file path - const DATABASE = '../diskoverdb.sqlite3'; - }}`}} - - """ - - os.makedirs(os.environ['DEST'], exist_ok=True) - path = os.path.join(os.environ['DEST'], os.environ['FILE']) - with open(path, 'w') as w: - w.write(Config) - - - initial_es_config.py: |- - import os - Config = f"""# diskover default/sample config file - # - # default search paths for config - # macOS: ~/.config/diskover and ~/Library/Application Support/diskover - # Other Unix: ~/.config/diskover and /etc/diskover - # Windows: %APPDATA%\diskover where the APPDATA environment variable falls back to %HOME%\AppData\Roaming if undefined - # - appName: diskover - #logLevel: WARN - #logLevel: DEBUG - logLevel: INFO - logToFile: False - #logToFile: True - logDirectory: /tmp/ - - diskover: - # max number of crawl threads - # a thread is created up to maxthreads for each directory at level 1 of tree dir arg - # set to a number or leave blank to auto set based on number of cpus - #maxthreads: 20 - maxthreads: - # block size used for du size - blocksize: 512 - excludes: - # directory names and absolute paths you want to exclude from crawl - # directory excludes uses python re.search for string search (regex) - # directory excludes are case-sensitive - # Examples: .* or .backup or .backup* or /dir/dirname - # to exclude none use empty list [] - dirs: [".*", ".snapshot", ".Snapshot", "~snapshot", "~Snapshot", ".zfs"] - #dirs: [] - # files you want to exclude from crawl - # can include wildcards (.*, *.doc or NULLEXT for files with no extension) - # file names are case-sensitive, extensions are not - files: [".*", "Thumbs.db", ".DS_Store", "._.DS_Store", ".localized", "desktop.ini"] - #files: [] - # exclude empty 0 byte files, set to True to exclude empty files or False to not exclude - emptyfiles: True - # exclude empty dirs, set to True to exclude empty dirs or False to not exclude - emptydirs: True - # exclude files smaller than min size in bytes - minfilesize: 1 - #minfilesize: 512 - # exclude files modified less than x days ago - minmtime: 0 - #minmtime: 30 - # exclude files modified more than x days ago - maxmtime: 36500 - # exclude files changed less than x days ago - minctime: 0 - # exclude files changed more than x days ago - maxctime: 36500 - # exclude files accessed less than x days ago - minatime: 0 - # exclude files accessed more than x days ago - maxatime: 36500 - includes: - # directory names and absolute paths you want to include (whitelist), case-sensitive, - # to include none use empty list [] - #dirs: [".recycle"] - dirs: [] - # files you want to include (whitelist), case-sensitive - files: [] - ownersgroups: - # control how owner (username) and group fields are stored for file and directory docs - # store uid and gid's instead of trying to get owner and group names - uidgidonly: False - # owner/group names contain domain name set to True - domain: False - # character separator used on cifs/nfs mounts to separte user/group and domain name, usually \ or @ - domainsep: \ - # if domain name comes first before character separator, set this to True, otherwise False - domainfirst: True - # when indexing owner and group fields, keep the domain name - keepdomain: False - replacepaths: - # translate path names set to True to enable or False to disable. - # Set to True if crawling in Windows to replace drive letters and \ with / - replace: False - #from: /mnt/ - #to: /vols/ - from: - to: - plugins: - # set to True to enable all plugins or False to disable all plugins - enable: False - # list of plugins (by name) to use for directories - dirs: ['unixperms'] - # list of plugins (by name) to use for files - files: ['unixperms'] - other: - # restore atime/mtime for files and dirs during crawl - # set to True or False, default False (useful for cifs which does not work with noatime mount option) - # for nfs, it's preferable to use mount options ro,noatime,nodiratime - restoretimes: False - - databases: - elasticsearch: - host: '{os.environ['ES_HOST']}' - port: {os.environ['ES_PORT']} - #user: elastic - #password: changeme - user: '{os.environ['ES_USER']}' - password: '{os.environ['ES_PASS']}' - # set https to True if using HTTP TLS/SSL or False if using http - # for AWS ES, you will most likely want to set this to True - # override with env var ES_HTTPS - https: False - # compress http data - # for AWS ES, you will most likely want to set this to True - httpcompress: False - # timeout for connection to ES (default is 10) - timeout: 30 - # number of connections kept open to ES when crawling (default is 10) - maxsize: 20 - # max retries for ES operations (default is 0) - maxretries: 10 - # wait for at least yellow status before bulk uploading (default is False), set to True if you want to wait - wait: False - # chunk size for ES bulk operations (default is 500) - chunksize: 1000 - # the below settings are to optimize ES for crawling - # index refresh interval (default is 1s), set to -1 to disable refresh during crawl (fastest performance but no index searches), after crawl is set back to 1s - indexrefresh: 30s - # transaction log flush threshold size (default 512mb) - translogsize: 1gb - # transaction log sync interval time (default 5s) - translogsyncint: 30s - # search scroll size (default 100 docs) - scrollsize: 1000 - """ - - os.makedirs(os.environ['DEST'], exist_ok=True) - path = os.path.join(os.environ['DEST'], os.environ['FILE']) - - with open(path, 'w') as w: - w.write(Config) diff --git a/library/ix-dev/charts/diskoverdata/templates/pre-install-job.yaml b/library/ix-dev/charts/diskoverdata/templates/pre-install-job.yaml deleted file mode 100644 index ef2e8691c4..0000000000 --- a/library/ix-dev/charts/diskoverdata/templates/pre-install-job.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: "{{ template "common.names.fullname" . }}-preinstall-job" - labels: - app.kubernetes.io/managed-by: {{ .Release.Service | quote }} - app.kubernetes.io/instance: {{ .Release.Name | quote }} - app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} - helm.sh/chart: {{ template "common.names.chart" . }} - annotations: - "helm.sh/hook": pre-install - "helm.sh/hook-delete-policy": hook-succeeded -spec: - template: - metadata: - name: "{{ template "common.names.fullname" . }}-preinstall-hook" - labels: - app.kubernetes.io/managed-by: {{ .Release.Service | quote }} - app.kubernetes.io/instance: {{ .Release.Name | quote }} - helm.sh/chart: {{ template "common.names.chart" . }} - spec: - restartPolicy: Never - containers: - - name: pre-install-job - image: "alpine:latest" - command: ["/bin/sh", "-c"] - args: - - {{ include "add.user" . }} - {{ include "change.user.permissions" . }} - volumeMounts: {{ include "common.storage.configureAppVolumeMountsInContainer" (dict "appVolumeMounts" .Values.elasticSearchAppVolumeMounts ) | nindent 12 }} - volumes: {{ include "common.storage.configureAppVolumes" (dict "appVolumeMounts" .Values.elasticSearchAppVolumeMounts "emptyDirVolumes" .Values.emptyDirVolumes "ixVolumes" .Values.ixVolumes) | nindent 8 }} diff --git a/library/ix-dev/charts/diskoverdata/templates/service.yaml b/library/ix-dev/charts/diskoverdata/templates/service.yaml deleted file mode 100644 index ee91e81309..0000000000 --- a/library/ix-dev/charts/diskoverdata/templates/service.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{ $svc := .Values.service }} -{{ $selectors := list }} -{{ $selectors = mustAppend $selectors (dict "key" "app" "value" (include "common.names.name" .) ) }} -{{ $selectors = mustAppend $selectors (dict "key" "release" "value" .Release.Name ) }} -{{ $ports := list }} -{{ $ports = mustAppend $ports (dict "name" "web" "port" .Values.web_port "nodePort" .Values.web_port "targetPort" 80) }} -{{ $params := . }} -{{ $_ := set $params "commonService" (dict "type" "NodePort" "ports" $ports ) }} -{{ $_1 := set .Values "extraSelectorLabels" $selectors }} -{{ include "common.classes.service" $params }} diff --git a/library/ix-dev/charts/diskoverdata/templates/tests/deployment-check.yaml b/library/ix-dev/charts/diskoverdata/templates/tests/deployment-check.yaml deleted file mode 100644 index 7e888eb0b9..0000000000 --- a/library/ix-dev/charts/diskoverdata/templates/tests/deployment-check.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- $serviceName := (include "common.names.fullname" .) -}} -apiVersion: v1 -kind: Pod -metadata: - name: {{ .Release.Name }}-test-pod - labels: - app: {{ .Release.Name }} - release: {{ .Release.Name }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: test-curl - image: alpine/curl - imagePullPolicy: "IfNotPresent" - command: - - /bin/sh - - -ec - - | - curl --connect-timeout 5 --max-time 10 --retry 5 --retry-delay 15 --retry-max-time 90 --retry-all-errors -ksf http://{{ $serviceName }}:{{.Values.web_port}}/ - restartPolicy: Never diff --git a/library/ix-dev/charts/diskoverdata/to_keep_versions.md b/library/ix-dev/charts/diskoverdata/to_keep_versions.md new file mode 100644 index 0000000000..a3d09442c3 --- /dev/null +++ b/library/ix-dev/charts/diskoverdata/to_keep_versions.md @@ -0,0 +1,4 @@ +# 1.0.15 + +This version is kept because it contains a fix that is needed for migration to v2.x.x +It should be safe to remove few months after v2.x.x is released. diff --git a/library/ix-dev/charts/diskoverdata/to_keep_versions.yaml b/library/ix-dev/charts/diskoverdata/to_keep_versions.yaml new file mode 100644 index 0000000000..1c05288cf2 --- /dev/null +++ b/library/ix-dev/charts/diskoverdata/to_keep_versions.yaml @@ -0,0 +1 @@ +- 1.0.15 diff --git a/library/ix-dev/charts/diskoverdata/values.yaml b/library/ix-dev/charts/diskoverdata/values.yaml index cf372ce49c..db702b5582 100644 --- a/library/ix-dev/charts/diskoverdata/values.yaml +++ b/library/ix-dev/charts/diskoverdata/values.yaml @@ -3,16 +3,44 @@ image: repository: linuxserver/diskover tag: "2.0.1" -elasticsearch: - image: - pullPolicy: IfNotPresent - repository: elasticsearch - tag: "7.5.2" +elasticSearchImage: + pullPolicy: IfNotPresent + repository: elasticsearch + tag: "7.5.2" -python: - image: - pullPolicy: IfNotPresent - repository: python - tag: "3.10" +resources: + limits: + cpu: 4000m + memory: 8Gi -es_user: elasticsearch +podOptions: + dnsConfig: + options: [] + +diskoverConfig: + cronSchedule: '' + username: '' + password: '' + additionalEnvs: [] + +diskoverID: + user: 568 + group: 568 + +diskoverNetwork: + webPort: 32000 + +diskoverStorage: + config: + type: ixVolume + ixVolumeConfig: + datasetName: config + data: + type: ixVolume + ixVolumeConfig: + datasetName: data + esdata: + type: ixVolume + ixVolumeConfig: + datasetName: esdata + additionalStorages: []