diff --git a/Dockerfile b/Dockerfile index a73e75ae46..70957e3ae6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -70,7 +70,7 @@ ENV HELM_VERSION=v3.10.3 ENV TERRAFORM_VERSION=v1.2.9 # renovate: datasource=github-releases depName=pluralsh/plural-cli -ENV CLI_VERSION=v0.8.1 +ENV CLI_VERSION=v0.8.2 # renovate: datasource=github-tags depName=kubernetes/kubernetes ENV KUBECTL_VERSION=v1.25.5 diff --git a/assets/src/components/cd/globalSettings/GlobalSettings.tsx b/assets/src/components/cd/globalSettings/GlobalSettings.tsx index 60a38c70ae..d4054419b7 100644 --- a/assets/src/components/cd/globalSettings/GlobalSettings.tsx +++ b/assets/src/components/cd/globalSettings/GlobalSettings.tsx @@ -15,7 +15,9 @@ import { useDeploymentSettingsQuery, } from 'generated/graphql' -import { useMemo } from 'react' +import { useContext, useMemo } from 'react' + +import { LoginContext } from 'components/contexts' import { CD_BASE_CRUMBS } from '../ContinuousDeployment' @@ -47,6 +49,10 @@ const directory = [ path: 'repositories', label: 'Repositories', }, + { + path: 'auto-update', + label: 'Auto Update', + }, ] type GlobalSettingsContextType = { @@ -60,6 +66,7 @@ export const useGlobalSettingsContext = () => export function GlobalSettings() { const theme = useTheme() const { pathname } = useLocation() + const { configuration } = useContext(LoginContext) const { data, refetch } = useDeploymentSettingsQuery({}) const outletContext = useMemo( @@ -70,6 +77,16 @@ export function GlobalSettings() { [data, refetch] ) + const prunedDirectory = useMemo( + () => + directory.filter( + ({ path }) => + path !== 'auto-update' || + (configuration?.byok && !data?.deploymentSettings?.selfManaged) + ), + [configuration, data] + ) + return ( @@ -80,7 +97,7 @@ export function GlobalSettings() { }} > diff --git a/assets/src/components/cd/globalSettings/SelfManage.tsx b/assets/src/components/cd/globalSettings/SelfManage.tsx new file mode 100644 index 0000000000..a0e152a71f --- /dev/null +++ b/assets/src/components/cd/globalSettings/SelfManage.tsx @@ -0,0 +1,44 @@ +import { CodeEditor } from '@pluralsh/design-system' +import { GqlError } from 'components/utils/Alert' +import { ScrollablePage } from 'components/utils/layout/ScrollablePage' +import { useSelfManageMutation } from 'generated/graphql' +import { useState } from 'react' +import { useNavigate } from 'react-router-dom' + +const INTRO_TEXT = + '# Paste your helm values file here\n' + + '# this will create a service that will auto-update your instance\n' + + '# with these values persisted throughout' + +export default function SelfManage() { + const navigate = useNavigate() + const [values, setValues] = useState(INTRO_TEXT) + const [mutation, { loading, error }] = useSelfManageMutation({ + variables: { values }, + onCompleted: () => navigate('/cd/services'), + }) + + return ( + + {error && ( + + )} + mutation()} + saveLabel="Configure" + /> + + ) +} diff --git a/assets/src/components/cd/repos/HelmHealthChip.tsx b/assets/src/components/cd/repos/HelmHealthChip.tsx new file mode 100644 index 0000000000..948b69ba0f --- /dev/null +++ b/assets/src/components/cd/repos/HelmHealthChip.tsx @@ -0,0 +1,35 @@ +import { Chip, Tooltip } from '@pluralsh/design-system' + +export function HelmHealthChip({ + ready, + message, +}: { + ready: boolean + message?: string | null | undefined +}) { + const chip = ( + + {ready ? 'Ready' : 'Failed'} + + ) + + const errorLines = (message || '').split('\n').map((line, i, arr) => ( + <> + {line} + {i !== arr.length - 1 &&
} + + )) + + if (message) { + return ( + {errorLines}} + > + {chip} + + ) + } + + return chip +} diff --git a/assets/src/components/cd/services/ServiceRevisionColumns.tsx b/assets/src/components/cd/services/ServiceRevisionColumns.tsx index 9f3528ec08..fced41d5c4 100644 --- a/assets/src/components/cd/services/ServiceRevisionColumns.tsx +++ b/assets/src/components/cd/services/ServiceRevisionColumns.tsx @@ -22,21 +22,28 @@ import { useServiceContext } from './service/ServiceDetails' const columnHelper = createColumnHelper>() -const ColGitRef = columnHelper.accessor((row) => row?.git.ref, { +const ColGitRef = columnHelper.accessor((row) => row, { id: 'gitRef', - header: 'Commit ref', + header: 'Revision', meta: { truncate: true }, - cell: ({ row: { original }, getValue }) => ( - - - - ), + cell: ({ getValue }) => { + const rev = getValue() + const ref = rev?.helm?.chart + ? `${rev.helm.chart}@${rev.helm.version}` + : rev?.git?.ref + + return ( + + + + ) + }, }) const ColMessage = columnHelper.accessor((row) => row?.message, { @@ -164,7 +171,7 @@ const ColActions = columnHelper.accessor((row) => row, { following revision:

-

{revision?.git.ref}
+
{revision?.git?.ref}
sha: {revision?.sha} diff --git a/assets/src/components/cd/services/ServiceSettings.tsx b/assets/src/components/cd/services/ServiceSettings.tsx index a67fb5af81..e55f20e023 100644 --- a/assets/src/components/cd/services/ServiceSettings.tsx +++ b/assets/src/components/cd/services/ServiceSettings.tsx @@ -1,10 +1,12 @@ import { Button, Switch } from '@pluralsh/design-system' import { ServiceDeploymentsRowFragment, + ServiceUpdateAttributes, + useHelmRepositoryQuery, useUpdateServiceDeploymentMutation, } from 'generated/graphql' import { useTheme } from 'styled-components' -import { FormEvent, useCallback, useEffect, useRef } from 'react' +import { FormEvent, useCallback, useEffect, useMemo, useRef } from 'react' import { GqlError } from 'components/utils/Alert' import { ModalMountTransition } from 'components/utils/ModalMountTransition' @@ -17,6 +19,7 @@ import { ServiceGitFolderField, ServiceGitRefField, } from './deployModal/DeployServiceSettingsGit' +import { ChartForm } from './deployModal/DeployServiceSettingsHelm' export function ServiceSettings({ serviceDeployment, @@ -41,6 +44,26 @@ export function ServiceSettings({ ) } +function ChartUpdate({ repo, state, updateState }) { + const { data } = useHelmRepositoryQuery({ + variables: { + name: repo?.name || '', + namespace: repo?.namespace || '', + }, + skip: !repo?.name || !repo?.namespace, + }) + + return ( + updateState({ helmChart: chart })} + version={state.helmVersion} + setVersion={(vsn) => updateState({ helmVersion: vsn })} + /> + ) +} + export function ModalForm({ serviceDeployment, open, @@ -59,22 +82,39 @@ export function ModalForm({ update: updateState, hasUpdates, } = useUpdateState({ - gitRef: serviceDeployment.git.ref ?? '', - gitFolder: serviceDeployment.git.folder ?? '', + gitRef: serviceDeployment.git?.ref ?? '', + gitFolder: serviceDeployment.git?.folder ?? '', + helmChart: serviceDeployment.helm?.chart ?? '', + helmVersion: serviceDeployment.helm?.version ?? '', protect: !!serviceDeployment.protect, }) + const attributes = useMemo(() => { + const git = + state.gitRef && state.gitFolder + ? { folder: state.gitFolder, ref: state.gitRef } + : null + const helm = + state.helmChart && state.helmVersion + ? { chart: state.helmChart, version: state.helmVersion } + : null + let attributes: ServiceUpdateAttributes = { protect: state.protect } + + if (git) { + attributes = { git, ...attributes } + } + if (helm) { + attributes = { helm, ...attributes } + } + + return attributes + }, [state]) + const [updateService, { loading, error }] = useUpdateServiceDeploymentMutation({ variables: { id: serviceDeployment.id, - attributes: { - git: { - folder: state.gitFolder, - ref: state.gitRef, - }, - protect: state.protect, - }, + attributes, }, onCompleted: () => { refetch?.() @@ -138,18 +178,29 @@ export function ModalForm({ gap: theme.spacing.medium, }} > - { - updateState({ gitRef: e.currentTarget.value }) - }} - /> - { - updateState({ gitFolder: e.currentTarget.value }) - }} - /> + {!serviceDeployment.helm?.chart && ( + <> + { + updateState({ gitRef: e.currentTarget.value }) + }} + /> + { + updateState({ gitFolder: e.currentTarget.value }) + }} + /> + + )} + {serviceDeployment.helm?.chart && ( + + )} updateState({ protect: checked })} diff --git a/assets/src/components/cd/services/ServicesColumns.tsx b/assets/src/components/cd/services/ServicesColumns.tsx index 5f31468a44..e72335018e 100644 --- a/assets/src/components/cd/services/ServicesColumns.tsx +++ b/assets/src/components/cd/services/ServicesColumns.tsx @@ -82,56 +82,73 @@ export const ColCluster = columnHelper.accessor( } ) -export const ColRepo = columnHelper.accessor( - ({ node }) => node?.repository?.url, - { - id: 'repository', - header: 'Repository', - enableSorting: true, - meta: { truncate: true, gridTemplate: 'minmax(180px,1fr)' }, - cell: ({ getValue }) => ( +export const ColRepo = columnHelper.accessor(({ node }) => node, { + id: 'repository', + header: 'Repository', + enableSorting: true, + meta: { truncate: true, gridTemplate: 'minmax(180px,1fr)' }, + cell: ({ getValue }) => { + const theme = useTheme() + const svc = getValue() + const git = svc?.repository + const helm = svc?.helmRepository + const url = git?.url || helm?.spec?.url || '' + + return (
} + icon={ + git ? ( + + ) : ( + getProviderIconURL('byok', theme.mode === 'dark') + ) + } > - {getValue()} + {url}
- ), - } -) + ) + }, +}) export const ColRef = columnHelper.accessor(({ node }) => node, { id: 'gitLocation', - header: 'Git Location', + header: 'Reference', enableSorting: true, // meta: { truncate: true }, cell: ({ getValue }) => { const svc = getValue() if (!svc) return null - const { - git: { ref, folder }, - message, - } = svc + const { message } = svc - const refStr = shortenSha1(ref) + const refStr = shortenSha1(svc.git?.ref || '') return ( - {message || ''}} - > - - {refStr}@{folder} - - + <> + {svc.helm && ( + + {svc.helm?.chart}@{svc.helm?.version} + + )} + {svc.git && ( + {message || ''}} + > + + {refStr}@{svc.git?.folder} + + + )} + ) }, }) diff --git a/assets/src/components/cd/services/deployModal/DeployService.tsx b/assets/src/components/cd/services/deployModal/DeployService.tsx index 68b4b5471b..ae205f0c1a 100644 --- a/assets/src/components/cd/services/deployModal/DeployService.tsx +++ b/assets/src/components/cd/services/deployModal/DeployService.tsx @@ -4,6 +4,7 @@ import { GitHubIcon, PadlockLockedIcon, Stepper, + Switch, } from '@pluralsh/design-system' import { useClustersTinyQuery, @@ -26,10 +27,12 @@ import { DeployServiceSettingsSecrets, Secret, } from './DeployServiceSettingsSecrets' +import DeployServiceSettingsHelm from './DeployServiceSettingsHelm' enum FormState { Initial = 'initial', Git = 'git', + Helm = 'helm', Secrets = 'secrets', } @@ -78,6 +81,9 @@ export function DeployServiceModal({ const [clusterId, setClusterId] = useState('') const [name, setName] = useState('') const [repositoryId, setRepositoryId] = useState('') + const [repository, setRepository] = useState(null) + const [chart, setChart] = useState('') + const [version, setVersion] = useState('') const [gitFolder, setGitFolder] = useState('') const [gitRef, setGitRef] = useState('') const [namespace, setNamespace] = useState('') @@ -99,6 +105,10 @@ export function DeployServiceModal({ })) }, [secrets]) + const helm = + repository && chart && version ? { repository, chart, version } : null + const git = gitRef && gitFolder ? { ref: gitRef, folder: gitFolder } : null + const [mutation, { loading: mutationLoading, error: mutationError }] = useCreateServiceDeploymentMutation({ variables: { @@ -107,7 +117,8 @@ export function DeployServiceModal({ repositoryId, name, namespace, - git: { ref: gitRef, folder: gitFolder }, + git, + helm, configuration, }, }, @@ -127,9 +138,12 @@ export function DeployServiceModal({ const initialFormValid = name && namespace && clusterId const allowGoToGit = formState === FormState.Initial && initialFormValid - const gitSettingsValid = repositoryId && gitFolder && gitRef + const gitSettingsValid = + (repositoryId && gitFolder && gitRef) || (chart && version && repository) const allowGoToSecrets = - formState === FormState.Git && initialFormValid && gitSettingsValid + (formState === FormState.Git || formState === FormState.Helm) && + initialFormValid && + gitSettingsValid const allowDeploy = formState === FormState.Secrets && @@ -190,7 +204,7 @@ export function DeployServiceModal({ Go back - ) : formState === FormState.Git ? ( + ) : formState === FormState.Git || formState === FormState.Helm ? ( <> + + setFormState(selected ? FormState.Helm : FormState.Git) + } + > + Use Helm Source +