Skip to content

Commit ad6b895

Browse files
author
Bogdan Tsechoev
committed
Merge branch 'pg-image-not-found-fallback' into 'master'
fix: white screen crash for custom Docker tags See merge request postgres-ai/database-lab!1043
2 parents 597d1d3 + 7e52d75 commit ad6b895

File tree

3 files changed

+182
-58
lines changed

3 files changed

+182
-58
lines changed

ui/packages/shared/pages/Instance/Configuration/index.tsx

Lines changed: 89 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*--------------------------------------------------------------------------
66
*/
77

8-
import { useState, useEffect } from 'react'
8+
import { useState, useEffect, useMemo } from 'react'
99
import { observer } from 'mobx-react-lite'
1010
import Editor from '@monaco-editor/react'
1111
import {
@@ -35,6 +35,9 @@ import {
3535
uniqueChipValue,
3636
customOrGenericImage,
3737
genericDockerImages,
38+
getImageMajorVersion,
39+
createFallbackDockerImage,
40+
createEnhancedDockerImages,
3841
} from './utils'
3942
import {
4043
SelectWithTooltip,
@@ -194,6 +197,32 @@ export const Configuration = observer(
194197
const [{ formik, connectionData, isConnectionDataValid }] =
195198
useForm(onSubmit)
196199

200+
// Memoized enhanced Docker images to avoid recreation on every render
201+
// This combines predefined images with any custom image from configuration
202+
const enhancedDockerImages = useMemo(() => {
203+
return createEnhancedDockerImages(
204+
configData?.dockerImageType === 'Generic Postgres' ? configData?.dockerPath : undefined,
205+
configData?.dockerImageType === 'Generic Postgres' ? configData?.dockerTag : undefined
206+
)
207+
}, [configData?.dockerPath, configData?.dockerTag, configData?.dockerImageType])
208+
209+
// Memoized computed values from enhanced images
210+
const dockerImageVersions = useMemo(() => {
211+
return enhancedDockerImages
212+
.map((image) => image.pg_major_version)
213+
.filter((value, index, self) => self.indexOf(value) === index)
214+
.sort((a, b) => Number(a) - Number(b))
215+
}, [enhancedDockerImages])
216+
217+
// Memoized tags and locations for performance
218+
const dockerTags = useMemo(() => {
219+
return enhancedDockerImages.map((image) => image.tag)
220+
}, [enhancedDockerImages])
221+
222+
const dockerLocations = useMemo(() => {
223+
return enhancedDockerImages.map((image) => image.location)
224+
}, [enhancedDockerImages])
225+
197226
const scrollToField = () => {
198227
const errorElement = document.querySelector('.Mui-error')
199228
if (errorElement) {
@@ -457,30 +486,23 @@ export const Configuration = observer(
457486
e: React.ChangeEvent<HTMLInputElement>,
458487
) => {
459488
if (e.target.value === 'Generic Postgres') {
460-
const genericImageVersions = genericDockerImages
461-
.map((image) => image.pg_major_version)
462-
.filter((value, index, self) => self.indexOf(value) === index)
463-
.sort((a, b) => Number(a) - Number(b))
464-
const currentDockerImage = genericImageVersions.slice(-1)[0]
489+
// Use memoized enhanced list for better performance
490+
const currentDockerImage = dockerImageVersions.slice(-1)[0]
465491

466492
setDockerState({
467493
...dockerState,
468-
tags: genericDockerImages
469-
.map((image) => image.tag)
470-
.filter((tag) => tag.startsWith(currentDockerImage)),
471-
locations: genericDockerImages
472-
.map((image) => image.location)
473-
.filter((location) => location?.includes(currentDockerImage)),
474-
images: genericImageVersions,
475-
data: genericDockerImages,
494+
tags: dockerTags.filter((tag) => tag.startsWith(currentDockerImage)),
495+
locations: dockerLocations.filter((location) => location?.includes(currentDockerImage)),
496+
images: dockerImageVersions,
497+
data: enhancedDockerImages,
476498
})
477499

478500
formik.setValues({
479501
...formik.values,
480502
dockerImage: currentDockerImage,
481503
dockerImageType: e.target.value,
482-
dockerTag: genericDockerImages.map((image) => image.tag)[0],
483-
dockerPath: genericDockerImages.map((image) => image.location)[0],
504+
dockerTag: dockerTags[0],
505+
dockerPath: dockerLocations[0],
484506
sharedPreloadLibraries:
485507
'pg_stat_statements,pg_stat_kcache,pg_cron,pgaudit,anon',
486508
})
@@ -520,16 +542,28 @@ export const Configuration = observer(
520542
tags: updatedDockerTags,
521543
})
522544

523-
const currentLocation = dockerState.data.find(
524-
(image) => image.tag === updatedDockerTags[0],
525-
)?.location as string
545+
// Add safety check for empty array
546+
const firstTag = updatedDockerTags[0]
547+
if (firstTag) {
548+
const currentLocation = dockerState.data.find(
549+
(image) => image.tag === firstTag,
550+
)?.location
526551

527-
formik.setValues({
528-
...formik.values,
529-
dockerTag: updatedDockerTags[0],
530-
dockerImage: e.target.value,
531-
dockerPath: currentLocation,
532-
})
552+
formik.setValues({
553+
...formik.values,
554+
dockerTag: firstTag,
555+
dockerImage: e.target.value,
556+
dockerPath: currentLocation || '',
557+
})
558+
} else {
559+
// Fallback when no matching tags found
560+
formik.setValues({
561+
...formik.values,
562+
dockerImage: e.target.value,
563+
dockerTag: '',
564+
dockerPath: '',
565+
})
566+
}
533567
} else {
534568
formik.setValues({
535569
...formik.values,
@@ -553,41 +587,46 @@ export const Configuration = observer(
553587

554588
if (customOrGenericImage(configData?.dockerImageType)) {
555589
if (configData?.dockerImageType === 'Generic Postgres') {
556-
const genericImageVersions = genericDockerImages
557-
.map((image) => image.pg_major_version)
558-
.filter((value, index, self) => self.indexOf(value) === index)
559-
.sort((a, b) => Number(a) - Number(b))
560-
const currentDockerImage =
561-
genericDockerImages.filter(
562-
(image) => image.location === configData?.dockerPath,
563-
)[0] ||
564-
genericDockerImages.filter((image) =>
565-
configData?.dockerPath?.includes(image.pg_major_version),
566-
)[0]
590+
// Use memoized enhanced list for better performance
591+
const currentDockerImage = enhancedDockerImages.find(
592+
(image) => image.location === configData?.dockerPath || image.tag === configData?.dockerTag
593+
)
567594

568-
setDockerState({
569-
...dockerState,
570-
tags: genericDockerImages
571-
.map((image) => image.tag)
572-
.filter((tag) =>
595+
if (currentDockerImage) {
596+
setDockerState({
597+
...dockerState,
598+
tags: dockerTags.filter((tag) =>
573599
tag.startsWith(currentDockerImage.pg_major_version),
574600
),
575-
images: genericImageVersions,
576-
data: genericDockerImages,
577-
})
601+
images: dockerImageVersions,
602+
data: enhancedDockerImages,
603+
})
578604

579-
formik.setFieldValue('dockerTag', currentDockerImage?.tag)
580-
formik.setFieldValue(
581-
'dockerImage',
582-
currentDockerImage.pg_major_version,
583-
)
605+
formik.setFieldValue('dockerTag', currentDockerImage.tag)
606+
formik.setFieldValue('dockerImage', currentDockerImage.pg_major_version)
607+
formik.setFieldValue('dockerPath', currentDockerImage.location)
608+
} else {
609+
// Fallback: shouldn't happen with enhancedDockerImages, but keep for safety
610+
const fallbackVersion = dockerImageVersions.slice(-1)[0]
611+
612+
setDockerState({
613+
...dockerState,
614+
tags: dockerTags.filter((tag) => tag.startsWith(fallbackVersion)),
615+
images: dockerImageVersions,
616+
data: enhancedDockerImages,
617+
})
618+
619+
formik.setFieldValue('dockerTag', configData?.dockerTag || '')
620+
formik.setFieldValue('dockerImage', fallbackVersion)
621+
formik.setFieldValue('dockerPath', configData?.dockerPath || '')
622+
}
584623
} else {
585624
formik.setFieldValue('dockerImage', configData?.dockerPath)
586625
}
587626
}
588627
}
589628
}
590-
}, [config])
629+
}, [config, configData?.dockerPath, configData?.dockerTag, configData?.dockerImageType])
591630

592631
useEffect(() => {
593632
getEngine(instanceId).then((res) => {

ui/packages/shared/pages/Instance/Configuration/utils/index.ts

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import { FormValues } from '../useForm'
55

66
const seContainerRegistry = 'se-images'
77
const genericImagePrefix = 'postgresai/extended-postgres'
8-
// since some tags are rc, we need to specify the exact tags to use
8+
// Predefined list of Docker images for UI display
9+
// This list is shown to users for convenient selection
10+
// IMPORTANT: if user specified an image in config that's not in this list,
11+
// it will be automatically added via createEnhancedDockerImages()
912
const dockerImagesConfig = {
1013
'9.6': ['0.5.3', '0.5.2', '0.5.1'],
1114
'10': ['0.5.3', '0.5.2', '0.5.1'],
@@ -110,11 +113,22 @@ export const getImageType = (imageUrl: string) => {
110113
}
111114

112115
export const getImageMajorVersion = (pgImage: string | undefined) => {
113-
const pgImageVersion = pgImage?.split(':')[1]
114-
const pgServerVersion = pgImageVersion?.split('-')[0]
115-
return pgServerVersion?.includes('.')
116-
? pgServerVersion?.split('.')[0]
117-
: pgServerVersion
116+
if (!pgImage) return undefined
117+
118+
try {
119+
const pgImageVersion = pgImage.split(':')[1]
120+
if (!pgImageVersion) return undefined
121+
122+
const pgServerVersion = pgImageVersion.split('-')[0]
123+
if (!pgServerVersion) return undefined
124+
125+
return pgServerVersion.includes('.')
126+
? pgServerVersion.split('.')[0]
127+
: pgServerVersion
128+
} catch (error) {
129+
// Return undefined for malformed image strings
130+
return undefined
131+
}
118132
}
119133

120134
export const formatDatabases = (databases: DatabaseType | null) => {
@@ -151,3 +165,71 @@ export const postUniqueCustomOptions = (options: string) => {
151165

152166
export const customOrGenericImage = (dockerImage: string | undefined) =>
153167
dockerImage === 'Generic Postgres' || dockerImage === 'custom'
168+
169+
export const createFallbackDockerImage = (
170+
dockerPath: string,
171+
dockerTag: string,
172+
): DockerImage => {
173+
const majorVersion = getImageMajorVersion(dockerPath) || '17' // Default to 17 if version can't be extracted
174+
175+
return {
176+
package_group: 'postgresai',
177+
pg_major_version: majorVersion,
178+
tag: dockerTag || `${majorVersion}-custom`,
179+
location: dockerPath,
180+
}
181+
}
182+
183+
// Creates enhanced list of Docker images, including image from configuration
184+
export const createEnhancedDockerImages = (
185+
configDockerPath?: string,
186+
configDockerTag?: string,
187+
): DockerImage[] => {
188+
let enhancedImages = [...genericDockerImages]
189+
190+
// If there's an image in config, check if we need to add it
191+
if (configDockerPath && configDockerTag) {
192+
const existingImage = genericDockerImages.find(
193+
(image) => image.location === configDockerPath || image.tag === configDockerTag
194+
)
195+
196+
// If image not found in predefined list, add it
197+
if (!existingImage) {
198+
// Check if this is a Generic Postgres image
199+
if (configDockerPath.includes(genericImagePrefix)) {
200+
// For Generic Postgres images create proper structure
201+
const majorVersion = getImageMajorVersion(configDockerPath)
202+
if (majorVersion) {
203+
const configImage: DockerImage = {
204+
package_group: 'postgresai',
205+
pg_major_version: majorVersion,
206+
tag: configDockerTag,
207+
location: configDockerPath,
208+
}
209+
enhancedImages.push(configImage)
210+
} else {
211+
// Fallback if version extraction failed
212+
const configImage = createFallbackDockerImage(configDockerPath, configDockerTag)
213+
enhancedImages.push(configImage)
214+
}
215+
} else {
216+
// For custom images use fallback
217+
const configImage = createFallbackDockerImage(configDockerPath, configDockerTag)
218+
enhancedImages.push(configImage)
219+
}
220+
}
221+
}
222+
223+
return enhancedImages
224+
}
225+
226+
// Checks if image is loaded from configuration (not from predefined list)
227+
export const isConfigLoadedImage = (
228+
dockerPath: string,
229+
dockerTag: string,
230+
): boolean => {
231+
const existingImage = genericDockerImages.find(
232+
(image) => image.location === dockerPath || image.tag === dockerTag
233+
)
234+
return !existingImage
235+
}

ui/packages/shared/types/api/entities/config.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,15 @@ export const formatConfig = (config: configTypes) => {
6363
debug: config.global?.debug,
6464
dockerImage: isSeDockerImage(dockerImage)
6565
? getImageMajorVersion(dockerImage)
66+
: dockerImage && getImageType(dockerImage) === 'Generic Postgres'
67+
? getImageMajorVersion(dockerImage) || dockerImage
6668
: dockerImage,
6769
...(dockerImage && {
6870
dockerImageType: getImageType(dockerImage),
6971
}),
70-
...(isSeDockerImage(dockerImage) && {
71-
dockerTag: dockerImage?.split(':')[1],
72+
// Extract dockerTag for both SE images and Generic Postgres images
73+
...(dockerImage && dockerImage.includes(':') && {
74+
dockerTag: dockerImage.split(':')[1],
7275
}),
7376
dockerPath: dockerImage,
7477
tuningParams: formatTuningParams(config.databaseConfigs?.configs),

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy