Skip to content

Commit

Permalink
feat(pci-object-storage): add container
Browse files Browse the repository at this point in the history
ref: DTCORE-2884
Signed-off-by: Florian Renaut <[email protected]>
  • Loading branch information
frenautvh committed Dec 19, 2024
1 parent 787a4e4 commit b12b7b7
Show file tree
Hide file tree
Showing 9 changed files with 401 additions and 75 deletions.
11 changes: 0 additions & 11 deletions packages/manager/apps/pci-object-storage/src/api/data/objects.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
import { v6 } from '@ovh-ux/manager-core-api';
import { OPENIO_DEFAULT_REGION } from '@/constants';

export type TAccess = {
token: string;
endpoints: { region: string; url: string }[];
};
export const getAccessToken = async (projectId: string): Promise<TAccess> => {
const { data } = await v6.post<TAccess>(
`/cloud/project/${projectId}/storage/access`,
);
return data;
};

export const deleteS3Object = async (
projectId: string,
containerId: string,
Expand Down
156 changes: 156 additions & 0 deletions packages/manager/apps/pci-object-storage/src/api/data/storages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,159 @@ export const updateStorage = async ({

return data;
};

export const createSwiftStorage = async ({
projectId,
archive,
containerName,
region,
}: {
projectId: string;
archive: boolean;
containerName: string;
region: string;
}) => {
const { data } = await v6.post<TStorage>(
`/cloud/project/${projectId}/storage`,
{
archive,
containerName,
region,
},
);

return data;
};

export const createS3Storage = async ({
projectId,
containerName,
ownerId,
region,
encryption,
versioning,
}: {
projectId: string;
containerName: string;
region: string;
ownerId: string;
encryption: string;
versioning: boolean;
}) => {
const { data } = await v6.post<TStorage>(
`/cloud/project/${projectId}/region/${region}/storage`,
{
name: containerName,
ownerId,
encryption: {
sseAlgorithm: encryption,
},
...(versioning
? {
versioning: {
status: versioning ? 'enabled' : 'disabled',
},
}
: {}),
},
);

return data;
};

export type TStorageAccess = {
endpoints: { region: string; url: string }[];
token: string;
};

export const getStorageAccess = async ({
projectId,
}: {
projectId: string;
}) => {
const { data } = await v6.post<TStorageAccess>(
`/cloud/project/${projectId}/storage/access`,
);

return data;
};

export const getContainerMetaData = async ({
projectId,
containerName,
region,
}: {
projectId: string;
containerName: string;
region: string;
}) => {
const access = await getStorageAccess({ projectId });
const endpoint = access.endpoints?.find(
(_endpoint) => _endpoint.region === region,
);
if (!endpoint) {
throw new Error(`No endpoint found for region ${region}`);
}
const response = await fetch(
`${endpoint.url}/${encodeURIComponent(containerName)}`,
{
method: 'HEAD',
headers: {
'X-Auth-Token': access.token,
},
},
);
const metadataRegex = /^(X-Container|X-Storage)/i;
const metadata = Object.keys(response.headers)
.filter((key) => metadataRegex.test(key))
.reduce(
(obj, key) => ({
...obj,
[key]: response.headers[key],
}),
{},
);
return { endpoint, token: access.token, metadata };
};

export const setContainerAsStatic = async ({
projectId,
containerId,
}: {
projectId: string;
containerId: string;
}) => {
const { data } = await v6.post(
`/cloud/project/${projectId}/storage/${containerId}/static`,
);

return data;
};

export const setContainerAsPublic = async ({
projectId,
containerName,
region,
}: {
projectId: string;
containerName: string;
region: string;
}) => {
const { endpoint, token, metadata } = await getContainerMetaData({
projectId,
containerName,
region,
});
const X_CONTAINER_READ = 'x-container-read';
const X_CONTAINER_READ_PUBLIC_VALUE = '.r:*,.rlistings';
if (metadata[X_CONTAINER_READ] !== X_CONTAINER_READ_PUBLIC_VALUE) {
return fetch(`${endpoint.url}/${encodeURIComponent(containerName)}`, {
method: 'PUT',
headers: {
'X-Auth-Token': token,
[X_CONTAINER_READ]: X_CONTAINER_READ_PUBLIC_VALUE,
},
}).then(() => true);
}
return true;
};
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import {
addUser,
deleteObject,
deleteS3Object,
getAccessToken,
} from '@/api/data/objects';
import { addUser, deleteObject, deleteS3Object } from '@/api/data/objects';
import queryClient from '@/queryClient';
import { TStorage } from '../data/storages';
import { TStorage, getStorageAccess } from '../data/storages';

export const useAccessToken = (projectId: string) =>
useQuery({
queryKey: ['project', projectId, 'access'],
queryFn: () => getAccessToken(projectId),
queryFn: () => getStorageAccess({ projectId }),
});

type DeleteObjectProps = {
Expand Down Expand Up @@ -41,7 +36,7 @@ export const useDeleteObject = ({
storage.s3StorageType,
);
} else {
const response = await getAccessToken(projectId);
const response = await getStorageAccess({ projectId });
deleteObject(
projectId,
storage.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,19 @@ import {
TStorage,
getStorage,
updateStorage,
createSwiftStorage,
createS3Storage,
setContainerAsStatic,
setContainerAsPublic,
} from '../data/storages';
import {
OBJECT_CONTAINER_MODE_LOCAL_ZONE,
OBJECT_CONTAINER_MODE_MONO_ZONE,
OBJECT_CONTAINER_MODE_MULTI_ZONES,
OBJECT_CONTAINER_OFFER_STORAGE_STANDARD,
OBJECT_CONTAINER_OFFER_SWIFT,
OBJECT_CONTAINER_TYPE_PUBLIC,
OBJECT_CONTAINER_TYPE_STATIC,
} from '@/constants';
import { paginateResults } from '@/helpers';

Expand Down Expand Up @@ -264,3 +272,75 @@ export const useUpdateStorage = ({
...mutation,
};
};

export interface UseCreateContainerArgs {
offer: string;
archive?: boolean;
containerName: string;
region: string;
ownerId?: string;
encryption?: string;
versioning?: boolean;
containerType?: string;
}

export const useCreateContainer = ({
projectId,
onSuccess,
onError,
}: {
projectId: string;
onSuccess: (container: TStorage) => void;
onError: (error: ApiError) => void;
}) => {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async (args: UseCreateContainerArgs) => {
let result = null;
if (args.offer === OBJECT_CONTAINER_OFFER_SWIFT) {
result = await createSwiftStorage({
projectId,
archive: args.archive || false,
containerName: args.containerName,
region: args.region,
});
} else if (args.offer === OBJECT_CONTAINER_OFFER_STORAGE_STANDARD) {
result = createS3Storage({
projectId,
containerName: args.containerName,
ownerId: args.ownerId,
region: args.region,
encryption: args.encryption,
versioning: args.versioning,
});
}
if (!result) throw new Error(`${args.offer}: unknown container offer!`);
if (args.containerType === OBJECT_CONTAINER_TYPE_STATIC) {
await setContainerAsStatic({
projectId,
containerId: result.id,
});
} else if (args.containerType === OBJECT_CONTAINER_TYPE_PUBLIC) {
await setContainerAsPublic({
projectId,
containerName: args.containerName,
region: args.region,
});
}
return result;
},
onError,
onSuccess: async (result) => {
await queryClient.invalidateQueries({
queryKey: getStorageQueryKey(projectId),
});
onSuccess(result);
},
});

return {
createContainer: (container: UseCreateContainerArgs) =>
mutation.mutate(container),
...mutation,
};
};
3 changes: 3 additions & 0 deletions packages/manager/apps/pci-object-storage/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ export const OBJECT_CONTAINER_TYPES = [

export const CONTAINER_COMMERCIAL_NAME = 'storage-replicated';

export const NO_ENCRYPTION_VALUE = 'plaintext';
export const ENCRYPTION_ALGORITHM_SSE_S3 = 'AES256';

export const STORAGE_ASYNC_REPLICATION_LINK = {
DEFAULT:
'https://help.ovhcloud.com/csm/en-public-cloud-storage-s3-asynchronous-replication-buckets?id=kb_article_view&sysparm_article=KB0062417',
Expand Down
Loading

0 comments on commit b12b7b7

Please sign in to comment.