objectivefs refinement, better iscsi session handling
Signed-off-by: Travis Glenn Hansen <travisghansen@yahoo.com>
This commit is contained in:
parent
d70b45b909
commit
ed32cf8db0
|
|
@ -16,6 +16,7 @@ if [[ -n "${IMAGE_TAG}" ]]; then
|
|||
docker buildx build --progress plain --pull --push --platform "${DOCKER_BUILD_PLATFORM}" -t ${DOCKER_REPO}:${IMAGE_TAG} \
|
||||
--label "org.opencontainers.image.created=$(date -u --iso-8601=seconds)" \
|
||||
--label "org.opencontainers.image.revision=${GITHUB_SHA}" \
|
||||
--build-arg OBJECTIVEFS_DOWNLOAD_ID=${OBJECTIVEFS_DOWNLOAD_ID} \
|
||||
.
|
||||
else
|
||||
:
|
||||
|
|
|
|||
|
|
@ -464,6 +464,7 @@ jobs:
|
|||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }}
|
||||
GHCR_PASSWORD: ${{ secrets.GHCR_PASSWORD }}
|
||||
OBJECTIVEFS_DOWNLOAD_ID: ${{ secrets.OBJECTIVEFS_DOWNLOAD_ID }}
|
||||
DOCKER_CLI_EXPERIMENTAL: enabled
|
||||
DOCKER_BUILD_PLATFORM: linux/amd64,linux/arm64,linux/arm/v7,linux/s390x,linux/ppc64le
|
||||
IMAGE_TAG: ${{needs.determine-image-tag.outputs.tag}}
|
||||
|
|
|
|||
25
Dockerfile
25
Dockerfile
|
|
@ -9,7 +9,7 @@ ARG BUILDPLATFORM
|
|||
RUN echo "I am running build on $BUILDPLATFORM, building for $TARGETPLATFORM"
|
||||
|
||||
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
|
||||
&& 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=v20.11.1
|
||||
|
|
@ -26,8 +26,8 @@ ENV PATH=/usr/local/lib/nodejs/bin:$PATH
|
|||
|
||||
# Run as a non-root user
|
||||
RUN useradd --create-home csi \
|
||||
&& mkdir /home/csi/app \
|
||||
&& chown -R csi: /home/csi
|
||||
&& mkdir /home/csi/app \
|
||||
&& chown -R csi: /home/csi
|
||||
WORKDIR /home/csi/app
|
||||
USER csi
|
||||
|
||||
|
|
@ -50,21 +50,22 @@ ENV DEBIAN_FRONTEND=noninteractive
|
|||
|
||||
ARG TARGETPLATFORM
|
||||
ARG BUILDPLATFORM
|
||||
ARG OBJECTIVEFS_DOWNLOAD_ID
|
||||
|
||||
RUN echo "I am running on final $BUILDPLATFORM, building for $TARGETPLATFORM"
|
||||
|
||||
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
|
||||
&& 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_ENV=production
|
||||
|
||||
# Workaround for https://github.com/nodejs/node/issues/37219
|
||||
RUN test $(uname -m) != armv7l || ( \
|
||||
apt-get update \
|
||||
&& apt-get install -y libatomic1 \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
)
|
||||
apt-get update \
|
||||
&& apt-get install -y libatomic1 \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
)
|
||||
|
||||
# install node
|
||||
#ENV PATH=/usr/local/lib/nodejs/bin:$PATH
|
||||
|
|
@ -75,8 +76,8 @@ 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 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/*
|
||||
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
|
||||
#RUN apt-get update && \
|
||||
|
|
@ -84,7 +85,7 @@ RUN apt-get update && \
|
|||
# rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# install objectivefs
|
||||
ENV OBJECTIVEFS_VERSION=7.1
|
||||
ENV OBJECTIVEFS_VERSION=7.2
|
||||
ADD docker/objectivefs-installer.sh /usr/local/sbin
|
||||
RUN chmod +x /usr/local/sbin/objectivefs-installer.sh && objectivefs-installer.sh
|
||||
|
||||
|
|
@ -112,7 +113,7 @@ RUN chmod +x /usr/local/bin/oneclient
|
|||
|
||||
# Run as a non-root user
|
||||
RUN useradd --create-home csi \
|
||||
&& chown -R csi: /home/csi
|
||||
&& chown -R csi: /home/csi
|
||||
|
||||
COPY --from=build --chown=csi:csi /home/csi/app /home/csi/app
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ 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}"
|
||||
wget "https://objectivefs.com/user/download/${OBJECTIVEFS_DOWNLOAD_ID}/${DEB_FILE}"
|
||||
dpkg -i "${DEB_FILE}"
|
||||
|
||||
rm "${DEB_FILE}"
|
||||
|
|
|
|||
|
|
@ -592,75 +592,6 @@ class ControllerObjectiveFSDriver extends CsiBaseDriver {
|
|||
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,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -674,19 +605,6 @@ class ControllerObjectiveFSDriver extends CsiBaseDriver {
|
|||
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 {};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1265,11 +1265,6 @@ class CsiBaseDriver {
|
|||
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.")) {
|
||||
|
|
@ -1277,7 +1272,11 @@ class CsiBaseDriver {
|
|||
}
|
||||
}
|
||||
|
||||
let ofs_object_store = env["OBJECTSTORE"];
|
||||
for (const key in volume_context) {
|
||||
if (key.startsWith("env.")) {
|
||||
env[key.substr("env.".length)] = volume_context[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (!ofs_filesystem) {
|
||||
throw new GrpcError(
|
||||
|
|
@ -1286,6 +1285,16 @@ class CsiBaseDriver {
|
|||
);
|
||||
}
|
||||
|
||||
let ofs_object_store = env["OBJECTSTORE"];
|
||||
if (!ofs_object_store) {
|
||||
ofs_object_store = await objectivefs.getObjectStoreFromFilesystem(
|
||||
ofs_filesystem
|
||||
);
|
||||
if (ofs_object_store) {
|
||||
env["OBJECTSTORE"] = ofs_object_store;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ofs_object_store) {
|
||||
throw new GrpcError(
|
||||
grpc.status.FAILED_PRECONDITION,
|
||||
|
|
@ -1293,6 +1302,11 @@ class CsiBaseDriver {
|
|||
);
|
||||
}
|
||||
|
||||
// normalize fs to not include objectstore
|
||||
ofs_filesystem = await objectivefs.stripObjectStoreFromFilesystem(
|
||||
ofs_filesystem
|
||||
);
|
||||
|
||||
device = `${ofs_object_store}${ofs_filesystem}`;
|
||||
result = await mount.deviceIsMountedAtPath(
|
||||
device,
|
||||
|
|
|
|||
|
|
@ -181,6 +181,20 @@ function stringify(value) {
|
|||
return JSON.stringify(value, getCircularReplacer());
|
||||
}
|
||||
|
||||
function before_string(target, search) {
|
||||
if (!target.includes(search)) {
|
||||
return "";
|
||||
}
|
||||
return target.substring(0, target.indexOf(search));
|
||||
}
|
||||
|
||||
function after_string(target, search) {
|
||||
if (!target.includes(search)) {
|
||||
return "";
|
||||
}
|
||||
return target.substring(target.indexOf(search) + search.length);
|
||||
}
|
||||
|
||||
function default_supported_block_filesystems() {
|
||||
return ["btrfs", "exfat", "ext3", "ext4", "ext4dev", "ntfs", "vfat", "xfs"];
|
||||
}
|
||||
|
|
@ -266,6 +280,8 @@ module.exports.crc8 = crc8;
|
|||
module.exports.lockKeysFromRequest = lockKeysFromRequest;
|
||||
module.exports.getLargestNumber = getLargestNumber;
|
||||
module.exports.stringify = stringify;
|
||||
module.exports.before_string = before_string;
|
||||
module.exports.after_string = after_string;
|
||||
module.exports.stripWindowsDriveLetter = stripWindowsDriveLetter;
|
||||
module.exports.hasWindowsDriveLetter = hasWindowsDriveLetter;
|
||||
module.exports.axios_request = axios_request;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const cp = require("child_process");
|
||||
const { sleep } = require("./general");
|
||||
const { hostname_lookup, sleep } = require("./general");
|
||||
const net = require("net");
|
||||
|
||||
function getIscsiValue(value) {
|
||||
if (value == "<empty>") return null;
|
||||
|
|
@ -179,12 +180,34 @@ class ISCSI {
|
|||
const sessions = await iscsi.iscsiadm.getSessions();
|
||||
|
||||
let parsedPortal = iscsi.parsePortal(portal);
|
||||
let parsedPortalHostIP = "";
|
||||
if (parsedPortal.host) {
|
||||
// if host is not an ip address
|
||||
if (net.isIP(parsePortal.host) == 0) {
|
||||
// ipv6 response is without []
|
||||
parsedPortalHostIP =
|
||||
(await hostname_lookup(parsedPortal.host)) || "";
|
||||
}
|
||||
}
|
||||
|
||||
// set invalid hostname/ip string to ensure empty values do not errantly pass
|
||||
if (!parsedPortalHostIP) {
|
||||
parsedPortalHostIP = "--------------------------------------";
|
||||
}
|
||||
let session = false;
|
||||
sessions.every((i_session) => {
|
||||
// [2a10:4741:36:28:e61d:2dff:fe90:80fe]:3260
|
||||
// i_session.portal includes [] for ipv6
|
||||
if (
|
||||
`${i_session.iqn}` == tgtIQN &&
|
||||
(portal == i_session.portal ||
|
||||
`[${parsedPortal.host}]:${parsedPortal.port}` == i_session.portal)
|
||||
`${parsedPortal.host}:${parsedPortal.port}` == i_session.portal ||
|
||||
`${parsedPortalHostIP}:${parsedPortal.port}` ==
|
||||
i_session.portal ||
|
||||
`[${parsedPortal.host}]:${parsedPortal.port}` ==
|
||||
i_session.portal ||
|
||||
`[${parsedPortalHostIP}]:${parsedPortal.port}` ==
|
||||
i_session.portal)
|
||||
) {
|
||||
session = i_session;
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
const cp = require("child_process");
|
||||
const GeneralUtils = require("./general");
|
||||
|
||||
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";
|
||||
const EXIT_CODES = {
|
||||
64: "administrator can not mount filesystems",
|
||||
65: "unable to decrypt using passphrase",
|
||||
78: "missing or invalid passphrase",
|
||||
};
|
||||
|
||||
/**
|
||||
* https://objectivefs.com/
|
||||
|
|
@ -112,10 +116,30 @@ class ObjectiveFS {
|
|||
* @param {*} options
|
||||
*/
|
||||
async destroy(env, filesystem, options = []) {
|
||||
const objectivefs = this;
|
||||
if (!env) {
|
||||
env = {};
|
||||
}
|
||||
const objectivefs = this;
|
||||
|
||||
/**
|
||||
* delete safety checks for filesystem
|
||||
*
|
||||
* while it is possible to delete a fs without a pool we
|
||||
* should never be doing that in democratic-csi
|
||||
*/
|
||||
let fs_parts = filesystem.split("/");
|
||||
if (fs_parts.length != 2) {
|
||||
throw new Error(`filesystem safety check failed for fs: ${filesystem}`);
|
||||
}
|
||||
|
||||
if (!fs_parts[0]) {
|
||||
throw new Error(`filesystem safety check failed for fs: ${filesystem}`);
|
||||
}
|
||||
|
||||
if (!fs_parts[1]) {
|
||||
throw new Error(`filesystem safety check failed for fs: ${filesystem}`);
|
||||
}
|
||||
|
||||
let args = ["destroy"];
|
||||
args = args.concat(options);
|
||||
args = args.concat([filesystem]);
|
||||
|
|
@ -237,6 +261,18 @@ class ObjectiveFS {
|
|||
}
|
||||
}
|
||||
|
||||
async getObjectStoreFromFilesystem(filesystem) {
|
||||
if (filesystem.includes("://")) {
|
||||
return GeneralUtils.before_string("://");
|
||||
}
|
||||
}
|
||||
|
||||
async stripObjectStoreFromFilesystem(filesystem) {
|
||||
if (filesystem.includes("://")) {
|
||||
return GeneralUtils.after_string("://");
|
||||
}
|
||||
}
|
||||
|
||||
exec(command, args, options = {}) {
|
||||
if (!options.hasOwnProperty("timeout")) {
|
||||
options.timeout = DEFAULT_TIMEOUT;
|
||||
|
|
@ -250,9 +286,7 @@ class ObjectiveFS {
|
|||
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") {
|
||||
|
|
@ -296,12 +330,8 @@ class ObjectiveFS {
|
|||
});
|
||||
|
||||
child.on("close", function (code) {
|
||||
if (code == 78 && !stderr) {
|
||||
stderr += EXIT_CODE_78;
|
||||
}
|
||||
|
||||
if (code == 64 && !stderr) {
|
||||
stderr += EXIT_CODE_64;
|
||||
if (!stderr && EXIT_CODES[code]) {
|
||||
stderr += EXIT_CODES[code];
|
||||
}
|
||||
|
||||
const result = { code, stdout, stderr, timeout: false };
|
||||
|
|
|
|||
Loading…
Reference in New Issue