From 798c9f1d85257e32d7060275244777866b8abbaf Mon Sep 17 00:00:00 2001 From: Stavros Kois <47820033+stavros-k@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:57:30 +0200 Subject: [PATCH] Storj - migrate library (#2219) * storj - migrate library * dep update * lint * update ui * fixup * add migartion * fix type * fix script * fix perms * move over * valid key * update migration --- library/ix-dev/charts/storj/Chart.lock | 8 +- library/ix-dev/charts/storj/Chart.yaml | 6 +- library/ix-dev/charts/storj/README.md | 8 +- library/ix-dev/charts/storj/app-readme.md | 8 +- .../charts/storj/charts/common-1.2.9.tgz | Bin 0 -> 63210 bytes .../charts/storj/charts/common-2304.0.1.tgz | Bin 4994 -> 0 bytes .../ix-dev/charts/storj/ci/basic-values.yaml | 20 + .../ix-dev/charts/storj/ci/test-values.yaml | 25 - library/ix-dev/charts/storj/metadata.yaml | 10 +- .../ix-dev/charts/storj/migrations/migrate | 104 +++ library/ix-dev/charts/storj/questions.yaml | 707 ++++++++++++------ .../ix-dev/charts/storj/templates/NOTES.txt | 1 + .../charts/storj/templates/_configuration.tpl | 56 ++ .../charts/storj/templates/_helpers.tpl | 6 - .../charts/storj/templates/_migration.tpl | 35 + .../charts/storj/templates/_persistence.tpl | 63 ++ .../ix-dev/charts/storj/templates/_portal.tpl | 12 + .../charts/storj/templates/_service.tpl | 29 + .../ix-dev/charts/storj/templates/_storj.tpl | 93 +++ .../ix-dev/charts/storj/templates/common.yaml | 14 + .../charts/storj/templates/deployment.yaml | 109 --- .../storj/templates/initial_scripts.yaml | 18 - .../storj/templates/pre-install-job.yaml | 28 - .../charts/storj/templates/service.yaml | 11 - .../charts/storj/templates/storj-secrets.yaml | 9 - .../ix-dev/charts/storj/to_keep_versions.md | 4 + .../ix-dev/charts/storj/to_keep_versions.yaml | 1 + library/ix-dev/charts/storj/values.yaml | 46 +- 28 files changed, 979 insertions(+), 452 deletions(-) create mode 100644 library/ix-dev/charts/storj/charts/common-1.2.9.tgz delete mode 100644 library/ix-dev/charts/storj/charts/common-2304.0.1.tgz create mode 100644 library/ix-dev/charts/storj/ci/basic-values.yaml delete mode 100644 library/ix-dev/charts/storj/ci/test-values.yaml create mode 100755 library/ix-dev/charts/storj/migrations/migrate create mode 100644 library/ix-dev/charts/storj/templates/NOTES.txt create mode 100644 library/ix-dev/charts/storj/templates/_configuration.tpl delete mode 100644 library/ix-dev/charts/storj/templates/_helpers.tpl create mode 100644 library/ix-dev/charts/storj/templates/_migration.tpl create mode 100644 library/ix-dev/charts/storj/templates/_persistence.tpl create mode 100644 library/ix-dev/charts/storj/templates/_portal.tpl create mode 100644 library/ix-dev/charts/storj/templates/_service.tpl create mode 100644 library/ix-dev/charts/storj/templates/_storj.tpl create mode 100644 library/ix-dev/charts/storj/templates/common.yaml delete mode 100644 library/ix-dev/charts/storj/templates/deployment.yaml delete mode 100644 library/ix-dev/charts/storj/templates/initial_scripts.yaml delete mode 100644 library/ix-dev/charts/storj/templates/pre-install-job.yaml delete mode 100644 library/ix-dev/charts/storj/templates/service.yaml delete mode 100644 library/ix-dev/charts/storj/templates/storj-secrets.yaml create mode 100644 library/ix-dev/charts/storj/to_keep_versions.md create mode 100644 library/ix-dev/charts/storj/to_keep_versions.yaml diff --git a/library/ix-dev/charts/storj/Chart.lock b/library/ix-dev/charts/storj/Chart.lock index 2ad92e6e16..b6ab3f799b 100644 --- a/library/ix-dev/charts/storj/Chart.lock +++ b/library/ix-dev/charts/storj/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:41:28.076153579+03:00" + repository: file://../../../common + version: 1.2.9 +digest: sha256:af1a9a1f87e3e48453c9f25f909f5ebcd7fa6e25162b7b425448ba752bcdbc5c +generated: "2024-02-23T17:46:31.084498341+02:00" diff --git a/library/ix-dev/charts/storj/Chart.yaml b/library/ix-dev/charts/storj/Chart.yaml index a768d45836..7423f0dfd9 100644 --- a/library/ix-dev/charts/storj/Chart.yaml +++ b/library/ix-dev/charts/storj/Chart.yaml @@ -3,7 +3,7 @@ description: Share your storage on the internet and earn. annotations: title: Storj type: application -version: 1.0.18 +version: 2.0.0 apiVersion: v2 appVersion: v1.68.2 kubeVersion: '>=1.16.0-0' @@ -13,8 +13,8 @@ maintainers: 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://www.storj.io icon: https://media.sys.truenas.net/apps/storj/icons/icon.svg sources: diff --git a/library/ix-dev/charts/storj/README.md b/library/ix-dev/charts/storj/README.md index fc839a185b..40ef8c95cc 100644 --- a/library/ix-dev/charts/storj/README.md +++ b/library/ix-dev/charts/storj/README.md @@ -1,3 +1,7 @@ -# storj +# Storj -[storj](https://www.storj.io/) share your extra storage and earn money +[Storj](https://www.storj.io/) - share your extra storage and earn money + +During the first startup a container with root privileges is created. +And it will generate an identity (if it doesn't exist) +After the identity is created, the container will run as a non-root user. diff --git a/library/ix-dev/charts/storj/app-readme.md b/library/ix-dev/charts/storj/app-readme.md index fc839a185b..40ef8c95cc 100644 --- a/library/ix-dev/charts/storj/app-readme.md +++ b/library/ix-dev/charts/storj/app-readme.md @@ -1,3 +1,7 @@ -# storj +# Storj -[storj](https://www.storj.io/) share your extra storage and earn money +[Storj](https://www.storj.io/) - share your extra storage and earn money + +During the first startup a container with root privileges is created. +And it will generate an identity (if it doesn't exist) +After the identity is created, the container will run as a non-root user. diff --git a/library/ix-dev/charts/storj/charts/common-1.2.9.tgz b/library/ix-dev/charts/storj/charts/common-1.2.9.tgz new file mode 100644 index 0000000000000000000000000000000000000000..c69e6d479f05ba05becb2e99740a416ebc5185a4 GIT binary patch literal 63210 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 DZ|8@u literal 0 HcmV?d00001 diff --git a/library/ix-dev/charts/storj/charts/common-2304.0.1.tgz b/library/ix-dev/charts/storj/charts/common-2304.0.1.tgz deleted file mode 100644 index 5d289674bad3591cdbe3efa0223d59db73c3adac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4994 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 M|M_hRSpbRv06i53MgRZ+ diff --git a/library/ix-dev/charts/storj/ci/basic-values.yaml b/library/ix-dev/charts/storj/ci/basic-values.yaml new file mode 100644 index 0000000000..11d029099f --- /dev/null +++ b/library/ix-dev/charts/storj/ci/basic-values.yaml @@ -0,0 +1,20 @@ +storjConfig: + wallet: 0xab00000999999aaaaaaaccccccdddddddfffffff + authToken: user:sasjkadkjhakdlaskdlkajsd + email: user@example.com + domainAddress: localhost + gracePeriod: 120 + storageSizeGB: 500 + wallets: + zkSync: true + zkSyncEra: true + +storjStorage: + data: + type: pvc + identity: + type: pvc + +storjNetwork: + webPort: 30909 + p2pPort: 32767 diff --git a/library/ix-dev/charts/storj/ci/test-values.yaml b/library/ix-dev/charts/storj/ci/test-values.yaml deleted file mode 100644 index 5f75e84923..0000000000 --- a/library/ix-dev/charts/storj/ci/test-values.yaml +++ /dev/null @@ -1,25 +0,0 @@ -appVolumeMounts: - data: - emptyDir: true - mountPath: /app/config - identity: - emptyDir: true - mountPath: /app/identity -authToken: user:sasjkadkjhakdlaskdlkajsd -dnsConfig: - options: [] -domainAddress: localhost -email: user@example.com -emptyDirVolumes: true -environmentVariables: [] -extraAppVolumeMounts: [] -terminationGracePeriod: 120 -identityCreationMountPath: /root/.local/share/storj/identity/storagenode -nodePort: 30002 -runAsGroup: 568 -runAsUser: 568 -storageSize: 500 -wallet: 0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -webPort: 30000 -zksync: true -zksyncEra: true diff --git a/library/ix-dev/charts/storj/metadata.yaml b/library/ix-dev/charts/storj/metadata.yaml index cfbb743a32..ff77dff102 100644 --- a/library/ix-dev/charts/storj/metadata.yaml +++ b/library/ix-dev/charts/storj/metadata.yaml @@ -1,9 +1,9 @@ runAsContext: - - userName: root - groupName: root - gid: 0 - uid: 0 - description: Storj runs as root user. + - userName: storj + groupName: storj + gid: 568 + uid: 568 + description: Storj runs as non-root user. capabilities: - name: CHOWN description: Storj is able to chown files. diff --git a/library/ix-dev/charts/storj/migrations/migrate b/library/ix-dev/charts/storj/migrations/migrate new file mode 100755 index 0000000000..00d7ba6d80 --- /dev/null +++ b/library/ix-dev/charts/storj/migrations/migrate @@ -0,0 +1,104 @@ +#!/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 = [ + 'enableResourceLimits', 'cpuLimit', 'memLimit', 'dnsConfig', + 'environmentVariables', 'runAsUser', 'runAsGroup', 'webPort', + 'nodePort', 'wallet', 'authToken', 'email', 'domainAddress', + 'terminationGracePeriod', 'storageSize', 'zksync', 'zksyncEra', + 'extraAppVolumeMounts', 'appVolumeMounts', 'identityCreationMountPath', + ] + + values.update({ + # Migrate Network + 'storjNetwork': { + 'webPort': values['webPort'], + 'p2pPort': values['nodePort'], + }, + # Migrate Resources + 'resources': { + 'limits': { + 'cpu': values.get('cpuLimit', '4000m'), + 'memory': values.get('memLimit', '8Gi'), + } + }, + # Migrate DNS + 'podOptions': { + 'dnsConfig': { + 'options': [ + {'name': opt['name'], 'value': opt['value']} + for opt in values.get('dnsConfig', {}).get('options', []) + ] + } + }, + # Migrate ID + 'storjRunAs': { + 'user': values['runAsUser'], + 'group': values['runAsGroup'], + }, + # Migrate Config + 'storjConfig': { + 'wallet': values['wallet'], + 'authToken': values['authToken'], + 'email': values['email'], + 'domainAddress': values['domainAddress'], + 'storageSizeGB': values['storageSize'], + 'gracePeriod': values['terminationGracePeriod'], + 'wallets': { + 'zkSync': values['zksync'], + 'zkSyncEra': values['zksyncEra'], + }, + 'additionalEnvs': [e for e in values.get('environmentVariables', [])], + }, + # Migrate Storage + 'storjStorage': { + 'data': migrate_volume(values['appVolumeMounts']['data']), + 'identity': migrate_volume(values['appVolumeMounts']['identity']), + 'additionalStorages': [ + { + 'type': 'hostPath', + 'hostPathConfig': {'hostPath': e['hostPath']}, + 'mountPath': e['mountPath'], + } + for e in values.get('extraAppVolumeMounts', []) + ], + }, + + }) + + 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/storj/questions.yaml b/library/ix-dev/charts/storj/questions.yaml index 99225f0665..c8d0b0782f 100644 --- a/library/ix-dev/charts/storj/questions.yaml +++ b/library/ix-dev/charts/storj/questions.yaml @@ -1,302 +1,551 @@ groups: - - name: "Configuration" - description: "Storj application configuration" - - name: "Storage" - description: "Configure storage for storj" - - name: "Networking" - description: "Networking Configuration for storj" - - name: "Advanced DNS Settings" - description: "Configure DNS settings" - - name: "Resource Limits" - description: "Set CPU/memory limits for Kubernetes Pod" + - name: Storj Configuration + description: Configure Storj + - name: User and Group Configuration + description: Configure User and Group for Storj + - name: Advanced Pod Configuration + description: Configure Advanced Pod Options for Storj + - name: Network Configuration + description: Configure Network for Storj + - name: Storage Configuration + description: Configure Storage for Storj + - name: Resources Configuration + description: Configure Resources for Storj portals: web_portal: protocols: - - "http" + - "$kubernetes-resource_configmap_portal_protocol" host: - - "$node_ip" + - "$kubernetes-resource_configmap_portal_host" ports: - - "$variable-webPort" - path: "/" + - "$kubernetes-resource_configmap_portal_port" + path: "$kubernetes-resource_configmap_portal_path" questions: - - variable: webPort - label: "Web Port for Storj" - group: Networking - schema: - type: int - min: 9000 - max: 65535 - default: 20909 - required: true - - variable: nodePort - label: "Node Port for Storj" - description: | - This port will be used for both TCP and UDP traffic.
- Note that this port must be open on your firewall and that internal - Storj port will not be affected by this change, but only the external (Node Port) - group: Networking - schema: - type: int - min: 9000 - max: 65535 - default: 28967 - required: true - - - variable: dnsConfig - label: "DNS Configuration" - group: "Advanced DNS Settings" + - variable: storjConfig + label: "" + group: Storj Configuration schema: type: dict attrs: - - variable: options - label: "DNS Options" + - variable: wallet + label: Wallet + description: The wallet to use for Storj. + schema: + type: string + required: true + private: true + - variable: authToken + label: Auth Token + description: The auth token to use for Storj. + schema: + type: string + required: true + private: true + - variable: email + label: Email + description: The email to use for Storj. + schema: + type: string + required: true + - variable: domainAddress + label: Domain Address + description: The domain address to use for Storj. + schema: + type: string + required: true + private: true + - variable: gracePeriod + label: Grace Period + description: The grace period to use for Storj. + schema: + type: int + min: 30 + default: 30 + required: true + - variable: storageSizeGB + label: Storage Size + description: The storage size to use for Storj. + schema: + type: int + min: 500 + default: 500 + required: true + - variable: wallets + label: Opt-in to additional Wallets + schema: + type: dict + attrs: + - variable: zkSync + label: zkSync + description: Appends "zksync" to --operator.wallet-features flag to the storagenode command + schema: + type: boolean + default: false + - variable: zkSyncEra + label: zkSync Era + description: Appends "zksync-era" to --operator.wallet-features flag to the storagenode command + schema: + type: boolean + default: false + - variable: additionalEnvs + label: Additional Environment Variables + description: Configure additional environment variables for Storj. schema: type: list + default: [] 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: wallet - label: "Configure Wallet for Storj" - group: "Configuration" + - variable: storjRunAs + label: "" + group: User and Group Configuration schema: - type: string - required: true + type: dict + attrs: + - variable: user + label: User ID + description: The user id that Storj will run as. + schema: + type: int + min: 568 + default: 568 + required: true + - variable: group + label: Group ID + description: The group id that Storj will run as. + schema: + type: int + min: 568 + default: 568 + required: true - - variable: authToken - label: "Configure Auth token for Storj Node" - group: "Configuration" + - variable: podOptions + label: "" + group: Advanced Pod Configuration schema: - type: string - required: true - - - variable: email - label: "Configure Email for Storj" - group: "Configuration" - schema: - type: string - required: true - - - variable: domainAddress - label: "Add Your Storage Domain for Storj" - group: "Configuration" - schema: - type: string - required: true - - - variable: zksync - label: "Opt-in to zkSync" - description: Appends "zksync" to --operator.wallet-features flag to the storagenode command - group: "Configuration" - schema: - type: boolean - default: false - - - variable: zksyncEra - label: "Opt-in to zkSync era" - description: Appends "zksync-era" to --operator.wallet-features flag to the storagenode command - group: "Configuration" - schema: - type: boolean - default: false - - - variable: terminationGracePeriod - label: Termination Grace Period - description: Optional duration in seconds the pod needs to terminate gracefully. - group: "Configuration" - schema: - type: int - min: 30 - default: 30 - - - variable: storageSize - label: "Configure Storage Size You Want To Share in GB's" - group: Storage - schema: - type: int - min: 500 - default: 500 - - - variable: runAsUser - label: "Owner User ID" - group: "Configuration" - schema: - immutable: true - type: int - default: 568 - min: 1 - max: 65535 - - - variable: runAsGroup - label: "Owner Group ID" - group: "Configuration" - schema: - immutable: true - type: int - default: 568 - min: 1 - max: 65535 - - - - variable: environmentVariables - label: "Storj Extra Environment Variables" - group: "Configuration" - schema: - type: list - default: [] - items: - - variable: environmentVariable - label: "Environment Variable" + type: dict + attrs: + - 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: appVolumeMounts - label: "Storj Configuration" - group: "Storage" + - variable: storjNetwork + label: "" + group: Network Configuration + schema: + type: dict + attrs: + - variable: webPort + label: Web Port + description: The port for the Storj Web UI. + schema: + type: int + default: 20909 + min: 9000 + max: 65535 + required: true + - variable: p2pPort + label: P2P Port + description: | + This port will be used for both TCP and UDP traffic.
+ Note that this port must be open on your firewall and that internal + Storj port will not be affected by this change, but only the external (Node Port) + schema: + type: int + default: 28967 + min: 9000 + max: 65535 + required: true + - variable: hostNetwork + label: Host Network + description: | + Enable host network for Storj + schema: + type: boolean + default: false + + - variable: storjStorage + label: "" + group: Storage Configuration schema: type: dict attrs: - variable: data - label: "Configuration Data Volume to Share on Storj" + label: Storj Data Storage + description: The path to store Storj Data. schema: type: dict attrs: - - variable: datasetName - label: "Configuration 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 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 Config schema: - type: path - hidden: true - editable: false - default: "/app/config" - - variable: hostPathEnabled - label: "Enable Custom Host Path for Storj Configuration 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 Storj Configuration Volume" + label: Host Path + description: The host path to use for storage. schema: type: hostpath + show_if: [["aclEnable", "=", false]] required: true - variable: identity - label: "Configure Identity Volume for Storage Node" + label: Storj Identity Storage + description: The path to store Storj Identity. schema: type: dict attrs: - - variable: datasetName - label: "Configure Storj Identity Volume to Allocate" + - 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_identity" - editable: false - - variable: mountPath - label: "Configure Storj Identity Volume to Allocate" - 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: "identity" + - variable: aclEntries + label: ACL Configuration + schema: + type: dict + show_if: [["aclEnable", "=", true]] + attrs: [] + - variable: hostPathConfig + label: Host Path Config schema: - type: path - hidden: true - editable: false - default: "/app/identity" - - variable: hostPathEnabled - label: "Enable Custom Host Path for Storj Identity 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 Storj identity Volume" + label: Host Path + description: The host path to use for storage. schema: type: hostpath + show_if: [["aclEnable", "=", false]] required: true - - variable: extraAppVolumeMounts - label: "Extra Host Path Volumes" - group: "Storage" + - variable: additionalStorages + label: Additional Storage + description: Additional storage for Storj. + 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: 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 Config + 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: list - items: - - variable: extraAppVolume - label: "Host Path Volume" - description: "Add an extra host path volume for storj" + type: dict + attrs: + - variable: limits + label: Limits schema: type: dict attrs: - - variable: mountPath - label: "Mount Path in Pod" - description: "Path where the volume will be mounted inside the pod." + - variable: cpu + label: CPU + description: CPU limit for Storj. schema: - type: path + 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: hostPath - label: "Host Path" - description: "Host path" + - variable: memory + label: Memory + description: Memory limit for Storj. schema: - type: hostpath + 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 - - - variable: enableResourceLimits - label: "Enable Pod resource limits" - group: "Resource Limits" - 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" diff --git a/library/ix-dev/charts/storj/templates/NOTES.txt b/library/ix-dev/charts/storj/templates/NOTES.txt new file mode 100644 index 0000000000..ba4e01146c --- /dev/null +++ b/library/ix-dev/charts/storj/templates/NOTES.txt @@ -0,0 +1 @@ +{{ include "ix.v1.common.lib.chart.notes" $ }} diff --git a/library/ix-dev/charts/storj/templates/_configuration.tpl b/library/ix-dev/charts/storj/templates/_configuration.tpl new file mode 100644 index 0000000000..0d963c3be3 --- /dev/null +++ b/library/ix-dev/charts/storj/templates/_configuration.tpl @@ -0,0 +1,56 @@ +{{- define "storj.configuration" -}} +secret: + storj: + enabled: true + data: + authToken: {{ .Values.storjConfig.authToken | quote }} + storj-config: + enabled: true + data: + EMAIL: {{ .Values.storjConfig.email }} + STORAGE: {{ printf "%vGB" .Values.storjConfig.storageSizeGB }} + ADDRESS: {{ printf "%s:%v" .Values.storjConfig.domainAddress .Values.storjNetwork.p2pPort }} + WALLET: {{ .Values.storjConfig.wallet | quote }} +configmap: + storj: + enabled: true + data: + init_config.sh: | + #!/bin/sh + echo "Checking for identity certificate" + if ! [ -f ${DEFAULT_CERT_PATH} ] && ! [ -f ${DEFAULT_IDENTITY_CERT_PATH} ]; then + echo "Downloading identity generator tool" + curl -L https://github.com/storj/storj/releases/latest/download/identity_linux_amd64.zip -o identity_linux_amd64.zip + unzip -o identity_linux_amd64.zip + chmod +x identity + echo "Generating identity certificate" + ./identity create storagenode + echo "Authorizing identity certificate" + ./identity authorize storagenode ${AUTH_KEY} + echo "Storagenode identity certificate generated" + chown -R {{ .Values.storjRunAs.user }}:{{ .Values.storjRunAs.group }} {{ template "storj.idPath" }} + else + echo "Identity certificate already exists" + fi +{{- end -}} + +{{- define "storj.args" -}} + {{- $wallets := list -}} + {{- if .Values.storjConfig.wallets.zkSync -}} + {{- $wallets = mustAppend $wallets "zksync" -}} + {{- end -}} + + {{- if .Values.storjConfig.wallets.zkSyncEra -}} + {{- $wallets = mustAppend $wallets "zksync-era" -}} + {{- end -}} + +{{- if $wallets -}} +args: + - --operator.wallet-features={{ join "," $wallets }} +{{- end -}} + +{{- end -}} + +{{- define "storj.idPath" -}} + {{- print "/root/.local/share/storj/identity/storagenode" -}} +{{- end -}} diff --git a/library/ix-dev/charts/storj/templates/_helpers.tpl b/library/ix-dev/charts/storj/templates/_helpers.tpl deleted file mode 100644 index e0d5ef1a59..0000000000 --- a/library/ix-dev/charts/storj/templates/_helpers.tpl +++ /dev/null @@ -1,6 +0,0 @@ -{{/* -Retrieve cert path -*/}} -{{- define "certPath" -}} -{{ printf }} -{{- end -}} diff --git a/library/ix-dev/charts/storj/templates/_migration.tpl b/library/ix-dev/charts/storj/templates/_migration.tpl new file mode 100644 index 0000000000..5bee683e00 --- /dev/null +++ b/library/ix-dev/charts/storj/templates/_migration.tpl @@ -0,0 +1,35 @@ +{{- define "storj.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 "storj.migration" -}} + {{- $versions := (fromYaml (include "storj.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.18 */}} + {{- if and (eq $oldV.Major 1) (lt ($oldV.Patch | int) 18) -}} + {{/* Block the upgrade */}} + {{- fail "Migration to 2.x.x is only allowed from 1.0.18 or higher" -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/ix-dev/charts/storj/templates/_persistence.tpl b/library/ix-dev/charts/storj/templates/_persistence.tpl new file mode 100644 index 0000000000..ee8dad3a8d --- /dev/null +++ b/library/ix-dev/charts/storj/templates/_persistence.tpl @@ -0,0 +1,63 @@ +{{- define "storj.persistence" -}} +persistence: + data: + enabled: true + {{- include "ix.v1.common.app.storageOptions" (dict "storage" .Values.storjStorage.data) | nindent 4 }} + targetSelector: + storj: + storj: + mountPath: /app/config + {{- if and (eq .Values.storjStorage.data.type "ixVolume") + (not (.Values.storjStorage.data.ixVolumeConfig | default dict).aclEnable) }} + 01-permissions: + mountPath: /mnt/directories/data + {{- end }} + 03-setup: + mountPath: /app/config + identity: + enabled: true + {{- include "ix.v1.common.app.storageOptions" (dict "storage" .Values.storjStorage.identity) | nindent 4 }} + targetSelector: + storj: + storj: + mountPath: /app/identity + {{- if and (eq .Values.storjStorage.identity.type "ixVolume") + (not (.Values.storjStorage.identity.ixVolumeConfig | default dict).aclEnable) }} + 01-permissions: + mountPath: /mnt/directories/identity + {{- end }} + 02-generateid: + mountPath: {{ template "storj.idPath" }} + 03-setup: + mountPath: /app/identity + initscript: + enabled: true + type: configmap + objectName: storj + defaultMode: "0755" + targetSelector: + storj: + 02-generateid: + mountPath: /init_script/init_config.sh + subPath: init_config.sh + tmp: + enabled: true + type: emptyDir + targetSelector: + storj: + storj: + mountPath: /tmp + 02-generateid: + mountPath: /tmp + 03-setup: + mountPath: /tmp + {{- range $idx, $storage := .Values.storjStorage.additionalStorages }} + {{ printf "storj-%v:" (int $idx) }} + enabled: true + {{- include "ix.v1.common.app.storageOptions" (dict "storage" $storage) | nindent 4 }} + targetSelector: + storj: + storj: + mountPath: {{ $storage.mountPath }} + {{- end }} +{{- end -}} diff --git a/library/ix-dev/charts/storj/templates/_portal.tpl b/library/ix-dev/charts/storj/templates/_portal.tpl new file mode 100644 index 0000000000..b388b4b41a --- /dev/null +++ b/library/ix-dev/charts/storj/templates/_portal.tpl @@ -0,0 +1,12 @@ +{{- define "storj.portal" -}} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: portal +data: + port: {{ .Values.storjNetwork.webPort | quote }} + path: "/" + protocol: "http" + host: $node_ip +{{- end -}} diff --git a/library/ix-dev/charts/storj/templates/_service.tpl b/library/ix-dev/charts/storj/templates/_service.tpl new file mode 100644 index 0000000000..308b3797e7 --- /dev/null +++ b/library/ix-dev/charts/storj/templates/_service.tpl @@ -0,0 +1,29 @@ +{{- define "storj.service" -}} +service: + storj: + enabled: true + primary: true + type: NodePort + targetSelector: storj + ports: + webui: + enabled: true + primary: true + port: {{ .Values.storjNetwork.webPort }} + nodePort: {{ .Values.storjNetwork.webPort }} + targetPort: 14002 + targetSelector: storj + p2p-tcp: + enabled: true + port: {{ .Values.storjNetwork.p2pPort }} + nodePort: {{ .Values.storjNetwork.p2pPort }} + targetPort: 28967 + targetSelector: storj + p2p-udp: + enabled: true + port: {{ .Values.storjNetwork.p2pPort }} + nodePort: {{ .Values.storjNetwork.p2pPort }} + targetPort: 28967 + protocol: udp + targetSelector: storj +{{- end -}} diff --git a/library/ix-dev/charts/storj/templates/_storj.tpl b/library/ix-dev/charts/storj/templates/_storj.tpl new file mode 100644 index 0000000000..9866ad44ec --- /dev/null +++ b/library/ix-dev/charts/storj/templates/_storj.tpl @@ -0,0 +1,93 @@ +{{- define "storj.workload" -}} +workload: + storj: + enabled: true + primary: true + type: Deployment + podSpec: + hostNetwork: {{ .Values.storjNetwork.hostNetwork }} + terminationGracePeriodSeconds: {{ .Values.storjConfig.gracePeriod }} + containers: + storj: + enabled: true + primary: true + imageSelector: image + securityContext: + runAsUser: {{ .Values.storjRunAs.user }} + runAsGroup: {{ .Values.storjRunAs.group }} + readOnlyRootFilesystem: false + # capabilities: + # add: + # - CHOWN + # - DAC_OVERRIDE + # - FOWNER + # - SETGID + # - SETUID + # - KILL + {{- include "storj.args" $ | nindent 10 }} + envFrom: + - secretRef: + name: storj-config + {{ with .Values.storjConfig.additionalEnvs }} + envList: + {{ range $env := . }} + - name: {{ $env.name }} + value: {{ $env.value }} + {{ end }} + {{ end }} + probes: + liveness: + enabled: false + readiness: + enabled: false + startup: + enabled: false + initContainers: + {{- include "ix.v1.common.app.permissions" (dict "containerName" "01-permissions" + "UID" .Values.storjRunAs.user + "GID" .Values.storjRunAs.group + "mode" "check" + "type" "install") | nindent 8 }} + 02-generateid: + enabled: true + type: init + imageSelector: curlImage + securityContext: + runAsUser: 0 + runAsGroup: 0 + runAsNonRoot: false + readOnlyRootFilesystem: false + capabilities: + add: + - CHOWN + - FOWNER + - DAC_OVERRIDE + command: + - /bin/sh + - -c + args: + - ./init_script/init_config.sh + env: + DEFAULT_CERT_PATH: {{ template "storj.idPath" }}/ca.cert + DEFAULT_IDENTITY_CERT_PATH: {{ template "storj.idPath" }}/identity.cert + AUTH_KEY: + secretKeyRef: + name: storj + key: authToken + 03-setup: + enabled: true + type: init + imageSelector: image + envFrom: + - secretRef: + name: storj-config + securityContext: + runAsUser: {{ .Values.storjRunAs.user }} + runAsGroup: {{ .Values.storjRunAs.group }} + readOnlyRootFilesystem: false + command: + - /bin/sh + - -c + - | + test ! -f /app/config/config.yaml && export SETUP="true"; /entrypoint +{{- end -}} diff --git a/library/ix-dev/charts/storj/templates/common.yaml b/library/ix-dev/charts/storj/templates/common.yaml new file mode 100644 index 0000000000..f90117abfb --- /dev/null +++ b/library/ix-dev/charts/storj/templates/common.yaml @@ -0,0 +1,14 @@ +{{- include "ix.v1.common.loader.init" . -}} + +{{- include "storj.migration" $ -}} + +{{/* Merge the templates with Values */}} +{{- $_ := mustMergeOverwrite .Values (include "storj.workload" $ | fromYaml) -}} +{{- $_ := mustMergeOverwrite .Values (include "storj.configuration" $ | fromYaml) -}} +{{- $_ := mustMergeOverwrite .Values (include "storj.service" $ | fromYaml) -}} +{{- $_ := mustMergeOverwrite .Values (include "storj.persistence" $ | fromYaml) -}} + +{{/* Create the configmap for portal manually*/}} +{{- include "storj.portal" $ -}} + +{{- include "ix.v1.common.loader.apply" . -}} diff --git a/library/ix-dev/charts/storj/templates/deployment.yaml b/library/ix-dev/charts/storj/templates/deployment.yaml deleted file mode 100644 index 3dc62c16af..0000000000 --- a/library/ix-dev/charts/storj/templates/deployment.yaml +++ /dev/null @@ -1,109 +0,0 @@ -{{ include "common.storage.hostPathValidate" .Values }} -apiVersion: {{ template "common.capabilities.deployment.apiVersion" . }} -kind: Deployment -metadata: - name: {{ template "common.names.fullname" . }}-storj - 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: - terminationGracePeriodSeconds: {{ .Values.terminationGracePeriod | default 30 }} - initContainers: - - name: generate-identity - image: "alpine/curl:latest" - securityContext: - runAsUser: 0 - runAsGroup: 0 - command: - - ./init_script/init_config.sh - env: - {{ $envListIdentity := (default list) }} - {{ $envListIdentity = mustAppend $envListIdentity (dict "name" "DEFAULT_CERT_PATH" "value" (printf "%s/ca.cert" .Values.identityCreationMountPath)) }} - {{ $envListIdentity = mustAppend $envListIdentity (dict "name" "DEFAULT_IDENTITY_CERT_PATH" "value" (printf "%s/identity.cert" .Values.identityCreationMountPath)) }} - {{ $envListIdentity = mustAppend $envListIdentity (dict "name" "AUTH_KEY" "valueFromSecret" true "secretName" "storj-credentials" "secretKey" "authToken") }} - {{ include "common.containers.environmentVariables" (dict "environmentVariables" $envListIdentity) | nindent 12 }} - volumeMounts: - - name: initial-scripts - mountPath: /init_script/ - - name: identity - mountPath: {{ .Values.identityCreationMountPath }} - - name: setup - {{ include "common.containers.imageConfig" .Values.image | nindent 8 }} - command: - - /bin/sh - - -c - - 'test ! -f {{ .Values.appVolumeMounts.data.mountPath }}/config.yaml && export SETUP="true"; /entrypoint;' - {{ include "common.storage.allContainerVolumeMounts" .Values | nindent 8 }} - securityContext: - runAsUser: {{ .Values.runAsUser }} - runAsGroup: {{ .Values.runAsGroup }} - containers: - - name: {{ .Chart.Name }} - {{ include "common.containers.imageConfig" .Values.image | nindent 10 }} - {{ include "common.resources.limitation" . | nindent 10 }} - volumeMounts: {{ include "common.storage.configureAppVolumeMountsInContainer" .Values | nindent 12 }} - {{ range $index, $hostPathConfiguration := .Values.extraAppVolumeMounts }} - - name: extrappvolume-{{ $index }} - mountPath: {{ $hostPathConfiguration.mountPath }} - {{ end }} - securityContext: - runAsUser: {{ .Values.runAsUser }} - runAsGroup: {{ .Values.runAsGroup }} - ports: - - name: web - containerPort: 14002 - - name: tcp - containerPort: 28967 - protocol: TCP - - name: udp - containerPort: 28967 - protocol: UDP - {{- $walletFeats := list -}} - {{- if .Values.zksync }} - {{- $walletFeats = mustAppend $walletFeats "zksync" -}} - {{- end -}} - {{ if .Values.zksyncEra }} - {{- $walletFeats = mustAppend $walletFeats "zksync-era" -}} - {{- end -}} - {{- if $walletFeats }} - args: - - --operator.wallet-features={{ join "," $walletFeats }} - {{ end }} - env: - {{ $envList := (default list .Values.environmentVariables) }} - {{ $envList = mustAppend $envList (dict "name" "WALLET" "valueFromSecret" true "secretName" "storj-credentials" "secretKey" "wallet") }} - {{ $envList = mustAppend $envList (dict "name" "ADDRESS" "value" (printf "%s:%d" (.Values.domainAddress) (.Values.nodePort | int))) }} - {{ $envList = mustAppend $envList (dict "name" "EMAIL" "value" (printf "%s" (.Values.email))) }} - {{ $envList = mustAppend $envList (dict "name" "STORAGE" "value" (printf "%dGB" (.Values.storageSize | int))) }} - {{ include "common.containers.environmentVariables" (dict "environmentVariables" $envList) | nindent 12 }} -{{ 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 }} - - name: initial-scripts - configMap: - defaultMode: 0755 - name: "initial-scripts" diff --git a/library/ix-dev/charts/storj/templates/initial_scripts.yaml b/library/ix-dev/charts/storj/templates/initial_scripts.yaml deleted file mode 100644 index 3c13ae5e25..0000000000 --- a/library/ix-dev/charts/storj/templates/initial_scripts.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: "initial-scripts" - annotations: - rollme: {{ randAlphaNum 5 | quote }} - -data: - init_config.sh: |- - #!/bin/sh - if ! [ -f ${DEFAULT_CERT_PATH} ] && ! [ -f ${DEFAULT_IDENTITY_CERT_PATH} ]; then - curl -L https://github.com/storj/storj/releases/latest/download/identity_linux_amd64.zip -o identity_linux_amd64.zip - unzip -o identity_linux_amd64.zip - chmod +x identity - ./identity create storagenode - ./identity authorize storagenode ${AUTH_KEY} - chown -R {{ .Values.runAsUser }}:{{ .Values.runAsGroup }} {{ .Values.identityCreationMountPath }} - fi diff --git a/library/ix-dev/charts/storj/templates/pre-install-job.yaml b/library/ix-dev/charts/storj/templates/pre-install-job.yaml deleted file mode 100644 index 7a51d08121..0000000000 --- a/library/ix-dev/charts/storj/templates/pre-install-job.yaml +++ /dev/null @@ -1,28 +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 }} - 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: ["chown", "-R", "{{ .Values.runAsUser }}:{{ .Values.runAsGroup }}", "{{ .Values.appVolumeMounts.data.mountPath }}", "{{ .Values.appVolumeMounts.identity.mountPath }}"] - volumeMounts: {{ include "common.storage.configureAppVolumeMountsInContainer" .Values | nindent 12 }} - volumes: {{ include "common.storage.configureAppVolumes" .Values | nindent 8 }} diff --git a/library/ix-dev/charts/storj/templates/service.yaml b/library/ix-dev/charts/storj/templates/service.yaml deleted file mode 100644 index 085c55f344..0000000000 --- a/library/ix-dev/charts/storj/templates/service.yaml +++ /dev/null @@ -1,11 +0,0 @@ -{{ $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.webPort "nodePort" .Values.webPort "targetPort" 14002) }} -{{ $ports = mustAppend $ports (dict "name" "tcp" "port" .Values.nodePort "nodePort" .Values.nodePort "targetPort" 28967) }} -{{ $ports = mustAppend $ports (dict "name" "udp" "port" .Values.nodePort "nodePort" .Values.nodePort "targetPort" 28967 "protocol" "UDP") }} -{{ $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/storj/templates/storj-secrets.yaml b/library/ix-dev/charts/storj/templates/storj-secrets.yaml deleted file mode 100644 index f72579549f..0000000000 --- a/library/ix-dev/charts/storj/templates/storj-secrets.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: storj-credentials - labels: {{ include "common.labels" . | nindent 4 }} -type: Opaque -data: - authToken: {{ .Values.authToken | b64enc | quote }} - wallet: {{ .Values.wallet | b64enc | quote }} diff --git a/library/ix-dev/charts/storj/to_keep_versions.md b/library/ix-dev/charts/storj/to_keep_versions.md new file mode 100644 index 0000000000..2645025a35 --- /dev/null +++ b/library/ix-dev/charts/storj/to_keep_versions.md @@ -0,0 +1,4 @@ +# 1.0.18 + +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/storj/to_keep_versions.yaml b/library/ix-dev/charts/storj/to_keep_versions.yaml new file mode 100644 index 0000000000..123214dd11 --- /dev/null +++ b/library/ix-dev/charts/storj/to_keep_versions.yaml @@ -0,0 +1 @@ +- 1.0.18 diff --git a/library/ix-dev/charts/storj/values.yaml b/library/ix-dev/charts/storj/values.yaml index d45e76edb8..73eb0654ee 100644 --- a/library/ix-dev/charts/storj/values.yaml +++ b/library/ix-dev/charts/storj/values.yaml @@ -1,5 +1,49 @@ -identityCreationMountPath: /root/.local/share/storj/identity/storagenode image: pullPolicy: IfNotPresent repository: storjlabs/storagenode tag: 1d42f9ac3-v1.68.2-go1.18.8 +curlImage: + pullPolicy: IfNotPresent + repository: alpine/curl + tag: latest + +podOptions: + dnsConfig: + options: [] + +resources: + limits: + cpu: 4000m + memory: 8Gi + +storjConfig: + wallet: '' + authToken: '' + email: '' + domainAddress: '' + gracePeriod: 30 + storageSizeGB: 500 + wallets: + zkSync: false + zkSyncEra: false + additionalEnvs: [] + +storjRunAs: + user: 568 + group: 568 + +storjNetwork: + webPort: 20909 + p2pPort: 28967 + hostNetwork: false + +storjStorage: + data: + type: ixVolume + ixVolumeConfig: + datasetName: data + identity: + type: ixVolume + ixVolumeConfig: + datasetName: identity + additionalStorages: []