upgrade node version, objectivefs support, SCALE 24.04 support
Signed-off-by: Travis Glenn Hansen <travisghansen@yahoo.com>
This commit is contained in:
parent
79f16a0cf6
commit
15b120c3cc
|
|
@ -115,38 +115,6 @@ jobs:
|
|||
SYNOLOGY_PASSWORD: ${{ secrets.SANITY_SYNOLOGY_PASSWORD }}
|
||||
SYNOLOGY_VOLUME: ${{ secrets.SANITY_SYNOLOGY_VOLUME }}
|
||||
|
||||
csi-sanity-truenas-scale-22_12:
|
||||
needs:
|
||||
- build-npm-linux-amd64
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- truenas/scale/22.12/scale-iscsi.yaml
|
||||
- truenas/scale/22.12/scale-nfs.yaml
|
||||
# 80 char limit
|
||||
- truenas/scale/22.12/scale-smb.yaml
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- Linux
|
||||
- X64
|
||||
#- csi-sanity-truenas
|
||||
- csi-sanity-zfs-generic
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: node-modules-linux-amd64
|
||||
- name: csi-sanity
|
||||
run: |
|
||||
# run tests
|
||||
ci/bin/run.sh
|
||||
env:
|
||||
TEMPLATE_CONFIG_FILE: "./ci/configs/${{ matrix.config }}"
|
||||
TRUENAS_HOST: ${{ secrets.SANITY_TRUENAS_SCALE_22_12_HOST }}
|
||||
TRUENAS_USERNAME: ${{ secrets.SANITY_TRUENAS_USERNAME }}
|
||||
TRUENAS_PASSWORD: ${{ secrets.SANITY_TRUENAS_PASSWORD }}
|
||||
|
||||
csi-sanity-truenas-scale-23_10:
|
||||
needs:
|
||||
- build-npm-linux-amd64
|
||||
|
|
@ -179,6 +147,38 @@ jobs:
|
|||
TRUENAS_USERNAME: ${{ secrets.SANITY_TRUENAS_USERNAME }}
|
||||
TRUENAS_PASSWORD: ${{ secrets.SANITY_TRUENAS_PASSWORD }}
|
||||
|
||||
csi-sanity-truenas-scale-24_04:
|
||||
needs:
|
||||
- build-npm-linux-amd64
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- truenas/scale/24.04/scale-iscsi.yaml
|
||||
- truenas/scale/24.04/scale-nfs.yaml
|
||||
# 80 char limit
|
||||
- truenas/scale/24.04/scale-smb.yaml
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- Linux
|
||||
- X64
|
||||
#- csi-sanity-truenas
|
||||
- csi-sanity-zfs-generic
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: node-modules-linux-amd64
|
||||
- name: csi-sanity
|
||||
run: |
|
||||
# run tests
|
||||
ci/bin/run.sh
|
||||
env:
|
||||
TEMPLATE_CONFIG_FILE: "./ci/configs/${{ matrix.config }}"
|
||||
TRUENAS_HOST: ${{ secrets.SANITY_TRUENAS_SCALE_24_04_HOST }}
|
||||
TRUENAS_USERNAME: ${{ secrets.SANITY_TRUENAS_USERNAME }}
|
||||
TRUENAS_PASSWORD: ${{ secrets.SANITY_TRUENAS_PASSWORD }}
|
||||
|
||||
# ssh-based drivers
|
||||
csi-sanity-truenas-core-13_0:
|
||||
needs:
|
||||
|
|
@ -244,6 +244,41 @@ jobs:
|
|||
SERVER_USERNAME: ${{ secrets.SANITY_ZFS_GENERIC_USERNAME }}
|
||||
SERVER_PASSWORD: ${{ secrets.SANITY_ZFS_GENERIC_PASSWORD }}
|
||||
|
||||
# client drivers
|
||||
csi-sanity-objectivefs:
|
||||
needs:
|
||||
- build-npm-linux-amd64
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- objectivefs/objectivefs.yaml
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- Linux
|
||||
- X64
|
||||
- csi-sanity-client
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: node-modules-linux-amd64
|
||||
- name: csi-sanity
|
||||
run: |
|
||||
# run tests
|
||||
ci/bin/run.sh
|
||||
env:
|
||||
TEMPLATE_CONFIG_FILE: "./ci/configs/${{ matrix.config }}"
|
||||
OBJECTIVEFS_POOL: ${{ secrets.SANITY_OBJECTIVEFS_POOL }}
|
||||
OBJECTIVEFS_LICENSE: ${{ secrets.SANITY_OBJECTIVEFS_LICENSE }}
|
||||
OBJECTIVEFS_OBJECTSTORE: ${{ secrets.SANITY_OBJECTIVEFS_OBJECTSTORE }}
|
||||
OBJECTIVEFS_ENDPOINT: ${{ secrets.SANITY_OBJECTIVEFS_ENDPOINT }}
|
||||
OBJECTIVEFS_SECRET_KEY: ${{ secrets.SANITY_OBJECTIVEFS_SECRET_KEY }}
|
||||
OBJECTIVEFS_ACCESS_KEY: ${{ secrets.SANITY_OBJECTIVEFS_ACCESS_KEY }}
|
||||
OBJECTIVEFS_PASSPHRASE: ${{ secrets.SANITY_OBJECTIVEFS_PASSPHRASE }}
|
||||
|
||||
CSI_SANITY_SKIP: "should fail when requesting to create a snapshot with already existing name and different source volume ID|should fail when requesting to create a volume with already existing name and different capacity"
|
||||
|
||||
# client drivers
|
||||
csi-sanity-client:
|
||||
needs:
|
||||
|
|
@ -425,10 +460,11 @@ jobs:
|
|||
- determine-image-tag
|
||||
- csi-sanity-synology-dsm6
|
||||
- csi-sanity-synology-dsm7
|
||||
- csi-sanity-truenas-scale-22_12
|
||||
- csi-sanity-truenas-scale-23_10
|
||||
- csi-sanity-truenas-scale-24_04
|
||||
- csi-sanity-truenas-core-13_0
|
||||
- csi-sanity-zfs-generic
|
||||
- csi-sanity-objectivefs
|
||||
- csi-sanity-client
|
||||
- csi-sanity-client-windows
|
||||
- csi-sanity-zfs-local
|
||||
|
|
@ -464,10 +500,11 @@ jobs:
|
|||
needs:
|
||||
- csi-sanity-synology-dsm6
|
||||
- csi-sanity-synology-dsm7
|
||||
- csi-sanity-truenas-scale-22_12
|
||||
- csi-sanity-truenas-scale-23_10
|
||||
- csi-sanity-truenas-scale-24_04
|
||||
- csi-sanity-truenas-core-13_0
|
||||
- csi-sanity-zfs-generic
|
||||
- csi-sanity-objectivefs
|
||||
- csi-sanity-client
|
||||
- csi-sanity-client-windows
|
||||
- csi-sanity-zfs-local
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/*
|
|||
&& localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
|
||||
|
||||
ENV LANG=en_US.utf8
|
||||
ENV NODE_VERSION=v16.18.0
|
||||
ENV NODE_VERSION=v20.11.1
|
||||
ENV NODE_ENV=production
|
||||
|
||||
# install build deps
|
||||
|
|
@ -75,7 +75,7 @@ COPY --from=build /usr/local/lib/nodejs/bin/node /usr/local/bin/node
|
|||
# netbase is required by rpcbind/rpcinfo to work properly
|
||||
# /etc/{services,rpc} are required
|
||||
RUN apt-get update && \
|
||||
apt-get install -y netbase socat e2fsprogs exfatprogs xfsprogs btrfs-progs fatresize dosfstools ntfs-3g nfs-common cifs-utils fdisk gdisk cloud-guest-utils sudo rsync procps util-linux nvme-cli && \
|
||||
apt-get install -y wget netbase socat e2fsprogs exfatprogs xfsprogs btrfs-progs fatresize dosfstools ntfs-3g nfs-common cifs-utils fdisk gdisk cloud-guest-utils sudo rsync procps util-linux nvme-cli fuse && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# controller requirements
|
||||
|
|
@ -83,6 +83,11 @@ RUN apt-get update && \
|
|||
# apt-get install -y ansible && \
|
||||
# rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# install objectivefs
|
||||
ENV OBJECTIVEFS_VERSION=7.1
|
||||
ADD docker/objectivefs-installer.sh /usr/local/sbin
|
||||
RUN chmod +x /usr/local/sbin/objectivefs-installer.sh && objectivefs-installer.sh
|
||||
|
||||
# install wrappers
|
||||
ADD docker/iscsiadm /usr/local/sbin
|
||||
RUN chmod +x /usr/local/sbin/iscsiadm
|
||||
|
|
|
|||
|
|
@ -397,10 +397,58 @@ logger.info(
|
|||
bindSocket
|
||||
);
|
||||
|
||||
[`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach(
|
||||
const signalMapping = {
|
||||
1: "SIGHUP",
|
||||
2: "SIGINT",
|
||||
3: "SIGQUIT",
|
||||
4: "SIGILL",
|
||||
5: "SIGTRAP",
|
||||
6: "SIGABRT",
|
||||
7: "SIGEMT",
|
||||
8: "SIGFPE",
|
||||
9: "SIGKILL",
|
||||
10: "SIGBUS",
|
||||
11: "SIGSEGV",
|
||||
12: "SIGSYS",
|
||||
13: "SIGPIPE",
|
||||
14: "SIGALRM",
|
||||
15: "SIGTERM",
|
||||
16: "SIGURG",
|
||||
17: "SIGSTOP",
|
||||
18: "SIGTSTP",
|
||||
19: "SIGCONT",
|
||||
20: "SIGCHLD",
|
||||
21: "SIGTTIN",
|
||||
22: "SIGTTOU",
|
||||
23: "SIGIO",
|
||||
24: "SIGXCPU",
|
||||
25: "SIGXFSZ",
|
||||
26: "SIGVTALRM",
|
||||
27: "SIGPROF",
|
||||
28: "SIGWINCH",
|
||||
29: "SIGINFO",
|
||||
30: "SIGUSR1",
|
||||
31: "SIGUSR2",
|
||||
};
|
||||
|
||||
[(`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`)].forEach(
|
||||
(eventType) => {
|
||||
process.on(eventType, async (code) => {
|
||||
console.log(`running server shutdown, exit code: ${code}`);
|
||||
let codeNumber = null;
|
||||
let codeName = null;
|
||||
if (code > 0) {
|
||||
codeNumber = code;
|
||||
codeName = signalMapping[code];
|
||||
} else {
|
||||
codeNumber = Object.keys(signalMapping).find(
|
||||
(key) => signalMapping[key] === code
|
||||
);
|
||||
codeName = code;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`running server shutdown, exit code: ${codeNumber} (${codeName})`
|
||||
);
|
||||
|
||||
// attempt clean shutdown of in-flight requests
|
||||
try {
|
||||
|
|
@ -431,7 +479,7 @@ logger.info(
|
|||
}
|
||||
|
||||
console.log("server fully shutdown, exiting");
|
||||
process.exit(code);
|
||||
process.exit(codeNumber);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
driver: objectivefs
|
||||
|
||||
objectivefs:
|
||||
pool: ${OBJECTIVEFS_POOL}
|
||||
cli:
|
||||
sudoEnabled: false
|
||||
env:
|
||||
OBJECTIVEFS_LICENSE: ${OBJECTIVEFS_LICENSE}
|
||||
OBJECTSTORE: ${OBJECTIVEFS_OBJECTSTORE}
|
||||
ENDPOINT: ${OBJECTIVEFS_ENDPOINT}
|
||||
SECRET_KEY: ${OBJECTIVEFS_SECRET_KEY}
|
||||
ACCESS_KEY: ${OBJECTIVEFS_ACCESS_KEY}
|
||||
OBJECTIVEFS_PASSPHRASE: ${OBJECTIVEFS_PASSPHRASE}
|
||||
|
||||
_private:
|
||||
csi:
|
||||
volume:
|
||||
idHash:
|
||||
strategy: crc32
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
driver: freenas-api-iscsi
|
||||
|
||||
httpConnection:
|
||||
protocol: http
|
||||
host: ${TRUENAS_HOST}
|
||||
port: 80
|
||||
#apiKey:
|
||||
username: ${TRUENAS_USERNAME}
|
||||
password: ${TRUENAS_PASSWORD}
|
||||
|
||||
zfs:
|
||||
datasetParentName: tank/ci/${CI_BUILD_KEY}/v
|
||||
detachedSnapshotsDatasetParentName: tank/ci/${CI_BUILD_KEY}/s
|
||||
|
||||
zvolCompression:
|
||||
zvolDedup:
|
||||
zvolEnableReservation: false
|
||||
zvolBlocksize:
|
||||
|
||||
iscsi:
|
||||
targetPortal: ${TRUENAS_HOST}
|
||||
interface: ""
|
||||
namePrefix: "csi-ci-${CI_BUILD_KEY}-"
|
||||
nameSuffix: ""
|
||||
targetGroups:
|
||||
- targetGroupPortalGroup: 1
|
||||
targetGroupInitiatorGroup: 1
|
||||
targetGroupAuthType: None
|
||||
targetGroupAuthGroup:
|
||||
# 0-100 (0 == ignore)
|
||||
extentAvailThreshold: 0
|
||||
|
||||
# https://github.com/SCST-project/scst/blob/master/scst/src/dev_handlers/scst_vdisk.c#L203
|
||||
_private:
|
||||
csi:
|
||||
volume:
|
||||
idHash:
|
||||
strategy: crc16
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
driver: freenas-api-nfs
|
||||
|
||||
httpConnection:
|
||||
protocol: http
|
||||
host: ${TRUENAS_HOST}
|
||||
port: 80
|
||||
#apiKey:
|
||||
username: ${TRUENAS_USERNAME}
|
||||
password: ${TRUENAS_PASSWORD}
|
||||
|
||||
zfs:
|
||||
datasetParentName: tank/ci/${CI_BUILD_KEY}/v
|
||||
detachedSnapshotsDatasetParentName: tank/ci/${CI_BUILD_KEY}/s
|
||||
|
||||
datasetEnableQuotas: true
|
||||
datasetEnableReservation: false
|
||||
datasetPermissionsMode: "0777"
|
||||
datasetPermissionsUser: 0
|
||||
datasetPermissionsGroup: 0
|
||||
|
||||
nfs:
|
||||
shareHost: ${TRUENAS_HOST}
|
||||
shareAlldirs: false
|
||||
shareAllowedHosts: []
|
||||
shareAllowedNetworks: []
|
||||
shareMaprootUser: root
|
||||
shareMaprootGroup: root
|
||||
shareMapallUser: ""
|
||||
shareMapallGroup: ""
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
driver: freenas-api-smb
|
||||
|
||||
httpConnection:
|
||||
protocol: http
|
||||
host: ${TRUENAS_HOST}
|
||||
port: 80
|
||||
#apiKey:
|
||||
username: ${TRUENAS_USERNAME}
|
||||
password: ${TRUENAS_PASSWORD}
|
||||
|
||||
zfs:
|
||||
datasetParentName: tank/ci/${CI_BUILD_KEY}/v
|
||||
detachedSnapshotsDatasetParentName: tank/ci/${CI_BUILD_KEY}/s
|
||||
|
||||
datasetEnableQuotas: true
|
||||
datasetEnableReservation: false
|
||||
datasetPermissionsMode: "0770"
|
||||
datasetPermissionsUser: 1001
|
||||
datasetPermissionsGroup: 1001
|
||||
|
||||
smb:
|
||||
shareHost: ${TRUENAS_HOST}
|
||||
#nameTemplate: ""
|
||||
namePrefix: "csi-ci-${CI_BUILD_KEY}-"
|
||||
nameSuffix: ""
|
||||
shareAuxiliaryConfigurationTemplate: |
|
||||
#guest ok = yes
|
||||
#guest only = yes
|
||||
shareHome: false
|
||||
shareAllowedHosts: []
|
||||
shareDeniedHosts: []
|
||||
#shareDefaultPermissions: true
|
||||
shareGuestOk: false
|
||||
#shareGuestOnly: true
|
||||
#shareShowHiddenFiles: true
|
||||
shareRecycleBin: false
|
||||
shareBrowsable: false
|
||||
shareAccessBasedEnumeration: true
|
||||
shareTimeMachine: false
|
||||
#shareStorageTask:
|
||||
|
||||
node:
|
||||
mount:
|
||||
mount_flags: "username=smbroot,password=smbroot"
|
||||
|
||||
_private:
|
||||
csi:
|
||||
volume:
|
||||
idHash:
|
||||
strategy: crc16
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
PLATFORM_TYPE=${1}
|
||||
|
||||
if [[ "${PLATFORM_TYPE}" == "build" ]]; then
|
||||
PLATFORM=$BUILDPLATFORM
|
||||
else
|
||||
PLATFORM=$TARGETPLATFORM
|
||||
fi
|
||||
|
||||
if [[ "x${PLATFORM}" == "x" ]]; then
|
||||
PLATFORM="linux/amd64"
|
||||
fi
|
||||
|
||||
# these come from the --platform option of buildx, indirectly from DOCKER_BUILD_PLATFORM in main.yaml
|
||||
if [ "$PLATFORM" = "linux/amd64" ]; then
|
||||
export OBJECTIVEFS_ARCH="amd64"
|
||||
elif [ "$PLATFORM" = "linux/arm64" ]; then
|
||||
export OBJECTIVEFS_ARCH="arm64"
|
||||
else
|
||||
echo "unsupported/unknown PLATFORM ${PLATFORM}"
|
||||
fi
|
||||
|
||||
export DEB_FILE="objectivefs_${OBJECTIVEFS_VERSION}_${OBJECTIVEFS_ARCH}.deb"
|
||||
|
||||
echo "I am installing objectivefs $OBJECTIVEFS_VERSION"
|
||||
|
||||
wget "https://objectivefs.com/user/download/ac24htfht/${DEB_FILE}"
|
||||
dpkg -i "${DEB_FILE}"
|
||||
|
||||
rm "${DEB_FILE}"
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "democratic-csi",
|
||||
"version": "1.8.4",
|
||||
"version": "1.9.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "democratic-csi",
|
||||
"version": "1.8.4",
|
||||
"version": "1.9.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.8.4",
|
||||
|
|
@ -85,9 +85,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz",
|
||||
"integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
|
||||
"integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.4",
|
||||
|
|
@ -108,18 +108,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz",
|
||||
"integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==",
|
||||
"version": "8.57.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
|
||||
"integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@grpc/grpc-js": {
|
||||
"version": "1.9.9",
|
||||
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.9.tgz",
|
||||
"integrity": "sha512-vQ1qwi/Kiyprt+uhb1+rHMpyk4CVRMTGNUGGPRGS7pLNfWkdCHrGEnT6T3/JyC2VZgoOX/X1KwdoU0WYQAeYcQ==",
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.1.tgz",
|
||||
"integrity": "sha512-55ONqFytZExfOIjF1RjXPcVmT/jJqFzbbDqxK9jmRV4nxiYWtL9hENSW1Jfx0SdZfrvoqd44YJ/GJTqfRrawSQ==",
|
||||
"dependencies": {
|
||||
"@grpc/proto-loader": "^0.7.8",
|
||||
"@types/node": ">=12.12.47"
|
||||
|
|
@ -146,13 +146,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.13",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
|
||||
"integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
|
||||
"version": "0.11.14",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||
"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@humanwhocodes/object-schema": "^2.0.1",
|
||||
"debug": "^4.1.1",
|
||||
"@humanwhocodes/object-schema": "^2.0.2",
|
||||
"debug": "^4.3.1",
|
||||
"minimatch": "^3.0.5"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -173,9 +173,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/object-schema": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
|
||||
"integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
|
||||
"integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@kubernetes/client-node": {
|
||||
|
|
@ -205,9 +205,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@kubernetes/client-node/node_modules/@types/node": {
|
||||
"version": "18.18.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.9.tgz",
|
||||
"integrity": "sha512-0f5klcuImLnG4Qreu9hPj/rEfFq6YRc5n2mAjSsH+ec/mJL+3voBH0+8T7o8RpFjH7ovc+TRsL/c7OYIQsPTfQ==",
|
||||
"version": "18.19.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.18.tgz",
|
||||
"integrity": "sha512-80CP7B8y4PzZF0GWx15/gVWRrB5y/bIjNI84NK3cmQJu0WZwvmj2WMA5LcofQFVfLqqCSp545+U2LsrVzX36Zg==",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
|
|
@ -312,9 +312,9 @@
|
|||
"integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz",
|
||||
"integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==",
|
||||
"version": "20.11.20",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz",
|
||||
"integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
|
|
@ -341,9 +341,9 @@
|
|||
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz",
|
||||
"integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==",
|
||||
"version": "8.5.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
|
||||
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
|
|
@ -355,9 +355,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
|
||||
"integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
|
|
@ -439,9 +439,9 @@
|
|||
"integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g=="
|
||||
},
|
||||
"node_modules/async-mutex": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.0.tgz",
|
||||
"integrity": "sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==",
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.1.tgz",
|
||||
"integrity": "sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
|
|
@ -465,11 +465,11 @@
|
|||
"integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz",
|
||||
"integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==",
|
||||
"version": "1.6.7",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
|
||||
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"follow-redirects": "^1.15.4",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
|
|
@ -818,9 +818,9 @@
|
|||
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
|
||||
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
|
|
@ -838,16 +838,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz",
|
||||
"integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==",
|
||||
"version": "8.57.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
|
||||
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
"@eslint/eslintrc": "^2.1.3",
|
||||
"@eslint/js": "8.53.0",
|
||||
"@humanwhocodes/config-array": "^0.11.13",
|
||||
"@eslint/eslintrc": "^2.1.4",
|
||||
"@eslint/js": "8.57.0",
|
||||
"@humanwhocodes/config-array": "^0.11.14",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
"@ungap/structured-clone": "^1.2.0",
|
||||
|
|
@ -1017,9 +1017,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
|
||||
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
|
||||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
||||
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
|
|
@ -1059,9 +1059,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/flat-cache": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz",
|
||||
"integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
|
||||
"integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"flatted": "^3.2.9",
|
||||
|
|
@ -1069,13 +1069,13 @@
|
|||
"rimraf": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
"node": "^10.12.0 || >=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/flatted": {
|
||||
"version": "3.2.9",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz",
|
||||
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
|
||||
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fn.name": {
|
||||
|
|
@ -1084,9 +1084,9 @@
|
|||
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.3",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
|
||||
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
|
||||
"version": "1.15.5",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
||||
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
|
|
@ -1124,9 +1124,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz",
|
||||
"integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==",
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
||||
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
|
|
@ -1211,9 +1211,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "13.23.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz",
|
||||
"integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==",
|
||||
"version": "13.24.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
|
||||
"integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"type-fest": "^0.20.2"
|
||||
|
|
@ -1301,9 +1301,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||
"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
|
||||
"integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
|
|
@ -1688,9 +1688,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.29.4",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
|
|
@ -1817,12 +1817,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/openid-client": {
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.1.tgz",
|
||||
"integrity": "sha512-PtrWsY+dXg6y8mtMPyL/namZSYVz8pjXz3yJiBNZsEdCnu9miHLB4ELVC85WvneMKo2Rg62Ay7NkuCpM0bgiLQ==",
|
||||
"version": "5.6.4",
|
||||
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.4.tgz",
|
||||
"integrity": "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"jose": "^4.15.1",
|
||||
"jose": "^4.15.4",
|
||||
"lru-cache": "^6.0.0",
|
||||
"object-hash": "^2.2.0",
|
||||
"oidc-token-hash": "^5.0.3"
|
||||
|
|
@ -1982,9 +1982,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/protobufjs": {
|
||||
"version": "7.2.5",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz",
|
||||
"integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==",
|
||||
"version": "7.2.6",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz",
|
||||
"integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.2",
|
||||
|
|
@ -2243,9 +2243,9 @@
|
|||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
||||
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
|
|
@ -2305,9 +2305,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ssh2": {
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.14.0.tgz",
|
||||
"integrity": "sha512-AqzD1UCqit8tbOKoj6ztDDi1ffJZ2rV2SwlgrVVrHPkV5vWqGJOVp5pmtj18PunkPJAuKQsnInyKV+/Nb2bUnA==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz",
|
||||
"integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"asn1": "^0.2.6",
|
||||
|
|
@ -2317,8 +2317,8 @@
|
|||
"node": ">=10.16.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"cpu-features": "~0.0.8",
|
||||
"nan": "^2.17.0"
|
||||
"cpu-features": "~0.0.9",
|
||||
"nan": "^2.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sshpk": {
|
||||
|
|
@ -2644,9 +2644,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/winston-transport": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.6.0.tgz",
|
||||
"integrity": "sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==",
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz",
|
||||
"integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==",
|
||||
"dependencies": {
|
||||
"logform": "^2.3.2",
|
||||
"readable-stream": "^3.6.0",
|
||||
|
|
@ -2691,9 +2691,9 @@
|
|||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.14.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
|
||||
"integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
|
||||
"version": "8.16.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
|
||||
"integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "democratic-csi",
|
||||
"version": "1.8.4",
|
||||
"version": "1.9.0",
|
||||
"description": "kubernetes csi driver framework",
|
||||
"main": "bin/democratic-csi",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,740 @@
|
|||
const _ = require("lodash");
|
||||
const { CsiBaseDriver } = require("../index");
|
||||
const { GrpcError, grpc } = require("../../utils/grpc");
|
||||
const GeneralUtils = require("../../utils/general");
|
||||
const { ObjectiveFS } = require("../../utils/objectivefs");
|
||||
const registry = require("../../utils/registry");
|
||||
const semver = require("semver");
|
||||
const uuidv4 = require("uuid").v4;
|
||||
|
||||
const __REGISTRY_NS__ = "ControllerZfsLocalDriver";
|
||||
const MAX_VOLUME_NAME_LENGTH = 63;
|
||||
|
||||
class ControllerObjectiveFSDriver extends CsiBaseDriver {
|
||||
constructor(ctx, options) {
|
||||
super(...arguments);
|
||||
|
||||
options = options || {};
|
||||
options.service = options.service || {};
|
||||
options.service.identity = options.service.identity || {};
|
||||
options.service.controller = options.service.controller || {};
|
||||
options.service.node = options.service.node || {};
|
||||
|
||||
options.service.identity.capabilities =
|
||||
options.service.identity.capabilities || {};
|
||||
|
||||
options.service.controller.capabilities =
|
||||
options.service.controller.capabilities || {};
|
||||
|
||||
options.service.node.capabilities = options.service.node.capabilities || {};
|
||||
|
||||
if (!("service" in options.service.identity.capabilities)) {
|
||||
this.ctx.logger.debug("setting default identity service caps");
|
||||
|
||||
options.service.identity.capabilities.service = [
|
||||
//"UNKNOWN",
|
||||
"CONTROLLER_SERVICE",
|
||||
//"VOLUME_ACCESSIBILITY_CONSTRAINTS"
|
||||
];
|
||||
}
|
||||
|
||||
if (!("volume_expansion" in options.service.identity.capabilities)) {
|
||||
this.ctx.logger.debug("setting default identity volume_expansion caps");
|
||||
|
||||
options.service.identity.capabilities.volume_expansion = [
|
||||
//"UNKNOWN",
|
||||
//"ONLINE",
|
||||
//"OFFLINE"
|
||||
];
|
||||
}
|
||||
|
||||
if (!("rpc" in options.service.controller.capabilities)) {
|
||||
this.ctx.logger.debug("setting default controller caps");
|
||||
|
||||
options.service.controller.capabilities.rpc = [
|
||||
//"UNKNOWN",
|
||||
"CREATE_DELETE_VOLUME",
|
||||
//"PUBLISH_UNPUBLISH_VOLUME",
|
||||
"LIST_VOLUMES",
|
||||
//"GET_CAPACITY",
|
||||
//"CREATE_DELETE_SNAPSHOT",
|
||||
//"LIST_SNAPSHOTS",
|
||||
//"CLONE_VOLUME",
|
||||
//"PUBLISH_READONLY",
|
||||
//"EXPAND_VOLUME",
|
||||
];
|
||||
|
||||
if (semver.satisfies(this.ctx.csiVersion, ">=1.3.0")) {
|
||||
options.service.controller.capabilities.rpc
|
||||
.push
|
||||
//"VOLUME_CONDITION",
|
||||
//"GET_VOLUME"
|
||||
();
|
||||
}
|
||||
|
||||
if (semver.satisfies(this.ctx.csiVersion, ">=1.5.0")) {
|
||||
options.service.controller.capabilities.rpc.push(
|
||||
"SINGLE_NODE_MULTI_WRITER"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!("rpc" in options.service.node.capabilities)) {
|
||||
this.ctx.logger.debug("setting default node caps");
|
||||
|
||||
options.service.node.capabilities.rpc = [
|
||||
//"UNKNOWN",
|
||||
"STAGE_UNSTAGE_VOLUME",
|
||||
"GET_VOLUME_STATS",
|
||||
//"EXPAND_VOLUME"
|
||||
];
|
||||
|
||||
if (semver.satisfies(this.ctx.csiVersion, ">=1.3.0")) {
|
||||
//options.service.node.capabilities.rpc.push("VOLUME_CONDITION");
|
||||
}
|
||||
|
||||
if (semver.satisfies(this.ctx.csiVersion, ">=1.5.0")) {
|
||||
options.service.node.capabilities.rpc.push("SINGLE_NODE_MULTI_WRITER");
|
||||
/**
|
||||
* This is for volumes that support a mount time gid such as smb or fat
|
||||
*/
|
||||
//options.service.node.capabilities.rpc.push("VOLUME_MOUNT_GROUP");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getObjectiveFSClient() {
|
||||
const driver = this;
|
||||
return registry.getAsync(
|
||||
`${__REGISTRY_NS__}:objectivefsclient`,
|
||||
async () => {
|
||||
const options = {};
|
||||
options.sudo = _.get(
|
||||
driver.options,
|
||||
"objectivefs.cli.sudoEnabled",
|
||||
false
|
||||
);
|
||||
|
||||
return new ObjectiveFS({
|
||||
...options,
|
||||
env: _.get(driver.options, "objectivefs.env", {}),
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns Array
|
||||
*/
|
||||
getAccessModes(capability) {
|
||||
let access_modes = _.get(this.options, "csi.access_modes", null);
|
||||
if (access_modes !== null) {
|
||||
return access_modes;
|
||||
}
|
||||
|
||||
access_modes = [
|
||||
"UNKNOWN",
|
||||
"SINGLE_NODE_WRITER",
|
||||
"SINGLE_NODE_SINGLE_WRITER", // added in v1.5.0
|
||||
"SINGLE_NODE_MULTI_WRITER", // added in v1.5.0
|
||||
"SINGLE_NODE_READER_ONLY",
|
||||
"MULTI_NODE_READER_ONLY",
|
||||
"MULTI_NODE_SINGLE_WRITER",
|
||||
"MULTI_NODE_MULTI_WRITER",
|
||||
];
|
||||
|
||||
if (
|
||||
capability.access_type == "block" &&
|
||||
!access_modes.includes("MULTI_NODE_MULTI_WRITER")
|
||||
) {
|
||||
access_modes.push("MULTI_NODE_MULTI_WRITER");
|
||||
}
|
||||
|
||||
return access_modes;
|
||||
}
|
||||
|
||||
getFsTypes() {
|
||||
return ["fuse.objectivefs", "objectivefs"];
|
||||
}
|
||||
|
||||
assertCapabilities(capabilities) {
|
||||
const driver = this;
|
||||
this.ctx.logger.verbose("validating capabilities: %j", capabilities);
|
||||
|
||||
let message = null;
|
||||
let fs_types = driver.getFsTypes();
|
||||
const valid = capabilities.every((capability) => {
|
||||
if (capability.access_type != "mount") {
|
||||
message = `invalid access_type ${capability.access_type}`;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
capability.mount.fs_type &&
|
||||
!fs_types.includes(capability.mount.fs_type)
|
||||
) {
|
||||
message = `invalid fs_type ${capability.mount.fs_type}`;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.getAccessModes(capability).includes(capability.access_mode.mode)
|
||||
) {
|
||||
message = `invalid access_mode, ${capability.access_mode.mode}`;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return { valid, message };
|
||||
}
|
||||
|
||||
async getVolumeStatus(entry) {
|
||||
const driver = this;
|
||||
const object_store = _.get(driver.options, "objectivefs.env.OBJECTSTORE");
|
||||
const volume_id = entry.NAME.replace(object_store, "").split("/")[1];
|
||||
|
||||
if (!!!semver.satisfies(driver.ctx.csiVersion, ">=1.2.0")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let abnormal = false;
|
||||
let message = "OK";
|
||||
let volume_status = {};
|
||||
|
||||
//LIST_VOLUMES_PUBLISHED_NODES
|
||||
if (
|
||||
semver.satisfies(driver.ctx.csiVersion, ">=1.2.0") &&
|
||||
driver.options.service.controller.capabilities.rpc.includes(
|
||||
"LIST_VOLUMES_PUBLISHED_NODES"
|
||||
)
|
||||
) {
|
||||
// TODO: let drivers fill this in
|
||||
volume_status.published_node_ids = [];
|
||||
}
|
||||
|
||||
//VOLUME_CONDITION
|
||||
if (
|
||||
semver.satisfies(driver.ctx.csiVersion, ">=1.3.0") &&
|
||||
driver.options.service.controller.capabilities.rpc.includes(
|
||||
"VOLUME_CONDITION"
|
||||
)
|
||||
) {
|
||||
// TODO: let drivers fill ths in
|
||||
volume_condition = { abnormal, message };
|
||||
volume_status.volume_condition = volume_condition;
|
||||
}
|
||||
|
||||
return volume_status;
|
||||
}
|
||||
|
||||
async populateCsiVolumeFromData(entry) {
|
||||
const driver = this;
|
||||
const object_store = _.get(driver.options, "objectivefs.env.OBJECTSTORE");
|
||||
let filesystem = entry.NAME.replace(object_store, "");
|
||||
|
||||
let volume_content_source;
|
||||
let volume_context = {
|
||||
provisioner_driver: driver.options.driver,
|
||||
node_attach_driver: "objectivefs",
|
||||
filesystem,
|
||||
object_store,
|
||||
"env.OBJECTSTORE": object_store,
|
||||
};
|
||||
|
||||
if (driver.options.instance_id) {
|
||||
volume_context["provisioner_driver_instance_id"] =
|
||||
driver.options.instance_id;
|
||||
}
|
||||
let accessible_topology;
|
||||
|
||||
let volume = {
|
||||
volume_id: filesystem.split("/")[1],
|
||||
capacity_bytes: 0,
|
||||
content_source: volume_content_source,
|
||||
volume_context,
|
||||
accessible_topology,
|
||||
};
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure sane options are used etc
|
||||
* true = ready
|
||||
* false = not ready, but progressiong towards ready
|
||||
* throw error = faulty setup
|
||||
*
|
||||
* @param {*} call
|
||||
*/
|
||||
async Probe(call) {
|
||||
const driver = this;
|
||||
const pool = _.get(driver.options, "objectivefs.pool");
|
||||
const object_store = _.get(driver.options, "objectivefs.env.OBJECTSTORE");
|
||||
|
||||
if (driver.ctx.args.csiMode.includes("controller")) {
|
||||
if (!pool) {
|
||||
throw new GrpcError(
|
||||
grpc.status.FAILED_PRECONDITION,
|
||||
`objectivefs.pool not configured`
|
||||
);
|
||||
}
|
||||
|
||||
if (!object_store) {
|
||||
throw new GrpcError(
|
||||
grpc.status.FAILED_PRECONDITION,
|
||||
`env.OBJECTSTORE not configured`
|
||||
);
|
||||
}
|
||||
|
||||
return { ready: { value: true } };
|
||||
} else {
|
||||
return { ready: { value: true } };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an objectivefs filesystem as a new volume
|
||||
*
|
||||
* @param {*} call
|
||||
*/
|
||||
async CreateVolume(call) {
|
||||
const driver = this;
|
||||
const ofsClient = await driver.getObjectiveFSClient();
|
||||
const pool = _.get(driver.options, "objectivefs.pool");
|
||||
const object_store = _.get(driver.options, "objectivefs.env.OBJECTSTORE");
|
||||
const parameters = call.request.parameters;
|
||||
|
||||
if (!pool) {
|
||||
throw new GrpcError(
|
||||
grpc.status.FAILED_PRECONDITION,
|
||||
`objectivefs.pool not configured`
|
||||
);
|
||||
}
|
||||
|
||||
if (!object_store) {
|
||||
throw new GrpcError(
|
||||
grpc.status.FAILED_PRECONDITION,
|
||||
`env.OBJECTSTORE not configured`
|
||||
);
|
||||
}
|
||||
|
||||
const context_env = {};
|
||||
for (const key in parameters) {
|
||||
if (key.startsWith("env.")) {
|
||||
context_env[key] = parameters[key];
|
||||
}
|
||||
}
|
||||
context_env["env.OBJECTSTORE"] = object_store;
|
||||
|
||||
// filesystem names are always lower-cased by ofs
|
||||
let volume_id = await driver.getVolumeIdFromCall(call);
|
||||
let volume_content_source = call.request.volume_content_source;
|
||||
volume_id = volume_id.toLowerCase();
|
||||
const filesystem = `${pool}/${volume_id}`;
|
||||
|
||||
if (volume_id.length >= MAX_VOLUME_NAME_LENGTH) {
|
||||
throw new GrpcError(
|
||||
grpc.status.INVALID_ARGUMENT,
|
||||
`derived volume_id ${volume_id} is too long for objectivefs`
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
call.request.volume_capabilities &&
|
||||
call.request.volume_capabilities.length > 0
|
||||
) {
|
||||
const result = this.assertCapabilities(call.request.volume_capabilities);
|
||||
if (result.valid !== true) {
|
||||
throw new GrpcError(grpc.status.INVALID_ARGUMENT, result.message);
|
||||
}
|
||||
} else {
|
||||
throw new GrpcError(
|
||||
grpc.status.INVALID_ARGUMENT,
|
||||
"missing volume_capabilities"
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!call.request.capacity_range ||
|
||||
Object.keys(call.request.capacity_range).length === 0
|
||||
) {
|
||||
call.request.capacity_range = {
|
||||
required_bytes: 1073741824, // meaningless
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
call.request.capacity_range.required_bytes > 0 &&
|
||||
call.request.capacity_range.limit_bytes > 0 &&
|
||||
call.request.capacity_range.required_bytes >
|
||||
call.request.capacity_range.limit_bytes
|
||||
) {
|
||||
throw new GrpcError(
|
||||
grpc.status.OUT_OF_RANGE,
|
||||
`required_bytes is greather than limit_bytes`
|
||||
);
|
||||
}
|
||||
|
||||
let capacity_bytes =
|
||||
call.request.capacity_range.required_bytes ||
|
||||
call.request.capacity_range.limit_bytes;
|
||||
|
||||
if (!capacity_bytes) {
|
||||
//should never happen, value must be set
|
||||
throw new GrpcError(
|
||||
grpc.status.INVALID_ARGUMENT,
|
||||
`volume capacity is required (either required_bytes or limit_bytes)`
|
||||
);
|
||||
}
|
||||
|
||||
// ensure *actual* capacity is not greater than limit
|
||||
if (
|
||||
call.request.capacity_range.limit_bytes &&
|
||||
call.request.capacity_range.limit_bytes > 0 &&
|
||||
capacity_bytes > call.request.capacity_range.limit_bytes
|
||||
) {
|
||||
throw new GrpcError(
|
||||
grpc.status.OUT_OF_RANGE,
|
||||
`required volume capacity is greater than limit`
|
||||
);
|
||||
}
|
||||
|
||||
if (volume_content_source) {
|
||||
//should never happen, cannot clone with this driver
|
||||
throw new GrpcError(
|
||||
grpc.status.INVALID_ARGUMENT,
|
||||
`cloning is not enabled`
|
||||
);
|
||||
}
|
||||
|
||||
await ofsClient.create({}, filesystem, ["-f"]);
|
||||
|
||||
let volume_context = {
|
||||
provisioner_driver: driver.options.driver,
|
||||
node_attach_driver: "objectivefs",
|
||||
filesystem,
|
||||
...context_env,
|
||||
};
|
||||
|
||||
if (driver.options.instance_id) {
|
||||
volume_context["provisioner_driver_instance_id"] =
|
||||
driver.options.instance_id;
|
||||
}
|
||||
|
||||
const res = {
|
||||
volume: {
|
||||
volume_id,
|
||||
//capacity_bytes: capacity_bytes, // kubernetes currently pukes if capacity is returned as 0
|
||||
capacity_bytes: 0,
|
||||
content_source: volume_content_source,
|
||||
volume_context,
|
||||
},
|
||||
};
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a volume
|
||||
*
|
||||
* Deleting a volume consists of the following steps:
|
||||
* 1. delete directory
|
||||
*
|
||||
* @param {*} call
|
||||
*/
|
||||
async DeleteVolume(call) {
|
||||
const driver = this;
|
||||
const ofsClient = await driver.getObjectiveFSClient();
|
||||
const pool = _.get(driver.options, "objectivefs.pool");
|
||||
|
||||
let volume_id = call.request.volume_id;
|
||||
if (!volume_id) {
|
||||
throw new GrpcError(
|
||||
grpc.status.INVALID_ARGUMENT,
|
||||
`volume_id is required`
|
||||
);
|
||||
}
|
||||
|
||||
volume_id = volume_id.toLowerCase();
|
||||
const filesystem = `${pool}/${volume_id}`;
|
||||
await ofsClient.destroy({}, filesystem, []);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} call
|
||||
*/
|
||||
async ControllerExpandVolume(call) {
|
||||
throw new GrpcError(
|
||||
grpc.status.UNIMPLEMENTED,
|
||||
`operation not supported by driver`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: consider volume_capabilities?
|
||||
*
|
||||
* @param {*} call
|
||||
*/
|
||||
async GetCapacity(call) {
|
||||
throw new GrpcError(
|
||||
grpc.status.UNIMPLEMENTED,
|
||||
`operation not supported by driver`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* TODO: check capability to ensure not asking about block volumes
|
||||
*
|
||||
* @param {*} call
|
||||
*/
|
||||
async ListVolumes(call) {
|
||||
const driver = this;
|
||||
const ofsClient = await driver.getObjectiveFSClient();
|
||||
const pool = _.get(driver.options, "objectivefs.pool");
|
||||
|
||||
let entries = [];
|
||||
let entries_length = 0;
|
||||
let next_token;
|
||||
let uuid;
|
||||
let response;
|
||||
|
||||
const max_entries = call.request.max_entries;
|
||||
const starting_token = call.request.starting_token;
|
||||
|
||||
// get data from cache and return immediately
|
||||
if (starting_token) {
|
||||
let parts = starting_token.split(":");
|
||||
uuid = parts[0];
|
||||
let start_position = parseInt(parts[1]);
|
||||
let end_position;
|
||||
if (max_entries > 0) {
|
||||
end_position = start_position + max_entries;
|
||||
}
|
||||
entries = this.ctx.cache.get(`ListVolumes:result:${uuid}`);
|
||||
if (entries) {
|
||||
entries_length = entries.length;
|
||||
entries = entries.slice(start_position, end_position);
|
||||
if (max_entries > 0 && end_position > entries_length) {
|
||||
next_token = `${uuid}:${end_position}`;
|
||||
} else {
|
||||
next_token = null;
|
||||
}
|
||||
const data = {
|
||||
entries: entries,
|
||||
next_token: next_token,
|
||||
};
|
||||
|
||||
return data;
|
||||
} else {
|
||||
throw new GrpcError(
|
||||
grpc.status.ABORTED,
|
||||
`invalid starting_token: ${starting_token}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
entries = [];
|
||||
const list_entries = await ofsClient.list({});
|
||||
for (const entry of list_entries) {
|
||||
if (entry.KIND != "ofs") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let volume = await driver.populateCsiVolumeFromData(entry);
|
||||
if (volume) {
|
||||
let status = await driver.getVolumeStatus(entry);
|
||||
entries.push({
|
||||
volume,
|
||||
status,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (max_entries && entries.length > max_entries) {
|
||||
uuid = uuidv4();
|
||||
this.ctx.cache.set(`ListVolumes:result:${uuid}`, entries);
|
||||
next_token = `${uuid}:${max_entries}`;
|
||||
entries = entries.slice(0, max_entries);
|
||||
}
|
||||
|
||||
const data = {
|
||||
entries: entries,
|
||||
next_token: next_token,
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} call
|
||||
*/
|
||||
async ListSnapshots(call) {
|
||||
throw new GrpcError(
|
||||
grpc.status.UNIMPLEMENTED,
|
||||
`operation not supported by driver`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} call
|
||||
*/
|
||||
async CreateSnapshot(call) {
|
||||
throw new GrpcError(
|
||||
grpc.status.UNIMPLEMENTED,
|
||||
`operation not supported by driver`
|
||||
);
|
||||
|
||||
const driver = this;
|
||||
|
||||
// both these are required
|
||||
let source_volume_id = call.request.source_volume_id;
|
||||
let name = call.request.name;
|
||||
|
||||
if (!source_volume_id) {
|
||||
throw new GrpcError(
|
||||
grpc.status.INVALID_ARGUMENT,
|
||||
`snapshot source_volume_id is required`
|
||||
);
|
||||
}
|
||||
|
||||
source_volume_id = source_volume_id.toLowerCase();
|
||||
|
||||
if (!name) {
|
||||
throw new GrpcError(
|
||||
grpc.status.INVALID_ARGUMENT,
|
||||
`snapshot name is required`
|
||||
);
|
||||
}
|
||||
|
||||
driver.ctx.logger.verbose("requested snapshot name: %s", name);
|
||||
|
||||
let invalid_chars;
|
||||
invalid_chars = name.match(/[^a-z0-9_\-:.+]+/gi);
|
||||
if (invalid_chars) {
|
||||
invalid_chars = String.prototype.concat(
|
||||
...new Set(invalid_chars.join(""))
|
||||
);
|
||||
throw new GrpcError(
|
||||
grpc.status.INVALID_ARGUMENT,
|
||||
`snapshot name contains invalid characters: ${invalid_chars}`
|
||||
);
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/32106243/regex-to-remove-all-non-alpha-numeric-and-replace-spaces-with/32106277
|
||||
name = name.replace(/[^a-z0-9_\-:.+]+/gi, "");
|
||||
|
||||
driver.ctx.logger.verbose("cleansed snapshot name: %s", name);
|
||||
|
||||
const snapshot_id = `${source_volume_id}-${name}`;
|
||||
const volume_path = driver.getControllerVolumePath(source_volume_id);
|
||||
const snapshot_path = driver.getControllerSnapshotPath(snapshot_id);
|
||||
|
||||
// do NOT overwrite existing snapshot
|
||||
if (!(await driver.directoryExists(snapshot_path))) {
|
||||
await driver.cloneDir(volume_path, snapshot_path);
|
||||
}
|
||||
|
||||
let size_bytes = await driver.getDirectoryUsage(snapshot_path);
|
||||
return {
|
||||
snapshot: {
|
||||
/**
|
||||
* The purpose of this field is to give CO guidance on how much space
|
||||
* is needed to create a volume from this snapshot.
|
||||
*/
|
||||
size_bytes,
|
||||
snapshot_id,
|
||||
source_volume_id: source_volume_id,
|
||||
//https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/timestamp.proto
|
||||
creation_time: {
|
||||
seconds: Math.round(new Date().getTime() / 1000),
|
||||
nanos: 0,
|
||||
},
|
||||
ready_to_use: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* In addition, if clones have been created from a snapshot, then they must
|
||||
* be destroyed before the snapshot can be destroyed.
|
||||
*
|
||||
* @param {*} call
|
||||
*/
|
||||
async DeleteSnapshot(call) {
|
||||
throw new GrpcError(
|
||||
grpc.status.UNIMPLEMENTED,
|
||||
`operation not supported by driver`
|
||||
);
|
||||
|
||||
const driver = this;
|
||||
|
||||
const snapshot_id = call.request.snapshot_id;
|
||||
|
||||
if (!snapshot_id) {
|
||||
throw new GrpcError(
|
||||
grpc.status.INVALID_ARGUMENT,
|
||||
`snapshot_id is required`
|
||||
);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} call
|
||||
*/
|
||||
async ValidateVolumeCapabilities(call) {
|
||||
const driver = this;
|
||||
const ofsClient = await driver.getObjectiveFSClient();
|
||||
const pool = _.get(driver.options, "objectivefs.pool");
|
||||
|
||||
const volume_id = call.request.volume_id;
|
||||
if (!volume_id) {
|
||||
throw new GrpcError(grpc.status.INVALID_ARGUMENT, `missing volume_id`);
|
||||
}
|
||||
|
||||
const filesystem = `${pool}/${volume_id}`;
|
||||
const entries = await ofsClient.list({}, filesystem);
|
||||
const exists = entries.some((entry) => {
|
||||
return entry.NAME.endsWith(filesystem) && entry.KIND == "ofs";
|
||||
});
|
||||
|
||||
if (!exists) {
|
||||
throw new GrpcError(
|
||||
grpc.status.NOT_FOUND,
|
||||
`invalid volume_id: ${volume_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const capabilities = call.request.volume_capabilities;
|
||||
if (!capabilities || capabilities.length === 0) {
|
||||
throw new GrpcError(grpc.status.INVALID_ARGUMENT, `missing capabilities`);
|
||||
}
|
||||
|
||||
const result = this.assertCapabilities(call.request.volume_capabilities);
|
||||
|
||||
if (result.valid !== true) {
|
||||
return { message: result.message };
|
||||
}
|
||||
|
||||
return {
|
||||
confirmed: {
|
||||
volume_context: call.request.volume_context,
|
||||
volume_capabilities: call.request.volume_capabilities, // TODO: this is a bit crude, should return *ALL* capabilities, not just what was requested
|
||||
parameters: call.request.parameters,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.ControllerObjectiveFSDriver = ControllerObjectiveFSDriver;
|
||||
|
|
@ -12,6 +12,7 @@ const {
|
|||
const { ControllerNfsClientDriver } = require("./controller-nfs-client");
|
||||
const { ControllerSmbClientDriver } = require("./controller-smb-client");
|
||||
const { ControllerLustreClientDriver } = require("./controller-lustre-client");
|
||||
const { ControllerObjectiveFSDriver } = require("./controller-objectivefs");
|
||||
const { ControllerSynologyDriver } = require("./controller-synology");
|
||||
const { NodeManualDriver } = require("./node-manual");
|
||||
|
||||
|
|
@ -50,6 +51,8 @@ function factory(ctx, options) {
|
|||
return new ControllerLocalHostpathDriver(ctx, options);
|
||||
case "lustre-client":
|
||||
return new ControllerLustreClientDriver(ctx, options);
|
||||
case "objectivefs":
|
||||
return new ControllerObjectiveFSDriver(ctx, options);
|
||||
case "node-manual":
|
||||
return new NodeManualDriver(ctx, options);
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -183,8 +183,17 @@ class FreeNASApiDriver extends CsiBaseDriver {
|
|||
const apiVersion = httpClient.getApiVersion();
|
||||
const zb = await this.getZetabyte();
|
||||
const truenasVersion = semver.coerce(
|
||||
await httpApiClient.getSystemVersionMajorMinor()
|
||||
await httpApiClient.getSystemVersionMajorMinor(),
|
||||
{ loose: true }
|
||||
);
|
||||
|
||||
if (!truenasVersion) {
|
||||
throw new GrpcError(
|
||||
grpc.status.UNKNOWN,
|
||||
`unable to detect TrueNAS version`
|
||||
);
|
||||
}
|
||||
|
||||
const isScale = await httpApiClient.getIsScale();
|
||||
|
||||
let volume_context;
|
||||
|
|
|
|||
|
|
@ -231,8 +231,17 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
|
|||
const apiVersion = httpClient.getApiVersion();
|
||||
const zb = await this.getZetabyte();
|
||||
const truenasVersion = semver.coerce(
|
||||
await httpApiClient.getSystemVersionMajorMinor()
|
||||
await httpApiClient.getSystemVersionMajorMinor(),
|
||||
{ loose: true }
|
||||
);
|
||||
|
||||
if (!truenasVersion) {
|
||||
throw new GrpcError(
|
||||
grpc.status.UNKNOWN,
|
||||
`unable to detect TrueNAS version`
|
||||
);
|
||||
}
|
||||
|
||||
const isScale = await httpApiClient.getIsScale();
|
||||
|
||||
let volume_context;
|
||||
|
|
@ -1996,7 +2005,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
|
|||
this.ctx.logger.debug("zfs props data: %j", properties);
|
||||
let iscsiName =
|
||||
properties[FREENAS_ISCSI_ASSETS_NAME_PROPERTY_NAME].value;
|
||||
|
||||
|
||||
// name correlates to the extent NOT the target
|
||||
let kName = iscsiName.replaceAll(".", "_");
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ const k8s = require("@kubernetes/client-node");
|
|||
const { GrpcError, grpc } = require("../utils/grpc");
|
||||
const Handlebars = require("handlebars");
|
||||
const { Mount } = require("../utils/mount");
|
||||
const { ObjectiveFS } = require("../utils/objectivefs");
|
||||
const { OneClient } = require("../utils/oneclient");
|
||||
const { Filesystem } = require("../utils/filesystem");
|
||||
const { ISCSI } = require("../utils/iscsi");
|
||||
|
|
@ -181,6 +182,15 @@ class CsiBaseDriver {
|
|||
});
|
||||
}
|
||||
|
||||
getDefaultObjectiveFSInstance() {
|
||||
return registry.get(
|
||||
`${__REGISTRY_NS__}:default_objectivefs_instance`,
|
||||
() => {
|
||||
return new ObjectiveFS();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns CsiProxyClient
|
||||
|
|
@ -456,6 +466,9 @@ class CsiBaseDriver {
|
|||
|
||||
/**
|
||||
* technically zfs allows `:` and `.` in addition to `_` and `-`
|
||||
* TODO: make this more specific to each driver
|
||||
* in particular Nomad per-alloc feature uses names with <name>-[<index>] syntax so square brackets are present
|
||||
* TODO: allow for replacing chars vs absolute failure?
|
||||
*/
|
||||
let invalid_chars;
|
||||
invalid_chars = volume_id.match(/[^a-z0-9_\-]/gi);
|
||||
|
|
@ -728,6 +741,7 @@ class CsiBaseDriver {
|
|||
}
|
||||
|
||||
switch (node_attach_driver) {
|
||||
case "objectivefs":
|
||||
case "oneclient":
|
||||
// move along
|
||||
break;
|
||||
|
|
@ -1246,6 +1260,65 @@ class CsiBaseDriver {
|
|||
return {};
|
||||
}
|
||||
|
||||
break;
|
||||
case "objectivefs":
|
||||
let objectivefs = driver.getDefaultObjectiveFSInstance();
|
||||
let ofs_filesystem = volume_context.filesystem;
|
||||
let env = {};
|
||||
for (const key in volume_context) {
|
||||
if (key.startsWith("env.")) {
|
||||
env[key.substr("env.".length)] = volume_context[key];
|
||||
}
|
||||
}
|
||||
|
||||
for (const key in normalizedSecrets) {
|
||||
if (key.startsWith("env.")) {
|
||||
env[key.substr("env.".length)] = normalizedSecrets[key];
|
||||
}
|
||||
}
|
||||
|
||||
let ofs_object_store = env["OBJECTSTORE"];
|
||||
|
||||
if (!ofs_filesystem) {
|
||||
throw new GrpcError(
|
||||
grpc.status.FAILED_PRECONDITION,
|
||||
`missing ofs volume filesystem`
|
||||
);
|
||||
}
|
||||
|
||||
if (!ofs_object_store) {
|
||||
throw new GrpcError(
|
||||
grpc.status.FAILED_PRECONDITION,
|
||||
`missing required ofs volume env.OBJECTSTORE`
|
||||
);
|
||||
}
|
||||
|
||||
device = `${ofs_object_store}${ofs_filesystem}`;
|
||||
result = await mount.deviceIsMountedAtPath(
|
||||
device,
|
||||
staging_target_path
|
||||
);
|
||||
|
||||
if (result) {
|
||||
return {};
|
||||
}
|
||||
|
||||
result = await objectivefs.mount(
|
||||
env,
|
||||
ofs_filesystem,
|
||||
staging_target_path,
|
||||
mount_flags
|
||||
);
|
||||
|
||||
if (result) {
|
||||
return {};
|
||||
}
|
||||
|
||||
throw new GrpcError(
|
||||
grpc.status.UNKNOWN,
|
||||
`failed to mount objectivefs: ${device}`
|
||||
);
|
||||
|
||||
break;
|
||||
case "oneclient":
|
||||
let oneclient = driver.getDefaultOneClientInstance();
|
||||
|
|
@ -2932,6 +3005,7 @@ class CsiBaseDriver {
|
|||
case "nfs":
|
||||
case "smb":
|
||||
case "lustre":
|
||||
case "objectivefs":
|
||||
case "oneclient":
|
||||
case "hostpath":
|
||||
case "iscsi":
|
||||
|
|
|
|||
|
|
@ -121,6 +121,10 @@ class NodeManualDriver extends CsiBaseDriver {
|
|||
driverResourceType = "filesystem";
|
||||
fs_types = ["lustre"];
|
||||
break;
|
||||
case "objectivefs":
|
||||
driverResourceType = "filesystem";
|
||||
fs_types = ["objectivefs", "fuse.objectivefs"];
|
||||
break;
|
||||
case "oneclient":
|
||||
driverResourceType = "filesystem";
|
||||
fs_types = ["oneclient", "fuse.oneclient"];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,320 @@
|
|||
const cp = require("child_process");
|
||||
|
||||
const DEFAULT_TIMEOUT = process.env.MOUNT_DEFAULT_TIMEOUT || 30000;
|
||||
|
||||
const EXIT_CODE_64 = "administrator can not mount filesystems";
|
||||
const EXIT_CODE_78 = "missing or invalid passphrase";
|
||||
|
||||
/**
|
||||
* https://objectivefs.com/
|
||||
*/
|
||||
class ObjectiveFS {
|
||||
constructor(options = {}) {
|
||||
const objectivefs = this;
|
||||
objectivefs.options = options;
|
||||
|
||||
options.paths = options.paths || {};
|
||||
if (!options.paths.objectivefs) {
|
||||
options.paths.objectivefs = "mount.objectivefs";
|
||||
}
|
||||
|
||||
if (!options.paths.sudo) {
|
||||
options.paths.sudo = "/usr/bin/sudo";
|
||||
}
|
||||
|
||||
if (!options.paths.chroot) {
|
||||
options.paths.chroot = "/usr/sbin/chroot";
|
||||
}
|
||||
|
||||
if (!options.env) {
|
||||
options.env = {};
|
||||
}
|
||||
|
||||
if (!options.executor) {
|
||||
options.executor = {
|
||||
spawn: cp.spawn,
|
||||
//spawn: cp.execFile,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mount.objectivefs [-o <opt>[,<opt>]..] <filesystem> <dir>
|
||||
*
|
||||
* @param {*} env
|
||||
* @param {*} filesystem
|
||||
* @param {*} target
|
||||
* @param {*} options
|
||||
*/
|
||||
async mount(env, filesystem, target, options = []) {
|
||||
if (!env) {
|
||||
env = {};
|
||||
}
|
||||
const objectivefs = this;
|
||||
let args = [];
|
||||
args = args.concat(options);
|
||||
args = args.concat([filesystem, target]);
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await objectivefs.exec(
|
||||
objectivefs.options.paths.objectivefs,
|
||||
args,
|
||||
{ env, operation: "mount" }
|
||||
);
|
||||
return result;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mount.objectivefs create <your filesystem name>
|
||||
* mount.objectivefs create -f <bucket>/<fs>
|
||||
*
|
||||
* @param {*} env
|
||||
* @param {*} filesystem
|
||||
* @param {*} options
|
||||
*/
|
||||
async create(env, filesystem, options = []) {
|
||||
if (!env) {
|
||||
env = {};
|
||||
}
|
||||
const objectivefs = this;
|
||||
let args = ["create"];
|
||||
args = args.concat(options);
|
||||
args = args.concat([filesystem]);
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await objectivefs.exec(
|
||||
objectivefs.options.paths.objectivefs,
|
||||
args,
|
||||
{ env }
|
||||
);
|
||||
return result;
|
||||
} catch (err) {
|
||||
if (err.code == 1 && err.stderr.includes("filesystem already exists")) {
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* echo 'y' | mount.objectivefs destroy <bucket>/<fs>
|
||||
*
|
||||
* @param {*} env
|
||||
* @param {*} filesystem
|
||||
* @param {*} options
|
||||
*/
|
||||
async destroy(env, filesystem, options = []) {
|
||||
if (!env) {
|
||||
env = {};
|
||||
}
|
||||
const objectivefs = this;
|
||||
let args = ["destroy"];
|
||||
args = args.concat(options);
|
||||
args = args.concat([filesystem]);
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await objectivefs.exec(
|
||||
"/bin/bash",
|
||||
[
|
||||
"-c",
|
||||
`echo y | ${objectivefs.options.paths.objectivefs} ${args.join(" ")}`,
|
||||
],
|
||||
{ env }
|
||||
);
|
||||
|
||||
return result;
|
||||
} catch (err) {
|
||||
if (
|
||||
err.code == 68 &&
|
||||
err.stdout.includes("does not look like an ObjectiveFS filesystem")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
parseListOutput(data) {
|
||||
const lines = data.split("\n");
|
||||
let headers = [];
|
||||
let entries = [];
|
||||
lines.forEach((line, i) => {
|
||||
if (line.length < 1) {
|
||||
return;
|
||||
}
|
||||
const parts = line.split("\t");
|
||||
if (i == 0) {
|
||||
headers = parts.map((header) => {
|
||||
return header.trim();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let entry = {};
|
||||
headers.forEach((name, index) => {
|
||||
entry[name.trim()] = parts[index].trim();
|
||||
});
|
||||
|
||||
entries.push(entry);
|
||||
});
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* mount.objectivefs list [-asvz] [<filesystem>[@<time>]]
|
||||
*
|
||||
* @param {*} env
|
||||
* @param {*} filesystem
|
||||
* @param {*} options
|
||||
*/
|
||||
async list(env, filesystem = null, options = []) {
|
||||
if (!env) {
|
||||
env = {};
|
||||
}
|
||||
const objectivefs = this;
|
||||
let args = ["list"];
|
||||
args = args.concat(options);
|
||||
if (filesystem) {
|
||||
args = args.concat([filesystem]);
|
||||
}
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await objectivefs.exec(
|
||||
objectivefs.options.paths.objectivefs,
|
||||
args,
|
||||
{ env }
|
||||
);
|
||||
|
||||
return objectivefs.parseListOutput(result.stdout);
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mount.objectivefs snapshot <filesystem>
|
||||
*
|
||||
* NOTE: fs must be mount on node to function
|
||||
*
|
||||
* @param {*} env
|
||||
* @param {*} filesystem
|
||||
* @param {*} options
|
||||
*/
|
||||
async snapshot(env, filesystem = null, options = []) {
|
||||
if (!env) {
|
||||
env = {};
|
||||
}
|
||||
const objectivefs = this;
|
||||
let args = ["list"];
|
||||
args = args.concat(options);
|
||||
if (filesystem) {
|
||||
args = args.concat([filesystem]);
|
||||
}
|
||||
|
||||
let result;
|
||||
try {
|
||||
// NOTE: Successfully created snapshot: minio://ofs/test@2024-02-13T07:56:38Z (2024-02-13T00:56:38)
|
||||
result = await objectivefs.exec(
|
||||
objectivefs.options.paths.objectivefs,
|
||||
args,
|
||||
{ env }
|
||||
);
|
||||
|
||||
return result;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
exec(command, args, options = {}) {
|
||||
if (!options.hasOwnProperty("timeout")) {
|
||||
options.timeout = DEFAULT_TIMEOUT;
|
||||
}
|
||||
|
||||
const objectivefs = this;
|
||||
args = args || [];
|
||||
|
||||
if (objectivefs.options.sudo) {
|
||||
args.unshift(command);
|
||||
command = objectivefs.options.paths.sudo;
|
||||
}
|
||||
|
||||
// OBJECTIVEFS_ENV
|
||||
options.env = { ...{}, ...objectivefs.options.env, ...options.env };
|
||||
//console.log(options);
|
||||
|
||||
// truncate admin key during mount operations
|
||||
if (options.operation == "mount") {
|
||||
delete options.operation;
|
||||
// standard license is 24
|
||||
// admin key is 8
|
||||
if (
|
||||
options.env.OBJECTIVEFS_LICENSE &&
|
||||
options.env.OBJECTIVEFS_LICENSE.length > 24
|
||||
) {
|
||||
options.env.OBJECTIVEFS_LICENSE =
|
||||
options.env.OBJECTIVEFS_LICENSE.substr(0, 24);
|
||||
}
|
||||
}
|
||||
|
||||
const cleansedLog = `${command} ${args.join(" ")}`;
|
||||
console.log("executing objectivefs command: %s", cleansedLog);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let stdin;
|
||||
if (options.stdin) {
|
||||
stdin = options.stdin;
|
||||
delete options.stdin;
|
||||
}
|
||||
const child = objectivefs.options.executor.spawn(command, args, options);
|
||||
if (stdin) {
|
||||
child.stdin.write(stdin);
|
||||
}
|
||||
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
|
||||
child.stdout.on("data", function (data) {
|
||||
stdout = stdout + data;
|
||||
});
|
||||
|
||||
child.stderr.on("data", function (data) {
|
||||
stderr = stderr + data;
|
||||
});
|
||||
|
||||
child.on("close", function (code) {
|
||||
if (code == 78 && !stderr) {
|
||||
stderr += EXIT_CODE_78;
|
||||
}
|
||||
|
||||
if (code == 64 && !stderr) {
|
||||
stderr += EXIT_CODE_64;
|
||||
}
|
||||
|
||||
const result = { code, stdout, stderr, timeout: false };
|
||||
|
||||
// timeout scenario
|
||||
if (code === null) {
|
||||
result.timeout = true;
|
||||
reject(result);
|
||||
}
|
||||
|
||||
if (code) {
|
||||
reject(result);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.ObjectiveFS = ObjectiveFS;
|
||||
Loading…
Reference in New Issue