import { FormikErrors, useFormikContext } from 'formik'; import _ from 'lodash'; import { useMemo } from 'react'; import DockerIcon from '@/assets/ico/vendor/docker.svg?c'; import { useImages } from '@/react/docker/images/queries/useImages'; import { imageContainsURL, getUniqueTagListFromImages, } from '@/react/docker/images/utils'; import { useEnvironmentId } from '@/react/hooks/useEnvironmentId'; import { useEnvironmentRegistries } from '@/react/portainer/environments/queries/useEnvironmentRegistries'; import { Registry, RegistryId, RegistryTypes, } from '@/react/portainer/registries/types/registry'; import { useRegistry } from '@/react/portainer/registries/queries/useRegistry'; import { Button } from '@@/buttons'; import { FormControl } from '@@/form-components/FormControl'; import { InputGroup } from '@@/form-components/InputGroup'; import { PortainerSelect } from '@@/form-components/PortainerSelect'; import { Input } from '@@/form-components/Input'; import { Values } from './types'; import { InputSearch } from './InputSearch'; import { getIsDockerHubRegistry } from './utils'; export function SimpleForm({ autoComplete, values, errors, fieldNamespace, }: { autoComplete?: boolean; values: Values; errors?: FormikErrors; fieldNamespace?: string; }) { const { setFieldValue } = useFormikContext(); const registryQuery = useRegistry(values.registryId); const registry = registryQuery.data; const registryUrl = getRegistryURL(registry) || 'docker.io'; const isDockerHubRegistry = getIsDockerHubRegistry(registry); return ( <> setFieldValue(namespaced('registryId'), value)} value={values.registryId} inputId="registry-field" /> {registryUrl} setFieldValue(namespaced('image'), value)} value={values.image} registry={registry} autoComplete={autoComplete} inputId="image-field" /> {isDockerHubRegistry && ( )} ); function namespaced(field: string) { return fieldNamespace ? `${fieldNamespace}.${field}` : field; } } function getImagesForRegistry( images: string[], registries: Array, registry?: Registry ) { if (isKnownRegistry(registry)) { const url = getRegistryURL(registry); const registryImages = images.filter((image) => image.includes(url)); return registryImages.map((image) => image.replace(new RegExp(`${url}/?`), '') ); } const knownRegistries = registries.filter((reg) => isKnownRegistry(reg)); const registryImages = knownRegistries.flatMap((registry) => images.filter((image) => image.includes(registry.URL)) ); return _.difference(images, registryImages).filter( (image) => !imageContainsURL(image) ); } function RegistrySelector({ value, onChange, inputId, }: { value: RegistryId | undefined; onChange: (value: RegistryId | undefined) => void; inputId?: string; }) { const environmentId = useEnvironmentId(); const registriesQuery = useEnvironmentRegistries(environmentId, { select: (registries) => registries .sort((a, b) => a.Name.localeCompare(b.Name)) .map((registry) => ({ label: registry.Name, value: registry.Id, })), }); return ( ); } function ImageField({ value, onChange, registry, autoComplete, inputId, }: { value: string; onChange: (value: string) => void; registry?: Registry; autoComplete?: boolean; inputId?: string; }) { return autoComplete ? ( ) : ( onChange(e.target.value)} id={inputId} /> ); } function ImageFieldAutoComplete({ value, onChange, registry, inputId, }: { value: string; onChange: (value: string) => void; registry?: Registry; inputId?: string; }) { const environmentId = useEnvironmentId(); const registriesQuery = useEnvironmentRegistries(environmentId); const imagesQuery = useImages(environmentId, { select: (images) => getUniqueTagListFromImages(images), }); const imageOptions = useMemo(() => { const images = getImagesForRegistry( imagesQuery.data || [], registriesQuery.data || [], registry ); return images.map((image) => ({ label: image, value: image, })); }, [registry, imagesQuery.data, registriesQuery.data]); return ( onChange(value)} data-cy="component-imageInput" placeholder="e.g. my-image:my-tag" options={imageOptions} inputId={inputId} /> ); } function isKnownRegistry(registry?: Registry) { return registry && registry.Type !== RegistryTypes.ANONYMOUS && registry.URL; } function getRegistryURL(registry?: Registry) { if (!registry) { return ''; } if ( registry.Type !== RegistryTypes.GITLAB && registry.Type !== RegistryTypes.GITHUB ) { return registry.URL; } if (registry.Type === RegistryTypes.GITLAB) { return `${registry.URL}/${registry.Gitlab?.ProjectPath}`; } if (registry.Type === RegistryTypes.GITHUB) { const namespace = registry.Github?.UseOrganisation ? registry.Github?.OrganisationName : registry.Username; return `${registry.URL}/${namespace}`; } return ''; }