diff --git a/packages/clients/src/api/instance/v1/api.utils.ts b/packages/clients/src/api/instance/v1/api.utils.ts index 5f96f543d..51b053e80 100644 --- a/packages/clients/src/api/instance/v1/api.utils.ts +++ b/packages/clients/src/api/instance/v1/api.utils.ts @@ -1,3 +1,4 @@ +import { validatePathParam } from '../../../bridge' import { createExponentialBackoffStrategy, tryAtIntervals, @@ -11,6 +12,8 @@ import { SNAPSHOT_TRANSIENT_STATUSES, VOLUME_TRANSIENT_STATUSES, } from './content.gen' +import { unmarshalSetImageResponse } from './marshalling.gen' +import { marshalSetImageRequestWithID } from './marshalling.utils' import type { GetImageRequest, GetPrivateNICRequest, @@ -26,13 +29,18 @@ import type { Volume, VolumeServerTemplate, } from './types.gen' -import type { SetSecurityGroupRuleRequest } from './types.private.gen' +import type { + SetImageResponse, + SetSecurityGroupRuleRequest, +} from './types.private.gen' import type { AttachVolumeRequest, AttachVolumeResponse, CreateServerRequest, DetachVolumeRequest, DetachVolumeResponse, + UpdateImageRequest, + UpdateImageResponse, UpdateSecurityGroupRequest, UpdateSecurityGroupResponse, UpdateSecurityGroupRuleRequest, @@ -467,4 +475,39 @@ export class InstanceV1UtilsAPI extends API { zone: request.zone, } as UpdateServerRequest).then(obj => obj as DetachVolumeResponse) } + + /** + * Updates an image. + * + * @param request - The request {@link UpdateImageRequest} + * @returns A Promise of UpdateImageResponse + */ + updateImage = ( + request: Readonly, + ): Promise => + this.getImage({ zone: request.zone, imageId: request.imageId }) + .then(res => validateNotUndefined(res.image)) + .then(image => ({ + ...image, + name: request.name ?? image.name, + tags: request.tags ?? image.tags, + id: image.id, + })) + .then(imageReq => + this.client.fetch( + { + body: JSON.stringify( + marshalSetImageRequestWithID(imageReq, this.client.settings), + ), + headers: { 'Content-Type': 'application/json; charset=utf-8' }, + method: 'PUT', + path: `/instance/v1/zones/${validatePathParam( + 'zone', + imageReq.zone ?? this.client.settings.defaultZone, + )}/images/${validatePathParam('id', imageReq.id)}`, + }, + unmarshalSetImageResponse, + ), + ) + .then(res => ({ image: res.image })) } diff --git a/packages/clients/src/api/instance/v1/marshalling.utils.ts b/packages/clients/src/api/instance/v1/marshalling.utils.ts new file mode 100644 index 000000000..9d7f7cc8d --- /dev/null +++ b/packages/clients/src/api/instance/v1/marshalling.utils.ts @@ -0,0 +1,97 @@ +import { type DefaultValues } from '../../../bridge' +import type { + Bootscript, + ServerSummary, + Volume, + VolumeSummary, +} from './types.gen' +import type { SetImageRequest } from './types.private.gen' + +const marshalVolumeSummary = ( + request: VolumeSummary, + defaults: DefaultValues, +): Record => ({ + id: request.id, + name: request.name, + size: request.size, + volume_type: request.volumeType, +}) + +const marshalServerSummary = ( + request: ServerSummary, + defaults: DefaultValues, +): Record => ({ + id: request.id, + name: request.name, +}) + +const marshalBootscript = ( + request: Bootscript, + defaults: DefaultValues, +): Record => ({ + arch: request.arch, + bootcmdargs: request.bootcmdargs, + default: request.default, + dtb: request.dtb, + id: request.id, + initrd: request.initrd, + kernel: request.kernel, + organization: request.organization, + project: request.project, + public: request.public, + title: request.title, + zone: request.zone, +}) + +const marshalVolume = ( + request: Volume, + defaults: DefaultValues, +): Record => ({ + creation_date: request.creationDate, + export_uri: request.exportUri, + id: request.id, + modification_date: request.modificationDate, + name: request.name, + organization: request.organization, + project: request.project, + server: request.server + ? marshalServerSummary(request.server, defaults) + : undefined, + size: request.size, + state: request.state, + tags: request.tags, + volume_type: request.volumeType, + zone: request.zone, +}) + +export const marshalSetImageRequestWithID = ( + request: SetImageRequest, + defaults: DefaultValues, +): Record => ({ + arch: request.arch, + creation_date: request.creationDate, + default_bootscript: request.defaultBootscript + ? marshalBootscript(request.defaultBootscript, defaults) + : undefined, + extra_volumes: request.extraVolumes + ? Object.entries(request.extraVolumes).reduce( + (acc, [key, value]) => ({ + ...acc, + [key]: marshalVolume(value, defaults), + }), + {}, + ) + : undefined, + from_server: request.fromServer, + modification_date: request.modificationDate, + id: request.id, + name: request.name, + organization: request.organization, + project: request.project, + public: request.public, + root_volume: request.rootVolume + ? marshalVolumeSummary(request.rootVolume, defaults) + : undefined, + state: request.state, + tags: request.tags, +}) diff --git a/packages/clients/src/api/instance/v1/types.utils.ts b/packages/clients/src/api/instance/v1/types.utils.ts index 1e2c79709..4a2ab0268 100644 --- a/packages/clients/src/api/instance/v1/types.utils.ts +++ b/packages/clients/src/api/instance/v1/types.utils.ts @@ -1,5 +1,6 @@ import type { Zone } from '../../../scw/locality' import type { + Image, SecurityGroup, SecurityGroupPolicy, SecurityGroupRule, @@ -93,6 +94,18 @@ export interface DetachVolumeResponse { server?: Server } +export type UpdateImageRequest = { + /** Zone to target. If none is passed will use default zone from the config */ + zone?: Zone + imageId: string + name?: string + tags?: string[] +} + +export interface UpdateImageResponse { + image?: Image +} + export type { CreateServerRequest, UpdateServerRequest,