From b76a0720686c762e9bd07eedbd4bdd23821c8737 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:58:54 +0200 Subject: [PATCH 01/19] chore(deps): bump google.golang.org/protobuf from 1.30.0 to 1.33.0 (#958) Bumps google.golang.org/protobuf from 1.30.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a37726088..14f66d3cb 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( golang.org/x/exp v0.0.0-20230420155640-133eef4313cb golang.org/x/net v0.17.0 google.golang.org/grpc v1.56.3 - google.golang.org/protobuf v1.30.0 + google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.27.1 k8s.io/apimachinery v0.27.1 diff --git a/go.sum b/go.sum index 60f418e42..39193a77e 100644 --- a/go.sum +++ b/go.sum @@ -376,8 +376,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From a4fcb002187b2a09b25d41655509dd1089bf0889 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 16:31:46 +0200 Subject: [PATCH 02/19] chore(deps): bump github.com/docker/docker (#956) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 14f66d3cb..2c176cdd7 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( require ( github.com/docker/distribution v2.8.2+incompatible - github.com/docker/docker v23.0.8+incompatible + github.com/docker/docker v24.0.9+incompatible github.com/docker/go-connections v0.4.0 github.com/google/go-containerregistry v0.15.1 golang.org/x/sync v0.1.0 diff --git a/go.sum b/go.sum index 39193a77e..eb24d0c83 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,8 @@ github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuD github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v23.0.8+incompatible h1:z4ZCIwfqHgOEwhxmAWugSL1PFtPQmLP60EVhJYJPaX8= -github.com/docker/docker v23.0.8+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= +github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= From 852d84a6ffa66560dd087cd4482c0f91f4e09ba6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 16:57:17 +0200 Subject: [PATCH 03/19] chore(deps): bump follow-redirects from 1.15.5 to 1.15.6 in /web/crux-ui (#955) --- web/crux-ui/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web/crux-ui/package-lock.json b/web/crux-ui/package-lock.json index 199786d26..ebafae26e 100644 --- a/web/crux-ui/package-lock.json +++ b/web/crux-ui/package-lock.json @@ -4573,9 +4573,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -11906,9 +11906,9 @@ "dev": true }, "follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==" + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" }, "for-each": { "version": "0.3.3", From 374756b51c43e00e8f6e625bbb8eec3f59317cc4 Mon Sep 17 00:00:00 2001 From: Mate Vago Date: Mon, 15 Apr 2024 16:58:04 +0200 Subject: [PATCH 04/19] fix(crux): get instance config (#961) --- web/crux/src/app/deploy/deploy.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/web/crux/src/app/deploy/deploy.service.ts b/web/crux/src/app/deploy/deploy.service.ts index 997c8fedc..754266388 100644 --- a/web/crux/src/app/deploy/deploy.service.ts +++ b/web/crux/src/app/deploy/deploy.service.ts @@ -178,6 +178,7 @@ export default class DeployService { registry: true, }, }, + config: true, }, }) From 664aa153d99bec4f947929180bccd47592a03fce Mon Sep 17 00:00:00 2001 From: Mate Vago Date: Mon, 15 Apr 2024 17:31:51 +0200 Subject: [PATCH 05/19] refactor(crux-ui): move deployment related components to the root deployments folder (#960) --- .../deployments/add-deployment-to-version-card.tsx | 0 .../deployments/copy-deployment-card.tsx | 0 .../deployments/create-deployment-token-card.tsx | 0 .../deployment-container-status-list.tsx | 0 .../deployments/deployment-details-card.tsx | 0 .../deployments/deployment-details-section.tsx | 0 .../deployments/deployment-status-tag.tsx | 0 .../deployments/deployment-token-card.tsx | 0 .../deployments/deployment-view-list.tsx | 0 .../deployments/deployment-view-tile.tsx | 0 .../deployments/edit-deployment-card.tsx | 0 .../deployments/edit-deployment-instances.tsx | 0 .../deployments/instances/edit-instance-card.tsx | 4 ++-- .../deployments/instances/use-instance-state.ts | 0 .../deployments/use-copy-deployment-state.ts | 0 .../deployments/use-deployment-state.tsx | 0 .../src/components/nodes/node-deployment-list.tsx | 4 +--- .../projects/versions/use-version-state.ts | 2 +- .../versions/version-deployments-section.tsx | 2 +- .../projects/versions/version-sections.tsx | 4 ++-- web/crux-ui/src/pages/[teamSlug]/deployments.tsx | 8 +++----- .../[teamSlug]/deployments/[deploymentId].tsx | 14 +++++++------- .../deployments/[deploymentId]/deploy.tsx | 4 ++-- .../[deploymentId]/instances/[instanceId].tsx | 4 ++-- 24 files changed, 21 insertions(+), 25 deletions(-) rename web/crux-ui/src/components/{projects/versions => }/deployments/add-deployment-to-version-card.tsx (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/copy-deployment-card.tsx (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/create-deployment-token-card.tsx (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/deployment-container-status-list.tsx (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/deployment-details-card.tsx (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/deployment-details-section.tsx (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/deployment-status-tag.tsx (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/deployment-token-card.tsx (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/deployment-view-list.tsx (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/deployment-view-tile.tsx (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/edit-deployment-card.tsx (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/edit-deployment-instances.tsx (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/instances/edit-instance-card.tsx (92%) rename web/crux-ui/src/components/{projects/versions => }/deployments/instances/use-instance-state.ts (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/use-copy-deployment-state.ts (100%) rename web/crux-ui/src/components/{projects/versions => }/deployments/use-deployment-state.tsx (100%) diff --git a/web/crux-ui/src/components/projects/versions/deployments/add-deployment-to-version-card.tsx b/web/crux-ui/src/components/deployments/add-deployment-to-version-card.tsx similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/add-deployment-to-version-card.tsx rename to web/crux-ui/src/components/deployments/add-deployment-to-version-card.tsx diff --git a/web/crux-ui/src/components/projects/versions/deployments/copy-deployment-card.tsx b/web/crux-ui/src/components/deployments/copy-deployment-card.tsx similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/copy-deployment-card.tsx rename to web/crux-ui/src/components/deployments/copy-deployment-card.tsx diff --git a/web/crux-ui/src/components/projects/versions/deployments/create-deployment-token-card.tsx b/web/crux-ui/src/components/deployments/create-deployment-token-card.tsx similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/create-deployment-token-card.tsx rename to web/crux-ui/src/components/deployments/create-deployment-token-card.tsx diff --git a/web/crux-ui/src/components/projects/versions/deployments/deployment-container-status-list.tsx b/web/crux-ui/src/components/deployments/deployment-container-status-list.tsx similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/deployment-container-status-list.tsx rename to web/crux-ui/src/components/deployments/deployment-container-status-list.tsx diff --git a/web/crux-ui/src/components/projects/versions/deployments/deployment-details-card.tsx b/web/crux-ui/src/components/deployments/deployment-details-card.tsx similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/deployment-details-card.tsx rename to web/crux-ui/src/components/deployments/deployment-details-card.tsx diff --git a/web/crux-ui/src/components/projects/versions/deployments/deployment-details-section.tsx b/web/crux-ui/src/components/deployments/deployment-details-section.tsx similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/deployment-details-section.tsx rename to web/crux-ui/src/components/deployments/deployment-details-section.tsx diff --git a/web/crux-ui/src/components/projects/versions/deployments/deployment-status-tag.tsx b/web/crux-ui/src/components/deployments/deployment-status-tag.tsx similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/deployment-status-tag.tsx rename to web/crux-ui/src/components/deployments/deployment-status-tag.tsx diff --git a/web/crux-ui/src/components/projects/versions/deployments/deployment-token-card.tsx b/web/crux-ui/src/components/deployments/deployment-token-card.tsx similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/deployment-token-card.tsx rename to web/crux-ui/src/components/deployments/deployment-token-card.tsx diff --git a/web/crux-ui/src/components/projects/versions/deployments/deployment-view-list.tsx b/web/crux-ui/src/components/deployments/deployment-view-list.tsx similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/deployment-view-list.tsx rename to web/crux-ui/src/components/deployments/deployment-view-list.tsx diff --git a/web/crux-ui/src/components/projects/versions/deployments/deployment-view-tile.tsx b/web/crux-ui/src/components/deployments/deployment-view-tile.tsx similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/deployment-view-tile.tsx rename to web/crux-ui/src/components/deployments/deployment-view-tile.tsx diff --git a/web/crux-ui/src/components/projects/versions/deployments/edit-deployment-card.tsx b/web/crux-ui/src/components/deployments/edit-deployment-card.tsx similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/edit-deployment-card.tsx rename to web/crux-ui/src/components/deployments/edit-deployment-card.tsx diff --git a/web/crux-ui/src/components/projects/versions/deployments/edit-deployment-instances.tsx b/web/crux-ui/src/components/deployments/edit-deployment-instances.tsx similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/edit-deployment-instances.tsx rename to web/crux-ui/src/components/deployments/edit-deployment-instances.tsx diff --git a/web/crux-ui/src/components/projects/versions/deployments/instances/edit-instance-card.tsx b/web/crux-ui/src/components/deployments/instances/edit-instance-card.tsx similarity index 92% rename from web/crux-ui/src/components/projects/versions/deployments/instances/edit-instance-card.tsx rename to web/crux-ui/src/components/deployments/instances/edit-instance-card.tsx index f59b9b46d..8d7aade05 100644 --- a/web/crux-ui/src/components/projects/versions/deployments/instances/edit-instance-card.tsx +++ b/web/crux-ui/src/components/deployments/instances/edit-instance-card.tsx @@ -7,8 +7,8 @@ import { InstanceJsonContainerConfig, mergeJsonConfigToInstanceContainerConfig, } from '@app/models' -import EditImageHeading from '../../images/edit-image-heading' -import EditImageJson from '../../images/edit-image-json' +import EditImageHeading from '../../projects/versions/images/edit-image-heading' +import EditImageJson from '../../projects/versions/images/edit-image-json' import { DeploymentActions, DeploymentState } from '../use-deployment-state' import useInstanceState from './use-instance-state' diff --git a/web/crux-ui/src/components/projects/versions/deployments/instances/use-instance-state.ts b/web/crux-ui/src/components/deployments/instances/use-instance-state.ts similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/instances/use-instance-state.ts rename to web/crux-ui/src/components/deployments/instances/use-instance-state.ts diff --git a/web/crux-ui/src/components/projects/versions/deployments/use-copy-deployment-state.ts b/web/crux-ui/src/components/deployments/use-copy-deployment-state.ts similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/use-copy-deployment-state.ts rename to web/crux-ui/src/components/deployments/use-copy-deployment-state.ts diff --git a/web/crux-ui/src/components/projects/versions/deployments/use-deployment-state.tsx b/web/crux-ui/src/components/deployments/use-deployment-state.tsx similarity index 100% rename from web/crux-ui/src/components/projects/versions/deployments/use-deployment-state.tsx rename to web/crux-ui/src/components/deployments/use-deployment-state.tsx diff --git a/web/crux-ui/src/components/nodes/node-deployment-list.tsx b/web/crux-ui/src/components/nodes/node-deployment-list.tsx index a80e259a9..9466b4f97 100644 --- a/web/crux-ui/src/components/nodes/node-deployment-list.tsx +++ b/web/crux-ui/src/components/nodes/node-deployment-list.tsx @@ -1,6 +1,4 @@ -import DeploymentStatusTag, { - deploymentStatusTranslation, -} from '@app/components/projects/versions/deployments/deployment-status-tag' +import DeploymentStatusTag, { deploymentStatusTranslation } from '@app/components/deployments/deployment-status-tag' import Filters from '@app/components/shared/filters' import { DyoCard } from '@app/elements/dyo-card' import { chipsQALabelFromValue } from '@app/elements/dyo-chips' diff --git a/web/crux-ui/src/components/projects/versions/use-version-state.ts b/web/crux-ui/src/components/projects/versions/use-version-state.ts index db130d127..6c6c496b0 100644 --- a/web/crux-ui/src/components/projects/versions/use-version-state.ts +++ b/web/crux-ui/src/components/projects/versions/use-version-state.ts @@ -41,7 +41,7 @@ import { import WebSocketClientEndpoint from '@app/websockets/websocket-client-endpoint' import useTranslation from 'next-translate/useTranslation' import { useEffect, useState } from 'react' -import useCopyDeploymentState from './deployments/use-copy-deployment-state' +import useCopyDeploymentState from '../../deployments/use-copy-deployment-state' // state export type ImageTagsMap = { [key: string]: RegistryImageTags } // image key to RegistryImageTags diff --git a/web/crux-ui/src/components/projects/versions/version-deployments-section.tsx b/web/crux-ui/src/components/projects/versions/version-deployments-section.tsx index f1b56126d..4860c0c7f 100644 --- a/web/crux-ui/src/components/projects/versions/version-deployments-section.tsx +++ b/web/crux-ui/src/components/projects/versions/version-deployments-section.tsx @@ -39,7 +39,7 @@ import { QA_MODAL_LABEL_DEPLOYMENT_NOTE, } from 'quality-assurance' import { useEffect, useState } from 'react' -import DeploymentStatusTag, { deploymentStatusTranslation } from './deployments/deployment-status-tag' +import DeploymentStatusTag, { deploymentStatusTranslation } from '../../deployments/deployment-status-tag' import { VersionActions } from './use-version-state' interface VersionDeploymentsSectionProps { diff --git a/web/crux-ui/src/components/projects/versions/version-sections.tsx b/web/crux-ui/src/components/projects/versions/version-sections.tsx index f4ef1dcc5..bb9d2785e 100644 --- a/web/crux-ui/src/components/projects/versions/version-sections.tsx +++ b/web/crux-ui/src/components/projects/versions/version-sections.tsx @@ -4,8 +4,8 @@ import { ProjectDetails, VERSION_SECTIONS_STATE_VALUES } from '@app/models' import { parseStringUnionType } from '@app/utils' import { useRouter } from 'next/dist/client/router' import React, { useEffect, useRef } from 'react' -import AddDeploymentToVersionCard from './deployments/add-deployment-to-version-card' -import CopyDeploymentCard from './deployments/copy-deployment-card' +import AddDeploymentToVersionCard from '../../deployments/add-deployment-to-version-card' +import CopyDeploymentCard from '../../deployments/copy-deployment-card' import AddImagesCard from './images/add-images-card' import { VerionState, VersionActions, VersionSection } from './use-version-state' import VersionDeploymentsSection from './version-deployments-section' diff --git a/web/crux-ui/src/pages/[teamSlug]/deployments.tsx b/web/crux-ui/src/pages/[teamSlug]/deployments.tsx index 5ca879d09..30bfb04b7 100644 --- a/web/crux-ui/src/pages/[teamSlug]/deployments.tsx +++ b/web/crux-ui/src/pages/[teamSlug]/deployments.tsx @@ -1,10 +1,8 @@ import AddDeploymentCard from '@app/components/deployments/add-deployment-card' +import CopyDeploymentCard from '@app/components/deployments/copy-deployment-card' +import DeploymentStatusTag, { deploymentStatusTranslation } from '@app/components/deployments/deployment-status-tag' +import useCopyDeploymentState from '@app/components/deployments/use-copy-deployment-state' import { Layout } from '@app/components/layout' -import CopyDeploymentCard from '@app/components/projects/versions/deployments/copy-deployment-card' -import DeploymentStatusTag, { - deploymentStatusTranslation, -} from '@app/components/projects/versions/deployments/deployment-status-tag' -import useCopyDeploymentState from '@app/components/projects/versions/deployments/use-copy-deployment-state' import { BreadcrumbLink } from '@app/components/shared/breadcrumb' import Filters from '@app/components/shared/filters' import PageHeading from '@app/components/shared/page-heading' diff --git a/web/crux-ui/src/pages/[teamSlug]/deployments/[deploymentId].tsx b/web/crux-ui/src/pages/[teamSlug]/deployments/[deploymentId].tsx index 28ca78eac..915320789 100644 --- a/web/crux-ui/src/pages/[teamSlug]/deployments/[deploymentId].tsx +++ b/web/crux-ui/src/pages/[teamSlug]/deployments/[deploymentId].tsx @@ -1,13 +1,13 @@ import EditorBadge from '@app/components/editor/editor-badge' import { Layout } from '@app/components/layout' import NodeConnectionCard from '@app/components/nodes/node-connection-card' -import CopyDeploymentCard from '@app/components/projects/versions/deployments/copy-deployment-card' -import CreateDeploymentTokenCard from '@app/components/projects/versions/deployments/create-deployment-token-card' -import DeploymentDetailsSection from '@app/components/projects/versions/deployments/deployment-details-section' -import DeploymentTokenCard from '@app/components/projects/versions/deployments/deployment-token-card' -import EditDeploymentCard from '@app/components/projects/versions/deployments/edit-deployment-card' -import EditDeploymentInstances from '@app/components/projects/versions/deployments/edit-deployment-instances' -import useDeploymentState from '@app/components/projects/versions/deployments/use-deployment-state' +import CopyDeploymentCard from '@app/components/deployments/copy-deployment-card' +import CreateDeploymentTokenCard from '@app/components/deployments/create-deployment-token-card' +import DeploymentDetailsSection from '@app/components/deployments/deployment-details-section' +import DeploymentTokenCard from '@app/components/deployments/deployment-token-card' +import EditDeploymentCard from '@app/components/deployments/edit-deployment-card' +import EditDeploymentInstances from '@app/components/deployments/edit-deployment-instances' +import useDeploymentState from '@app/components/deployments/use-deployment-state' import { BreadcrumbLink } from '@app/components/shared/breadcrumb' import PageHeading from '@app/components/shared/page-heading' import { DetailsPageMenu } from '@app/components/shared/page-menu' diff --git a/web/crux-ui/src/pages/[teamSlug]/deployments/[deploymentId]/deploy.tsx b/web/crux-ui/src/pages/[teamSlug]/deployments/[deploymentId]/deploy.tsx index 13841bcaf..0b2bc7c0d 100644 --- a/web/crux-ui/src/pages/[teamSlug]/deployments/[deploymentId]/deploy.tsx +++ b/web/crux-ui/src/pages/[teamSlug]/deployments/[deploymentId]/deploy.tsx @@ -1,8 +1,8 @@ import { Layout } from '@app/components/layout' import DeploymentContainerStatusList, { ContainerProgress, -} from '@app/components/projects/versions/deployments/deployment-container-status-list' -import DeploymentDetailsCard from '@app/components/projects/versions/deployments/deployment-details-card' +} from '@app/components/deployments/deployment-container-status-list' +import DeploymentDetailsCard from '@app/components/deployments/deployment-details-card' import { BreadcrumbLink } from '@app/components/shared/breadcrumb' import EventsTerminal from '@app/components/shared/events-terminal' import PageHeading from '@app/components/shared/page-heading' diff --git a/web/crux-ui/src/pages/[teamSlug]/deployments/[deploymentId]/instances/[instanceId].tsx b/web/crux-ui/src/pages/[teamSlug]/deployments/[deploymentId]/instances/[instanceId].tsx index e9d310444..a668f4ddf 100644 --- a/web/crux-ui/src/pages/[teamSlug]/deployments/[deploymentId]/instances/[instanceId].tsx +++ b/web/crux-ui/src/pages/[teamSlug]/deployments/[deploymentId]/instances/[instanceId].tsx @@ -2,8 +2,8 @@ import EditorBadge from '@app/components/editor/editor-badge' import useEditorState from '@app/components/editor/use-editor-state' import useItemEditorState from '@app/components/editor/use-item-editor-state' import { Layout } from '@app/components/layout' -import useInstanceState from '@app/components/projects/versions/deployments/instances/use-instance-state' -import useDeploymentState from '@app/components/projects/versions/deployments/use-deployment-state' +import useInstanceState from '@app/components/deployments/instances/use-instance-state' +import useDeploymentState from '@app/components/deployments/use-deployment-state' import CommonConfigSection from '@app/components/projects/versions/images/config/common-config-section' import configToFilters from '@app/components/projects/versions/images/config/config-to-filters' import CraneConfigSection from '@app/components/projects/versions/images/config/crane-config-section' From 22614cd4a46ec72d4cceb697b688cb5abcf0df2d Mon Sep 17 00:00:00 2001 From: Mate Vago Date: Tue, 16 Apr 2024 14:24:59 +0200 Subject: [PATCH 06/19] fix(agent): deprecated types.AuthConfig (#962) --- golang/internal/helper/image/image.go | 3 ++- golang/pkg/builder/container/utils.go | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/golang/internal/helper/image/image.go b/golang/internal/helper/image/image.go index 602a95121..aa0ea4af2 100644 --- a/golang/internal/helper/image/image.go +++ b/golang/internal/helper/image/image.go @@ -19,6 +19,7 @@ import ( "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/registry" "github.com/docker/docker/client" "github.com/docker/docker/errdefs" "github.com/google/go-containerregistry/pkg/authn" @@ -419,7 +420,7 @@ func authConfigToBasicAuth(authConfigEncoded string) (string, error) { return "", err } - var authOpts types.AuthConfig + var authOpts registry.AuthConfig err = json.Unmarshal(authConfigJSON, &authOpts) if err != nil { return "", err diff --git a/golang/pkg/builder/container/utils.go b/golang/pkg/builder/container/utils.go index 333f29bf7..a53e3ad3c 100644 --- a/golang/pkg/builder/container/utils.go +++ b/golang/pkg/builder/container/utils.go @@ -7,7 +7,7 @@ import ( "fmt" "io" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/registry" "github.com/dyrector-io/dyrectorio/golang/internal/dogger" @@ -19,7 +19,7 @@ func registryAuthBase64(user, password string) string { return "" } - authConfig := types.AuthConfig{ + authConfig := registry.AuthConfig{ Username: user, Password: password, } @@ -70,7 +70,6 @@ func ReadDockerLogsFromReadCloser(logs io.ReadCloser, skip, take int) []string { count := binary.BigEndian.Uint32(header[4:]) data := make([]byte, count) _, err = logs.Read(data) - if err != nil { if err != io.EOF { panic(err) From 995230c8ec079028ab408ec97bd88ea60fb04085 Mon Sep 17 00:00:00 2001 From: Nandor Magyar Date: Fri, 19 Apr 2024 17:17:19 +0200 Subject: [PATCH 07/19] fix: crane uses port/targetPort in serviceMonitors for metrics (#964) --- golang/api/v1/deploy.go | 4 ++- golang/internal/mapper/grpc.go | 2 +- golang/pkg/crane/k8s/service_monitor.go | 29 ++++++++++---------- golang/pkg/crane/k8s/service_monitor_test.go | 2 +- protobuf/go/agent/agent.pb.go | 8 +++--- protobuf/proto/agent.proto | 2 +- web/crux/proto/agent.proto | 2 +- web/crux/src/app/deploy/deploy.mapper.ts | 2 +- web/crux/src/grpc/protobuf/proto/agent.ts | 8 +++--- 9 files changed, 31 insertions(+), 28 deletions(-) diff --git a/golang/api/v1/deploy.go b/golang/api/v1/deploy.go index c3607bd84..2b444ff5b 100644 --- a/golang/api/v1/deploy.go +++ b/golang/api/v1/deploy.go @@ -256,8 +256,10 @@ type ContainerConfig struct { } type Metrics struct { + // Path the path to be scraped, if not defined /metrics is used Path string `json:"path"` - Port string `json:"port"` + // Port exposed port of the service where metrics are available + Port int `json:"port"` } func (c *ContainerConfig) Strings(appConfig *config.CommonConfiguration) []string { diff --git a/golang/internal/mapper/grpc.go b/golang/internal/mapper/grpc.go index c7a77d427..55898e5c6 100644 --- a/golang/internal/mapper/grpc.go +++ b/golang/internal/mapper/grpc.go @@ -224,7 +224,7 @@ func mapCraneConfig(crane *agent.CraneContainerConfig, containerConfig *v1.Conta if crane.Metrics != nil { containerConfig.Metrics = &v1.Metrics{ Path: crane.Metrics.Path, - Port: crane.Metrics.Path, + Port: int(crane.Metrics.Port), } } } diff --git a/golang/pkg/crane/k8s/service_monitor.go b/golang/pkg/crane/k8s/service_monitor.go index c8f119f55..f7fd2bb40 100644 --- a/golang/pkg/crane/k8s/service_monitor.go +++ b/golang/pkg/crane/k8s/service_monitor.go @@ -11,6 +11,7 @@ import ( smv1 "github.com/prometheus-operator/prometheus-operator/pkg/client/applyconfiguration/monitoring/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" ) type ServiceMonitor struct { @@ -58,28 +59,28 @@ func (sm *ServiceMonitor) Deploy(namespace, serviceName string, metricParams v1. return err } - portName := metricParams.Port - - if portName == "" { - portName = firstPort - } - metricsPath := metricParams.Path if metricsPath == "" { metricsPath = "/metrics" } + endpoint := smv1.Endpoint().WithPath(metricsPath) + if metricParams.Port != 0 { + endpoint = endpoint.WithTargetPort(intstr.FromInt(metricParams.Port)) + } else { + endpoint = endpoint.WithPort(firstPort) + } + smApplyConfig := smv1.ServiceMonitor(serviceName, namespace). WithSpec(smv1.ServiceMonitorSpec(). - WithEndpoints( - smv1.Endpoint().WithPort(portName).WithPath(metricsPath), - ).WithSelector( - metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app": serviceName, + WithEndpoints(endpoint). + WithSelector( + metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": serviceName, + }, }, - }, - )) + )) _, err = sm.ClientSet.MonitoringV1().ServiceMonitors(namespace).Apply(sm.Ctx, smApplyConfig, metav1.ApplyOptions{ FieldManager: sm.appConfig.FieldManagerName, diff --git a/golang/pkg/crane/k8s/service_monitor_test.go b/golang/pkg/crane/k8s/service_monitor_test.go index a483f559b..adb7cfb35 100644 --- a/golang/pkg/crane/k8s/service_monitor_test.go +++ b/golang/pkg/crane/k8s/service_monitor_test.go @@ -31,7 +31,7 @@ func TestMain(m *testing.M) { func TestServiceMonitorSpawning(t *testing.T) { m, err := k8s.NewServiceMonitor(context.Background(), k8s.NewClient(getTestConfig())) assert.Nil(t, err, "client is spawned without errors") - err = m.Deploy("crane-sm-test", "test-sm", v1.Metrics{Path: "/metrics", Port: "tcp-metrics"}, "") + err = m.Deploy("crane-sm-test", "test-sm", v1.Metrics{Path: "/metrics", Port: 8080}, "") assert.Nil(t, err, "no errors expected with default parameters") } diff --git a/protobuf/go/agent/agent.pb.go b/protobuf/go/agent/agent.pb.go index 3075cf5e3..2212e21d7 100644 --- a/protobuf/go/agent/agent.pb.go +++ b/protobuf/go/agent/agent.pb.go @@ -1433,7 +1433,7 @@ type Metrics struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Port string `protobuf:"bytes,1,opt,name=port,proto3" json:"port,omitempty"` + Port int32 `protobuf:"varint,1,opt,name=port,proto3" json:"port,omitempty"` Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` } @@ -1469,11 +1469,11 @@ func (*Metrics) Descriptor() ([]byte, []int) { return file_protobuf_proto_agent_proto_rawDescGZIP(), []int{18} } -func (x *Metrics) GetPort() string { +func (x *Metrics) GetPort() int32 { if x != nil { return x.Port } - return "" + return 0 } func (x *Metrics) GetPath() string { @@ -2763,7 +2763,7 @@ var file_protobuf_proto_agent_proto_rawDesc = []byte{ 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x31, 0x0a, 0x07, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x96, 0x01, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x05, diff --git a/protobuf/proto/agent.proto b/protobuf/proto/agent.proto index c80b78380..90ae346d5 100644 --- a/protobuf/proto/agent.proto +++ b/protobuf/proto/agent.proto @@ -187,7 +187,7 @@ message Marker { } message Metrics { - string port = 1; + int32 port = 1; string path = 2; } diff --git a/web/crux/proto/agent.proto b/web/crux/proto/agent.proto index c80b78380..90ae346d5 100644 --- a/web/crux/proto/agent.proto +++ b/web/crux/proto/agent.proto @@ -187,7 +187,7 @@ message Marker { } message Metrics { - string port = 1; + int32 port = 1; string path = 2; } diff --git a/web/crux/src/app/deploy/deploy.mapper.ts b/web/crux/src/app/deploy/deploy.mapper.ts index 3e9c4f6c9..77dbf0f8f 100644 --- a/web/crux/src/app/deploy/deploy.mapper.ts +++ b/web/crux/src/app/deploy/deploy.mapper.ts @@ -387,7 +387,7 @@ export default class DeployMapper { metrics: config.metrics?.enabled ? { path: config.metrics.path ?? null, - port: config.metrics.port?.toString() ?? null, + port: config.metrics.port ?? null, } : null, } diff --git a/web/crux/src/grpc/protobuf/proto/agent.ts b/web/crux/src/grpc/protobuf/proto/agent.ts index 248c0cf9f..d41320e39 100644 --- a/web/crux/src/grpc/protobuf/proto/agent.ts +++ b/web/crux/src/grpc/protobuf/proto/agent.ts @@ -267,7 +267,7 @@ export interface Marker_IngressEntry { } export interface Metrics { - port: string + port: number path: string } @@ -1049,17 +1049,17 @@ export const Marker_IngressEntry = { } function createBaseMetrics(): Metrics { - return { port: '', path: '' } + return { port: 0, path: '' } } export const Metrics = { fromJSON(object: any): Metrics { - return { port: isSet(object.port) ? String(object.port) : '', path: isSet(object.path) ? String(object.path) : '' } + return { port: isSet(object.port) ? Number(object.port) : 0, path: isSet(object.path) ? String(object.path) : '' } }, toJSON(message: Metrics): unknown { const obj: any = {} - message.port !== undefined && (obj.port = message.port) + message.port !== undefined && (obj.port = Math.round(message.port)) message.path !== undefined && (obj.path = message.path) return obj }, From afd6526d3c8f93a3aafea4e8047758268b66b959 Mon Sep 17 00:00:00 2001 From: Nandor Magyar Date: Fri, 19 Apr 2024 17:40:27 +0200 Subject: [PATCH 08/19] chore(agent): use new docker types, go upgrade (#963) --- .github/workflows/builder_image_golang.yaml | 2 +- .github/workflows/product_builder.yaml | 14 +- .golangci.yml | 12 +- go.mod | 47 ++- go.sum | 113 +++-- golang/Makefile | 10 +- golang/api/v1/deploy.go | 214 +++------- golang/internal/config/config.go | 40 +- golang/internal/config/jwt_test.go | 2 +- golang/internal/dogger/dogger.go | 9 +- golang/internal/grpc/grpc.go | 21 +- golang/internal/helper/docker/container.go | 11 +- .../internal/helper/docker/container_test.go | 6 +- golang/internal/helper/image/image.go | 28 +- golang/internal/mapper/grpc.go | 18 +- golang/internal/mapper/grpc_test.go | 29 +- .../runtime/container/container_test.go | 31 +- golang/internal/util/array.go | 2 +- golang/internal/util/dotnet.go | 2 +- .../builder/container/container_builder.go | 72 ++-- .../container_builder_integration_test.go | 43 +- .../pkg/builder/container/docker_container.go | 2 +- golang/pkg/builder/container/exec_builder.go | 6 +- golang/pkg/builder/container/logger_test.go | 3 +- golang/pkg/builder/container/types.go | 69 +--- golang/pkg/builder/container/types_test.go | 56 --- golang/pkg/cli/config_file.go | 76 ++-- golang/pkg/cli/container_defaults.go | 25 +- golang/pkg/crane/config/config.go | 19 +- golang/pkg/crane/k8s/configmap.go | 3 +- golang/pkg/crane/k8s/delete_facade.go | 2 +- golang/pkg/crane/k8s/deploy_facade.go | 12 +- golang/pkg/crane/k8s/deployment.go | 18 +- golang/pkg/crane/k8s/ingress.go | 24 +- golang/pkg/crane/k8s/log.go | 5 +- golang/pkg/crane/k8s/namespace.go | 3 +- golang/pkg/crane/k8s/pvc.go | 2 +- golang/pkg/crane/k8s/secret.go | 2 +- golang/pkg/crane/k8s/service.go | 8 +- golang/pkg/crane/k8s/volume_helper.go | 2 +- golang/pkg/dagent/config/config.go | 23 +- golang/pkg/dagent/config/secret_test.go | 1 + golang/pkg/dagent/update/update.go | 26 +- golang/pkg/dagent/update/update_test.go | 17 +- golang/pkg/dagent/utils/container.go | 13 +- golang/pkg/dagent/utils/docker.go | 32 +- golang/pkg/dagent/utils/dockerhelper.go | 4 +- golang/pkg/dagent/utils/dockerhelper_test.go | 387 +++++++++--------- golang/pkg/dagent/utils/import_container.go | 4 +- golang/pkg/dagent/utils/init_container.go | 2 +- images/builder-golang/Dockerfile | 2 +- 51 files changed, 729 insertions(+), 845 deletions(-) delete mode 100644 golang/pkg/builder/container/types_test.go diff --git a/.github/workflows/builder_image_golang.yaml b/.github/workflows/builder_image_golang.yaml index 6471e5c6a..ab2973838 100644 --- a/.github/workflows/builder_image_golang.yaml +++ b/.github/workflows/builder_image_golang.yaml @@ -12,7 +12,7 @@ permissions: env: GITHUB_REGISTRY: ghcr.io BUILDER_IMAGE_NAME: dyrector-io/dyrectorio/builder-images/golang - VERSION: 2 + VERSION: 3 jobs: build: runs-on: ubuntu-22.04 diff --git a/.github/workflows/product_builder.yaml b/.github/workflows/product_builder.yaml index 984193757..59288134f 100644 --- a/.github/workflows/product_builder.yaml +++ b/.github/workflows/product_builder.yaml @@ -52,7 +52,7 @@ jobs: runs-on: ubuntu-22.04 container: # yamlfmt resides here because alpine doesn't provide yamlfmt package - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:3 steps: - name: Checkout uses: actions/checkout@v3 @@ -115,7 +115,7 @@ jobs: runs-on: ubuntu-22.04 needs: gather_changes container: - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:3 defaults: run: working-directory: ${{ env.GOLANG_WORKING_DIRECTORY }} @@ -139,7 +139,7 @@ jobs: runs-on: ubuntu-22.04 needs: gather_changes container: - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:3 defaults: run: working-directory: ${{ env.GOLANG_WORKING_DIRECTORY }} @@ -163,7 +163,7 @@ jobs: runs-on: ubuntu-22.04 needs: gather_changes container: - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:3 defaults: run: working-directory: ${{ env.GOLANG_WORKING_DIRECTORY }} @@ -197,7 +197,7 @@ jobs: runs-on: ubuntu-22.04 needs: gather_changes container: - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:3 defaults: run: working-directory: ${{ env.GOLANG_WORKING_DIRECTORY }} @@ -258,7 +258,7 @@ jobs: - go_integration - gather_changes container: - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:3 defaults: run: working-directory: ${{ env.GOLANG_WORKING_DIRECTORY }} @@ -661,7 +661,7 @@ jobs: packages: write runs-on: ubuntu-22.04 container: - image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:2 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/golang:3 defaults: run: working-directory: ${{ env.GOLANG_WORKING_DIRECTORY }} diff --git a/.golangci.yml b/.golangci.yml index 86b58953e..55100744a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,7 +1,5 @@ run: timeout: 5m - skip-dirs: - - internal/cache linters-settings: depguard: rules: @@ -19,8 +17,6 @@ linters-settings: funlen: lines: 100 statements: 50 - gci: - local-prefixes: github.com/golangci/golangci-lint goconst: min-len: 2 min-occurrences: 2 @@ -50,22 +46,18 @@ linters-settings: - condition - return govet: - check-shadowing: true + enable-all: true lll: line-length: 140 - maligned: - suggest-new: true misspell: locale: US nolintlint: - allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space) allow-unused: false # report any unused nolint directives require-explanation: false # don't require an explanation for nolint directives require-specific: false # don't require nolint directives to be specific about which linter is being skipped tenv: all: true staticcheck: - go: "1.18" checks: - all linters: @@ -123,3 +115,5 @@ issues: - path: _test\.go linters: - gomnd + exclude-dirs: + - internal/cache diff --git a/go.mod b/go.mod index 2c176cdd7..51485011a 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/dyrector-io/dyrectorio -go 1.20 +go 1.21 require ( github.com/ProtonMail/gopenpgp/v2 v2.7.1 @@ -10,12 +10,12 @@ require ( github.com/ilyakaznacheev/cleanenv v1.4.2 github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.29.1 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.9.0 github.com/thanhpk/randstr v1.0.5 github.com/urfave/cli/v2 v2.25.1 golang.org/x/exp v0.0.0-20230420155640-133eef4313cb - golang.org/x/net v0.17.0 - google.golang.org/grpc v1.56.3 + golang.org/x/net v0.23.0 + google.golang.org/grpc v1.63.0 google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.27.1 @@ -25,23 +25,35 @@ require ( require ( github.com/docker/distribution v2.8.2+incompatible - github.com/docker/docker v24.0.9+incompatible + github.com/docker/docker v26.0.2+incompatible github.com/docker/go-connections v0.4.0 github.com/google/go-containerregistry v0.15.1 - golang.org/x/sync v0.1.0 + golang.org/x/sync v0.6.0 ) require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect - github.com/docker/cli v23.0.5+incompatible // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/cli v26.0.2+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/klauspost/compress v1.16.5 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/vbatts/tar-split v0.11.3 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 // indirect + go.opentelemetry.io/otel v1.25.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 // indirect + go.opentelemetry.io/otel/metric v1.25.0 // indirect + go.opentelemetry.io/otel/sdk v1.25.0 // indirect + go.opentelemetry.io/otel/trace v1.25.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect ) require ( @@ -63,14 +75,14 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.10.2 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic v0.6.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/iancoleman/strcase v0.3.0 @@ -94,16 +106,15 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/crypto v0.17.0 // indirect + golang.org/x/crypto v0.21.0 // indirect golang.org/x/mod v0.10.0 // indirect - golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/oauth2 v0.17.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.8.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/appengine v1.6.8 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gotest.tools/v3 v3.4.0 // indirect diff --git a/go.sum b/go.sum index eb24d0c83..f83e11407 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,8 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2 github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY= github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -34,6 +36,8 @@ github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBS github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -41,15 +45,18 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE= -github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/cli v26.0.2+incompatible h1:4C4U8ZqrlNDe/R1U1zFFX+YsCFiVUicJqo4WVdInJas= +github.com/docker/cli v26.0.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= -github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v26.0.2+incompatible h1:yGVmKUFGgcxA6PXWAokO0sQL22BrQ67cgVjko8tGdXE= +github.com/docker/docker v26.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -66,11 +73,17 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= @@ -78,6 +91,7 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= @@ -85,6 +99,7 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -93,7 +108,6 @@ github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= @@ -106,8 +120,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -116,18 +130,22 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.15.1 h1:RsJ9NbfxYWF8Wl4VmvkpN3zYATwuvlPq2j20zmcs63E= github.com/google/go-containerregistry v0.15.1/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= @@ -150,6 +168,7 @@ github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -167,6 +186,8 @@ github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp9 github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -179,7 +200,9 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= +github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= +github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= @@ -195,13 +218,15 @@ github.com/prometheus-operator/prometheus-operator/pkg/client v0.64.0/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -215,8 +240,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/thanhpk/randstr v1.0.5 h1:AdFhPTLzdJsoAfaRk7tG/zBhXjpy2VRBWdFM5r3CsZ8= github.com/thanhpk/randstr v1.0.5/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= @@ -232,14 +258,30 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsr github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 h1:cEPbyTSEHlQR89XVlyo78gqluF8Y3oMeBkXGWzQsfXY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8= +go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= +go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 h1:dT33yIHtmsqpixFsSQPwNeY5drM9wTcoL8h0FWF4oGM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 h1:Mbi5PKN7u322woPa85d7ebZ+SOvEoPvoiBu+ryHWgfA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0/go.mod h1:e7ciERRhZaOZXVjx5MiL8TK5+Xv7G5Gv5PA2ZDEJdL8= +go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= +go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= +go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= +go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= +go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= +go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= +go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230420155640-133eef4313cb h1:rhjz/8Mbfa8xROFiH+MQphmAmgqRM0bOMnytznhWEXk= golang.org/x/exp v0.0.0-20230420155640-133eef4313cb/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= @@ -258,7 +300,6 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -269,12 +310,12 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -282,8 +323,9 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -307,20 +349,20 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= @@ -346,15 +388,18 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -362,8 +407,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8= +google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/golang/Makefile b/golang/Makefile index ac219b41c..589bc1ad0 100644 --- a/golang/Makefile +++ b/golang/Makefile @@ -16,11 +16,11 @@ LDFLAGS := -ldflags "-X '${PACKAGE}/internal/version.BuildTimestamp=${BUILD_TIME # default tag is latest for building image_version ?= latest -GOAIR=v1.45.0 -GOSEC=v2.17.0 -GOLANGCI=v1.54.2 -GOFUMPT=v0.5.0 -YAMLFMT=v0.9.0 +GOAIR=v1.51.0 +GOSEC=v2.19.0 +GOLANGCI=v1.57.2 +GOFUMPT=v0.6.0 +YAMLFMT=v0.11.0 # support for: linux darwin windows GOOS?=linux diff --git a/golang/api/v1/deploy.go b/golang/api/v1/deploy.go index 2b444ff5b..68c8bd61b 100644 --- a/golang/api/v1/deploy.go +++ b/golang/api/v1/deploy.go @@ -21,15 +21,15 @@ import ( ) type DeployImageRequest struct { - RequestID string `json:"RequestId" binding:"required"` RegistryAuth *imageHelper.RegistryAuth `json:"RegistryAuth,omitempty"` - InstanceConfig InstanceConfig `json:"InstanceConfig" binding:"required"` - ContainerConfig ContainerConfig `json:"ContainerConfig" binding:"required"` - RuntimeConfig Base64JSONBytes `json:"RuntimeConfig,omitempty"` Registry *string `json:"Registry,omitempty"` + RequestID string `json:"RequestId" binding:"required"` ImageName string `json:"ImageName" binding:"required"` Tag string `json:"Tag" binding:"required"` Issuer string `json:"Issuer"` + InstanceConfig InstanceConfig `json:"InstanceConfig" binding:"required"` + RuntimeConfig Base64JSONBytes `json:"RuntimeConfig,omitempty"` + ContainerConfig ContainerConfig `json:"ContainerConfig" binding:"required"` } type VersionData struct { Version string `json:"version" binding:"required"` @@ -55,12 +55,12 @@ func (d *DeployImageRequest) Strings(appConfig *config.CommonConfiguration) []st } type DeployImageResponse struct { - Started bool `json:"started"` - Error string `json:"error"` RequestID *string `json:"requestId"` ImageName *string `json:"imageName"` + Error string `json:"error"` Tag string `json:"tag"` Logs []string `json:"logs"` + Started bool `json:"started"` } type DeployVersionResponse []DeployImageResponse @@ -68,22 +68,14 @@ type DeployVersionResponse []DeployImageResponse type Base64JSONBytes []byte type InstanceConfig struct { - // prefix of the container, identifies namespace - ContainerPreName string `json:"containerPreName" binding:"required"` - // not-in-use - MountPath string `json:"mountPath"` - // name of the instance eg. configmaps - Name string `json:"name"` - // variables for instance; configmaps: name-common, name must be defined - Environment map[string]string `json:"environment,omitempty"` - // not-in-use/unimplemented; registry is taken from containerConfig - Registry string `json:"registry"` - // not-in-use/unimplemented; git repository prefix - RepositoryPreName string `json:"repositoryPreName"` - // namespace global envs + Environment map[string]string `json:"environment,omitempty"` SharedEnvironment map[string]string `json:"sharedEnvironment,omitempty"` - // use preexisting namespaced envs - UseSharedEnvs bool `json:"useSharedEnvs" validate:"excluded_with=SharedEnvironment"` + ContainerPreName string `json:"containerPreName" binding:"required"` + MountPath string `json:"mountPath"` + Name string `json:"name"` + Registry string `json:"registry"` + RepositoryPreName string `json:"repositoryPreName"` + UseSharedEnvs bool `json:"useSharedEnvs" validate:"excluded_with=SharedEnvironment"` } func (i *InstanceConfig) Strings() []string { @@ -105,10 +97,10 @@ const ( ) type HealthCheckConfig struct { - Port uint16 `json:"Port"` LivenessProbe *Probe `json:"livenessProbe"` ReadinessProbe *Probe `json:"readinessProbe"` StartupProbe *Probe `json:"startupProbe"` + Port uint16 `json:"Port"` } type Resources struct { @@ -140,119 +132,54 @@ const ( ) type ExpectedState struct { - State ContainerState `json:"state"` Timeout *int32 `json:"timeout"` ExitCode *int32 `json:"exitCode"` + State ContainerState `json:"state"` } type ContainerConfig struct { - // ContainerPreName identifies namespace to be used - ContainerPreName string `json:"containerPreName"` - // name of the container used for service, configmap names, various component names - Container string `json:"container" binding:"required"` - // portbinding list contains external/interal ports - Ports []builder.PortBinding `json:"port" binding:"dive"` - // Port ranges to be exposed ! no native range support in k8s - PortRanges []builder.PortRangeBinding `json:"portRanges" binding:"dive"` - // mount list, if a name starts with @ it can be used by multiple components eg @data|/target/mount/path - Mounts []string `json:"mount"` - // volumes - Volumes []Volume `json:"volumes,omitempty" binding:"dive"` - // environment variables list - Environment map[string]string `json:"environment"` - // Secrets - Secrets map[string]string `json:"secrets,omitempty"` - // the type of the runtime text provided eg. dotnet-appsettings - RuntimeConfigType RuntimeConfigType `json:"runtimeConfigType"` - // create an ingress object or not - Expose bool `json:"expose"` - // use nginx tls configuration - ExposeTLS bool `json:"exposeTls"` - - /* - // proposal: all components need to match this - - // Domain name, if defined `.` otherwise `..` - // If RootDomain is empty it's omitted - RoutingDomain string `json:"routingDomain"` - // Set endpoint upload limit, default value is: 1m - // for docker hosts, this is needs to be bytes: 1000000 ~1m - RoutingUploadLimit string `json:"routingUploadLimit"` - */ - - // ingress prefix before hostname, `containerName.containerPrefix.` by default, this replaces both before root - IngressName string `json:"ingressName"` - // ingress hostname, env value used by default, can be overridden here - IngressHost string `json:"ingressHost"` - // ingress path for path based routing - IngressPath string `json:"ingressPath"` - // ingress path for path based routing - IngressStripPath bool `json:"ingressPathStrip"` - // ingress port to target - IngressPort uint16 `json:"ingressPort"` - // for docker hosts, this is needs to be bytes: 1000000 ~1m - IngressUploadLimit string `json:"ingressUploadLimit"` - // if put together with another instances consume their shared configs eg. -common config map, generated from here - Shared bool `json:"shared"` - // config container is spawned as an initcontainer copying files to a shared volume - ConfigContainer *ConfigContainer `json:"configContainer,omitempty"` - // import container uses rclone to copy over files before container startup - ImportContainer *ImportContainer `json:"importContainer,omitempty"` - // standard initContainers - InitContainers []InitContainer `json:"initContainers,omitempty" binding:"dive"` - // container UID - User *int64 `json:"user"` - // the initial command of a container have mixed terms - // docker --> k8s: entrypoint => command, cmd => args - // we use the k8s term here - // command is the active process of the container - Command []string `json:"command"` - // args are added to the command - Args []string `json:"args"` - // if we need to spawn a pseudo-terminal - TTY bool `json:"tty"` - // working directory of the container or pod - WorkingDirectory string `json:"workingDirectory"` - - // dagent only - // docker log config https://docs.docker.com/config/containers/logging/configure/ - LogConfig *container.LogConfig `json:"logConfig"` - RestartPolicy builder.RestartPolicyName `json:"restartPolicy"` - // bridge(container, default) host, none or network name - NetworkMode string `json:"networkMode"` - // extra networks - Networks []string `json:"networks"` - // docker only labels - DockerLabels map[string]string `json:"dockerLabels"` - - // k8s-only-section - // Deployments strategy, on deployment how to restart underlying pods - // Values: Recreate (all-at-once), RollingUpdate(one-by-one only if succeeds) - DeploymentStrategy string `json:"deploymentStrategy"` - // health check configuration - HealthCheckConfig HealthCheckConfig `json:"healthCheck"` - // custom header configuration - CustomHeaders []string `json:"customHeaders,omitempty"` - // resource management - ResourceConfig ResourceConfig `json:"resourceConfig"` - // add proxy and cors headers - ProxyHeaders bool `json:"proxyHeaders"` - // Expose service using external IP - // also sets the externalTrafficPolcy to "local" - UseLoadBalancer bool `json:"useLoadBalancer"` - // ExtraLBAnnotations, this is legacy - // Annotations.Service does the same, keeping it for compat - // lots of cloud provider specific configs can be put into annotations - // they vary enough to have it exposed like this - ExtraLBAnnotations map[string]string `json:"extraLBAnnotations,omitempty"` - // Annotations - Annotations Markers `json:"annotations"` - // k8s labels - Labels Markers `json:"labels"` - // Metrics - Metrics *Metrics `json:"metrics,omitempty"` - // Expected state - ExpectedState *ExpectedState `json:"expectedState,omitempty"` + HealthCheckConfig HealthCheckConfig `json:"healthCheck"` + Labels Markers `json:"labels"` + Annotations Markers `json:"annotations"` + ConfigContainer *ConfigContainer `json:"configContainer,omitempty"` + ImportContainer *ImportContainer `json:"importContainer,omitempty"` + ExpectedState *ExpectedState `json:"expectedState,omitempty"` + Environment map[string]string `json:"environment"` + Secrets map[string]string `json:"secrets,omitempty"` + LogConfig *container.LogConfig `json:"logConfig"` + ExtraLBAnnotations map[string]string `json:"extraLBAnnotations,omitempty"` + User *int64 `json:"user"` + Metrics *Metrics `json:"metrics,omitempty"` + DockerLabels map[string]string `json:"dockerLabels"` + ResourceConfig ResourceConfig `json:"resourceConfig"` + IngressPath string `json:"ingressPath"` + Container string `json:"container" binding:"required"` + DeploymentStrategy string `json:"deploymentStrategy"` + IngressUploadLimit string `json:"ingressUploadLimit"` + IngressHost string `json:"ingressHost"` + WorkingDirectory string `json:"workingDirectory"` + IngressName string `json:"ingressName"` + ContainerPreName string `json:"containerPreName"` + NetworkMode string `json:"networkMode"` + RestartPolicy container.RestartPolicyMode `json:"restartPolicy"` + RuntimeConfigType RuntimeConfigType `json:"runtimeConfigType"` + InitContainers []InitContainer `json:"initContainers,omitempty" binding:"dive"` + Volumes []Volume `json:"volumes,omitempty" binding:"dive"` + Args []string `json:"args"` + Command []string `json:"command"` + Networks []string `json:"networks"` + Ports []builder.PortBinding `json:"port" binding:"dive"` + PortRanges []builder.PortRangeBinding `json:"portRanges" binding:"dive"` + CustomHeaders []string `json:"customHeaders,omitempty"` + Mounts []string `json:"mount"` + IngressPort uint16 `json:"ingressPort"` + Shared bool `json:"shared"` + UseLoadBalancer bool `json:"useLoadBalancer"` + Expose bool `json:"expose"` + ProxyHeaders bool `json:"proxyHeaders"` + ExposeTLS bool `json:"exposeTls"` + IngressStripPath bool `json:"ingressPathStrip"` + TTY bool `json:"tty"` } type Metrics struct { @@ -329,20 +256,13 @@ type ImportContainer struct { // classic initContainer, also mimicked on docker // TODO(nandor-magyar): extend docs here type InitContainer struct { - // name of the init container, they must be unique within a pod - Name string `json:"name"` - // image to use - Image string `json:"image"` - // Reference(s) to already existing volume(s) - Volumes []VolumeLink `json:"volumes"` - // command to run, expecting exit code 0 - Command []string `json:"command"` - // arguments added to the command - Args []string `json:"args"` - // use env/secrets from the parent container - UseParent bool `json:"useParent"` - // envs directly defined - Envs map[string]string `json:"envs"` + Envs map[string]string `json:"envs"` + Name string `json:"name"` + Image string `json:"image"` + Volumes []VolumeLink `json:"volumes"` + Command []string `json:"command"` + Args []string `json:"args"` + UseParent bool `json:"useParent"` } type VolumeLink struct { @@ -432,10 +352,6 @@ func (jsonConfig *Base64JSONBytes) UnmarshalJSON(b []byte) error { } cleaned := util.RemoveJSONComment(decoded) - if err != nil { - return err - } - *jsonConfig = Base64JSONBytes(cleaned) return err @@ -473,6 +389,6 @@ func SetDeploymentDefaults( } if deployImageRequest.ContainerConfig.RestartPolicy == "" { - deployImageRequest.ContainerConfig.RestartPolicy = builder.RestartUnlessStoppedRestartPolicy + deployImageRequest.ContainerConfig.RestartPolicy = container.RestartPolicyUnlessStopped } } diff --git a/golang/internal/config/config.go b/golang/internal/config/config.go index 14c0d4488..389ac319b 100644 --- a/golang/internal/config/config.go +++ b/golang/internal/config/config.go @@ -21,29 +21,27 @@ import ( // Example: ValidJWT // Link: https://github.com/ilyakaznacheev/cleanenv#custom-value-setter type CommonConfiguration struct { - DefaultLimitsCPU string `yaml:"defaultLimitsCPU" env:"DEFAULT_LIMITS_CPU" env-default:"100m"` - DefaultLimitsMemory string `yaml:"defaultLimitsMemory" env:"DEFAULT_LIMITS_MEMORY" env-default:"128Mi"` - DefaultRequestsCPU string `yaml:"defaultRequestsCPU" env:"DEFAULT_REQUESTS_CPU" env-default:"50m"` - DefaultRequestMemory string `yaml:"defaultRequestMemory" env:"DEFAULT_REQUESTS_MEMORY" env-default:"64Mi"` - DefaultVolumeSize string `yaml:"defaultVolumeSize" env:"DEFAULT_VOLUME_SIZE" env-default:"1G"` - DefaultTag string `yaml:"defaultTag" env:"DEFAULT_TAG" env-default:"latest"` - DefaultTimeout time.Duration `yaml:"defaultTimeout" env:"DEFAULT_TIMEOUT" env-default:"5s"` + FallbackJwtToken *ValidJWT + JwtToken *ValidJWT + SecretPrivateKey string + DefaultRequestMemory string `yaml:"defaultRequestMemory" env:"DEFAULT_REQUESTS_MEMORY" env-default:"64Mi"` + DefaultVolumeSize string `yaml:"defaultVolumeSize" env:"DEFAULT_VOLUME_SIZE" env-default:"1G"` + DefaultTag string `yaml:"defaultTag" env:"DEFAULT_TAG" env-default:"latest"` + RootDomain string `yaml:"rootDomain" env:"ROOT_DOMAIN" env-default:""` + DefaultRequestsCPU string `yaml:"defaultRequestsCPU" env:"DEFAULT_REQUESTS_CPU" env-default:"50m"` + GrpcToken string `yaml:"grpcToken" env:"GRPC_TOKEN" env-default:""` + Name string `yaml:"name" env:"NAME" env-default:"dagent-go"` + DefaultLimitsMemory string `yaml:"defaultLimitsMemory" env:"DEFAULT_LIMITS_MEMORY" env-default:"128Mi"` + DefaultRegistry string `yaml:"registry" env:"DEFAULT_REGISTRY" env-default:"index.docker.io"` + DefaultLimitsCPU string `yaml:"defaultLimitsCPU" env:"DEFAULT_LIMITS_CPU" env-default:"100m"` + //nolint:lll + ImportContainerImage string `yaml:"importContainerImage" env:"IMPORT_CONTAINER_IMAGE" env-default:"rclone/rclone:1.57.0"` + ReadHeaderTimeout time.Duration `yaml:"readHeaderTimeout" env:"READ_HEADER_TIMEOUT" env-default:"15s"` GrpcKeepalive time.Duration `yaml:"grpcKeepalive" env:"GRPC_KEEPALIVE" env-default:"60s"` - GrpcToken string `yaml:"grpcToken" env:"GRPC_TOKEN" env-default:""` - Name string `yaml:"name" env:"NAME" env-default:"dagent-go"` - Debug bool `yaml:"debug" env:"DEBUG" env-default:"false"` - DebugUpdateAlways bool `yaml:"debugUpdateAlways" env:"DEBUG_UPDATE_ALWAYS" env-default:"false"` + DefaultTimeout time.Duration `yaml:"defaultTimeout" env:"DEFAULT_TIMEOUT" env-default:"5s"` DebugUpdateUseContainers bool `yaml:"debugUpdateUseContainers" env:"DEBUG_UPDATE_USE_CONTAINERS" env-default:"true"` - ImportContainerImage string `yaml:"importContainerImage" env:"IMPORT_CONTAINER_IMAGE" env-default:"rclone/rclone:1.57.0"` //nolint:lll - RootDomain string `yaml:"rootDomain" env:"ROOT_DOMAIN" env-default:""` - ReadHeaderTimeout time.Duration `yaml:"readHeaderTimeout" env:"READ_HEADER_TIMEOUT" env-default:"15s"` - // DefaultRegistry container registry used for container name expansion - DefaultRegistry string `yaml:"registry" env:"DEFAULT_REGISTRY" env-default:"index.docker.io"` - // gRPC token is set separately, because nested structures are not yet suppported in cleanenv - JwtToken *ValidJWT - FallbackJwtToken *ValidJWT - // injected from crane/dagent - SecretPrivateKey string + DebugUpdateAlways bool `yaml:"debugUpdateAlways" env:"DEBUG_UPDATE_ALWAYS" env-default:"false"` + Debug bool `yaml:"debug" env:"DEBUG" env-default:"false"` } const ( diff --git a/golang/internal/config/jwt_test.go b/golang/internal/config/jwt_test.go index 4854ceff0..83e5f63fc 100644 --- a/golang/internal/config/jwt_test.go +++ b/golang/internal/config/jwt_test.go @@ -16,9 +16,9 @@ import ( ) type jwtTest struct { + expErr error name string jwtTokenStringified string - expErr error } func (j jwtTest) run(t *testing.T) { diff --git a/golang/internal/dogger/dogger.go b/golang/internal/dogger/dogger.go index dba4386f5..ca7a6bfc8 100644 --- a/golang/internal/dogger/dogger.go +++ b/golang/internal/dogger/dogger.go @@ -33,14 +33,13 @@ type status struct { } type DeploymentLogger struct { + stream agent.Agent_DeploymentStatusClient + ctx context.Context + LogWriter + appConfig *config.CommonConfiguration deploymentID string requestID string - stream agent.Agent_DeploymentStatusClient logs []string - ctx context.Context - appConfig *config.CommonConfiguration - - LogWriter } func NewDeploymentLogger(ctx context.Context, deploymentID *string, diff --git a/golang/internal/grpc/grpc.go b/golang/internal/grpc/grpc.go index de30b5e43..89ff2dd33 100644 --- a/golang/internal/grpc/grpc.go +++ b/golang/internal/grpc/grpc.go @@ -40,8 +40,8 @@ type Connection struct { } type ContainerLogEvent struct { - Message string Error error + Message string } type ContainerLogReader interface { @@ -379,26 +379,21 @@ func initWithToken( } log.Info().Str("address", address).Msg("Dialing to address.") - conn, err := grpc.Dial(address, opts...) + conn, err := grpc.NewClient(address, opts...) if err != nil { - log.Panic().Stack().Err(err).Msg("Failed to dial gRPC") + return err } for { state := conn.GetState() - if state != connectivity.Ready { - log.Debug().Msgf("Waiting for state to change: %d", state) - conn.WaitForStateChange(loop.Ctx, state) - log.Debug().Msgf("State Changed to: %d", conn.GetState()) - } else { + if state == connectivity.Ready || state == connectivity.Idle { break } - } - if err != nil { - log.Error().Stack().Err(err).Msg("gRPC connection error") + log.Debug().Msgf("Waiting for state to change: %s", state.String()) + conn.WaitForStateChange(loop.Ctx, state) + log.Debug().Msgf("State Changed to: %d", conn.GetState()) } grpcConn.Conn = conn - return loop.grpcLoop(token) } @@ -498,7 +493,7 @@ func executeVersionDeployRequest( dog.SetRequestID(imageReq.RequestID) var versionData *v1.VersionData - if len(req.VersionName) > 0 { + if req.VersionName != "" { versionData = &v1.VersionData{Version: req.VersionName, ReleaseNotes: req.ReleaseNotes} } diff --git a/golang/internal/helper/docker/container.go b/golang/internal/helper/docker/container.go index 5c069ade5..75ccb3f43 100644 --- a/golang/internal/helper/docker/container.go +++ b/golang/internal/helper/docker/container.go @@ -8,6 +8,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/image" "github.com/docker/docker/client" "github.com/rs/zerolog/log" @@ -57,7 +58,7 @@ func deleteContainerByIDAndState(ctx context.Context, dog *dogger.DeploymentLogg dog.WriteContainerState(common.ContainerState_WAITING, state, dogger.Info, "Removing container: "+helper.FirstN(id, VisibleIDLimit)) } - if err = cli.ContainerRemove(ctx, id, types.ContainerRemoveOptions{}); err != nil { + if err = cli.ContainerRemove(ctx, id, container.RemoveOptions{}); err != nil { return fmt.Errorf("could not remove container (%s): %s", helper.FirstN(id, VisibleIDLimit), err.Error()) } @@ -108,7 +109,7 @@ func GetAllContainers(ctx context.Context) ([]types.Container, error) { log.Fatal().Err(err).Send() } - containers, err := cli.ContainerList(ctx, types.ContainerListOptions{All: true}) + containers, err := cli.ContainerList(ctx, container.ListOptions{All: true}) if err != nil { return []types.Container{}, err } @@ -173,7 +174,7 @@ func DeleteImage(ctx context.Context, imageID string) error { log.Fatal().Err(err).Send() } - _, err = cli.ImageRemove(ctx, imageID, types.ImageRemoveOptions{}) + _, err = cli.ImageRemove(ctx, imageID, image.RemoveOptions{}) return err } @@ -189,8 +190,8 @@ func checkOneContainer(containers []types.Container) (*types.Container, error) { } } -func containerListOptionsfilter(filtertype, filter string) types.ContainerListOptions { - return types.ContainerListOptions{ +func containerListOptionsfilter(filtertype, filter string) container.ListOptions { + return container.ListOptions{ All: true, Filters: filters.NewArgs( filters.KeyValuePair{ diff --git a/golang/internal/helper/docker/container_test.go b/golang/internal/helper/docker/container_test.go index 4554a4569..ef431e0cd 100644 --- a/golang/internal/helper/docker/container_test.go +++ b/golang/internal/helper/docker/container_test.go @@ -55,7 +55,7 @@ func (testSuite *DockerContainerHelperTestSuite) SetupSuite() { preparedContainer := containerbuilder.NewDockerBuilder(context.Background()). WithImage(nginxImage). WithName(fmt.Sprintf("%s-%s", testSuite.prefix, testSuite.containerNames[i])). - WithRestartPolicy(containerbuilder.NoRestartPolicy) + WithRestartPolicy(container.RestartPolicyDisabled) testSuite.testContainers = append(testSuite.testContainers, preparedContainer) } } @@ -79,7 +79,7 @@ func (testSuite *DockerContainerHelperTestSuite) SetupTest() { // this function executes after each test case func (testSuite *DockerContainerHelperTestSuite) TearDownTest() { containers, err := testSuite.dockerClient.ContainerList(testSuite.ctx, - types.ContainerListOptions{ + container.ListOptions{ All: true, Filters: filters.NewArgs( filters.KeyValuePair{ @@ -97,7 +97,7 @@ func (testSuite *DockerContainerHelperTestSuite) TearDownTest() { if err != nil { log.Warn().Err(err).Send() } - err = testSuite.dockerClient.ContainerRemove(testSuite.ctx, containers[i].ID, types.ContainerRemoveOptions{}) + err = testSuite.dockerClient.ContainerRemove(testSuite.ctx, containers[i].ID, container.RemoveOptions{}) if err != nil { log.Warn().Err(err).Send() } diff --git a/golang/internal/helper/image/image.go b/golang/internal/helper/image/image.go index aa0ea4af2..a3d58c5bb 100644 --- a/golang/internal/helper/image/image.go +++ b/golang/internal/helper/image/image.go @@ -17,8 +17,8 @@ import ( "github.com/dyrector-io/dyrectorio/protobuf/go/agent" "github.com/docker/distribution/reference" - "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/registry" "github.com/docker/docker/client" "github.com/docker/docker/errdefs" @@ -32,11 +32,11 @@ import ( type PullResponse struct { ID string `json:"id"` Status string `json:"status"` + Progress string `json:"progress"` ProgressDetail struct { Current int64 `json:"current"` Total int64 `json:"total"` } `json:"progressDetail"` - Progress string `json:"progress"` } type remoteCheck struct { @@ -66,9 +66,8 @@ func GetRegistryURL(reg *string, registryAuth *RegistryAuth) string { return registryAuth.URL } else if reg != nil { return *reg - } else { - return "" } + return "" } func GetRegistryURLProto(reg *string, registryAuth *agent.RegistryAuth) string { @@ -76,13 +75,12 @@ func GetRegistryURLProto(reg *string, registryAuth *agent.RegistryAuth) string { return registryAuth.Url } else if reg != nil { return *reg - } else { - return "" } + return "" } -func GetImageByReference(ctx context.Context, cli client.APIClient, ref string) (*types.ImageSummary, error) { - images, err := cli.ImageList(ctx, types.ImageListOptions{ +func GetImageByReference(ctx context.Context, cli client.APIClient, ref string) (*image.Summary, error) { + images, err := cli.ImageList(ctx, image.ListOptions{ Filters: filters.NewArgs(filters.KeyValuePair{Key: "reference", Value: ref}), }) if err != nil { @@ -105,7 +103,7 @@ func Exists( logger io.StringWriter, expandedImageName, encodedAuth string, ) (*ExistResult, error) { exists := ExistResult{} - images, err := cli.ImageList(ctx, types.ImageListOptions{ + images, err := cli.ImageList(ctx, image.ListOptions{ Filters: filters.NewArgs(filters.KeyValuePair{Key: "reference", Value: expandedImageName}), }) if err != nil { @@ -163,7 +161,7 @@ func Pull(ctx context.Context, cli client.APIClient, logger io.StringWriter, exp } } - reader, err := cli.ImagePull(ctx, expandedImageName, types.ImagePullOptions{RegistryAuth: authCreds}) + reader, err := cli.ImagePull(ctx, expandedImageName, image.PullOptions{RegistryAuth: authCreds}) if err != nil { return err } @@ -248,7 +246,7 @@ func shouldUseLocalImage(ctx context.Context, cli client.APIClient, } func pullImage(ctx context.Context, cli client.APIClient, imageName, encodedAuth string) (io.ReadCloser, error) { - options := types.ImagePullOptions{ + options := image.PullOptions{ RegistryAuth: encodedAuth, } @@ -349,8 +347,8 @@ func checkRemote(ctx context.Context, check remoteCheck) (err error) { return errDigestsMatching } -func ParseReference(image string) (reference.Reference, error) { - return reference.ParseAnyReference(strings.ToLower(image)) +func ParseReference(img string) (reference.Reference, error) { + return reference.ParseAnyReference(strings.ToLower(img)) } func ExpandImageName(imageWithTag string) (string, error) { @@ -371,8 +369,8 @@ func ExpandImageName(imageWithTag string) (string, error) { return named.String(), nil } -func ExpandImageNameWithTag(image, tag string) (string, error) { - ref, err := ParseReference(image) +func ExpandImageNameWithTag(img, tag string) (string, error) { + ref, err := ParseReference(img) if err != nil { return "", err } diff --git a/golang/internal/mapper/grpc.go b/golang/internal/mapper/grpc.go index 55898e5c6..e78d0503d 100644 --- a/golang/internal/mapper/grpc.go +++ b/golang/internal/mapper/grpc.go @@ -159,12 +159,12 @@ func mapDagentConfig(dagent *agent.DagentContainerConfig, containerConfig *v1.Co } if dagent.RestartPolicy != nil { - containerConfig.RestartPolicy = mapRestartPolicy(dagent.RestartPolicy.String()) + containerConfig.RestartPolicy = container.RestartPolicyMode(ProtoEnumToKebabCase(dagent.RestartPolicy.String())) } if dagent.LogConfig != nil { containerConfig.LogConfig = &container.LogConfig{ - Type: strings.ToLower(strings.ReplaceAll(strings.TrimPrefix(dagent.LogConfig.Driver.String(), "DRIVER_TYPE_"), "_", "-")), + Type: ProtoEnumToKebabCase(strings.TrimPrefix(dagent.LogConfig.Driver.String(), "DRIVER_TYPE_")), Config: dagent.LogConfig.Options, } } @@ -182,6 +182,14 @@ func mapDagentConfig(dagent *agent.DagentContainerConfig, containerConfig *v1.Co } } +func ProtoEnumToKebabCase(in string) string { + res := strings.ToLower(strings.ReplaceAll(in, "_", "-")) + if strings.Contains(res, "unspecified") || strings.Contains(res, "undefined") { + return "" + } + return res +} + func mapCraneConfig(crane *agent.CraneContainerConfig, containerConfig *v1.ContainerConfig) { containerConfig.DeploymentStrategy = strcase.ToCamel(crane.DeploymentStrategy.String()) @@ -229,12 +237,6 @@ func mapCraneConfig(crane *agent.CraneContainerConfig, containerConfig *v1.Conta } } -func mapRestartPolicy(policy string) builder.RestartPolicyName { - lower := strings.ToLower(policy) - - return builder.RestartPolicyName(strings.Replace(lower, "_", "-", -1)) -} - func mapContainerState(state common.ContainerState) v1.ContainerState { switch state { case common.ContainerState_CONTAINER_STATE_UNSPECIFIED: diff --git a/golang/internal/mapper/grpc_test.go b/golang/internal/mapper/grpc_test.go index 2c21c5f9f..271d23217 100644 --- a/golang/internal/mapper/grpc_test.go +++ b/golang/internal/mapper/grpc_test.go @@ -30,6 +30,33 @@ func TestMapDeployImageRequest(t *testing.T) { assert.Equal(t, expected, res) } +type RestartTestCase struct { + policy *common.RestartPolicy + dockerType container.RestartPolicyMode +} + +func TestMapDeployImageRequestRestartPolicies(t *testing.T) { + req := testDeployRequest() + expected := testExpectedCommon(req) + cfg := testAppConfig() + + cases := []RestartTestCase{ + {common.RestartPolicy_NO.Enum(), container.RestartPolicyDisabled}, + {common.RestartPolicy_ON_FAILURE.Enum(), container.RestartPolicyOnFailure}, + {common.RestartPolicy_ALWAYS.Enum(), container.RestartPolicyAlways}, + {common.RestartPolicy_UNLESS_STOPPED.Enum(), container.RestartPolicyUnlessStopped}, + // should be "" to use container runtime default setting, but we enforce 'unless-stopped' + {common.RestartPolicy_UNDEFINED.Enum(), container.RestartPolicyUnlessStopped}, + {common.RestartPolicy_POLICY_UNSPECIFIED.Enum(), container.RestartPolicyUnlessStopped}, + } + for _, tC := range cases { + req.Dagent.RestartPolicy = tC.policy + expected.ContainerConfig.RestartPolicy = tC.dockerType + res := MapDeployImage(req, cfg) + assert.Equal(t, expected, res) + } +} + func testExpectedCommon(req *agent.DeployRequest) *v1.DeployImageRequest { return &v1.DeployImageRequest{ RequestID: "testID", @@ -97,7 +124,7 @@ func testExpectedCommon(req *agent.DeployRequest) *v1.DeployImageRequest { Type: "365", Config: map[string]string{"opt1": "v1", "opt2": "v2"}, }, - RestartPolicy: "always", + RestartPolicy: container.RestartPolicyAlways, Networks: []string{"n1", "n2"}, NetworkMode: "BRIDGE", CustomHeaders: []string(nil), diff --git a/golang/internal/runtime/container/container_test.go b/golang/internal/runtime/container/container_test.go index cfcaa1a88..c985051fe 100644 --- a/golang/internal/runtime/container/container_test.go +++ b/golang/internal/runtime/container/container_test.go @@ -15,6 +15,7 @@ import ( containerRuntime "github.com/dyrector-io/dyrectorio/golang/internal/runtime/container" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/system" "github.com/docker/docker/client" "github.com/stretchr/testify/assert" ) @@ -22,21 +23,21 @@ import ( type mockDockerClient struct { client.APIClient version string - info *types.Info + info *system.Info } type mockErrDockerClient struct { client.APIClient } -func newMockClient(version string, info *types.Info) client.APIClient { +func newMockClient(version string, info *system.Info) client.APIClient { return &mockDockerClient{ version: version, info: info, } } -func (m mockDockerClient) Info(ctx context.Context) (types.Info, error) { +func (m mockDockerClient) Info(ctx context.Context) (system.Info, error) { return *m.info, nil } @@ -54,30 +55,30 @@ func (m mockErrDockerClient) ServerVersion(ctx context.Context) (types.Version, return types.Version{}, fmt.Errorf("expected version error") } -func (m mockErrDockerClient) Info(ctx context.Context) (types.Info, error) { - return types.Info{}, fmt.Errorf("expected info error") +func (m mockErrDockerClient) Info(ctx context.Context) (system.Info, error) { + return system.Info{}, fmt.Errorf("expected info error") } -func getDockerInfoDocker() *types.Info { - return &types.Info{ +func getDockerInfoDocker() *system.Info { + return &system.Info{ InitBinary: "docker-init", } } -func getDockerInfoPodman() *types.Info { - return &types.Info{ +func getDockerInfoPodman() *system.Info { + return &system.Info{ InitBinary: "", } } -func getDockerInfoInvalid() *types.Info { - return &types.Info{ +func getDockerInfoInvalid() *system.Info { + return &system.Info{ InitBinary: "invalidInitBinaryString", } } type VersionTestCase struct { - Info *types.Info + Info *system.Info MockClientVersion string } @@ -207,7 +208,7 @@ func getMajorMinor(f *testing.F, versionStr string) (uint16, uint16) { return uint16(major), uint16(minor) } -func fuzzVersionWithRuntime(f *testing.F, dockerInfo *types.Info, majorMin, minorMin uint16) func(t *testing.T, major, minor, patch uint16) { +func fuzzVersionWithRuntime(_ *testing.F, dockerInfo *system.Info, majorMin, minorMin uint16) func(t *testing.T, major, minor, patch uint16) { return func(t *testing.T, major, minor, patch uint16) { versionStr := fmt.Sprintf("%d.%d.%d", major, minor, patch) @@ -233,7 +234,7 @@ func FuzzVersionCheckPodman(f *testing.F) { f.Add(uint16(20), uint16(0), uint16(10)) f.Add(uint16(4), uint16(0), uint16(0)) majorMin, minorMin := getMajorMinor(f, containerRuntime.RecommendedPodmanServerVersion) - dockerInfo := &types.Info{} + dockerInfo := &system.Info{} f.Fuzz(fuzzVersionWithRuntime(f, dockerInfo, majorMin, minorMin)) } @@ -242,7 +243,7 @@ func FuzzVersionCheckDocker(f *testing.F) { f.Add(uint16(20), uint16(0), uint16(10)) f.Add(uint16(21), uint16(0), uint16(10)) f.Add(uint16(4), uint16(0), uint16(0)) - dockerInfo := &types.Info{ + dockerInfo := &system.Info{ InitBinary: "docker-init", } majorMin, minorMin := getMajorMinor(f, containerRuntime.RecommendedDockerServerVersion) diff --git a/golang/internal/util/array.go b/golang/internal/util/array.go index 3f1ea8a5e..d9f4ea853 100644 --- a/golang/internal/util/array.go +++ b/golang/internal/util/array.go @@ -16,7 +16,7 @@ func ContainsMatcher[T comparable](arr []T, item T, comp func(T, T) bool) bool { return false } for _, v := range arr { - if comp != nil && comp(v, item) { + if comp(v, item) { return true } } diff --git a/golang/internal/util/dotnet.go b/golang/internal/util/dotnet.go index d5dccbc33..d69ca7902 100644 --- a/golang/internal/util/dotnet.go +++ b/golang/internal/util/dotnet.go @@ -12,7 +12,7 @@ func MapAppsettingsToEnv(in *string) (map[string]string, error) { if in == nil { return map[string]string{}, errors.New("Nil input") } - if in != nil && *in == "" { + if *in == "" { return map[string]string{}, errors.New("Empty input") } envList := map[string]string{} diff --git a/golang/pkg/builder/container/container_builder.go b/golang/pkg/builder/container/container_builder.go index f915c287b..47a5396a3 100644 --- a/golang/pkg/builder/container/container_builder.go +++ b/golang/pkg/builder/container/container_builder.go @@ -43,7 +43,7 @@ type Builder interface { WithLogConfig(config *container.LogConfig) Builder WithRegistryAuth(auth *imageHelper.RegistryAuth) Builder WithAutoRemove(remove bool) Builder - WithRestartPolicy(policy RestartPolicyName) Builder + WithRestartPolicy(policy container.RestartPolicyMode) Builder WithEntrypoint(cmd []string) Builder WithCmd(cmd []string) Builder WithShell(shell []string) Builder @@ -63,39 +63,39 @@ type Builder interface { } type DockerContainerBuilder struct { - ctx context.Context + logger dogger.LogWriter client client.APIClient - containerID *string + ctx context.Context + logConfig *container.LogConfig networkMap map[string]string - networkAliases []string + labels map[string]string + pullDisplayFn imageHelper.PullDisplayFn + containerID *string + user *int64 + workingDirectory string containerName string imageWithTag string - envList []string - labels map[string]string - logConfig *container.LogConfig - portList []PortBinding - portRanges []PortRangeBinding - mountList []mount.Mount + registryAuth string networkMode string + restartPolicy container.RestartPolicyMode networks []string - registryAuth string - remove bool - withoutConflict bool - restartPolicy RestartPolicyName + hooksPostStart []LifecycleFunc + hooksPreStart []LifecycleFunc + hooksPreCreate []LifecycleFunc entrypoint []string cmd []string shell []string - tty bool - user *int64 - imagePriority imageHelper.PullPriority - pullDisplayFn imageHelper.PullDisplayFn - logger dogger.LogWriter - workingDirectory string - extraHosts []string - hooksPreCreate []LifecycleFunc hooksPostCreate []LifecycleFunc - hooksPreStart []LifecycleFunc - hooksPostStart []LifecycleFunc + mountList []mount.Mount + portRanges []PortRangeBinding + portList []PortBinding + envList []string + networkAliases []string + extraHosts []string + imagePriority imageHelper.PullPriority + tty bool + withoutConflict bool + remove bool } // A shorthand function for creating a new DockerContainerBuilder and calling WithClient. @@ -208,7 +208,7 @@ func (dc *DockerContainerBuilder) WithRegistryAuth(auth *imageHelper.RegistryAut } // Sets the restart policy of the container. -func (dc *DockerContainerBuilder) WithRestartPolicy(policy RestartPolicyName) Builder { +func (dc *DockerContainerBuilder) WithRestartPolicy(policy container.RestartPolicyMode) Builder { dc.restartPolicy = policy return dc } @@ -306,12 +306,6 @@ func (dc *DockerContainerBuilder) WithPostStartHooks(hooks ...LifecycleFunc) Bui } func builderToDockerConfig(dc *DockerContainerBuilder) (hostConfig *container.HostConfig, containerConfig *container.Config, err error) { - hostConfig = &container.HostConfig{} - containerConfig = &container.Config{} - if dc.containerName != "" { - containerConfig.Hostname = dc.containerName - } - portListNat := portListToNatBinding(dc.portRanges, dc.portList) exposedPortSet := getPortSet(dc.portRanges, dc.portList) hostConfig = &container.HostConfig{ @@ -332,6 +326,9 @@ func builderToDockerConfig(dc *DockerContainerBuilder) (hostConfig *container.Ho Shell: dc.shell, WorkingDir: dc.workingDirectory, } + if dc.containerName != "" { + containerConfig.Hostname = dc.containerName + } if dc.user != nil { containerConfig.User = fmt.Sprint(*dc.user) @@ -341,8 +338,13 @@ func builderToDockerConfig(dc *DockerContainerBuilder) (hostConfig *container.Ho hostConfig.LogConfig = *dc.logConfig } - policy := container.RestartPolicy{} - policy.Name = string(dc.restartPolicy) + policy := container.RestartPolicy{ + Name: dc.restartPolicy, + } + err = container.ValidateRestartPolicy(policy) + if err != nil { + return nil, nil, errors.Join(err, fmt.Errorf("builder: invalid restart policy")) + } hostConfig.RestartPolicy = policy if dc.networkMode != "" { @@ -392,7 +394,7 @@ func (dc *DockerContainerBuilder) Create() (Container, error) { if err != nil { dc.logError(fmt.Sprintln("Container create failed: ", err)) } - containers, err := dc.client.ContainerList(dc.ctx, types.ContainerListOptions{ + containers, err := dc.client.ContainerList(dc.ctx, container.ListOptions{ All: true, Filters: filters.NewArgs(filters.KeyValuePair{Key: "id", Value: containerCreateResp.ID}), }) @@ -435,7 +437,7 @@ func (dc *DockerContainerBuilder) CreateAndStartWaitUntilExit() (Container, *Wai return cont, res, err } - logReader, err := dc.client.ContainerLogs(dc.ctx, *cont.GetContainerID(), types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true}) + logReader, err := dc.client.ContainerLogs(dc.ctx, *cont.GetContainerID(), container.LogsOptions{ShowStdout: true, ShowStderr: true}) if err != nil { return cont, res, err } diff --git a/golang/pkg/builder/container/container_builder_integration_test.go b/golang/pkg/builder/container/container_builder_integration_test.go index 336309a19..7d1f67f49 100644 --- a/golang/pkg/builder/container/container_builder_integration_test.go +++ b/golang/pkg/builder/container/container_builder_integration_test.go @@ -10,7 +10,7 @@ import ( "time" "github.com/AlekSi/pointer" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/client" "github.com/docker/go-connections/nat" @@ -28,6 +28,9 @@ func baseBuilder(ctx context.Context) containerbuilder.Builder { } func containerCleanup(container containerbuilder.Container) { + if container == nil { + return + } ctx := context.Background() if container.GetContainerID() != nil { dockerHelper.DeleteContainerByID(ctx, nil, *container.GetContainerID()) @@ -70,6 +73,18 @@ func TestNameWithBuilder(t *testing.T) { assert.NoError(t, err) } +func TestRestartNegative(t *testing.T) { + var _ containerbuilder.Builder = (*containerbuilder.DockerContainerBuilder)(nil) + + builder := containerbuilder.NewDockerBuilder(context.Background()). + WithImage("ghcr.io/dyrector-io/mirror/nginx:mainline-alpine"). + WithRestartPolicy(container.RestartPolicyMode("invalid")) + + cont, err := builder.CreateAndStart() + defer containerCleanup(cont) + assert.Error(t, err, "error is thrown for invalid restart policy") +} + func TestEnvPortsLabelsRestartPolicySettings(t *testing.T) { cont, err := containerbuilder.NewDockerBuilder(context.Background()). WithName("test02"). @@ -92,7 +107,7 @@ func TestEnvPortsLabelsRestartPolicySettings(t *testing.T) { "LABEL1": "TEST", "LABEL2": "1234", }). - WithRestartPolicy(containerbuilder.AlwaysRestartPolicy). + WithRestartPolicy(container.RestartPolicyAlways). WithImage("ghcr.io/dyrector-io/mirror/nginx:mainline-alpine"). CreateAndStart() @@ -103,25 +118,25 @@ func TestEnvPortsLabelsRestartPolicySettings(t *testing.T) { panic(err) } - container, err := cli.ContainerInspect(context.Background(), *cont.GetContainerID()) + containerResp, err := cli.ContainerInspect(context.Background(), *cont.GetContainerID()) assert.Nil(t, err) - assert.Equal(t, "/test02", container.Name) + assert.Equal(t, "/test02", containerResp.Name) - assert.Contains(t, container.Config.Env, "A=B") - assert.Contains(t, container.Config.Env, "E_N_V=123") + assert.Contains(t, containerResp.Config.Env, "A=B") + assert.Contains(t, containerResp.Config.Env, "E_N_V=123") - assertPortBinding(t, container.HostConfig.PortBindings, "1234", "2345") + assertPortBinding(t, containerResp.HostConfig.PortBindings, "1234", "2345") for testPort := 0; testPort < 10; testPort++ { - assertPortBinding(t, container.HostConfig.PortBindings, fmt.Sprint(10+testPort), fmt.Sprint(30+testPort)) + assertPortBinding(t, containerResp.HostConfig.PortBindings, fmt.Sprint(10+testPort), fmt.Sprint(30+testPort)) } - assert.Contains(t, container.Config.Labels, "LABEL1") - assert.Equal(t, container.Config.Labels["LABEL1"], "TEST") - assert.Contains(t, container.Config.Labels, "LABEL2") - assert.Equal(t, container.Config.Labels["LABEL2"], "1234") + assert.Contains(t, containerResp.Config.Labels, "LABEL1") + assert.Equal(t, containerResp.Config.Labels["LABEL1"], "TEST") + assert.Contains(t, containerResp.Config.Labels, "LABEL2") + assert.Equal(t, containerResp.Config.Labels["LABEL2"], "1234") - assert.Equal(t, container.HostConfig.RestartPolicy.Name, string(containerbuilder.AlwaysRestartPolicy)) + assert.Equal(t, string(containerResp.HostConfig.RestartPolicy.Name), string(container.RestartPolicyAlways)) } func TestLogging(t *testing.T) { @@ -240,7 +255,7 @@ func TestConflict(t *testing.T) { t.Fatal(err) } - list, err := cli.ContainerList(context.Background(), types.ContainerListOptions{ + list, err := cli.ContainerList(context.Background(), container.ListOptions{ All: true, Filters: filters.NewArgs(filters.KeyValuePair{Key: "id", Value: *cont2.GetContainerID()}), }) diff --git a/golang/pkg/builder/container/docker_container.go b/golang/pkg/builder/container/docker_container.go index 02bb10800..7afa397fa 100644 --- a/golang/pkg/builder/container/docker_container.go +++ b/golang/pkg/builder/container/docker_container.go @@ -69,7 +69,7 @@ func (d DockerContainer) Start(ctx context.Context, cli client.APIClient) error return hookError } - err := cli.ContainerStart(ctx, d.container.ID, types.ContainerStartOptions{}) + err := cli.ContainerStart(ctx, d.container.ID, container.StartOptions{}) if err != nil { return err } diff --git a/golang/pkg/builder/container/exec_builder.go b/golang/pkg/builder/container/exec_builder.go index e4258ec49..f3229bcd8 100644 --- a/golang/pkg/builder/container/exec_builder.go +++ b/golang/pkg/builder/container/exec_builder.go @@ -32,12 +32,12 @@ type ExecBuilder interface { type DockerExecBuilder struct { ctx context.Context - client *client.Client + logger *dogger.LogWriter containerID *string user *int64 + client *client.Client workingDir string cmd []string - logger *dogger.LogWriter tty bool detach bool attachStdin bool @@ -175,9 +175,9 @@ func (de *DockerExecBuilder) Create() (Exec, error) { type Exec struct { ctx context.Context + execStartCheck types.ExecStartCheck client *client.Client ExecID string - execStartCheck types.ExecStartCheck } func (e Exec) Start() error { diff --git a/golang/pkg/builder/container/logger_test.go b/golang/pkg/builder/container/logger_test.go index e2083eb61..d3cd2cc39 100644 --- a/golang/pkg/builder/container/logger_test.go +++ b/golang/pkg/builder/container/logger_test.go @@ -7,10 +7,9 @@ import ( ) type TestLogger struct { + dogger.LogWriter test *testing.T gotMessage bool - - dogger.LogWriter } func (testLogger *TestLogger) WriteInfo(s ...string) { diff --git a/golang/pkg/builder/container/types.go b/golang/pkg/builder/container/types.go index 20cbaac6e..e7f568000 100644 --- a/golang/pkg/builder/container/types.go +++ b/golang/pkg/builder/container/types.go @@ -2,7 +2,6 @@ package container import ( "context" - "encoding/json" "fmt" "github.com/AlekSi/pointer" @@ -40,74 +39,12 @@ type PortRangeBinding struct { External PortRange `json:"external" binding:"required"` } -// RestartPolicyName defines the restart policy used by a container. -type RestartPolicyName string - -const ( - EmptyRestartPolicy RestartPolicyName = "" - AlwaysRestartPolicy RestartPolicyName = "always" - RestartUnlessStoppedRestartPolicy RestartPolicyName = "unless-stopped" - NoRestartPolicy RestartPolicyName = "no" - OnFailureRestartPolicy RestartPolicyName = "on-failure" -) - -// RestartPolicyUnmarshalInvalidError represents custom error regarding restart policy -type RestartPolicyUnmarshalInvalidError struct{} - -func (e *RestartPolicyUnmarshalInvalidError) Error() string { - return "restart policy invalid value provided" -} - -// policyToString static mapping enum type into the docker supported string values -var policyToString = map[RestartPolicyName]string{ - EmptyRestartPolicy: "unless-stopped", - RestartUnlessStoppedRestartPolicy: "unless-stopped", - NoRestartPolicy: "no", - AlwaysRestartPolicy: "always", - OnFailureRestartPolicy: "on-failure", -} - -// policyToID static mapping string values eg. from JSON into enums -var policyToID = map[string]RestartPolicyName{ - "": RestartUnlessStoppedRestartPolicy, - "unless-stopped": RestartUnlessStoppedRestartPolicy, - "no": NoRestartPolicy, - "always": AlwaysRestartPolicy, - "on-failure": OnFailureRestartPolicy, -} - -// custom enum marshal JSON interface implementation -func (policy RestartPolicyName) MarshalJSON() ([]byte, error) { - str, ok := policyToString[policy] - if !ok { - return nil, &RestartPolicyUnmarshalInvalidError{} - } - return []byte(fmt.Sprintf(`%q`, str)), nil -} - -// custom enum unmarshal JSON interface implementation -func (policy *RestartPolicyName) UnmarshalJSON(b []byte) error { - var j string - err := json.Unmarshal(b, &j) - if err != nil { - return err - } - - if _, ok := policyToID[j]; ok { - *policy = policyToID[j] - } else { - *policy = RestartPolicyName("") - err = &RestartPolicyUnmarshalInvalidError{} - } - return err -} - type ParentContainer struct { + Logger *dogger.LogWriter + *types.Container Name string MountList []mount.Mount Environment []string - Logger *dogger.LogWriter - *types.Container } // Hook function which can be used to add custom logic before and after events of the lifecycle of a container. @@ -116,6 +53,6 @@ type LifecycleFunc func(ctx context.Context, client client.APIClient, cont Paren // WaitResult with the status code from the container type WaitResult struct { - StatusCode int64 Logs []string + StatusCode int64 } diff --git a/golang/pkg/builder/container/types_test.go b/golang/pkg/builder/container/types_test.go deleted file mode 100644 index f417a2844..000000000 --- a/golang/pkg/builder/container/types_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package container_test - -import ( - "encoding/json" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/dyrector-io/dyrectorio/golang/pkg/builder/container" -) - -func TestRestartPolicyNameMarshal(t *testing.T) { - testPolicies := map[container.RestartPolicyName]string{ - container.EmptyRestartPolicy: "unless-stopped", - container.RestartUnlessStoppedRestartPolicy: "unless-stopped", - container.NoRestartPolicy: "no", - container.AlwaysRestartPolicy: "always", - container.OnFailureRestartPolicy: "on-failure", - } - - for key, value := range testPolicies { - text, err := json.Marshal(key) - - expectedResult := fmt.Sprintf("%q", value) - - assert.Nil(t, err) - assert.Equal(t, expectedResult, string(text)) - } -} - -func TestRestartPolicyNameUnmarshal(t *testing.T) { - testPolicies := map[string]container.RestartPolicyName{ - "": container.RestartUnlessStoppedRestartPolicy, - "unless-stopped": container.RestartUnlessStoppedRestartPolicy, - "no": container.NoRestartPolicy, - "always": container.AlwaysRestartPolicy, - "on-failure": container.OnFailureRestartPolicy, - } - - for key, value := range testPolicies { - var parsed container.RestartPolicyName - jsonValue := fmt.Sprintf("%q", key) - err := json.Unmarshal([]byte(jsonValue), &parsed) - - assert.Nil(t, err) - assert.Equal(t, value, parsed) - } -} - -func TestRestartPolicyNameUnmarshalError(t *testing.T) { - var parsed container.RestartPolicyName - err := json.Unmarshal([]byte("\"not-valid-policy-name\""), &parsed) - - assert.ErrorIs(t, err, &container.RestartPolicyUnmarshalInvalidError{}) -} diff --git a/golang/pkg/cli/config_file.go b/golang/pkg/cli/config_file.go index 7438f5714..129b89109 100644 --- a/golang/pkg/cli/config_file.go +++ b/golang/pkg/cli/config_file.go @@ -25,29 +25,28 @@ import ( // State itself exists per-execution, settingsFile is persisted // freedesktop spec folders used by default, $XDG_CONFIG_HOME type State struct { - Ctx context.Context - SettingsFile SettingsFile + Ctx context.Context + *Containers InternalHostDomain string EnvFile []string - *Containers + SettingsFile SettingsFile } // ArgsFlags are commandline arguments type ArgsFlags struct { - SettingsWrite bool - SettingsExists bool - SettingsFilePath string - Command string - ImageTag string - Prefix string - CruxDisabled bool - CruxUIDisabled bool - LocalAgent bool - PreferLocalImages bool - EnvFile string - // pipeline mode - FullyContainerized bool + EnvFile string Network string + SettingsFilePath string + Command string + ImageTag string + Prefix string + CruxDisabled bool + CruxUIDisabled bool + LocalAgent bool + PreferLocalImages bool + SettingsWrite bool + FullyContainerized bool + SettingsExists bool Silent bool } @@ -68,8 +67,8 @@ type Containers struct { type ContainerSettings struct { Image string Name string - Disabled bool CruxAddr string + Disabled bool } // SettingsFile will be read/written as this struct @@ -83,32 +82,32 @@ type SettingsFile struct { // Options are "globals" for the SettingsFile struct type Options struct { - TimeZone string `yaml:"timezone" env-default:"UTC"` - CruxAgentGrpcPort uint `yaml:"crux-agentgrpc-port" env-default:"5000"` - CruxHTTPPort uint `yaml:"crux-http-port" env-default:"1848"` - CruxUIPort uint `yaml:"crux-ui-port" env-default:"3000"` + KratosPostgresUser string `yaml:"kratosPostgresUser" env-default:"kratos"` + KratosPostgresPassword string `yaml:"kratosPostgresPassword"` + TraefikDockerSocket string `yaml:"traefikDockerSocket" env-default:"/var/run/docker.sock"` + MailFromName string `yaml:"mailFromName" env-default:"dyrector.io - Platform"` CruxSecret string `yaml:"crux-secret"` CruxEncryptionKey string `yaml:"crux-encryption-key"` - CruxPostgresPort uint `yaml:"cruxPostgresPort" env-default:"5432"` + KratosSecret string `yaml:"kratosSecret"` CruxPostgresDB string `yaml:"cruxPostgresDB" env-default:"crux"` CruxPostgresUser string `yaml:"cruxPostgresUser" env-default:"crux"` CruxPostgresPassword string `yaml:"cruxPostgresPassword"` + TimeZone string `yaml:"timezone" env-default:"UTC"` + KratosPostgresDB string `yaml:"kratosPostgresDB" env-default:"kratos"` + MailFromEmail string `yaml:"mailFromEmail" env-default:"noreply@example.com"` TraefikWebPort uint `yaml:"traefikWebPort" env-default:"8000"` - TraefikUIPort uint `yaml:"traefikUIPort" env-default:"8080"` - TraefikDockerSocket string `yaml:"traefikDockerSocket" env-default:"/var/run/docker.sock"` - TraefikIsDockerSocketNamedPipe bool `yaml:"traefikIsDockerSocketNamedPipe" env-default:"false"` - KratosAdminPort uint `yaml:"kratosAdminPort" env-default:"4434"` + CruxUIPort uint `yaml:"crux-ui-port" env-default:"3000"` KratosPublicPort uint `yaml:"kratosPublicPort" env-default:"4433"` KratosPostgresPort uint `yaml:"kratosPostgresPort" env-default:"5433"` - KratosPostgresDB string `yaml:"kratosPostgresDB" env-default:"kratos"` - KratosPostgresUser string `yaml:"kratosPostgresUser" env-default:"kratos"` - KratosPostgresPassword string `yaml:"kratosPostgresPassword"` - KratosSecret string `yaml:"kratosSecret"` - MailSlurperSMTPPort uint `yaml:"mailSlurperSMTPPort" env-default:"1025"` + TraefikUIPort uint `yaml:"traefikUIPort" env-default:"8080"` + CruxHTTPPort uint `yaml:"crux-http-port" env-default:"1848"` + CruxAgentGrpcPort uint `yaml:"crux-agentgrpc-port" env-default:"5000"` MailSlurperUIPort uint `yaml:"mailSlurperUIPort" env-default:"4436"` + MailSlurperSMTPPort uint `yaml:"mailSlurperSMTPPort" env-default:"1025"` + CruxPostgresPort uint `yaml:"cruxPostgresPort" env-default:"5432"` MailSlurperAPIPort uint `yaml:"mailSlurperAPIPort" env-default:"4437"` - MailFromName string `yaml:"mailFromName" env-default:"dyrector.io - Platform"` - MailFromEmail string `yaml:"mailFromEmail" env-default:"noreply@example.com"` + KratosAdminPort uint `yaml:"kratosAdminPort" env-default:"4434"` + TraefikIsDockerSocketNamedPipe bool `yaml:"traefikIsDockerSocketNamedPipe" env-default:"false"` } const ( @@ -131,14 +130,15 @@ const ( func SettingsExists(settingsPath string) bool { settingsFilePath := SettingsFileLocation(settingsPath) - if _, err := os.Stat(settingsFilePath); err == nil { + _, err := os.Stat(settingsFilePath) + if err == nil { return true - } else if errors.Is(err, os.ErrNotExist) { - return false - } else { - log.Fatal().Err(err).Stack().Send() + } + if errors.Is(err, os.ErrNotExist) { return false } + log.Fatal().Err(err).Stack().Send() + return false } // SettingsFileLocation is assembling the location of the settings file diff --git a/golang/pkg/cli/container_defaults.go b/golang/pkg/cli/container_defaults.go index 6ade00bc8..5dac3f72b 100644 --- a/golang/pkg/cli/container_defaults.go +++ b/golang/pkg/cli/container_defaults.go @@ -13,6 +13,7 @@ import ( "time" "github.com/AlekSi/pointer" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" "github.com/docker/docker/client" "github.com/rs/zerolog/log" @@ -62,7 +63,7 @@ func GetCrux(state *State, args *ArgsFlags) containerbuilder.Builder { crux := baseContainer(state.Ctx, args). WithImage(fmt.Sprintf("%s:%s", state.Crux.Image, state.SettingsFile.Version)). WithName(state.Containers.Crux.Name). - WithRestartPolicy(containerbuilder.AlwaysRestartPolicy). + WithRestartPolicy(container.RestartPolicyAlways). WithEnv(getCruxEnvs(state, args)). WithNetworks([]string{state.SettingsFile.Network}). WithNetworkAliases(state.Containers.Crux.Name). @@ -110,8 +111,8 @@ func getCruxInitContainer(state *State, args *ArgsFlags) containerbuilder.Lifecy fmt.Sprintf("ENCRYPTION_SECRET_KEY=%s", state.SettingsFile.CruxEncryptionKey), }, state.EnvFile...) - return func(ctx context.Context, client client.APIClient, - parentCont containerbuilder.ParentContainer, + return func(ctx context.Context, _ client.APIClient, + _ containerbuilder.ParentContainer, ) error { cruxMigrate := baseContainer(ctx, args). WithImage(fmt.Sprintf("%s:%s", state.Crux.Image, state.SettingsFile.Version)). @@ -213,7 +214,7 @@ func GetCruxUI(state *State, args *ArgsFlags) containerbuilder.Builder { cruxUI := baseContainer(state.Ctx, args). WithImage(fmt.Sprintf("%s:%s", state.CruxUI.Image, state.SettingsFile.Version)). WithName(state.Containers.CruxUI.Name). - WithRestartPolicy(containerbuilder.AlwaysRestartPolicy). + WithRestartPolicy(container.RestartPolicyAlways). WithEnv(envs). WithNetworks([]string{state.SettingsFile.Network}). WithNetworkAliases(state.Containers.CruxUI.Name). @@ -281,7 +282,7 @@ func GetTraefik(state *State, args *ArgsFlags) containerbuilder.Builder { traefik := baseContainer(state.Ctx, args). WithImage("docker.io/library/traefik:v2.9"). WithName(state.Containers.Traefik.Name). - WithRestartPolicy(containerbuilder.AlwaysRestartPolicy). + WithRestartPolicy(container.RestartPolicyAlways). WithNetworks([]string{state.SettingsFile.Network}). WithNetworkAliases(state.Containers.Traefik.Name). WithMountPoints([]mount.Mount{{ @@ -296,7 +297,7 @@ func GetTraefik(state *State, args *ArgsFlags) containerbuilder.Builder { label.DyrectorioOrg + label.ContainerPrefix: args.Prefix, label.DyrectorioOrg + label.ServiceCategory: label.GetHiddenServiceCategory("internal"), }). - WithPostStartHooks(func(ctx context.Context, client client.APIClient, + WithPostStartHooks(func(ctx context.Context, _ client.APIClient, cont containerbuilder.ParentContainer, ) error { return CopyTraefikConfiguration( @@ -311,8 +312,8 @@ func GetTraefik(state *State, args *ArgsFlags) containerbuilder.Builder { if args.FullyContainerized { traefikHost := state.Containers.Traefik.Name traefik. - WithPostStartHooks(func(ctx context.Context, client client.APIClient, - cont containerbuilder.ParentContainer, + WithPostStartHooks(func(ctx context.Context, _ client.APIClient, + _ containerbuilder.ParentContainer, ) error { addr := fmt.Sprintf("http://%s:%d/api/status", traefikHost, state.SettingsFile.TraefikWebPort) return healthProbe(ctx, addr) @@ -338,7 +339,7 @@ func GetKratos(state *State, args *ArgsFlags) containerbuilder.Builder { kratos := baseContainer(state.Ctx, args). WithImage(fmt.Sprintf("%s:%s", state.Kratos.Image, state.SettingsFile.Version)). WithName(state.Containers.Kratos.Name). - WithRestartPolicy(containerbuilder.AlwaysRestartPolicy). + WithRestartPolicy(container.RestartPolicyAlways). WithEnv(getKratosEnvs(state)). WithNetworks([]string{state.SettingsFile.Network}). WithNetworkAliases(state.Containers.Kratos.Name). @@ -385,7 +386,7 @@ func getKratosInitContainer(state *State, args *ArgsFlags) containerbuilder.Life state.SettingsFile.KratosPostgresDB), }, state.EnvFile...) - return func(ctx context.Context, client client.APIClient, parentCont containerbuilder.ParentContainer) error { + return func(_ context.Context, _ client.APIClient, _ containerbuilder.ParentContainer) error { kratosMigrate := baseContainer(state.Ctx, args). WithImage(fmt.Sprintf("%s:%s", state.Kratos.Image, state.SettingsFile.Version)). WithName(state.Containers.KratosMigrate.Name). @@ -452,7 +453,7 @@ func GetMailSlurper(state *State, args *ArgsFlags) containerbuilder.Builder { mailslurper := baseContainer(state.Ctx, args). WithImage(mailSlurperImage). WithName(state.Containers.MailSlurper.Name). - WithRestartPolicy(containerbuilder.AlwaysRestartPolicy). + WithRestartPolicy(container.RestartPolicyAlways). WithNetworks([]string{state.SettingsFile.Network}). WithNetworkAliases(state.Containers.MailSlurper.Name). WithLabels(map[string]string{ @@ -563,7 +564,7 @@ func getBasePostgres(state *State, args *ArgsFlags) containerbuilder.Builder { basePostgres := baseContainer(state.Ctx, args). WithImage(postgresImage). WithNetworks([]string{state.SettingsFile.Network}). - WithRestartPolicy(containerbuilder.AlwaysRestartPolicy) + WithRestartPolicy(container.RestartPolicyAlways) return basePostgres } diff --git a/golang/pkg/crane/config/config.go b/golang/pkg/crane/config/config.go index 5bb1f98e0..cf1c5fc8b 100644 --- a/golang/pkg/crane/config/config.go +++ b/golang/pkg/crane/config/config.go @@ -8,17 +8,16 @@ import ( // Crane(kubernetes)-specific configuration options type Configuration struct { + FieldManagerName string `yaml:"fieldManagerName" env:"FIELD_MANAGER_NAME" env-default:"crane-dyrector-io"` + KeyIssuer string `yaml:"keyIssuer" env:"KEY_ISSUER" env-default:"co.dyrector.io/issuer"` + KubeConfig string `yaml:"kubeConfig" env:"KUBECONFIG" env-default:""` + OwnDeployment string `yaml:"ownDeployment" env:"CRANE_DEPLOYMENT_NAME"` + OwnNamespace string `yaml:"ownNamespace" env:"CRANE_DEPLOYMENT_NAMESPACE"` + SecretName string `yaml:"secretName" env:"SECRET_NAME" env-default:"dyrectorio-secret"` + Namespace string `yaml:"namespace" env:"SECRET_NAMESPACE" env-default:"dyrectorio"` config.CommonConfiguration - CraneInCluster bool `yaml:"craneInCluster" env:"CRANE_IN_CLUSTER" env-default:"false"` DefaultKubeTimeout time.Duration `yaml:"defaultKubeTimeout" env:"DEFAULT_KUBE_TIMEOUT" env-default:"2m"` - FieldManagerName string `yaml:"fieldManagerName" env:"FIELD_MANAGER_NAME" env-default:"crane-dyrector-io"` - ForceOnConflicts bool `yaml:"forceOnConflicts" env:"FORCE_ON_CONFLICTS" env-default:"true"` - KeyIssuer string `yaml:"keyIssuer" env:"KEY_ISSUER" env-default:"co.dyrector.io/issuer"` - KubeConfig string `yaml:"kubeConfig" env:"KUBECONFIG" env-default:""` TestTimeoutDuration time.Duration `yaml:"testTimeout" env:"TEST_TIMEOUT" env-default:"15s"` - OwnDeployment string `yaml:"ownDeployment" env:"CRANE_DEPLOYMENT_NAME"` - OwnNamespace string `yaml:"ownNamespace" env:"CRANE_DEPLOYMENT_NAMESPACE"` - // for injecting SecretPrivateKey - SecretName string `yaml:"secretName" env:"SECRET_NAME" env-default:"dyrectorio-secret"` - Namespace string `yaml:"namespace" env:"SECRET_NAMESPACE" env-default:"dyrectorio"` + CraneInCluster bool `yaml:"craneInCluster" env:"CRANE_IN_CLUSTER" env-default:"false"` + ForceOnConflicts bool `yaml:"forceOnConflicts" env:"FORCE_ON_CONFLICTS" env-default:"true"` } diff --git a/golang/pkg/crane/k8s/configmap.go b/golang/pkg/crane/k8s/configmap.go index 5598543eb..ebe49f81c 100644 --- a/golang/pkg/crane/k8s/configmap.go +++ b/golang/pkg/crane/k8s/configmap.go @@ -17,9 +17,9 @@ import ( // facade object for configmap management type configmap struct { ctx context.Context + appConfig *config.Configuration status string avail []string - appConfig *config.Configuration } func newConfigmap(ctx context.Context, cfg *config.Configuration) *configmap { @@ -76,7 +76,6 @@ func (cm *configmap) deployConfigMapRuntime(runtimeType v1.RuntimeConfigType, na return err } err = cm.deployConfigMapData(namespace, fmt.Sprintf("%v-%v", containerName, runtimeType), envList) - if err != nil { return err } diff --git a/golang/pkg/crane/k8s/delete_facade.go b/golang/pkg/crane/k8s/delete_facade.go index dfaa71542..97e4e6495 100644 --- a/golang/pkg/crane/k8s/delete_facade.go +++ b/golang/pkg/crane/k8s/delete_facade.go @@ -15,7 +15,6 @@ import ( type DeleteFacade struct { ctx context.Context - name string deployment *Deployment namespace *Namespace service *Service @@ -23,6 +22,7 @@ type DeleteFacade struct { ingress *ingress pvc *PVC appConfig *config.Configuration + name string } func NewDeleteFacade(ctx context.Context, namespace, name string, cfg *config.Configuration) *DeleteFacade { diff --git a/golang/pkg/crane/k8s/deploy_facade.go b/golang/pkg/crane/k8s/deploy_facade.go index 7e5193296..5a922417d 100644 --- a/golang/pkg/crane/k8s/deploy_facade.go +++ b/golang/pkg/crane/k8s/deploy_facade.go @@ -19,28 +19,28 @@ import ( type DeployFacade struct { ctx context.Context - params *DeployFacadeParams + secret *Secret client *Client - image string deployment *Deployment namespace *Namespace service *Service configmap *configmap ingress *ingress - secret *Secret + params *DeployFacadeParams pvc *PVC ServiceMonitor *ServiceMonitor appConfig *config.Configuration + image string } type DeployFacadeParams struct { Ctx context.Context - Image string - InstanceConfig v1.InstanceConfig - ContainerConfig v1.ContainerConfig RuntimeConfig *string imagePullSecrets *imageHelper.RegistryAuth + Image string Issuer string + InstanceConfig v1.InstanceConfig + ContainerConfig v1.ContainerConfig } func NewDeployFacade(params *DeployFacadeParams, cfg *config.Configuration) *DeployFacade { diff --git a/golang/pkg/crane/k8s/deployment.go b/golang/pkg/crane/k8s/deployment.go index 0aea3dabe..235465183 100644 --- a/golang/pkg/crane/k8s/deployment.go +++ b/golang/pkg/crane/k8s/deployment.go @@ -39,8 +39,8 @@ var ErrPodHasNoOwner = errors.New("pod has no owner") // facade object for Deployment management type Deployment struct { ctx context.Context - status string appConfig *config.Configuration + status string } func NewDeployment(ctx context.Context, cfg *config.Configuration) *Deployment { @@ -48,19 +48,19 @@ func NewDeployment(ctx context.Context, cfg *config.Configuration) *Deployment { } type DeploymentParams struct { - namespace string - image string + volumes map[string]v1.Volume + annotations map[string]string containerConfig *v1.ContainerConfig + labels map[string]string pullSecretName string - configMapsEnv []string + namespace string + image string + issuer string secrets []string - volumes map[string]v1.Volume + configMapsEnv []string portList []builder.PortBinding command []string args []string - labels map[string]string - annotations map[string]string - issuer string } func (d *Deployment) DeployDeployment(p *DeploymentParams) error { @@ -74,7 +74,7 @@ func (d *Deployment) DeployDeployment(p *DeploymentParams) error { annot := map[string]string{} - if len(p.issuer) > 0 { + if p.issuer != "" { annot[d.appConfig.KeyIssuer] = p.issuer } diff --git a/golang/pkg/crane/k8s/ingress.go b/golang/pkg/crane/k8s/ingress.go index 203c367a2..6742fc1b7 100644 --- a/golang/pkg/crane/k8s/ingress.go +++ b/golang/pkg/crane/k8s/ingress.go @@ -23,20 +23,26 @@ import ( // facade object for ingress management type ingress struct { ctx context.Context - status string client *Client appConfig *config.Configuration + status string } type DeployIngressOptions struct { - namespace, containerName, ingressName, ingressHost, ingressPath, uploadLimit string - stripPrefix bool - port uint16 - portList []int32 - tls, proxyHeaders bool - customHeaders []string - labels map[string]string - annotations map[string]string + annotations map[string]string + labels map[string]string + containerName string + ingressName string + ingressHost string + ingressPath string + uploadLimit string + namespace string + customHeaders []string + portList []int32 + port uint16 + stripPrefix bool + proxyHeaders bool + tls bool } func newIngress(ctx context.Context, client *Client) *ingress { diff --git a/golang/pkg/crane/k8s/log.go b/golang/pkg/crane/k8s/log.go index 69b5aef5a..d29785a7c 100644 --- a/golang/pkg/crane/k8s/log.go +++ b/golang/pkg/crane/k8s/log.go @@ -26,11 +26,10 @@ import ( const LogBufferSize = 2048 type KubeContainerLogReader struct { + grpc.ContainerLogReader EventChannel chan grpc.ContainerLogEvent - LogStreams []io.ReadCloser ErrorGroup *errgroup.Group - - grpc.ContainerLogReader + LogStreams []io.ReadCloser } func (kubeReader *KubeContainerLogReader) Next() <-chan grpc.ContainerLogEvent { diff --git a/golang/pkg/crane/k8s/namespace.go b/golang/pkg/crane/k8s/namespace.go index 7968e0749..ac35accb5 100644 --- a/golang/pkg/crane/k8s/namespace.go +++ b/golang/pkg/crane/k8s/namespace.go @@ -19,8 +19,8 @@ import ( type Namespace struct { ctx context.Context client *Client - name string appConfig *config.Configuration + name string } // namespace entity @@ -74,7 +74,6 @@ func (n *Namespace) DeployNamespace(name string) error { FieldManager: n.appConfig.FieldManagerName, Force: n.appConfig.ForceOnConflicts, }) - if err != nil { return err } diff --git a/golang/pkg/crane/k8s/pvc.go b/golang/pkg/crane/k8s/pvc.go index a0f2efbe0..87b82e7e0 100644 --- a/golang/pkg/crane/k8s/pvc.go +++ b/golang/pkg/crane/k8s/pvc.go @@ -19,10 +19,10 @@ import ( type PVC struct { ctx context.Context client *Client - status string requested map[string]v1.Volume avail map[string]v1.Volume appConfig *config.Configuration + status string } func NewPVC(ctx context.Context, client *Client) *PVC { diff --git a/golang/pkg/crane/k8s/secret.go b/golang/pkg/crane/k8s/secret.go index 31ef8e455..69db5f9a7 100644 --- a/golang/pkg/crane/k8s/secret.go +++ b/golang/pkg/crane/k8s/secret.go @@ -43,8 +43,8 @@ type DockerConfigEntry struct { type Secret struct { ctx context.Context client *Client - avail []string appConfig *config.Configuration + avail []string } func NewSecret(ctx context.Context, client *Client) *Secret { diff --git a/golang/pkg/crane/k8s/service.go b/golang/pkg/crane/k8s/service.go index aa3e69611..e8bc54d9a 100644 --- a/golang/pkg/crane/k8s/service.go +++ b/golang/pkg/crane/k8s/service.go @@ -24,10 +24,10 @@ import ( type Service struct { ctx context.Context client *Client + appConfig *config.Configuration status string portsBound []int32 portNames []string - appConfig *config.Configuration } func NewService(ctx context.Context, client *Client) *Service { @@ -35,15 +35,15 @@ func NewService(ctx context.Context, client *Client) *Service { } type ServiceParams struct { + LBAnnotations map[string]string + labels map[string]string + annotations map[string]string namespace string name string selector string portBindings []builder.PortBinding portRanges []builder.PortRangeBinding useLB bool - LBAnnotations map[string]string - labels map[string]string - annotations map[string]string } func (s *Service) DeployService(params *ServiceParams) error { diff --git a/golang/pkg/crane/k8s/volume_helper.go b/golang/pkg/crane/k8s/volume_helper.go index 0716f9294..f2c1a81f0 100644 --- a/golang/pkg/crane/k8s/volume_helper.go +++ b/golang/pkg/crane/k8s/volume_helper.go @@ -20,7 +20,7 @@ func mapShortNotationToVolumeMap(volumes []string) map[string]v1.Volume { var volumeType v1.VolumeType - if len(name) > 0 { + if name != "" { switch name[0] { case '@': volumeType = v1.ReadWriteManyVolumeType diff --git a/golang/pkg/dagent/config/config.go b/golang/pkg/dagent/config/config.go index ce02cafa7..364aea786 100644 --- a/golang/pkg/dagent/config/config.go +++ b/golang/pkg/dagent/config/config.go @@ -6,20 +6,19 @@ import ( // Dagent(docker)-specific configuration options type Configuration struct { - config.CommonConfiguration - DataMountPath string `yaml:"dataMountPath" env:"DATA_MOUNT_PATH" env-default:"/srv/dagent"` + WebhookToken string `yaml:"webhookToken" env:"WEBHOOK_TOKEN" env-default:""` + TraefikAcmeMail string `yaml:"traefikAcmeMail" env:"TRAEFIK_ACME_MAIL" env-default:""` HostDockerSockPath string `yaml:"hostDockerSockPath" env:"HOST_DOCKER_SOCK_PATH" env-default:"/var/run/docker.sock"` InternalMountPath string `yaml:"internalMountPath" env:"INTERNAL_MOUNT_PATH" env-default:"/srv/dagent"` - LogDefaultSkip uint64 `yaml:"logDefaultSkip" env:"LOG_DEFAULT_SKIP" env-default:"0"` - LogDefaultTake uint64 `yaml:"logDefaultTake" env:"LOG_DEFAULT_TAKE" env-default:"100"` - TraefikAcmeMail string `yaml:"traefikAcmeMail" env:"TRAEFIK_ACME_MAIL" env-default:""` - TraefikEnabled bool `yaml:"traefikEnabled" env:"TRAEFIK_ENABLED" env-default:"false"` - // set to "DEBUG" to access the Traefik dashboard - TraefikLogLevel string `yaml:"traefikLogLevel" env:"TRAEFIK_LOG_LEVEL" env-default:"INFO"` - TraefikTLS bool `yaml:"traefikTLS" env:"TRAEFIK_TLS" env-default:"false"` - TraefikPort uint16 `yaml:"traefikPort" env:"TRAEFIK_PORT" env-default:"80"` - TraefikTLSPort uint16 `yaml:"traefikTLSPort" env:"TRAEFIK_TLS_PORT" env-default:"443"` - WebhookToken string `yaml:"webhookToken" env:"WEBHOOK_TOKEN" env-default:""` + DataMountPath string `yaml:"dataMountPath" env:"DATA_MOUNT_PATH" env-default:"/srv/dagent"` + TraefikLogLevel string `yaml:"traefikLogLevel" env:"TRAEFIK_LOG_LEVEL" env-default:"INFO"` + config.CommonConfiguration + LogDefaultSkip uint64 `yaml:"logDefaultSkip" env:"LOG_DEFAULT_SKIP" env-default:"0"` + LogDefaultTake uint64 `yaml:"logDefaultTake" env:"LOG_DEFAULT_TAKE" env-default:"100"` + TraefikPort uint16 `yaml:"traefikPort" env:"TRAEFIK_PORT" env-default:"80"` + TraefikTLSPort uint16 `yaml:"traefikTLSPort" env:"TRAEFIK_TLS_PORT" env-default:"443"` + TraefikEnabled bool `yaml:"traefikEnabled" env:"TRAEFIK_ENABLED" env-default:"false"` + TraefikTLS bool `yaml:"traefikTLS" env:"TRAEFIK_TLS" env-default:"false"` } const filePermReadWriteOnlyByOwner = 0o600 diff --git a/golang/pkg/dagent/config/secret_test.go b/golang/pkg/dagent/config/secret_test.go index 3c24bf61c..c9d894ce9 100644 --- a/golang/pkg/dagent/config/secret_test.go +++ b/golang/pkg/dagent/config/secret_test.go @@ -12,6 +12,7 @@ import ( "github.com/dyrector-io/dyrectorio/golang/pkg/dagent/config" ) +//nolint:gosec const ( missingKeyFile = "missing-file.key" testPrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- diff --git a/golang/pkg/dagent/update/update.go b/golang/pkg/dagent/update/update.go index 4a96a7bb7..9c51dd640 100644 --- a/golang/pkg/dagent/update/update.go +++ b/golang/pkg/dagent/update/update.go @@ -17,7 +17,7 @@ import ( "github.com/dyrector-io/dyrectorio/golang/pkg/dagent/utils" "github.com/dyrector-io/dyrectorio/protobuf/go/agent" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" "github.com/docker/docker/client" "github.com/docker/docker/errdefs" @@ -36,11 +36,11 @@ func getUniqueContainerName(ctx context.Context, cli client.APIClient, base stri count := 0 for { - container, err := docker.GetContainerByName(ctx, cli, name) + cont, err := docker.GetContainerByName(ctx, cli, name) if err != nil { return base, err } - if container == nil { + if cont == nil { return name, nil } @@ -91,7 +91,7 @@ func createNewDAgentContainer(ctx context.Context, cli client.APIClient, oldCont builder := containerbuilder.NewDockerBuilder(ctx). WithClient(cli). WithImage(imageWithTag). - WithRestartPolicy(containerbuilder.RestartPolicyName(inspect.HostConfig.RestartPolicy.Name)). + WithRestartPolicy(inspect.HostConfig.RestartPolicy.Name). WithName(name). WithEnv(inspect.Config.Env). WithMountPoints(mounts) @@ -125,12 +125,12 @@ func GetSelfContainerName(ctx context.Context) (string, error) { return "", err } - container, err := utils.GetOwnContainer(ctx, cli) + cont, err := utils.GetOwnContainer(ctx, cli) if err != nil { return "", err } - name := container.Names[0] + name := cont.Names[0] return name, nil } @@ -146,12 +146,12 @@ func ExecuteSelfUpdate(ctx context.Context, cli client.APIClient, command *agent return nil } - container, err := utils.GetOwnContainer(ctx, cli) + cont, err := utils.GetOwnContainer(ctx, cli) if err != nil { return err } - newImage, err := image.ExpandImageNameWithTag(container.Image, tag) + newImage, err := image.ExpandImageNameWithTag(cont.Image, tag) if err != nil { return err } @@ -177,7 +177,7 @@ func ExecuteSelfUpdate(ctx context.Context, cli client.APIClient, command *agent log.Warn().Msg("Updating matching image tags") } - originalName := container.Names[0] + originalName := cont.Names[0] rename, err := getUniqueContainerName(ctx, cli, originalName+"-update") if err != nil { @@ -186,14 +186,14 @@ func ExecuteSelfUpdate(ctx context.Context, cli client.APIClient, command *agent log.Debug().Str("oldName", originalName).Str("newName", rename).Msg("Renaming DAgent container") - err = cli.ContainerRename(ctx, container.ID, rename) + err = cli.ContainerRename(ctx, cont.ID, rename) if err != nil { return err } - err = createNewDAgentContainer(ctx, cli, container.ID, originalName, newImage) + err = createNewDAgentContainer(ctx, cli, cont.ID, originalName, newImage) if err != nil { - renameErr := cli.ContainerRename(ctx, container.ID, originalName) + renameErr := cli.ContainerRename(ctx, cont.ID, originalName) if renameErr != nil { return fmt.Errorf("%s (%s)", err.Error(), renameErr.Error()) } @@ -236,7 +236,7 @@ func RemoveSelf(ctx context.Context, options grpc.UpdateOptions) error { return err } - err = cli.ContainerRemove(ctx, self.ID, types.ContainerRemoveOptions{ + err = cli.ContainerRemove(ctx, self.ID, container.RemoveOptions{ Force: true, }) if err != nil { diff --git a/golang/pkg/dagent/update/update_test.go b/golang/pkg/dagent/update/update_test.go index f49b9a000..3cb06b3fa 100644 --- a/golang/pkg/dagent/update/update_test.go +++ b/golang/pkg/dagent/update/update_test.go @@ -13,11 +13,12 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "github.com/docker/docker/errdefs" "github.com/dyrector-io/dyrectorio/golang/internal/grpc" - "github.com/dyrector-io/dyrectorio/golang/internal/helper/image" + imagehelper "github.com/dyrector-io/dyrectorio/golang/internal/helper/image" "github.com/dyrector-io/dyrectorio/golang/pkg/dagent/update" "github.com/dyrector-io/dyrectorio/protobuf/go/agent" v1 "github.com/opencontainers/image-spec/specs-go/v1" @@ -68,7 +69,7 @@ func NewMockClient(opts ...MockClientOpt) client.APIClient { return cli } -func (d *DockerClientMock) ContainerList(ctx context.Context, opts types.ContainerListOptions) ([]types.Container, error) { +func (d *DockerClientMock) ContainerList(ctx context.Context, opts container.ListOptions) ([]types.Container, error) { imgs := []types.Container{} if len(opts.Filters.Get("name")) > 0 && opts.Filters.Get("name")[0] == "^own-container-update$" { @@ -85,11 +86,11 @@ func (d *DockerClientMock) ContainerList(ctx context.Context, opts types.Contain return imgs, nil } -func (d *DockerClientMock) ImageList(ctx context.Context, opts types.ImageListOptions) ([]types.ImageSummary, error) { +func (d *DockerClientMock) ImageList(ctx context.Context, opts types.ImageListOptions) ([]image.Summary, error) { if d.denyImageExistence { - return []types.ImageSummary{}, nil + return []image.Summary{}, nil } - return []types.ImageSummary{ + return []image.Summary{ {}, }, nil } @@ -128,7 +129,7 @@ func (d *DockerClientMock) ContainerCreate(context.Context, *container.Config, * return container.CreateResponse{}, nil } -func (d *DockerClientMock) ContainerStart(context.Context, string, types.ContainerStartOptions) error { +func (d *DockerClientMock) ContainerStart(context.Context, string, container.StartOptions) error { return nil } @@ -136,7 +137,7 @@ type DockerClientErrMock struct { client.APIClient } -func (d *DockerClientErrMock) ContainerList(ctx context.Context, opts types.ContainerListOptions) ([]types.Container, error) { +func (d *DockerClientErrMock) ContainerList(ctx context.Context, opts container.ListOptions) ([]types.Container, error) { return []types.Container{{}}, errors.New("test error") } @@ -164,7 +165,7 @@ func TestSelfUpdateInvalid(t *testing.T) { Tag: "-----", TimeoutSeconds: 1, }, defaultUpdateOptions) - assert.ErrorIs(t, update.RewriteUpdateErrors(err), image.ErrInvalidTag) + assert.ErrorIs(t, update.RewriteUpdateErrors(err), imagehelper.ErrInvalidTag) } func TestSelfUpdateOK(t *testing.T) { diff --git a/golang/pkg/dagent/utils/container.go b/golang/pkg/dagent/utils/container.go index a0a7ad83c..7f715de6e 100644 --- a/golang/pkg/dagent/utils/container.go +++ b/golang/pkg/dagent/utils/container.go @@ -25,16 +25,11 @@ const ( ) type TraefikDeployRequest struct { - // LogLevel defaults to INFO LogLevel string `json:"logLevel"` - // if services exposed with certs, default: false - TLS bool `json:"TLS"` - // the email address for expiry notifications, sent by acme AcmeMail string `json:"acmeMail" binding:"required_if=TLS true"` - // HTTP port - Port uint16 `json:"port"` - // HTTPS port - TLSPort uint16 `json:"tlsPort"` + Port uint16 `json:"port"` + TLSPort uint16 `json:"tlsPort"` + TLS bool `json:"TLS"` } type UnknownContainerError struct{} @@ -109,7 +104,7 @@ func ExecTraefik(ctx context.Context, traefikDeployReq TraefikDeployRequest, cfg builder := containerbuilder.NewDockerBuilder(ctx).WithImage("index.docker.io/library/traefik:v2.8.0"). WithName("traefik"). WithMountPoints(mounts). - WithRestartPolicy(containerbuilder.AlwaysRestartPolicy). + WithRestartPolicy(container.RestartPolicyAlways). WithAutoRemove(false). WithoutConflict(). WithNetworkMode("host"). diff --git a/golang/pkg/dagent/utils/docker.go b/golang/pkg/dagent/utils/docker.go index 1a910bf33..4bd0fbedf 100644 --- a/golang/pkg/dagent/utils/docker.go +++ b/golang/pkg/dagent/utils/docker.go @@ -33,14 +33,14 @@ import ( "github.com/dyrector-io/dyrectorio/golang/internal/logdefer" "github.com/dyrector-io/dyrectorio/golang/internal/mapper" "github.com/dyrector-io/dyrectorio/golang/internal/util" - "github.com/dyrector-io/dyrectorio/golang/pkg/builder/container" + dockerbuilder "github.com/dyrector-io/dyrectorio/golang/pkg/builder/container" "github.com/dyrector-io/dyrectorio/golang/pkg/dagent/caps" "github.com/dyrector-io/dyrectorio/golang/pkg/dagent/config" "github.com/dyrector-io/dyrectorio/protobuf/go/agent" "github.com/dyrector-io/dyrectorio/protobuf/go/common" "github.com/docker/docker/api/types" - dockerContainer "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/mount" "github.com/docker/docker/client" @@ -70,7 +70,7 @@ func GetContainerLogs(name string, skip, take uint) []string { const BASE = 10 tail := skip + take - options := types.ContainerLogsOptions{ + options := container.LogsOptions{ ShowStderr: true, ShowStdout: true, Tail: strconv.FormatUint(uint64(tail), BASE), @@ -82,7 +82,7 @@ func GetContainerLogs(name string, skip, take uint) []string { } defer logdefer.LogDeferredErr(logs.Close, log.Warn(), "error closing container log reader") - return container.ReadDockerLogsFromReadCloser(logs, int(skip), int(take)) + return dockerbuilder.ReadDockerLogsFromReadCloser(logs, int(skip), int(take)) } func CopyToContainer(ctx context.Context, name string, meta v1.UploadFileData, fileHeader *multipart.FileHeader) error { @@ -396,7 +396,7 @@ func DeployImage(ctx context.Context, } } - builder := container.NewDockerBuilder(ctx) + builder := dockerbuilder.NewDockerBuilder(ctx) networkMode, networks := setNetwork(deployImageRequest) labels, err := setImageLabels(expandedImageName, deployImageRequest, cfg) if err != nil { @@ -469,14 +469,14 @@ func setNetwork(deployImageRequest *v1.DeployImageRequest) (networkMode string, return networkMode, deployImageRequest.ContainerConfig.Networks } -func WithInitContainers(dc container.Builder, containerConfig *v1.ContainerConfig, +func WithInitContainers(dc dockerbuilder.Builder, containerConfig *v1.ContainerConfig, dog *dogger.DeploymentLogger, envMap map[string]string, cfg *config.Configuration, ) { - initFuncs := []container.LifecycleFunc{} + initFuncs := []dockerbuilder.LifecycleFunc{} if containerConfig.ImportContainer != nil { initFuncs = append(initFuncs, func(ctx context.Context, client client.APIClient, - parentCont container.ParentContainer, + parentCont dockerbuilder.ParentContainer, ) error { if initError := spawnImportContainer(ctx, client, parentCont.Name, parentCont.MountList, containerConfig.ImportContainer, dog, cfg); initError != nil { @@ -490,7 +490,7 @@ func WithInitContainers(dc container.Builder, containerConfig *v1.ContainerConfi if len(containerConfig.InitContainers) > 0 { initFuncs = append(initFuncs, func(ctx context.Context, client client.APIClient, - parentCont container.ParentContainer, + parentCont dockerbuilder.ParentContainer, ) error { for i := range containerConfig.InitContainers { var initContConfig *InitContainerConfig @@ -566,7 +566,7 @@ func mountStrToDocker(mountIn []string, containerPreName, containerName string, mountStr := mountIn[i] if strings.ContainsRune(mountStr, '|') { mountSplit := strings.Split(mountStr, "|") - if len(mountSplit[0]) > 0 && len(mountSplit[1]) > 0 { + if mountSplit[0] != "" && mountSplit[1] != "" { containerPath := path.Join(cfg.InternalMountPath, containerPreName, containerName, mountSplit[0]) hostPath := "" if strings.HasPrefix(mountSplit[0], "/") { @@ -593,7 +593,7 @@ func mountStrToDocker(mountIn []string, containerPreName, containerName string, func createRuntimeConfigFileOnHost(mounts []mount.Mount, containerName, containerPreName, runtimeConfig string, cfg *config.Configuration, ) ([]mount.Mount, error) { - if len(runtimeConfig) > 0 { + if runtimeConfig != "" { configDir := path.Join(cfg.InternalMountPath, containerPreName, containerName, "config") _, err := os.Stat(configDir) if os.IsNotExist(err) { @@ -713,7 +713,7 @@ func SecretList(ctx context.Context, prefix, name string) ([]string, error) { return nil, err } - containers, err := cli.ContainerList(ctx, types.ContainerListOptions{ + containers, err := cli.ContainerList(ctx, container.ListOptions{ All: true, Filters: filters.NewArgs(filters.KeyValuePair{Key: "name", Value: fmt.Sprintf("^/?%s-%s$", prefix, name)}), }) @@ -756,11 +756,11 @@ func ContainerCommand(ctx context.Context, command *common.ContainerCommandReque } if operation == common.ContainerOperation_START_CONTAINER { - err = cli.ContainerStart(ctx, cont.ID, types.ContainerStartOptions{}) + err = cli.ContainerStart(ctx, cont.ID, container.StartOptions{}) } else if operation == common.ContainerOperation_STOP_CONTAINER { - err = cli.ContainerStop(ctx, cont.ID, dockerContainer.StopOptions{}) + err = cli.ContainerStop(ctx, cont.ID, container.StopOptions{}) } else if operation == common.ContainerOperation_RESTART_CONTAINER { - err = cli.ContainerRestart(ctx, cont.ID, dockerContainer.StopOptions{}) + err = cli.ContainerRestart(ctx, cont.ID, container.StopOptions{}) } else { log.Error().Str("operation", operation.String()).Str("prefix", prefix).Str("name", name).Msg("Unknown operation") } @@ -910,7 +910,7 @@ func ContainerLog(ctx context.Context, request *agent.ContainerLogRequest) (*grp eventChannel := make(chan grpc.ContainerLogEvent) - reader, err := cli.ContainerLogs(ctx, containerID, types.ContainerLogsOptions{ + reader, err := cli.ContainerLogs(ctx, containerID, container.LogsOptions{ ShowStderr: true, ShowStdout: true, Follow: streaming, diff --git a/golang/pkg/dagent/utils/dockerhelper.go b/golang/pkg/dagent/utils/dockerhelper.go index 0cc92c1dc..652a3a006 100644 --- a/golang/pkg/dagent/utils/dockerhelper.go +++ b/golang/pkg/dagent/utils/dockerhelper.go @@ -31,7 +31,7 @@ func getContainerIdentifierFromEvent(event *events.Message) *common.ContainerIde name, hasValue := event.Actor.Attributes["name"] if !hasValue { return nil - } else if len(prefix) > 0 { + } else if prefix != "" { name = strings.TrimPrefix(name, prefix+"-") } @@ -76,7 +76,7 @@ func messageToStateItem(ctx context.Context, prefix string, event *events.Messag return createRemovedState(containerID), nil } - containerState := mapper.MapDockerContainerEventToContainerState(event.Action) + containerState := mapper.MapDockerContainerEventToContainerState(string(event.Action)) // Ingored events are mapped to unspecified, for example tty, exec, oom, etc. if containerState == common.ContainerState_CONTAINER_STATE_UNSPECIFIED { return nil, nil diff --git a/golang/pkg/dagent/utils/dockerhelper_test.go b/golang/pkg/dagent/utils/dockerhelper_test.go index 337e4092e..900ae8e21 100644 --- a/golang/pkg/dagent/utils/dockerhelper_test.go +++ b/golang/pkg/dagent/utils/dockerhelper_test.go @@ -1,193 +1,194 @@ -//go:build unit -// +build unit - -package utils - -import ( - "context" - "testing" - - "github.com/docker/docker/api/types/events" - "github.com/dyrector-io/dyrectorio/golang/internal/label" - "github.com/stretchr/testify/assert" - - dockerHelper "github.com/dyrector-io/dyrectorio/golang/internal/helper/docker" - containerbuilder "github.com/dyrector-io/dyrectorio/golang/pkg/builder/container" - "github.com/dyrector-io/dyrectorio/protobuf/go/common" -) - -const nginxImage = "ghcr.io/dyrector-io/mirror/nginx:mainline-alpine" - -func TestGetContainerIdentifierFromEventEmptyAttributes(t *testing.T) { - event := events.Message{ - Actor: events.Actor{ - Attributes: map[string]string{}, - }, - } - - id := GetContainerIdentifierFromEvent(&event) - assert.Nil(t, id) -} - -func TestGetContainerIdentifierFromEventPrefix(t *testing.T) { - event := events.Message{ - Actor: events.Actor{ - Attributes: map[string]string{ - label.DyrectorioOrg + label.ContainerPrefix: "prefix", - }, - }, - } - - id := GetContainerIdentifierFromEvent(&event) - assert.Nil(t, id) -} - -func TestGetContainerIdentifierFromEventName(t *testing.T) { - event := events.Message{ - Actor: events.Actor{ - Attributes: map[string]string{ - "name": "prefix-name", - }, - }, - } - - id := GetContainerIdentifierFromEvent(&event) - assert.NotNil(t, id) - assert.Equal(t, "prefix-name", id.Name) - assert.Equal(t, "", id.Prefix) -} - -func TestGetContainerIdentifierFromEventBoth(t *testing.T) { - event := events.Message{ - Actor: events.Actor{ - Attributes: map[string]string{ - label.DyrectorioOrg + label.ContainerPrefix: "prefix", - "name": "prefix-name", - }, - }, - } - - id := GetContainerIdentifierFromEvent(&event) - assert.NotNil(t, id) - assert.Equal(t, "name", id.Name) - assert.Equal(t, "prefix", id.Prefix) -} - -func TestEventToMessageNonContainer(t *testing.T) { - event := events.Message{ - Type: "non-container", - } - - message, err := EventToMessage(context.Background(), "", &event) - assert.Nil(t, message) - assert.Nil(t, err) -} - -func TestEventToMessageNoName(t *testing.T) { - event := events.Message{ - Type: "container", - Actor: events.Actor{ - Attributes: map[string]string{}, - }, - } - - message, err := EventToMessage(context.Background(), "", &event) - assert.Nil(t, message) - assert.EqualError(t, err, "event has no container name") -} - -func TestEventToMessageActionDestroy(t *testing.T) { - event := events.Message{ - Type: "container", - Action: "destroy", - Actor: events.Actor{ - Attributes: map[string]string{ - "name": "prefix-name", - }, - }, - } - - message, err := EventToMessage(context.Background(), "", &event) - assert.NoError(t, err) - - expected := &common.ContainerStateItem{ - Id: &common.ContainerIdentifier{ - Name: "prefix-name", - Prefix: "", - }, - Command: "", - CreatedAt: nil, - State: common.ContainerState_REMOVED, - Reason: "", - Status: "", - Ports: []*common.ContainerStateItemPort{}, - ImageName: "", - ImageTag: "", - } - - assert.Equal(t, expected, message) -} - -func TestEventToMessageUnknownAction(t *testing.T) { - event := events.Message{ - Type: "container", - Action: "unknown-action", - Actor: events.Actor{ - Attributes: map[string]string{ - "name": "prefix-name", - }, - }, - } - - message, err := EventToMessage(context.Background(), "", &event) - assert.Nil(t, message) - assert.Nil(t, err) -} - -func TestEventToMessageNoContainer(t *testing.T) { - event := events.Message{ - Type: "container", - Action: "unknown-action", - Actor: events.Actor{ - ID: "container-id-does-not-exist", - Attributes: map[string]string{ - "name": "prefix-name", - }, - }, - } - - message, err := EventToMessage(context.Background(), "", &event) - assert.Nil(t, message) - assert.Nil(t, err) -} - -func TestEventToMessageContainer(t *testing.T) { - builder, err := containerbuilder.NewDockerBuilder(context.Background()). - WithImage(nginxImage). - WithName("test-event-to-message"). - WithRestartPolicy(containerbuilder.NoRestartPolicy). - Create() - - assert.NoError(t, err) - - containerID := *builder.GetContainerID() - - event := events.Message{ - Type: "container", - Action: "create", - Actor: events.Actor{ - ID: containerID, - Attributes: map[string]string{ - "name": "prefix-name", - }, - }, - } - - message, err := EventToMessage(context.Background(), "", &event) - - dockerHelper.DeleteContainerByID(context.Background(), nil, containerID) - - assert.Nil(t, err) - assert.NotNil(t, message) - assert.Equal(t, common.ContainerState_WAITING, message.State) -} +//go:build unit +// +build unit + +package utils + +import ( + "context" + "testing" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/events" + "github.com/dyrector-io/dyrectorio/golang/internal/label" + "github.com/stretchr/testify/assert" + + dockerHelper "github.com/dyrector-io/dyrectorio/golang/internal/helper/docker" + containerbuilder "github.com/dyrector-io/dyrectorio/golang/pkg/builder/container" + "github.com/dyrector-io/dyrectorio/protobuf/go/common" +) + +const nginxImage = "ghcr.io/dyrector-io/mirror/nginx:mainline-alpine" + +func TestGetContainerIdentifierFromEventEmptyAttributes(t *testing.T) { + event := events.Message{ + Actor: events.Actor{ + Attributes: map[string]string{}, + }, + } + + id := GetContainerIdentifierFromEvent(&event) + assert.Nil(t, id) +} + +func TestGetContainerIdentifierFromEventPrefix(t *testing.T) { + event := events.Message{ + Actor: events.Actor{ + Attributes: map[string]string{ + label.DyrectorioOrg + label.ContainerPrefix: "prefix", + }, + }, + } + + id := GetContainerIdentifierFromEvent(&event) + assert.Nil(t, id) +} + +func TestGetContainerIdentifierFromEventName(t *testing.T) { + event := events.Message{ + Actor: events.Actor{ + Attributes: map[string]string{ + "name": "prefix-name", + }, + }, + } + + id := GetContainerIdentifierFromEvent(&event) + assert.NotNil(t, id) + assert.Equal(t, "prefix-name", id.Name) + assert.Equal(t, "", id.Prefix) +} + +func TestGetContainerIdentifierFromEventBoth(t *testing.T) { + event := events.Message{ + Actor: events.Actor{ + Attributes: map[string]string{ + label.DyrectorioOrg + label.ContainerPrefix: "prefix", + "name": "prefix-name", + }, + }, + } + + id := GetContainerIdentifierFromEvent(&event) + assert.NotNil(t, id) + assert.Equal(t, "name", id.Name) + assert.Equal(t, "prefix", id.Prefix) +} + +func TestEventToMessageNonContainer(t *testing.T) { + event := events.Message{ + Type: "non-container", + } + + message, err := EventToMessage(context.Background(), "", &event) + assert.Nil(t, message) + assert.Nil(t, err) +} + +func TestEventToMessageNoName(t *testing.T) { + event := events.Message{ + Type: "container", + Actor: events.Actor{ + Attributes: map[string]string{}, + }, + } + + message, err := EventToMessage(context.Background(), "", &event) + assert.Nil(t, message) + assert.EqualError(t, err, "event has no container name") +} + +func TestEventToMessageActionDestroy(t *testing.T) { + event := events.Message{ + Type: "container", + Action: "destroy", + Actor: events.Actor{ + Attributes: map[string]string{ + "name": "prefix-name", + }, + }, + } + + message, err := EventToMessage(context.Background(), "", &event) + assert.NoError(t, err) + + expected := &common.ContainerStateItem{ + Id: &common.ContainerIdentifier{ + Name: "prefix-name", + Prefix: "", + }, + Command: "", + CreatedAt: nil, + State: common.ContainerState_REMOVED, + Reason: "", + Status: "", + Ports: []*common.ContainerStateItemPort{}, + ImageName: "", + ImageTag: "", + } + + assert.Equal(t, expected, message) +} + +func TestEventToMessageUnknownAction(t *testing.T) { + event := events.Message{ + Type: "container", + Action: "unknown-action", + Actor: events.Actor{ + Attributes: map[string]string{ + "name": "prefix-name", + }, + }, + } + + message, err := EventToMessage(context.Background(), "", &event) + assert.Nil(t, message) + assert.Nil(t, err) +} + +func TestEventToMessageNoContainer(t *testing.T) { + event := events.Message{ + Type: "container", + Action: "unknown-action", + Actor: events.Actor{ + ID: "container-id-does-not-exist", + Attributes: map[string]string{ + "name": "prefix-name", + }, + }, + } + + message, err := EventToMessage(context.Background(), "", &event) + assert.Nil(t, message) + assert.Nil(t, err) +} + +func TestEventToMessageContainer(t *testing.T) { + builder, err := containerbuilder.NewDockerBuilder(context.Background()). + WithImage(nginxImage). + WithName("test-event-to-message"). + WithRestartPolicy(container.RestartPolicyDisabled). + Create() + + assert.NoError(t, err) + + containerID := *builder.GetContainerID() + + event := events.Message{ + Type: "container", + Action: "create", + Actor: events.Actor{ + ID: containerID, + Attributes: map[string]string{ + "name": "prefix-name", + }, + }, + } + + message, err := EventToMessage(context.Background(), "", &event) + + dockerHelper.DeleteContainerByID(context.Background(), nil, containerID) + + assert.Nil(t, err) + assert.NotNil(t, message) + assert.Equal(t, common.ContainerState_WAITING, message.State) +} diff --git a/golang/pkg/dagent/utils/import_container.go b/golang/pkg/dagent/utils/import_container.go index 64859fe0a..389ce5e1c 100644 --- a/golang/pkg/dagent/utils/import_container.go +++ b/golang/pkg/dagent/utils/import_container.go @@ -54,8 +54,8 @@ func spawnImportContainer( WithMountPoints([]mount.Mount{targetVolume}). WithoutConflict(). WithLogWriter(dog). - WithPreStartHooks(func(ctx context.Context, client client.APIClient, - parentCont containerbuilder.ParentContainer, + WithPreStartHooks(func(_ context.Context, _ client.APIClient, + _ containerbuilder.ParentContainer, ) error { dog.WriteDeploymentStatus(common.DeploymentStatus_IN_PROGRESS, "Waiting for import container to finish") return nil diff --git a/golang/pkg/dagent/utils/init_container.go b/golang/pkg/dagent/utils/init_container.go index 0215f82d9..3d52231af 100644 --- a/golang/pkg/dagent/utils/init_container.go +++ b/golang/pkg/dagent/utils/init_container.go @@ -61,7 +61,7 @@ func spawnInitContainer( WithNetworks(initCont.Networks). WithoutConflict(). WithPreStartHooks( - func(ctx context.Context, client client.APIClient, parentCont containerbuilder.ParentContainer) error { + func(_ context.Context, _ client.APIClient, _ containerbuilder.ParentContainer) error { dog.WriteDeploymentStatus(common.DeploymentStatus_IN_PROGRESS, "Waiting for init container to finish") return nil }). diff --git a/images/builder-golang/Dockerfile b/images/builder-golang/Dockerfile index 060897cbf..c626ad838 100644 --- a/images/builder-golang/Dockerfile +++ b/images/builder-golang/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/library/golang:1.20-alpine3.18 +FROM docker.io/library/golang:1.22-alpine3.19 ENV GOLANGCI_LINT_CACHE $GOPATH/cache ENV GOCACHE $GOPATH/cache From f93b13ce0189160036813a44cfcbcdd126eefbef Mon Sep 17 00:00:00 2001 From: Nandor Magyar Date: Fri, 26 Apr 2024 13:56:52 +0200 Subject: [PATCH 09/19] fix(crux-ui): add IPv6 listen for dual-stack listening (#965) --- web/crux-ui/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/crux-ui/Dockerfile b/web/crux-ui/Dockerfile index 52f02ece4..ca92415b2 100644 --- a/web/crux-ui/Dockerfile +++ b/web/crux-ui/Dockerfile @@ -28,7 +28,7 @@ COPY --from=BUILDER --chown=node:node /app/.next/static ./.next/static USER node EXPOSE 3000 -ENV HOSTNAME "0.0.0.0" +ENV HOSTNAME "::" ENV PORT 3000 LABEL org.opencontainers.image.source="https://github.com/dyrector-io/dyrectorio/web/crux-ui" From 0806774be96e117744903a12acfc38f336cef773 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:13:39 +0200 Subject: [PATCH 10/19] chore(deps): bump protobufjs from 7.2.4 to 7.2.6 in /web/crux (#957) * chore(deps): bump protobufjs from 7.2.4 to 7.2.6 in /web/crux Bumps [protobufjs](https://github.com/protobufjs/protobuf.js) from 7.2.4 to 7.2.6. - [Release notes](https://github.com/protobufjs/protobuf.js/releases) - [Changelog](https://github.com/protobufjs/protobuf.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/protobufjs/protobuf.js/compare/protobufjs-v7.2.4...protobufjs-v7.2.6) --- updated-dependencies: - dependency-name: protobufjs dependency-type: indirect ... Signed-off-by: dependabot[bot] * fix: protobuf path * fix(crux): prettier warning --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mate Vago Co-authored-by: Levente Orban --- web/crux/package-lock.json | 6 +++--- web/crux/src/main.ts | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/web/crux/package-lock.json b/web/crux/package-lock.json index 09a2d61a1..9e07ebb96 100644 --- a/web/crux/package-lock.json +++ b/web/crux/package-lock.json @@ -10787,9 +10787,9 @@ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" }, "node_modules/protobufjs": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz", - "integrity": "sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ==", + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", + "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", diff --git a/web/crux/src/main.ts b/web/crux/src/main.ts index 53dd3420c..4da6b2ca0 100644 --- a/web/crux/src/main.ts +++ b/web/crux/src/main.ts @@ -7,6 +7,7 @@ import { NestExpressApplication } from '@nestjs/platform-express' import { SwaggerModule } from '@nestjs/swagger' import { Logger as PinoLogger } from 'nestjs-pino' import { join } from 'path' +import { Root as ProtoRoot } from 'protobufjs' import AppModule from './app.module' import AuditLoggerInterceptor from './app/audit.logger/audit.logger.interceptor' import metricsServerBootstrap from './app/metrics/metrics.server' @@ -115,6 +116,15 @@ const serve = async () => { app.useWebSocketAdapter(new DyoWsAdapter(app, authGuard)) + const REWRITE_PROTO_PATH = 'protobuf/proto/' + ProtoRoot.prototype.resolvePath = (_: string, target: string) => { + if (target.startsWith(REWRITE_PROTO_PATH)) { + const strippedPath = target.substring(REWRITE_PROTO_PATH.length) + return join(__dirname, `../proto/${strippedPath}`) + } + return target + } + // agent app.connectMicroservice({ transport: Transport.GRPC, From 61f28e513a818165759971f174893b8c1afeaa79 Mon Sep 17 00:00:00 2001 From: Mate Vago Date: Tue, 7 May 2024 16:16:50 +0200 Subject: [PATCH 11/19] feat(web): composer (#967) --- .github/workflows/product_builder.yaml | 1 + web/crux-ui/i18n.json | 1 + web/crux-ui/locales/en/common.json | 2 + web/crux-ui/locales/en/compose.json | 24 + web/crux-ui/locales/en/nodes.json | 1 - web/crux-ui/package-lock.json | 22 +- web/crux-ui/package.json | 2 + web/crux-ui/public/composer.svg | 1 + .../composer/compose-environment.tsx | 45 + .../components/composer/compose-file-card.tsx | 35 + .../composer/converted-container.tsx | 60 + .../components/composer/dot-env-file-card.tsx | 62 + .../composer/generate-version-card.tsx | 221 +++ .../components/composer/use-composer-state.ts | 317 ++++ .../deployments/add-deployment-card.tsx | 68 +- .../create-deployment-token-card.tsx | 4 +- web/crux-ui/src/components/main/sidebar.tsx | 6 + .../src/components/nodes/dyo-node-setup.tsx | 4 +- .../nodes/node-sections-heading.tsx | 2 +- .../projects/select-project-chips.tsx | 71 + .../registries/create-registry-token-card.tsx | 6 +- .../registries/edit-registry-card.tsx | 1 + .../shared/sh-editor-dynamic-module.tsx | 7 + .../src/components/shared/sh-editor.tsx | 6 +- .../shared/yaml-editor-dynamic-module.tsx | 7 + .../src/components/shared/yaml-editor.tsx | 44 + web/crux-ui/src/elements/dyo-indicator.tsx | 2 +- web/crux-ui/src/elements/dyo-input.tsx | 4 +- web/crux-ui/src/hooks/use-submit.ts | 2 +- web/crux-ui/src/models/compose.ts | 397 +++++ web/crux-ui/src/models/image.ts | 5 + web/crux-ui/src/models/index.ts | 1 + web/crux-ui/src/models/registry.ts | 30 +- web/crux-ui/src/pages/composer.tsx | 202 +++ web/crux-ui/src/routes.ts | 1 + web/crux-ui/src/validations/common.ts | 20 +- web/crux-ui/src/validations/compose.ts | 92 + web/crux-ui/src/validations/deployment.ts | 2 +- web/crux-ui/src/validations/index.ts | 1 + web/crux-ui/tailwind.config.js | 2 + web/crux/package-lock.json | 1532 +++++++---------- .../image.add-to-version.team-access.guard.ts | 2 +- web/crux/src/app/registry/registry.dto.ts | 3 + web/crux/src/app/registry/registry.mapper.ts | 22 +- 44 files changed, 2331 insertions(+), 1009 deletions(-) create mode 100644 web/crux-ui/locales/en/compose.json create mode 100644 web/crux-ui/public/composer.svg create mode 100644 web/crux-ui/src/components/composer/compose-environment.tsx create mode 100644 web/crux-ui/src/components/composer/compose-file-card.tsx create mode 100644 web/crux-ui/src/components/composer/converted-container.tsx create mode 100644 web/crux-ui/src/components/composer/dot-env-file-card.tsx create mode 100644 web/crux-ui/src/components/composer/generate-version-card.tsx create mode 100644 web/crux-ui/src/components/composer/use-composer-state.ts create mode 100644 web/crux-ui/src/components/projects/select-project-chips.tsx create mode 100644 web/crux-ui/src/components/shared/sh-editor-dynamic-module.tsx create mode 100644 web/crux-ui/src/components/shared/yaml-editor-dynamic-module.tsx create mode 100644 web/crux-ui/src/components/shared/yaml-editor.tsx create mode 100644 web/crux-ui/src/models/compose.ts create mode 100644 web/crux-ui/src/pages/composer.tsx create mode 100644 web/crux-ui/src/validations/compose.ts diff --git a/.github/workflows/product_builder.yaml b/.github/workflows/product_builder.yaml index 59288134f..a889b4104 100644 --- a/.github/workflows/product_builder.yaml +++ b/.github/workflows/product_builder.yaml @@ -42,6 +42,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: config-preset: conventionalcommits + config-version: 7.0.2 - name: Run title validation # if it's not a PR we skip if: ${{ github.event_name == 'pull_request' }} diff --git a/web/crux-ui/i18n.json b/web/crux-ui/i18n.json index 94f155d6d..495e61677 100644 --- a/web/crux-ui/i18n.json +++ b/web/crux-ui/i18n.json @@ -41,6 +41,7 @@ "/[teamSlug]/deployments/[deploymentId]/instances/[instanceId]": ["images", "deployments", "container"], "/status": ["status"], "/templates": ["templates", "projects"], + "/composer": ["compose", "versions", "container"], "/[teamSlug]/dashboard": ["dashboard"], "/[teamSlug]/storages": ["storages"], "/[teamSlug]/storages/[storageId]": ["storages"], diff --git a/web/crux-ui/locales/en/common.json b/web/crux-ui/locales/en/common.json index 61e4d7e1e..5b61ce912 100644 --- a/web/crux-ui/locales/en/common.json +++ b/web/crux-ui/locales/en/common.json @@ -53,6 +53,7 @@ "address": "Address", "actions": "Actions", "note": "Note", + "containers": "Containers", "editName": "Edit {{name}}", "add": "Add", @@ -89,6 +90,7 @@ "profile": "Profile", "tokens": "Tokens", "templates": "Templates", + "composer": "Composer", "pipelines": "Pipelines", "storages": "Storages", "configBundles": "Config bundles", diff --git a/web/crux-ui/locales/en/compose.json b/web/crux-ui/locales/en/compose.json new file mode 100644 index 000000000..366f44ca7 --- /dev/null +++ b/web/crux-ui/locales/en/compose.json @@ -0,0 +1,24 @@ +{ + "compose": "Compose", + "composeFile": "Compose file", + "dotEnvFile": ".env file", + "errors": { + "failedToParseFile": "Failed to parse the {{file}}" + }, + "registryFound": "Registry found", + "missingRegistry": "Missing registry", + "entrypoint": "Entrypoint", + "pasteYourCompose": "Paste your compose file", + "defaultDotEnv": "Default .env", + "addDotEnv": "Add .env", + "generate": "Generate", + "generateVersion": "Generate version", + "target": "Target", + "targetType": { + "new-project": "New project", + "existing-project": "Existing project" + }, + "versionName": "Version name", + "versionType": "Version type", + "projectName": "Project name" +} diff --git a/web/crux-ui/locales/en/nodes.json b/web/crux-ui/locales/en/nodes.json index cd48e6a47..bd09fda17 100644 --- a/web/crux-ui/locales/en/nodes.json +++ b/web/crux-ui/locales/en/nodes.json @@ -1,7 +1,6 @@ { "nodesName": "Nodes - {{name}}", - "containers": "Containers", "logs": "Logs", "inspect": "Inspect", diff --git a/web/crux-ui/package-lock.json b/web/crux-ui/package-lock.json index ebafae26e..1cb2f02be 100644 --- a/web/crux-ui/package-lock.json +++ b/web/crux-ui/package-lock.json @@ -12,6 +12,7 @@ "@ory/kratos-client": "^1.1.0", "clsx": "^2.0.0", "formik": "^2.4.3", + "js-yaml": "^4.1.0", "next": "^14.1.1", "next-translate": "^2.6.2", "openpgp": "^5.10.1", @@ -32,6 +33,7 @@ "@playwright/test": "^1.42.0", "@types/google-protobuf": "^3.15.6", "@types/jest": "^29.5.3", + "@types/js-yaml": "^4.0.9", "@types/jsoneditor": "^9.9.0", "@types/node": "20.11.25", "@types/openpgp": "^4.4.18", @@ -2146,6 +2148,12 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -2584,8 +2592,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/aria-query": { "version": "5.3.0", @@ -6073,7 +6080,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -10167,6 +10173,12 @@ "pretty-format": "^29.0.0" } }, + "@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true + }, "@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -10479,8 +10491,7 @@ "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "aria-query": { "version": "5.3.0", @@ -12986,7 +12997,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "requires": { "argparse": "^2.0.1" } diff --git a/web/crux-ui/package.json b/web/crux-ui/package.json index b1895046c..88645bedb 100644 --- a/web/crux-ui/package.json +++ b/web/crux-ui/package.json @@ -34,6 +34,7 @@ "@ory/kratos-client": "^1.1.0", "clsx": "^2.0.0", "formik": "^2.4.3", + "js-yaml": "^4.1.0", "next": "^14.1.1", "next-translate": "^2.6.2", "openpgp": "^5.10.1", @@ -54,6 +55,7 @@ "@playwright/test": "^1.42.0", "@types/google-protobuf": "^3.15.6", "@types/jest": "^29.5.3", + "@types/js-yaml": "^4.0.9", "@types/jsoneditor": "^9.9.0", "@types/node": "20.11.25", "@types/openpgp": "^4.4.18", diff --git a/web/crux-ui/public/composer.svg b/web/crux-ui/public/composer.svg new file mode 100644 index 000000000..cca5bf436 --- /dev/null +++ b/web/crux-ui/public/composer.svg @@ -0,0 +1 @@ + diff --git a/web/crux-ui/src/components/composer/compose-environment.tsx b/web/crux-ui/src/components/composer/compose-environment.tsx new file mode 100644 index 000000000..2a116925f --- /dev/null +++ b/web/crux-ui/src/components/composer/compose-environment.tsx @@ -0,0 +1,45 @@ +import DyoWrap from '@app/elements/dyo-wrap' +import useTranslation from 'next-translate/useTranslation' +import DotEnvFileCard from './dot-env-file-card' +import { + ComposerDispatch, + ComposerState, + changeEnvFileName, + convertEnvFile, + removeEnvFile, + selectDefaultEnvironment, +} from './use-composer-state' + +type ComposeEnvironmentProps = { + className?: string + state: ComposerState + dispatch: ComposerDispatch +} + +const ComposeEnvironment = (props: ComposeEnvironmentProps) => { + const { className, state, dispatch } = props + + const { t } = useTranslation('compose') + + const onEnvFileChange = (name: string, text: string) => dispatch(convertEnvFile(t, name, text)) + const onEnvNameChange = (from: string, to: string) => dispatch(changeEnvFileName(from, to)) + const onRemoveDotEnv = (name: string) => dispatch(removeEnvFile(name)) + + const defaultDotEnv = selectDefaultEnvironment(state) + + return ( + + {state.environment.map((it, index) => ( + onEnvFileChange(it.name, text)} + onNameChange={it !== defaultDotEnv ? name => onEnvNameChange(it.name, name) : null} + onRemove={it !== defaultDotEnv ? () => onRemoveDotEnv(it.name) : null} + /> + ))} + + ) +} + +export default ComposeEnvironment diff --git a/web/crux-ui/src/components/composer/compose-file-card.tsx b/web/crux-ui/src/components/composer/compose-file-card.tsx new file mode 100644 index 000000000..21bc303ee --- /dev/null +++ b/web/crux-ui/src/components/composer/compose-file-card.tsx @@ -0,0 +1,35 @@ +import { DyoCard } from '@app/elements/dyo-card' +import { DyoHeading } from '@app/elements/dyo-heading' +import DyoMessage from '@app/elements/dyo-message' +import clsx from 'clsx' +import useTranslation from 'next-translate/useTranslation' +import YamlEditor from '../shared/yaml-editor-dynamic-module' + +type ComposeFileCardProps = { + className?: string + errorMessage?: string + initialText: string + onChange: (text: string) => void +} + +const ComposeFileCard = (props: ComposeFileCardProps) => { + const { className, errorMessage, initialText, onChange } = props + + const { t } = useTranslation('compose') + + return ( + + + {t('composeFile')} + + + {errorMessage ? ( + + ) : null} + + + + ) +} + +export default ComposeFileCard diff --git a/web/crux-ui/src/components/composer/converted-container.tsx b/web/crux-ui/src/components/composer/converted-container.tsx new file mode 100644 index 000000000..cf37397e1 --- /dev/null +++ b/web/crux-ui/src/components/composer/converted-container.tsx @@ -0,0 +1,60 @@ +import { DyoCard } from '@app/elements/dyo-card' +import { DyoHeading } from '@app/elements/dyo-heading' +import DyoIcon from '@app/elements/dyo-icon' +import DyoIndicator from '@app/elements/dyo-indicator' +import { ConvertedContainer, imageConfigToJsonContainerConfig } from '@app/models' +import { writeToClipboard } from '@app/utils' +import clsx from 'clsx' +import useTranslation from 'next-translate/useTranslation' +import { OFFLINE_EDITOR_STATE } from '../editor/use-item-editor-state' +import EditImageJson from '../projects/versions/images/edit-image-json' + +type ConvertedContainerCardProps = { + className?: string + container: ConvertedContainer + hasRegistry?: boolean +} + +const ConvertedContainerCard = (props: ConvertedContainerCardProps) => { + const { className, container, hasRegistry } = props + + const { t } = useTranslation('compose') + + const onCopy = () => writeToClipboard(t, JSON.stringify(container.config, undefined, 2)) + + return ( + +
+ {`${t('common:container')}: ${ + container.config.name + }`} + + +
+ +
+ + + {`${t('common:image')}: ${container.image}`} +
+ + {!hasRegistry && {t('missingRegistry')}} + +
+ {}} + onParseError={() => {}} + convertConfigToJson={imageConfigToJsonContainerConfig} + /> +
+
+ ) +} + +export default ConvertedContainerCard diff --git a/web/crux-ui/src/components/composer/dot-env-file-card.tsx b/web/crux-ui/src/components/composer/dot-env-file-card.tsx new file mode 100644 index 000000000..529b2244c --- /dev/null +++ b/web/crux-ui/src/components/composer/dot-env-file-card.tsx @@ -0,0 +1,62 @@ +import { DyoCard } from '@app/elements/dyo-card' +import { DyoHeading } from '@app/elements/dyo-heading' +import DyoImgButton from '@app/elements/dyo-img-button' +import { DyoInput } from '@app/elements/dyo-input' +import DyoMessage from '@app/elements/dyo-message' +import { DotEnvironment } from '@app/models' +import clsx from 'clsx' +import useTranslation from 'next-translate/useTranslation' +import ShEditor from '../shared/sh-editor-dynamic-module' + +type DotEnvFileCardProps = { + className?: string + dotEnv: DotEnvironment + onNameChange?: (name: string) => void + onEnvChange: (text: string) => void + onRemove?: VoidFunction +} + +const DotEnvFileCard = (props: DotEnvFileCardProps) => { + const { className, dotEnv, onEnvChange: onChange, onNameChange, onRemove } = props + + const initialValue: string = Object.entries(dotEnv?.environment ?? {}) + .map(entry => { + const [key, val] = entry + + return `${key}=${val}` + }) + .join('\n') + + const { t } = useTranslation('compose') + + return ( + +
+ + {t('dotEnvFile')} + + + onNameChange(ev.target.value) : null} + /> + + {onRemove && } +
+ + {dotEnv.errorMessage ? ( + + ) : null} + + +
+ ) +} + +export default DotEnvFileCard diff --git a/web/crux-ui/src/components/composer/generate-version-card.tsx b/web/crux-ui/src/components/composer/generate-version-card.tsx new file mode 100644 index 000000000..1ac5cec21 --- /dev/null +++ b/web/crux-ui/src/components/composer/generate-version-card.tsx @@ -0,0 +1,221 @@ +import { DyoCard } from '@app/elements/dyo-card' +import DyoChips, { chipsQALabelFromValue } from '@app/elements/dyo-chips' +import DyoForm from '@app/elements/dyo-form' +import { DyoHeading } from '@app/elements/dyo-heading' +import { DyoInput } from '@app/elements/dyo-input' +import { DyoLabel } from '@app/elements/dyo-label' +import LoadingIndicator from '@app/elements/loading-indicator' +import { defaultApiErrorHandler } from '@app/errors' +import useDyoFormik from '@app/hooks/use-dyo-formik' +import { SubmitHook } from '@app/hooks/use-submit' +import useTeamRoutes from '@app/hooks/use-team-routes' +import { + AddImages, + COMPOSE_TARGET_TYPE_VALUES, + ComposeGenerateVersion, + ConvertedContainer, + CreateProject, + CreateVersion, + Project, + ProjectDetails, + Registry, + VERSION_TYPE_VALUES, + VersionDetails, + findRegistryByUrl, + imageUrlOfImageName as imageUrlOfImageTag, +} from '@app/models' +import { sendForm } from '@app/utils' +import { generateVersionSchema } from '@app/validations' +import useTranslation from 'next-translate/useTranslation' +import toast from 'react-hot-toast' +import SelectProjectChips from '../projects/select-project-chips' + +type GenerateVersionCardProps = { + className?: string + submit: SubmitHook + registries: Registry[] + containers: ConvertedContainer[] + onVersionGenerated: (project: Project, version: VersionDetails) => Promise +} + +const GenerateVersionCard = (props: GenerateVersionCardProps) => { + const { className, submit, registries, containers, onVersionGenerated } = props + + const { t } = useTranslation('compose') + const routes = useTeamRoutes() + + const handleApiError = defaultApiErrorHandler(t) + + const formik = useDyoFormik({ + submit, + validationSchema: generateVersionSchema, + initialValues: { + targetType: 'existing-project', + versionName: '', + versionType: 'incremental', + projectName: '', + project: null, + }, + onSubmit: async values => { + let { project } = values + + const addImagesBody: AddImages[] = containers.map(it => { + const url = imageUrlOfImageTag(it.image) + const registry = findRegistryByUrl(registries, url) + const imageName = url.replace(`${registry.imageUrlPrefix}/`, '') + + if (!registry) { + return null + } + + return { + registryId: registry.id, + images: [imageName], + } + }) + + const missingRegistry = addImagesBody.find(it => !it) + if (missingRegistry) { + toast.error(t('missingRegistry')) + return + } + + if (values.targetType === 'new-project') { + const body: CreateProject = { + name: values.projectName, + type: 'versioned', + } + + const res = await sendForm('POST', routes.project.api.list(), body) + project = res.ok ? ((await res.json()) as ProjectDetails) : null + if (!project) { + await handleApiError(res) + return + } + } + + const body: CreateVersion = { + name: values.versionName, + type: values.versionType, + } + + const res = await sendForm('POST', routes.project.versions(project.id).api.list(), body) + if (!res.ok) { + await handleApiError(res) + return + } + + const version = (await res.json()) as VersionDetails + + const createImagesRes = await sendForm( + 'POST', + routes.project.versions(project.id).api.images(version.id), + addImagesBody, + ) + if (!createImagesRes.ok) { + await handleApiError(createImagesRes) + return + } + + await onVersionGenerated(project, version) + }, + }) + + const onProjectsFetched = (projects: Project[] | null) => { + if (projects?.length >= 1) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + formik.setFieldValue('project', projects[0]) + } + } + + return formik.isSubmitting ? ( + + ) : ( + + + + {t('generateVersion')} + + +
+
+ + + {t('versionType')} + + t(`versions:${it}`)} + onSelectionChange={async (type): Promise => { + await formik.setFieldValue('versionType', type, false) + }} + qaLabel={chipsQALabelFromValue} + /> +
+ +
+ + {t('target')} + + + t(`targetType.${it}`)} + onSelectionChange={async (type): Promise => { + await formik.setFieldValue('targetType', type, false) + }} + qaLabel={chipsQALabelFromValue} + /> + + {formik.values.targetType === 'existing-project' ? ( + <> + {t('common:projects')} + + { + await formik.setFieldValue('project', it) + }} + errorMessage={formik.errors.project as string} + onProjectsFetched={onProjectsFetched} + /> + + ) : ( + + )} +
+
+
+
+ ) +} + +export default GenerateVersionCard diff --git a/web/crux-ui/src/components/composer/use-composer-state.ts b/web/crux-ui/src/components/composer/use-composer-state.ts new file mode 100644 index 000000000..ff2d85491 --- /dev/null +++ b/web/crux-ui/src/components/composer/use-composer-state.ts @@ -0,0 +1,317 @@ +import useRepatch, { RepatchAction } from '@app/hooks/use-repatch' +import { + Compose, + ComposeService, + ConvertedContainer, + DotEnvironment, + applyDotEnvToComposeService, + mapComposeServices, +} from '@app/models' +import { composeSchema, getValidationError } from '@app/validations' +import { load } from 'js-yaml' +import { Translate } from 'next-translate' +import { Dispatch } from 'react' + +export const DEFAULT_ENVIRONMENT_NAME = '.env' + +export type ComposerUpperSection = 'compose' | 'generate' +export type ComposerBottomSection = 'containers' | 'environment' + +type ParsedCompose = { + text: string + yaml?: Compose + error?: string +} + +export type ComposerState = { + upperSection: ComposerUpperSection + bottomSection: ComposerBottomSection + showDefaultDotEnv: boolean + compose: ParsedCompose + containers: ConvertedContainer[] + environment: DotEnvironment[] +} + +type ComposerAction = RepatchAction + +export type ComposerDispatch = Dispatch + +const initialState: ComposerState = { + upperSection: 'compose', + bottomSection: 'containers', + showDefaultDotEnv: true, + compose: null, + containers: [], + environment: [{ name: DEFAULT_ENVIRONMENT_NAME, environment: {} }], +} + +// actions +export const activateUpperSection = + (section: ComposerUpperSection): ComposerAction => + state => ({ + ...state, + upperSection: section, + bottomSection: section === 'generate' ? 'containers' : state.bottomSection, + }) + +export const activateBottomSection = + (section: ComposerBottomSection): ComposerAction => + state => ({ + ...state, + bottomSection: section, + }) + +export const toggleShowDefaultDotEnv = (): ComposerAction => state => ({ + ...state, + showDefaultDotEnv: !state.showDefaultDotEnv, +}) + +const applyEnvironments = ( + composeServices: Record, + envs: DotEnvironment[], +): Record => { + const services = Object.entries(composeServices ?? {}) + const appliedServices = services.map(entry => { + const [key, service] = entry + + const dotEnvName = service.env_file ?? DEFAULT_ENVIRONMENT_NAME + const dotEnv = envs.find(it => it.name === dotEnvName) + + const applied = applyDotEnvToComposeService(service, dotEnv.environment) + + return [key, applied] + }) + + return Object.fromEntries(appliedServices) +} + +type ApplyComposeToStateOptions = { + compose: ParsedCompose + envedCompose: Compose + t: Translate +} +const applyComposeToState = (state: ComposerState, options: ApplyComposeToStateOptions) => { + const { t } = options + + try { + const newContainers = mapComposeServices(options.envedCompose) + + return { + ...state, + compose: options.compose, + containers: newContainers, + } + } catch (err) { + console.error(err) + + return { + ...state, + compose: { + ...state.compose, + error: t('errors.failedToParseFile', { file: t('composeFile') }), + }, + containers: [], + } + } +} + +export const convertComposeFile = + (t: Translate, text: string): ComposerAction => + state => { + if (!text) { + return { + ...state, + compose: null, + } + } + + let compose: Compose = null + try { + compose = load(text) as Compose + + if (!compose) { + return { + ...state, + compose: { + text, + yaml: null, + error: null, + }, + } + } + } catch (err) { + console.error(err) + + return { + ...state, + compose: { + text, + yaml: null, + error: t('errors.failedToParseFile', { file: t('composeFile') }), + }, + containers: [], + } + } + + const error = getValidationError(composeSchema, compose, null, t) + if (error) { + return { + ...state, + compose: { + text, + yaml: null, + error: error.message, + }, + } + } + + const envedCompose = { + ...compose, + services: applyEnvironments(compose?.services, state.environment), + } + + return applyComposeToState(state, { + compose: { + text, + yaml: compose, + error: null, + }, + envedCompose, + t, + }) + } + +export const convertEnvFile = + (t: Translate, name: string, text: string): ComposerAction => + state => { + const { environment } = state + + const index = environment.findIndex(it => it.name === name) + if (index < 0) { + return state + } + + const dotEnv: DotEnvironment = { + ...environment[index], + } + + try { + const lines = text.split('\n') + + const keyValues = lines + .filter(it => it.includes('=')) + .map(line => { + const commentIndex = line.indexOf('#') + if (commentIndex > -1) { + line = line.substring(0, commentIndex).trim() + } + + const [key, value] = line.split('=') + return [key, value] + }) + .filter(it => { + const [key, value] = it + return key && value + }) + + dotEnv.environment = Object.fromEntries(keyValues) + dotEnv.errorMessage = null + } catch { + dotEnv.errorMessage = t('errors.failedToParseFile', { file: t('dotEnvFile') }) + } + + const newEnv = [...environment] + newEnv[index] = dotEnv + + const { compose } = state + + const envedCompose = { + ...compose.yaml, + services: applyEnvironments(compose?.yaml?.services, newEnv), + } + + const newState = applyComposeToState(state, { + compose, + envedCompose, + t, + }) + + return { + ...newState, + environment: newEnv, + } + } + +export const changeEnvFileName = + (from: string, to: string): ComposerAction => + state => { + const { environment } = state + + const index = environment.findIndex(it => it.name === from) + if (index < 0) { + return state + } + + const dotEnv: DotEnvironment = { + ...environment[index], + name: to, + } + + const newEnv = [...environment] + newEnv[index] = dotEnv + + return { + ...state, + environment: newEnv, + } + } + +export const addEnvFile = (): ComposerAction => state => { + const { environment } = state + + const dotEnv: DotEnvironment = { + environment: {}, + name: `.env.${environment.length}`, + errorMessage: null, + } + + const newEnv = [...environment, dotEnv] + + return { + ...state, + bottomSection: 'environment', + environment: newEnv, + } +} + +export const removeEnvFile = + (name: string): ComposerAction => + state => { + if (name === DEFAULT_ENVIRONMENT_NAME) { + return state + } + + const { environment } = state + + const index = environment.findIndex(it => it.name === name) + if (index < 0) { + return state + } + + return { + ...state, + environment: environment.filter(it => it.name !== name), + } + } + +// selectors +export const selectDefaultEnvironment = (state: ComposerState) => + state.environment.find(it => it.name === DEFAULT_ENVIRONMENT_NAME) + +export const selectShowDefaultEnvironment = (state: ComposerState) => + state.showDefaultDotEnv && state.bottomSection !== 'environment' && state.upperSection === 'compose' + +// hook +const useComposerState = () => useRepatch(initialState) + +export default useComposerState diff --git a/web/crux-ui/src/components/deployments/add-deployment-card.tsx b/web/crux-ui/src/components/deployments/add-deployment-card.tsx index 5b68e74b5..e36b461ce 100644 --- a/web/crux-ui/src/components/deployments/add-deployment-card.tsx +++ b/web/crux-ui/src/components/deployments/add-deployment-card.tsx @@ -26,6 +26,7 @@ import { createFullDeploymentSchema } from '@app/validations' import useTranslation from 'next-translate/useTranslation' import { useEffect } from 'react' import useSWR from 'swr' +import SelectProjectChips from '../projects/select-project-chips' interface AddDeploymentCardProps { className?: string @@ -40,7 +41,6 @@ const AddDeploymentCard = (props: AddDeploymentCardProps) => { const routes = useTeamRoutes() const { data: nodes, error: fetchNodesError } = useSWR(routes.node.api.list(), fetcher) - const { data: projects, error: fetchProjectsError } = useSWR(routes.project.api.list(), fetcher) const handleApiError = apiErrorHandler((stringId: string, status: number, dto: DyoApiError) => { if (deploymentHasError(dto)) { @@ -65,7 +65,7 @@ const AddDeploymentCard = (props: AddDeploymentCardProps) => { prefix: '', protected: false, versionId: null as string, - projectId: null as string, + project: null as Project, }, validationSchema: createFullDeploymentSchema, t, @@ -88,10 +88,8 @@ const AddDeploymentCard = (props: AddDeploymentCardProps) => { }, }) - const currentProject = projects?.find(it => it.id === formik.values.projectId) - const { data: versions, error: fetchVersionsError } = useSWR( - formik.values.projectId ? routes.project.versions(formik.values.projectId).api.list() : null, + formik.values.project ? routes.project.versions(formik.values.project.id).api.list() : null, fetcher, ) @@ -104,15 +102,6 @@ const AddDeploymentCard = (props: AddDeploymentCardProps) => { } }, [nodes, formikValues.nodeId, formikSetFieldValue]) - useEffect(() => { - if (projects?.length === 1) { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - formikSetFieldValue('projectId', projects[0].id) - // eslint-disable-next-line @typescript-eslint/no-floating-promises - formikSetFieldValue('prefix', projectNameToDeploymentPrefix(projects[0].name)) - } - }, [projects, formikSetFieldValue]) - useEffect(() => { if (versions?.length === 1) { // eslint-disable-next-line @typescript-eslint/no-floating-promises @@ -124,6 +113,17 @@ const AddDeploymentCard = (props: AddDeploymentCardProps) => { formikSetFieldError('versionId', null) }, [versions, formikSetFieldValue, formikSetFieldError]) + const onProjectsFetched = (projects: Project[] | null) => { + if (projects?.length === 1) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + formikSetFieldValue('project', projects[0]) + // eslint-disable-next-line @typescript-eslint/no-floating-promises + formikSetFieldValue('prefix', projectNameToDeploymentPrefix(projects[0].name)) + } + } + + const currentProject = formik.values.project + return (
@@ -166,33 +166,17 @@ const AddDeploymentCard = (props: AddDeploymentCardProps) => { )} {t('common:projects')} - {fetchProjectsError ? ( - - {t('errors:fetchFailed', { - type: t('common:projects'), - })} - - ) : !projects ? ( - {t('common:loading')} - ) : projects.length === 0 ? ( - - ) : !projects ? ( - {t('common:loading')} - ) : ( - <> - it.name} - selection={currentProject} - onSelectionChange={async it => { - await formik.setFieldValue('projectId', it.id) - await formik.setFieldValue('prefix', projectNameToDeploymentPrefix(it.name)) - }} - /> - {formik.errors.projectId && } - - )} + + { + await formik.setFieldValue('project', it) + await formik.setFieldValue('prefix', projectNameToDeploymentPrefix(it.name)) + }} + errorMessage={formik.errors.project as string} + onProjectsFetched={onProjectsFetched} + /> {currentProject && ( <> @@ -203,7 +187,7 @@ const AddDeploymentCard = (props: AddDeploymentCardProps) => { type: t('common:versions'), })} - ) : !versions && formik.values.projectId ? ( + ) : !versions && formik.values.project ? ( {t('common:loading')} ) : versions.length === 0 ? ( diff --git a/web/crux-ui/src/components/deployments/create-deployment-token-card.tsx b/web/crux-ui/src/components/deployments/create-deployment-token-card.tsx index 04ec65e8f..9496dd6e8 100644 --- a/web/crux-ui/src/components/deployments/create-deployment-token-card.tsx +++ b/web/crux-ui/src/components/deployments/create-deployment-token-card.tsx @@ -1,4 +1,4 @@ -import ShEditor from '@app/components/shared/sh-editor' +import ShEditor from '@app/components/shared/sh-editor-dynamic-module' import DyoButton from '@app/elements/dyo-button' import { DyoCard } from '@app/elements/dyo-card' import DyoChips, { chipsQALabelFromValue } from '@app/elements/dyo-chips' @@ -172,7 +172,7 @@ const CreateDeploymentTokenCard = (props: CreateDeploymentTokenCardProps) => {
- + )}
diff --git a/web/crux-ui/src/components/main/sidebar.tsx b/web/crux-ui/src/components/main/sidebar.tsx index f292f001b..81ed9b3cf 100644 --- a/web/crux-ui/src/components/main/sidebar.tsx +++ b/web/crux-ui/src/components/main/sidebar.tsx @@ -2,6 +2,7 @@ import DyoIcon from '@app/elements/dyo-icon' import DyoLink from '@app/elements/dyo-link' import useTeamRoutes from '@app/hooks/use-team-routes' import { + ROUTE_COMPOSER, ROUTE_DOCS, ROUTE_INDEX, ROUTE_LOGOUT, @@ -78,6 +79,11 @@ export const sidebarSectionsOf = (routes: TeamRoutes): MenuSection[] => [ text: 'templates', link: ROUTE_TEMPLATES, }, + { + icon: '/composer.svg', + text: 'composer', + link: ROUTE_COMPOSER, + }, ], }, { diff --git a/web/crux-ui/src/components/nodes/dyo-node-setup.tsx b/web/crux-ui/src/components/nodes/dyo-node-setup.tsx index 5e84b56e9..8072d7ab0 100644 --- a/web/crux-ui/src/components/nodes/dyo-node-setup.tsx +++ b/web/crux-ui/src/components/nodes/dyo-node-setup.tsx @@ -24,7 +24,7 @@ import { import { sendForm, writeToClipboard } from '@app/utils' import { nodeGenerateScriptSchema } from '@app/validations' import useTranslation from 'next-translate/useTranslation' -import ShEditor from '../shared/sh-editor' +import ShEditor from '../shared/sh-editor-dynamic-module' const expiresIn = (expireAt: Date): number => { const now = new Date().getTime() @@ -250,7 +250,7 @@ const DyoNodeSetup = (props: DyoNodeSetupProps) => {
{t('script')} - +
)} diff --git a/web/crux-ui/src/components/nodes/node-sections-heading.tsx b/web/crux-ui/src/components/nodes/node-sections-heading.tsx index 66bedf997..436f2997e 100644 --- a/web/crux-ui/src/components/nodes/node-sections-heading.tsx +++ b/web/crux-ui/src/components/nodes/node-sections-heading.tsx @@ -22,7 +22,7 @@ const NodeSectionsHeading = (props: NodeSectionsHeadingProps) => { className="mx-6" onClick={() => setSection('containers')} > - {t('containers')} + {t('common:containers')} Promise + errorMessage?: string | null + onProjectsFetched?: (projects: Project[] | null) => void +} + +const SelectProjectChips = (props: SelectProjectChipsProps) => { + const { + className, + name, + selection, + onSelectionChange, + errorMessage, + onProjectsFetched, + types = ['versioned', 'versionless'], + } = props + + const { t } = useTranslation('common') + const routes = useTeamRoutes() + + const { data: projectsData, error: fetchProjectsError } = useSWR(routes.project.api.list(), fetcher) + const projects = projectsData?.filter(it => types.includes(it.type)) + + useEffect(() => { + onProjectsFetched?.call(null, projects) + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [projectsData]) + + return fetchProjectsError ? ( + + {t('errors:fetchFailed', { + type: t('common:projects'), + })} + + ) : !projects ? ( + {t('common:loading')} + ) : projects.length === 0 ? ( + + ) : !projects ? ( + {t('common:loading')} + ) : ( + <> + it.name} + selection={selection} + onSelectionChange={onSelectionChange} + /> + {errorMessage && } + + ) +} + +export default SelectProjectChips diff --git a/web/crux-ui/src/components/registries/create-registry-token-card.tsx b/web/crux-ui/src/components/registries/create-registry-token-card.tsx index ff68525cb..f5d150bf5 100644 --- a/web/crux-ui/src/components/registries/create-registry-token-card.tsx +++ b/web/crux-ui/src/components/registries/create-registry-token-card.tsx @@ -1,4 +1,4 @@ -import ShEditor from '@app/components/shared/sh-editor' +import ShEditor from '@app/components/shared/sh-editor-dynamic-module' import DyoButton from '@app/elements/dyo-button' import { DyoCard } from '@app/elements/dyo-card' import DyoChips, { chipsQALabelFromValue } from '@app/elements/dyo-chips' @@ -113,12 +113,12 @@ const CreateRegistryTokenCard = (props: CreateRegistryTokenCardProps) => { {t('tokens:jwtToken')} - +
{t('toUseTheTokenInV2')} - + {t('readMoreAboutV2')} diff --git a/web/crux-ui/src/components/registries/edit-registry-card.tsx b/web/crux-ui/src/components/registries/edit-registry-card.tsx index 302b256fe..23c14dcf2 100644 --- a/web/crux-ui/src/components/registries/edit-registry-card.tsx +++ b/web/crux-ui/src/components/registries/edit-registry-card.tsx @@ -63,6 +63,7 @@ const EditRegistryCard = (props: EditRegistryCardProps) => { public: true, updatedAt: null, inUse: false, + imageUrlPrefix: '', }, ) diff --git a/web/crux-ui/src/components/shared/sh-editor-dynamic-module.tsx b/web/crux-ui/src/components/shared/sh-editor-dynamic-module.tsx new file mode 100644 index 000000000..9502720b6 --- /dev/null +++ b/web/crux-ui/src/components/shared/sh-editor-dynamic-module.tsx @@ -0,0 +1,7 @@ +import dynamic from 'next/dynamic' + +const ShEditor = dynamic(() => import('./sh-editor'), { + ssr: false, +}) + +export default ShEditor diff --git a/web/crux-ui/src/components/shared/sh-editor.tsx b/web/crux-ui/src/components/shared/sh-editor.tsx index 5cc02410b..105978446 100644 --- a/web/crux-ui/src/components/shared/sh-editor.tsx +++ b/web/crux-ui/src/components/shared/sh-editor.tsx @@ -8,16 +8,18 @@ import Editor from 'react-simple-code-editor' interface ShEditorProps { className?: string readOnly?: boolean - value?: string + initialValue?: string + onChange?: (text: string) => void } const ShEditor = (props: ShEditorProps) => { - const { className, readOnly, value } = props + const { className, readOnly, initialValue: value, onChange: propsOnChange } = props const [state, setState] = useState(value) const onChange = (text: string) => { setState(text) + propsOnChange?.call(null, text) } return ( diff --git a/web/crux-ui/src/components/shared/yaml-editor-dynamic-module.tsx b/web/crux-ui/src/components/shared/yaml-editor-dynamic-module.tsx new file mode 100644 index 000000000..9064602dc --- /dev/null +++ b/web/crux-ui/src/components/shared/yaml-editor-dynamic-module.tsx @@ -0,0 +1,7 @@ +import dynamic from 'next/dynamic' + +const YamlEditor = dynamic(() => import('./yaml-editor'), { + ssr: false, +}) + +export default YamlEditor diff --git a/web/crux-ui/src/components/shared/yaml-editor.tsx b/web/crux-ui/src/components/shared/yaml-editor.tsx new file mode 100644 index 000000000..87dec6050 --- /dev/null +++ b/web/crux-ui/src/components/shared/yaml-editor.tsx @@ -0,0 +1,44 @@ +import clsx from 'clsx' +import { highlight, languages } from 'prismjs' +import 'prismjs/components/prism-yaml' +import 'prismjs/themes/prism-tomorrow.css' +import { useRef, useState } from 'react' +import Editor from 'react-simple-code-editor' + +type YamlEditorProps = { + className?: string + readOnly?: boolean + initialValue?: string + onChange: (value: string) => void +} + +const YamlEditor = (props: YamlEditorProps) => { + const { className, readOnly, initialValue, onChange: propsOnChange } = props + + const [state, setState] = useState(initialValue ?? '') + const ref = useRef() + + const onChange = (text: string) => { + setState(text) + propsOnChange(text) + } + + return ( +
+ highlight(newValue, languages.yaml, 'yaml')} + /> +
+ ) +} + +export default YamlEditor diff --git a/web/crux-ui/src/elements/dyo-indicator.tsx b/web/crux-ui/src/elements/dyo-indicator.tsx index ac96b0280..9518cd8b4 100644 --- a/web/crux-ui/src/elements/dyo-indicator.tsx +++ b/web/crux-ui/src/elements/dyo-indicator.tsx @@ -9,7 +9,7 @@ interface DyoIndicatorProps { const DyoIndicator = (props: DyoIndicatorProps) => { const { className, color, title } = props - return
+ return
} export default DyoIndicator diff --git a/web/crux-ui/src/elements/dyo-input.tsx b/web/crux-ui/src/elements/dyo-input.tsx index bd2a0b155..46e82026c 100644 --- a/web/crux-ui/src/elements/dyo-input.tsx +++ b/web/crux-ui/src/elements/dyo-input.tsx @@ -48,7 +48,7 @@ export const DyoInput = forwardRef((props: DyoInputProps, ref: ForwardedRef )} - +
{children} - +
{!hidden && message && !inline ? ( diff --git a/web/crux-ui/src/hooks/use-submit.ts b/web/crux-ui/src/hooks/use-submit.ts index ef30bd69c..1c1cbcd94 100644 --- a/web/crux-ui/src/hooks/use-submit.ts +++ b/web/crux-ui/src/hooks/use-submit.ts @@ -35,8 +35,8 @@ const useSubmit = (): SubmitHook => { stateRef.current.submit = target if (stateRef.current.submitWhenSet) { - stateRef.current.submitWhenSet = false if (target) { + stateRef.current.submitWhenSet = false await target() } } diff --git a/web/crux-ui/src/models/compose.ts b/web/crux-ui/src/models/compose.ts new file mode 100644 index 000000000..13ff325cb --- /dev/null +++ b/web/crux-ui/src/models/compose.ts @@ -0,0 +1,397 @@ +import { v4 as uuid } from 'uuid' +import { + CONTAINER_LOG_DRIVER_VALUES, + CONTAINER_NETWORK_MODE_VALUES, + CONTAINER_VOLUME_TYPE_VALUES, + ContainerConfigData, + ContainerConfigPort, + ContainerConfigPortRange, + ContainerConfigVolume, + ContainerRestartPolicyType, + UniqueKey, + UniqueKeyValue, + VolumeType, +} from './container' +import { VersionType } from './version' +import { Project } from './project' + +export const COMPOSE_RESTART_VALUES = ['no', 'always', 'on-failure', 'unless-stopped'] as const +export type ComposeRestart = (typeof COMPOSE_RESTART_VALUES)[number] + +export type ComposeNamedNetwork = { + name?: string + external?: boolean +} + +export type ComposeNamedVolume = ComposeNamedNetwork + +export const COMPOSE_NETWORK_MODE_VALUES = CONTAINER_NETWORK_MODE_VALUES +export type ComposeNetworkMode = (typeof COMPOSE_NETWORK_MODE_VALUES)[number] + +export const COMPOSE_LOG_DRIVER_VALUES = CONTAINER_LOG_DRIVER_VALUES +export type ComposeLogDriver = (typeof COMPOSE_LOG_DRIVER_VALUES)[number] + +export type ComposeLogging = { + driver?: ComposeLogDriver // defaults to json-file + options?: Record +} + +export type ComposeService = { + container_name?: string + image: string + environment?: string[] + entrypoint?: string | string[] + command?: string | string[] + ports?: (string | number)[] + volumes?: string[] + restart?: ComposeRestart // defaults to no + labels?: string[] + network_mode: ComposeNetworkMode + networks?: string[] + logging?: ComposeLogging + tty?: boolean + working_dir?: string + user?: string // we only support numbers, so '0' will work but root won't + env_file?: string +} + +export type Compose = { + services: Record + volumes: Record + networks: Record +} + +export type DotEnvironment = { + name: string + errorMessage?: string + environment: Record +} + +export type ConvertedContainer = { + image: string + config: ContainerConfigData +} + +export const COMPOSE_TARGET_TYPE_VALUES = ['new-project', 'existing-project'] as const +export type ComposeTargetType = (typeof COMPOSE_TARGET_TYPE_VALUES)[number] + +export type ComposeGenerateVersion = { + targetType: ComposeTargetType + versionName: string + versionType: VersionType + projectName: string + project: Project | null +} + +const splitPortRange = (range: string): [number, number] | null => { + if (!range) { + return null + } + + const [from, to] = range.split('-') + if (!to) { + return null + } + + return [Number.parseInt(from, 10), Number.parseInt(to, 10)] +} + +const mapPort = (port: string | number): [ContainerConfigPort, ContainerConfigPortRange] => { + try { + if (typeof port === 'number') { + return [ + { + id: uuid(), + internal: port, + }, + null, + ] + } + + // string port + let [external, internal] = port.split(':', 2) + + if (!internal) { + // it only internal part + internal = external + external = null + } + + const internalRange = splitPortRange(internal) + if (!internalRange) { + return [ + { + id: uuid(), + internal: Number.parseInt(internal, 10), + external: external ? Number.parseInt(external, 10) : null, + }, + null, + ] + } + + const externalRange = splitPortRange(external) + const [internalFrom, internalTo] = internalRange + const [externalFrom, externalTo] = externalRange ?? [null, null] + + return [ + null, + { + id: uuid(), + internal: { + from: internalFrom, + to: internalTo, + }, + external: externalRange + ? { + from: externalFrom, + to: externalTo, + } + : null, + }, + ] + } catch { + return null + } +} + +const mapVolume = (volume: string): ContainerConfigVolume => { + const [name, path, type] = volume.split(':', 3) + + return { + id: uuid(), + name: path ? name : '', + path: path || name, + type: type && CONTAINER_VOLUME_TYPE_VALUES.includes(type as VolumeType) ? (type as VolumeType) : null, + } +} + +const mapRestart = (restart: ComposeRestart): ContainerRestartPolicyType => { + switch (restart) { + case 'no': + return 'no' + case 'always': + return 'always' + case 'on-failure': + return 'onFailure' + case 'unless-stopped': + return 'unlessStopped' + default: + return 'no' + } +} + +const mapUser = (user: string): number => { + try { + return Number.parseInt(user, 10) + } catch { + return -1 + } +} + +const mapKeyValues = (items: string[] | null): UniqueKeyValue[] | null => + items?.map(it => { + const [key, value] = it.split('=') + + return { + id: uuid(), + key, + value, + } + }) + +const mapStringOrStringArray = (candidate: string | string[]): UniqueKey[] => + !candidate + ? null + : (typeof candidate === 'string' ? [candidate] : candidate).map(key => ({ + id: uuid(), + key, + })) + +export const mapComposeServiceToContainerConfig = ( + service: ComposeService, + serviceKey: string, +): ContainerConfigData => { + const ports: ContainerConfigPort[] = [] + const portRanges: ContainerConfigPortRange[] = [] + service.ports?.forEach(it => { + const [port, portRange] = mapPort(it) + if (port) { + ports.push(port) + } else if (portRange) { + portRanges.push(portRange) + } + }) + + return { + name: service.container_name ?? serviceKey, + environment: mapKeyValues(service.environment), + commands: mapStringOrStringArray(service.entrypoint), + args: mapStringOrStringArray(service.command), + ports: ports.length > 0 ? ports : null, + portRanges: portRanges.length > 0 ? portRanges : null, + volumes: service.volumes?.map(it => mapVolume(it)), + restartPolicy: service.restart ? mapRestart(service.restart) : null, + dockerLabels: mapKeyValues(service.labels), + networkMode: service.network_mode, + networks: service.networks?.map(key => ({ + id: uuid(), + key, + })), + logConfig: !service.logging + ? null + : { + driver: service.logging.driver, + options: !service.logging.options + ? [] + : Object.entries(service.logging.options).map(entry => { + const [key, value] = entry + return { + id: uuid(), + key, + value, + } + }), + }, + tty: service.tty, + workingDirectory: service.working_dir, + user: mapUser(service.user), + expose: 'none', + capabilities: [], + deploymentStrategy: 'recreate', + proxyHeaders: false, + useLoadBalancer: false, + } +} + +export const mapComposeServices = (compose: Compose): ConvertedContainer[] => + Object.entries(compose.services).map(entry => { + const [key, service] = entry + + return { + image: service.image, + config: mapComposeServiceToContainerConfig(service, key), + } + }) + +class DotEnvApplicator { + constructor(private readonly dotEnv: Record) {} + + applyToString(candidate: string): string { + if (!candidate) { + return candidate + } + + candidate = candidate.replace(/\${[^}]*}/g, subStr => { + const envName = subStr.substring(2, subStr.length - 1) + return this.applyEnvToFoundEnv(envName) + }) + + candidate = candidate.replace(/\$[^{ ]*\s/g, subStr => { + const envName = subStr.substring(1).trim() + return this.applyEnvToFoundEnv(envName) + }) + + return candidate + } + + applyToStringArray(candidate: string[]): string[] { + if (!candidate) { + return candidate + } + + return candidate.map(it => this.applyToString(it)) + } + + applyToEnum(candidate: T): T { + return this.applyToString(candidate) as T + } + + applyToStringOrStringArray(candidate: string | string[]): string | string[] { + if (!candidate) { + return candidate + } + + if (typeof candidate === 'string') { + return this.applyToString(candidate) + } + + return this.applyToStringArray(candidate) + } + + applyToStringOrNumberArray(candidate: (string | number)[]): (string | number)[] { + if (!candidate) { + return candidate + } + + return candidate.map(it => { + if (typeof it === 'number') { + return it + } + + return this.applyToString(it) + }) + } + + applyToObjectValues(candidate: Record): Record { + if (!candidate) { + return candidate + } + + const result = Object.entries(candidate).map(it => { + const [key, value] = it + + return [key, this.applyToString(value)] + }) + + return Object.fromEntries(result) + } + + applyToBoolean(candidate: any): boolean { + if (typeof candidate !== 'string') { + return candidate + } + + const result = this.applyToString(candidate) + return result === 'true' + } + + private applyEnvToFoundEnv(key: string): string { + const [name, defaultValue] = key.split(':-') + if (defaultValue) { + key = name + } + + return this.dotEnv[key] ?? defaultValue ?? `\${${key}}` + } +} + +export const applyDotEnvToComposeService = ( + compose: ComposeService, + environment: Record, +): ComposeService => { + const dotEnv = new DotEnvApplicator(environment) + + return { + container_name: dotEnv.applyToString(compose.container_name), + image: dotEnv.applyToString(compose.image), + environment: dotEnv.applyToStringArray(compose.environment), + entrypoint: dotEnv.applyToStringOrStringArray(compose.entrypoint), + command: dotEnv.applyToStringOrStringArray(compose.command), + ports: dotEnv.applyToStringOrNumberArray(compose.ports), + volumes: dotEnv.applyToStringArray(compose.volumes), + restart: dotEnv.applyToEnum(compose.restart), + labels: dotEnv.applyToStringArray(compose.labels), + network_mode: dotEnv.applyToEnum(compose.network_mode), + networks: dotEnv.applyToStringArray(compose.networks), + logging: !compose.logging + ? compose.logging + : { + driver: dotEnv.applyToEnum(compose.logging.driver), + options: dotEnv.applyToObjectValues(compose.logging.options), + }, + tty: dotEnv.applyToBoolean(compose.tty), + working_dir: dotEnv.applyToString(compose.working_dir), + user: dotEnv.applyToString(compose.user), + env_file: compose.env_file, + } +} diff --git a/web/crux-ui/src/models/image.ts b/web/crux-ui/src/models/image.ts index 0cfbac18b..6f7285b64 100644 --- a/web/crux-ui/src/models/image.ts +++ b/web/crux-ui/src/models/image.ts @@ -29,6 +29,11 @@ export type PatchVersionImage = { export type ViewState = 'editor' | 'json' +export type AddImages = { + registryId: string + images: string[] +} + // ws export const WS_TYPE_ADD_IMAGES = 'add-images' diff --git a/web/crux-ui/src/models/index.ts b/web/crux-ui/src/models/index.ts index c83a59e50..7c3efe58d 100644 --- a/web/crux-ui/src/models/index.ts +++ b/web/crux-ui/src/models/index.ts @@ -20,3 +20,4 @@ export * from './template' export * from './token' export * from './user' export * from './version' +export * from './compose' diff --git a/web/crux-ui/src/models/registry.ts b/web/crux-ui/src/models/registry.ts index d05b2ed89..79c24af60 100644 --- a/web/crux-ui/src/models/registry.ts +++ b/web/crux-ui/src/models/registry.ts @@ -21,6 +21,7 @@ export type Registry = BasicRegistry & { icon?: string description?: string url: string + imageUrlPrefix: string } export type RegistryDetailsBase = Omit & { @@ -146,7 +147,7 @@ export type RegistryDetailsDto = RegistryDetails & { type UpsertRegistryDtoBase = Omit< RegistryDetails, - 'id' | 'updatedAt' | 'createdAt' | 'inUse' | 'token' | 'changeCredentials' + 'id' | 'updatedAt' | 'createdAt' | 'inUse' | 'token' | 'changeCredentials' | 'imageUrlPrefix' > export type CreateRegistryDto = UpsertRegistryDtoBase & { details: Omit< @@ -371,3 +372,30 @@ export const editableRegistryToDto = (ui: EditableRegistry): CreateRegistryDto = throw new Error(`Unknown registry type on: ${dto}`) } } + +export const imageUrlOfImageName = (image: string): string => { + if (!image) { + return null + } + + let [name] = image.split(':') + + if (name.includes('.') && !name.includes('docker.io') && !name.includes(REGISTRY_HUB_URL)) { + return name + } + + // hub image + + name = name.replace('index.docker.io/', '').replace('docker.io/', '').replace(REGISTRY_HUB_URL, '') + + if (name.includes('/') || name.startsWith('library')) { + return `${REGISTRY_HUB_URL}/${name}` + } + + return `${REGISTRY_HUB_URL}/library/${name}` +} + +export const findRegistryByUrl = (registries: Registry[], url: string): Registry | null => + !registries || !url + ? null + : registries.filter(it => it.type !== 'unchecked').find(it => url.startsWith(it.imageUrlPrefix)) diff --git a/web/crux-ui/src/pages/composer.tsx b/web/crux-ui/src/pages/composer.tsx new file mode 100644 index 000000000..edb686973 --- /dev/null +++ b/web/crux-ui/src/pages/composer.tsx @@ -0,0 +1,202 @@ +import ComposeEnvironment from '@app/components/composer/compose-environment' +import ComposeFileCard from '@app/components/composer/compose-file-card' +import ConvertedContainerCard from '@app/components/composer/converted-container' +import DotEnvFileCard from '@app/components/composer/dot-env-file-card' +import GenerateVersionCard from '@app/components/composer/generate-version-card' +import useComposerState, { + activateBottomSection, + activateUpperSection, + addEnvFile, + convertComposeFile, + convertEnvFile, + selectDefaultEnvironment, + selectShowDefaultEnvironment, + toggleShowDefaultDotEnv, +} from '@app/components/composer/use-composer-state' +import { Layout } from '@app/components/layout' +import { BreadcrumbLink } from '@app/components/shared/breadcrumb' +import PageHeading from '@app/components/shared/page-heading' +import DyoButton from '@app/elements/dyo-button' +import { DyoHeading } from '@app/elements/dyo-heading' +import DyoToggle from '@app/elements/dyo-toggle' +import DyoWrap from '@app/elements/dyo-wrap' +import useSubmit from '@app/hooks/use-submit' +import useTeamRoutes from '@app/hooks/use-team-routes' +import { Project, Registry, VersionDetails, findRegistryByUrl, imageUrlOfImageName } from '@app/models' +import { appendTeamSlug } from '@app/providers/team-routes' +import { ROUTE_COMPOSER, ROUTE_INDEX } from '@app/routes' +import { fetcher, redirectTo, teamSlugOrFirstTeam, withContextAuthorization } from '@app/utils' +import clsx from 'clsx' +import { GetServerSidePropsContext } from 'next' +import useTranslation from 'next-translate/useTranslation' +import { useRouter } from 'next/router' +import { useCallback, useEffect } from 'react' +import toast from 'react-hot-toast' +import useSWR from 'swr' + +const ComposerPage = () => { + const { t } = useTranslation('compose') + const router = useRouter() + const routes = useTeamRoutes() + + const { data: registries, error: fetchRegistriesError } = useSWR(routes.registry.api.list(), fetcher) + useEffect(() => { + if (fetchRegistriesError) { + toast.error( + t('errors:fetchFailed', { + type: t('common:registries'), + }), + ) + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [fetchRegistriesError]) + + const [state, dispatch] = useComposerState() + const showDefaultEnv = selectShowDefaultEnvironment(state) + + const hasRegistry = useCallback( + (image: string | null) => { + if (!registries && !image) { + return false + } + + const url = imageUrlOfImageName(image) + return !!findRegistryByUrl(registries, url) + }, + [registries], + ) + + const submit = useSubmit() + + const onComposeFileChange = (text: string) => dispatch(convertComposeFile(t, text)) + const onToggleShowDefaultDotEnv = () => dispatch(toggleShowDefaultDotEnv()) + + const onEnvFileChange = (name: string, text: string) => dispatch(convertEnvFile(t, name, text)) + const onAddDotEnv = () => dispatch(addEnvFile()) + + const onActivateGenerate = () => dispatch(activateUpperSection('generate')) + const onGenerateVersion = () => submit.trigger() + const onDiscardVersion = () => dispatch(activateUpperSection('compose')) + + const onVersionGenerated = async (project: Project, version: VersionDetails) => { + await router.push(routes.project.versions(project.id).details(version.id)) + } + + const defaultDotEnv = selectDefaultEnvironment(state) + + const pageLink: BreadcrumbLink = { + name: t('common:composer'), + url: ROUTE_COMPOSER, + } + + return ( + + + {state.upperSection === 'compose' ? ( + {t('generate')} + ) : ( + <> + + {t('common:discard')} + + + + {t('generate')} + + + )} + + + {state.upperSection === 'compose' ? ( +
+ + + {showDefaultEnv && ( + onEnvFileChange(defaultDotEnv.name, text)} /> + )} +
+ ) : ( + + )} + + {state.upperSection === 'compose' && ( +
+ dispatch(activateBottomSection('containers'))} + > + {t('common:containers')} + + + dispatch(activateBottomSection('environment'))} + > + {t('container:common.environment')} + + +
+ )} + + {state.bottomSection === 'containers' ? ( + state.containers.length > 0 ? ( + + {state.containers.map((it, index) => ( + + ))} + + ) : ( + + {t('pasteYourCompose')} + + ) + ) : state.bottomSection === 'environment' ? ( + + ) : null} +
+ ) +} + +export default ComposerPage + +const getPageServerSideProps = async (context: GetServerSidePropsContext) => { + const teamSlug = await teamSlugOrFirstTeam(context) + if (!teamSlug) { + return redirectTo(ROUTE_INDEX) + } + + return { + props: appendTeamSlug(teamSlug, {}), + } +} + +export const getServerSideProps = withContextAuthorization(getPageServerSideProps) diff --git a/web/crux-ui/src/routes.ts b/web/crux-ui/src/routes.ts index f165510e7..4e0a2c83d 100644 --- a/web/crux-ui/src/routes.ts +++ b/web/crux-ui/src/routes.ts @@ -26,6 +26,7 @@ export const ROUTE_TEAMS = '/teams' export const ROUTE_TEAMS_CREATE = '/teams/create' export const ROUTE_TEMPLATES = '/templates' +export const ROUTE_COMPOSER = '/composer' export const API_AUTH_REGISTER = '/api/auth/register' export const API_AUTH_LOGIN = '/api/auth/login' diff --git a/web/crux-ui/src/validations/common.ts b/web/crux-ui/src/validations/common.ts index 3a4a065f0..548bcd2da 100644 --- a/web/crux-ui/src/validations/common.ts +++ b/web/crux-ui/src/validations/common.ts @@ -1,7 +1,7 @@ import { DYO_ICONS } from '@app/elements/dyo-icon-picker' -import yup from './yup' import { Translate } from 'next-translate' import { ValidationError } from 'yup' +import yup from './yup' export type ErrorWithPath = { path: string @@ -89,6 +89,8 @@ export const nameRule = yup.string().required().trim().min(3).max(70).label('com export const descriptionRule = yup.string().optional().label('common:description') export const identityNameRule = yup.string().trim().max(16) export const passwordLengthRule = yup.string().min(8).max(70).label('common:password') +export const stringArrayRule = yup.array().of(yup.string()) +export const portRule = yup.number().min(1).max(65565).label('common:port') export const REGEX_ERROR_NO_WHITESPACES = { regex: 'errors:yup.string.notContainWhitespaces' } @@ -97,3 +99,19 @@ export const matchNoWhitespace = (schema: yup.StringSchema) => schema.matches(/^[^\s]+(\s+[^\s]+)*$/g, { message: REGEX_ERROR_NO_WHITESPACES }) // any characters but no trailing whitespaces + +export const matchValues = (name: string, valueSchema: yup.AnySchema) => + yup + .object() + .label('services') + .test(name, obj => { + if (!obj) { + return true + } + + Object.entries(obj).forEach(entry => { + const [serviceName, value] = entry + valueSchema.label(`service '${serviceName}'`).validateSync(value) + }) + return true + }) diff --git a/web/crux-ui/src/validations/compose.ts b/web/crux-ui/src/validations/compose.ts new file mode 100644 index 000000000..2cbc7bd00 --- /dev/null +++ b/web/crux-ui/src/validations/compose.ts @@ -0,0 +1,92 @@ +import { + COMPOSE_LOG_DRIVER_VALUES, + COMPOSE_NETWORK_MODE_VALUES, + COMPOSE_RESTART_VALUES, + COMPOSE_TARGET_TYPE_VALUES, + ComposeLogDriver, + ComposeNetworkMode, + ComposeRestart, + ComposeTargetType, + VERSION_TYPE_VALUES, + VersionType, +} from '@app/models' +import * as yup from 'yup' +import { matchValues, nameRule, portRule, stringArrayRule } from './common' + +const mixedStringOrStringArrayRule = yup.mixed().when({ + is: it => typeof it === 'string', + then: () => yup.string(), + otherwise: () => stringArrayRule, +}) + +export const composeNamedNetworkSchema = yup.object().shape({ + name: yup.string().optional().nullable(), + external: yup.bool().optional().nullable(), +}) + +export const composeNamedVolumeSchema = composeNamedNetworkSchema + +export const composeServiceSchema = yup.object().shape({ + container_name: yup.string().optional().nullable(), + image: yup.string(), + environment: yup.array().of(yup.string().test('environment', 'Invalid environment', it => it.includes('='))), + entrypoint: mixedStringOrStringArrayRule.label('compose:entrypoint').optional().nullable(), + command: mixedStringOrStringArrayRule.label('container:common.command').optional().nullable(), + ports: yup + .array() + .of( + yup.mixed().when({ + is: it => typeof it === 'string', + then: () => yup.string(), + otherwise: () => portRule, + }), + ) + .optional() + .nullable(), + volumes: stringArrayRule.label('container:common.volumes').optional().nullable(), + restart: yup.mixed().oneOf(COMPOSE_RESTART_VALUES).optional().nullable(), + labels: stringArrayRule.label('container:crane.labels').optional().nullable(), + network_mode: yup.mixed().oneOf(COMPOSE_NETWORK_MODE_VALUES).optional().nullable(), + networks: stringArrayRule.label('container:dagent.networks').optional().nullable(), + logging: yup + .object() + .shape({ + driver: yup.mixed().oneOf(COMPOSE_LOG_DRIVER_VALUES).optional().nullable(), + options: matchValues('logging.options', yup.string()), + }) + .optional() + .nullable(), + tty: yup.bool().optional().nullable(), + working_dir: yup.string().optional().nullable(), + user: yup.string().transform(it => { + if (Number.isNaN(it)) { + throw new yup.ValidationError('Invalid user') + } + + return it + }), + env_file: yup.string().optional().nullable(), +}) + +export const composeSchema = yup + .object() + .label('compose') + .shape({ + services: matchValues('services', composeServiceSchema), + }) + +export const generateVersionSchema = yup.object().shape({ + targetType: yup.mixed().oneOf(COMPOSE_TARGET_TYPE_VALUES), + versionName: nameRule, + versionType: yup.mixed().oneOf(VERSION_TYPE_VALUES), + projectName: nameRule.when('targetType', { + is: it => it === 'existing-project', + then: s => s.optional().nullable(), + otherwise: s => s.required(), + }), + project: yup.object().when('targetType', { + is: it => it === 'existing-project', + then: s => s.required(), + otherwise: s => s.optional().nullable(), + }), +}) diff --git a/web/crux-ui/src/validations/deployment.ts b/web/crux-ui/src/validations/deployment.ts index 135d2105c..257200a5a 100644 --- a/web/crux-ui/src/validations/deployment.ts +++ b/web/crux-ui/src/validations/deployment.ts @@ -31,7 +31,7 @@ export const createFullDeploymentSchema = yup.object().shape({ nodeId: yup.string().required(), prefix: prefixRule, versionId: yup.string().required().label('common:versions'), - projectId: yup.string().required().label('common:projects'), + project: yup.object().required().label('common:projects'), note: yup.string().nullable().optional().label('common:note'), }) diff --git a/web/crux-ui/src/validations/index.ts b/web/crux-ui/src/validations/index.ts index 8104eadfb..9561cfea8 100644 --- a/web/crux-ui/src/validations/index.ts +++ b/web/crux-ui/src/validations/index.ts @@ -13,5 +13,6 @@ export * from './registry' export * from './storage' export * from './team' export * from './template' +export * from './compose' export * from './token' export * from './version' diff --git a/web/crux-ui/tailwind.config.js b/web/crux-ui/tailwind.config.js index f25df3764..58dd3e708 100644 --- a/web/crux-ui/tailwind.config.js +++ b/web/crux-ui/tailwind.config.js @@ -41,9 +41,11 @@ module.exports = { }, height: { 128: '32rem', + 'screen-2/3': '66.666667vh', }, maxHeight: { 128: '32rem', + 'screen-2/3': '66.666667vh', }, boxShadow: { modal: '0 0 20px 5px rgb(0 0 0 / 0.25)', diff --git a/web/crux/package-lock.json b/web/crux/package-lock.json index 9e07ebb96..cfdf0d58d 100644 --- a/web/crux/package-lock.json +++ b/web/crux/package-lock.json @@ -779,11 +779,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", - "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" @@ -883,6 +883,144 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@css-inline/css-inline": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline/-/css-inline-0.13.0.tgz", + "integrity": "sha512-ZozAXBiW1I8hf6eW5eTNqhxUdNOBxrNNxxUnQRiKQpWcs5ORuGaiWwV5focMBTJ5WXGN+Z8VLP93BOwWFPzCJw==", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@css-inline/css-inline-darwin-arm64": "0.13.0", + "@css-inline/css-inline-darwin-x64": "0.13.0", + "@css-inline/css-inline-linux-arm-gnueabihf": "0.13.0", + "@css-inline/css-inline-linux-arm64-gnu": "0.13.0", + "@css-inline/css-inline-linux-arm64-musl": "0.13.0", + "@css-inline/css-inline-linux-x64-gnu": "0.13.0", + "@css-inline/css-inline-linux-x64-musl": "0.13.0", + "@css-inline/css-inline-win32-x64-msvc": "0.13.0" + } + }, + "node_modules/@css-inline/css-inline-darwin-arm64": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-arm64/-/css-inline-darwin-arm64-0.13.0.tgz", + "integrity": "sha512-A4QvlZdhp8v+3IHKF/UftRf5GrAVUMEHCGRuk2Dx594xn/UR4ieh+B70aMm5rfONh2hv5mlR9UcoYAkVpEQ99g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-darwin-x64": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-x64/-/css-inline-darwin-x64-0.13.0.tgz", + "integrity": "sha512-px9z4ypzeECMyBEtlrNzTMpA1tnw5MmMIiMkBRhb8UGRr2pOBZY3yd/eEIxWzVVSPt0aIjVDwUOJ3+d0Z+BskA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-arm-gnueabihf": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm-gnueabihf/-/css-inline-linux-arm-gnueabihf-0.13.0.tgz", + "integrity": "sha512-+uo0coLQNgk/AKeOB8mXSRd8VIlUg38zRSB9B9q0ior9oBCDPtEdn1HuCSvWxHoOSJ8QNNk+uwbz0zW4CETzFw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-arm64-gnu": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-gnu/-/css-inline-linux-arm64-gnu-0.13.0.tgz", + "integrity": "sha512-GVrsFbY5l0Hxyzxsm5S5JPGObvHm/Ybf2wZgnWBsQigxqGtr1FL535HaTwEnq6aHOpH3f08gR5Vx33gB7jG4pw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-arm64-musl": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-musl/-/css-inline-linux-arm64-musl-0.13.0.tgz", + "integrity": "sha512-V5h5+CRnE01EgoafI/kyjEcM8zvN+sKLnp17Aq9LqQfsut7mO3i72d8g/xeVC37DCLoGQFLvDCzbze2NbF2dIQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-x64-gnu": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-gnu/-/css-inline-linux-x64-gnu-0.13.0.tgz", + "integrity": "sha512-vbRV++73MW7dvz/AIbozkv4R68/k/sEp57hno/L6lx034VYxpCwdfqtGN4D0W1TOTzdr2b6qBOGNZ1oLKQZOQQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-x64-musl": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-musl/-/css-inline-linux-x64-musl-0.13.0.tgz", + "integrity": "sha512-2tCnwU23W/yMs9cGc2/i2jd9y2pjuntx0a5OytqX7s9fvUtmI3nc0Od6wuf51LnmdU+XAU8HLT9pZppsQiwPfQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-win32-x64-msvc": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-win32-x64-msvc/-/css-inline-win32-x64-msvc-0.13.0.tgz", + "integrity": "sha512-6VFhFSXp4FH+NzJhLd6fFi7jKCPvIRW+vq0tV+CPuiQ3zPzMfC9nIk8sB/1VJR8EcvBAjMV53YnacuDjRFRT9g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1514,11 +1652,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jonkemp/package-utils": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@jonkemp/package-utils/-/package-utils-1.0.8.tgz", - "integrity": "sha512-bIcKnH5YmtTYr7S6J3J86dn/rFiklwRpOqbTOQ9C0WMmR9FKHVb3bxs2UYfqEmNb93O4nbA97sb6rtz33i9SyA==" - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -1593,31 +1726,31 @@ } }, "node_modules/@nestjs-modules/mailer": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@nestjs-modules/mailer/-/mailer-1.9.1.tgz", - "integrity": "sha512-9kSDgg4qA6+2BXOzfY4IltL70uMGXDeE8u/dhkzM2gnCCOKu8Y+wIxWmh8xyLGYcrFHQ3Mke+ap0O1T98Tyjaw==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@nestjs-modules/mailer/-/mailer-1.11.2.tgz", + "integrity": "sha512-k07wyKbtCzxWMm6IqGwcGIisnXD/6sneGvUR8rBBZbxtLn1HE1FLGyiaXBrPui/0K7W41aS9x9jAIhfTawtlUg==", "dependencies": { - "glob": "10.3.3", - "inline-css": "4.0.2", - "mjml": "^4.14.1", + "@css-inline/css-inline": "0.13.0", + "glob": "10.3.10", + "mjml": "4.15.3", "preview-email": "3.0.19" }, "optionalDependencies": { - "@types/ejs": "^3.1.2", - "@types/pug": "2.0.6", + "@types/ejs": "^3.1.5", + "@types/pug": "^2.0.10", "ejs": "^3.1.9", - "handlebars": "^4.7.7", + "handlebars": "^4.7.8", "pug": "^3.0.2" }, "peerDependencies": { - "@nestjs/common": "^7.0.9 || ^8.0.0 || ^9.0.0 || ^10.0.0", - "@nestjs/core": "^7.0.9 || ^8.0.0 || ^9.0.0 || ^10.0.0", - "@types/ejs": "^3.0.3", - "@types/pug": "2.0.6", - "ejs": "^3.1.2", - "handlebars": "^4.7.6", - "nodemailer": "^6.4.6", - "pug": "^3.0.1" + "@nestjs/common": ">=7.0.9", + "@nestjs/core": ">=7.0.9", + "@types/ejs": ">=3.0.3", + "@types/pug": ">=2.0.6", + "ejs": ">=3.1.2", + "handlebars": ">=4.7.6", + "nodemailer": ">=6.4.6", + "pug": ">=3.0.1" } }, "node_modules/@nestjs-modules/mailer/node_modules/brace-expansion": { @@ -1629,18 +1762,18 @@ } }, "node_modules/@nestjs-modules/mailer/node_modules/glob": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", - "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", + "jackspeak": "^2.3.5", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" }, "bin": { - "glob": "dist/cjs/src/bin.js" + "glob": "dist/esm/bin.mjs" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -1650,9 +1783,9 @@ } }, "node_modules/@nestjs-modules/mailer/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -1664,9 +1797,9 @@ } }, "node_modules/@nestjs-modules/mailer/node_modules/minipass": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", - "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "engines": { "node": ">=16 || 14 >=14.17" } @@ -2371,14 +2504,6 @@ "@sinonjs/commons": "^3.0.0" } }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "engines": { - "node": ">= 6" - } - }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -2470,9 +2595,9 @@ "dev": true }, "node_modules/@types/ejs": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.2.tgz", - "integrity": "sha512-ZmiaE3wglXVWBM9fyVC17aGPkLo/UgaOjEiI2FXQfyczrCefORPxIe+2dVmnmk3zkVIbizjrlQzmPGhSYGXG5g==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", + "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", "optional": true }, "node_modules/@types/eslint": { @@ -2649,9 +2774,9 @@ } }, "node_modules/@types/pug": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.6.tgz", - "integrity": "sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", + "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", "optional": true }, "node_modules/@types/qs": { @@ -3081,9 +3206,12 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/abort-controller": { "version": "3.0.0", @@ -3462,17 +3590,6 @@ "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==" }, - "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -3481,9 +3598,9 @@ "peer": true }, "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "optional": true }, "node_modules/asynckit": { @@ -3667,11 +3784,6 @@ } ] }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" - }, "node_modules/bignumber.js": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", @@ -4244,11 +4356,6 @@ "node": ">= 6" } }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4335,11 +4442,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -4391,14 +4493,6 @@ "node": ">= 8" } }, - "node_modules/css-rules": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/css-rules/-/css-rules-1.1.0.tgz", - "integrity": "sha512-7L6krLIRwAEVCaVKyCEL6PQjQXUmf8DM9bWYKutlZd0DqOe0SiKIGQOkFb59AjDBb+3If7SDp3X8UlzDAgYSow==", - "dependencies": { - "cssom": "^0.5.0" - } - }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -4425,11 +4519,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" - }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -4437,14 +4526,6 @@ "dev": true, "peer": true }, - "node_modules/data-uri-to-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", - "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", - "engines": { - "node": ">= 6" - } - }, "node_modules/dateformat": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", @@ -4531,7 +4612,8 @@ "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, "node_modules/deepmerge": { "version": "4.3.1", @@ -4568,19 +4650,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/degenerator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.0.tgz", - "integrity": "sha512-pdRxyYVe0unlUE/eeXBxFdB8w8J7QNNf7hFE/BKOAlTCz0bkF9h1MC82ii0r1ypqB/PTKYDbg4K9SZT9yfd9Fg==", - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^1.14.3", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4627,15 +4696,6 @@ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -4831,9 +4891,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "optional": true, "dependencies": { "jake": "^10.8.5" @@ -4876,9 +4936,9 @@ } }, "node_modules/encoding-japanese": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.0.0.tgz", - "integrity": "sha512-++P0RhebUC8MJAwJOsT93dT+5oc5oPImp1HubZpAuCZ5kTLnhuuBhKHj2jJeO/Gj93idPBWmIuQ9QWMe5rX3pQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.1.0.tgz", + "integrity": "sha512-58XySVxUgVlBikBTbQ8WdDxBDHIdXucB16LO5PBHR8t75D54wQrNo4cg+58+R1CtJfKnsVsvt9XlteRaR8xw1w==", "engines": { "node": ">=8.10.0" } @@ -5092,91 +5152,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=4.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/escodegen/node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/escodegen/node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/eslint": { "version": "8.46.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", @@ -5719,6 +5694,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -5883,17 +5859,6 @@ "node": ">=4" } }, - "node_modules/extract-css": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extract-css/-/extract-css-3.0.1.tgz", - "integrity": "sha512-mLNcMxYX7JVPcGUw7pgjczasLnvimYGlXFWuSx2YQ421sZDlBq4Dh0UzsSeXutf80Z0P2BtV5ZZt0FbaWTOxsQ==", - "dependencies": { - "batch": "^0.6.1", - "href-content": "^2.0.2", - "list-stylesheets": "^2.0.1", - "style-data": "^2.0.1" - } - }, "node_modules/fast-copy": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.1.tgz", @@ -5929,7 +5894,8 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, "node_modules/fast-redact": { "version": "3.1.2", @@ -5996,14 +5962,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/file-uri-to-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz", - "integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==", - "engines": { - "node": ">= 6" - } - }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -6167,11 +6125,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/flat-util": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/flat-util/-/flat-util-1.1.9.tgz", - "integrity": "sha512-BOTMw/6rbbxVjv5JQvwgGMc2/6wWGd2VeyTvnzvvE49VRjS0tTxLbry/QVP1yPw8SaAOBYsnixmzruXoqjdUHA==" - }, "node_modules/flatted": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", @@ -6272,20 +6225,6 @@ "node": ">= 6" } }, - "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "dependencies": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -6338,39 +6277,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/ftp": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==", - "dependencies": { - "readable-stream": "1.1.x", - "xregexp": "2.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ftp/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, - "node_modules/ftp/node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/ftp/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -6508,55 +6414,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-uri": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz", - "integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==", - "dependencies": { - "@tootallnate/once": "1", - "data-uri-to-buffer": "3", - "debug": "4", - "file-uri-to-path": "2", - "fs-extra": "^8.1.0", - "ftp": "^0.3.10" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/get-uri/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/get-uri/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/get-uri/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -6591,6 +6453,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -6599,6 +6462,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", + "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -6857,22 +6721,6 @@ "node": ">= 6" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "engines": { - "node": ">=8" - } - }, - "node_modules/href-content": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/href-content/-/href-content-2.0.2.tgz", - "integrity": "sha512-f/e40VYI+KciPGfFzfdw1wu8dptpUA9rYQJNbpYVRI217lyuo7nBNO7BjYfTiQMhU/AthfvPDMvj46uAgzUccQ==", - "dependencies": { - "remote-content": "^3.0.1" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -6952,19 +6800,6 @@ "node": ">= 0.8" } }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -7087,23 +6922,6 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, - "node_modules/inline-css": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/inline-css/-/inline-css-4.0.2.tgz", - "integrity": "sha512-o8iZBpVRCs+v8RyEWKxB+4JRi6A4Wop6f3zzqEi0xVx2eIevbgcjXIKYDmQR2ZZ+DD5IVZ6JII0dt2GhJh8etw==", - "dependencies": { - "cheerio": "^1.0.0-rc.12", - "css-rules": "^1.1.0", - "extract-css": "^3.0.1", - "flat-util": "^1.1.9", - "pick-util": "^1.1.5", - "slick": "^1.12.2", - "specificity": "^0.4.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/inquirer": { "version": "8.2.5", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", @@ -7151,11 +6969,6 @@ "node": ">= 0.10" } }, - "node_modules/ip": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", - "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==" - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -7672,9 +7485,9 @@ } }, "node_modules/jackspeak": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", - "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -8330,14 +8143,15 @@ } }, "node_modules/js-beautify": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.9.tgz", - "integrity": "sha512-coM7xq1syLcMyuVGyToxcj2AlzhkDjmfklL8r0JgJ7A76wyGMpJ1oA35mr4APdYNO/o/4YY8H54NQIJzhMbhBg==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz", + "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==", "dependencies": { "config-chain": "^1.1.13", - "editorconfig": "^1.0.3", - "glob": "^8.1.0", - "nopt": "^6.0.0" + "editorconfig": "^1.0.4", + "glob": "^10.3.3", + "js-cookie": "^3.0.5", + "nopt": "^7.2.0" }, "bin": { "css-beautify": "js/bin/css-beautify.js", @@ -8345,7 +8159,66 @@ "js-beautify": "js/bin/js-beautify.js" }, "engines": { - "node": ">=12" + "node": ">=14" + } + }, + "node_modules/js-beautify/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "engines": { + "node": ">=14" } }, "node_modules/js-stringify": { @@ -8490,9 +8363,9 @@ } }, "node_modules/juice": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/juice/-/juice-9.1.0.tgz", - "integrity": "sha512-odblShmPrUoHUwRuC8EmLji5bPP2MLO1GL+gt4XU3tT2ECmbSrrMjtMQaqg3wgMFP2zvUzdPZGfxc5Trk3Z+fQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/juice/-/juice-10.0.0.tgz", + "integrity": "sha512-9f68xmhGrnIi6DBkiiP3rUrQN33SEuaKu1+njX6VgMP+jwZAsnT33WIzlrWICL9matkhYu3OyrqSUP55YTIdGg==", "dependencies": { "cheerio": "^1.0.0-rc.12", "commander": "^6.1.0", @@ -8591,19 +8464,19 @@ } }, "node_modules/libbase64": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.2.1.tgz", - "integrity": "sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz", + "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==" }, "node_modules/libmime": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.2.1.tgz", - "integrity": "sha512-A0z9O4+5q+ZTj7QwNe/Juy1KARNb4WaviO4mYeFC4b8dBT2EEqK2pkM+GC8MVnkOjqhl5nYQxRgnPYRRTNmuSQ==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.5.tgz", + "integrity": "sha512-nSlR1yRZ43L3cZCiWEw7ali3jY29Hz9CQQ96Oy+sSspYnIP5N54ucOPHqooBsXzwrX1pwn13VUE05q4WmzfaLg==", "dependencies": { - "encoding-japanese": "2.0.0", + "encoding-japanese": "2.1.0", "iconv-lite": "0.6.3", - "libbase64": "1.2.1", - "libqp": "2.0.1" + "libbase64": "1.3.0", + "libqp": "2.1.0" } }, "node_modules/libmime/node_modules/iconv-lite": { @@ -8623,9 +8496,9 @@ "integrity": "sha512-McGS7GV/WjJ2KjfOGhJU1oJn29RYeo7Q+RpANRbUNMQ9gj5XArpbjurSuyYPTejFwbaUojstQ4XyWCrAzGOUXw==" }, "node_modules/libqp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.0.1.tgz", - "integrity": "sha512-Ka0eC5LkF3IPNQHJmYBWljJsw0UvM6j+QdKRbWyCdTmYwvIDE6a7bCm0UkTAL/K+3KXK5qXT/ClcInU01OpdLg==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.0.tgz", + "integrity": "sha512-O6O6/fsG5jiUVbvdgT7YX3xY3uIadR6wEZ7+vy9u7PKHAlSEB6blvC1o5pHBjgsi95Uo0aiBBdkyFecj6jtb7A==" }, "node_modules/lines-and-columns": { "version": "1.2.4", @@ -8640,15 +8513,6 @@ "uc.micro": "^2.0.0" } }, - "node_modules/list-stylesheets": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/list-stylesheets/-/list-stylesheets-2.0.1.tgz", - "integrity": "sha512-UUEFowqvgRKT1+OJ59Ga5gTfVOP3hkbFo7DwNIZcMuXzJRWndYMHyDYbuqKe6lrw8KCY7c/GN5mEoLx0c54HAw==", - "dependencies": { - "cheerio": "1.0.0-rc.12", - "pick-util": "^1.1.5" - } - }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -8766,19 +8630,20 @@ } }, "node_modules/mailparser": { - "version": "3.6.7", - "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.6.7.tgz", - "integrity": "sha512-/3x8HW70DNehw+3vdOPKdlLuxOHoWcGB5jfx5vJ5XUbY9/2jUJbrrhda5Si8Dj/3w08U0y5uGAkqs5+SPTPKoA==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.7.1.tgz", + "integrity": "sha512-RCnBhy5q8XtB3mXzxcAfT1huNqN93HTYYyL6XawlIKycfxM/rXPg9tXoZ7D46+SgCS1zxKzw+BayDQSvncSTTw==", "dependencies": { - "encoding-japanese": "2.0.0", + "encoding-japanese": "2.1.0", "he": "1.2.0", "html-to-text": "9.0.5", "iconv-lite": "0.6.3", - "libmime": "5.2.1", + "libmime": "5.3.5", "linkify-it": "5.0.0", "mailsplit": "5.4.0", - "nodemailer": "6.9.9", - "tlds": "1.248.0" + "nodemailer": "6.9.13", + "punycode.js": "2.3.1", + "tlds": "1.252.0" } }, "node_modules/mailparser/node_modules/iconv-lite": { @@ -8802,6 +8667,14 @@ "libqp": "2.0.1" } }, + "node_modules/mailsplit/node_modules/encoding-japanese": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.0.0.tgz", + "integrity": "sha512-++P0RhebUC8MJAwJOsT93dT+5oc5oPImp1HubZpAuCZ5kTLnhuuBhKHj2jJeO/Gj93idPBWmIuQ9QWMe5rX3pQ==", + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/mailsplit/node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -8813,6 +8686,11 @@ "node": ">=0.10.0" } }, + "node_modules/mailsplit/node_modules/libbase64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.2.1.tgz", + "integrity": "sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew==" + }, "node_modules/mailsplit/node_modules/libmime": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.2.0.tgz", @@ -8824,6 +8702,11 @@ "libqp": "2.0.1" } }, + "node_modules/mailsplit/node_modules/libqp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.0.1.tgz", + "integrity": "sha512-Ka0eC5LkF3IPNQHJmYBWljJsw0UvM6j+QdKRbWyCdTmYwvIDE6a7bCm0UkTAL/K+3KXK5qXT/ClcInU01OpdLg==" + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -8862,14 +8745,6 @@ "node": ">= 0.6" } }, - "node_modules/mediaquery-text": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mediaquery-text/-/mediaquery-text-1.2.0.tgz", - "integrity": "sha512-cJyRqgYQi+hsYhRkyd5le0s4LsEPvOB7r+6X3jdEELNqVlM9mRIgyUPg9BzF+PuTqQH1ZekgIjYVOeWSXWq35Q==", - "dependencies": { - "cssom": "^0.5.0" - } - }, "node_modules/memfs": { "version": "3.4.13", "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz", @@ -8992,470 +8867,446 @@ } }, "node_modules/mjml": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml/-/mjml-4.14.1.tgz", - "integrity": "sha512-f/wnWWIVbeb/ge3ff7c/KYYizI13QbGIp03odwwkCThsJsacw4gpZZAU7V4gXY3HxSXP2/q3jxOfaHVbkfNpOQ==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml/-/mjml-4.15.3.tgz", + "integrity": "sha512-bW2WpJxm6HS+S3Yu6tq1DUPFoTxU9sPviUSmnL7Ua+oVO3WA5ILFWqvujUlz+oeuM+HCwEyMiP5xvKNPENVjYA==", "dependencies": { - "@babel/runtime": "^7.14.6", - "mjml-cli": "4.14.1", - "mjml-core": "4.14.1", - "mjml-migrate": "4.14.1", - "mjml-preset-core": "4.14.1", - "mjml-validator": "4.13.0" + "@babel/runtime": "^7.23.9", + "mjml-cli": "4.15.3", + "mjml-core": "4.15.3", + "mjml-migrate": "4.15.3", + "mjml-preset-core": "4.15.3", + "mjml-validator": "4.15.3" }, "bin": { "mjml": "bin/mjml" } }, "node_modules/mjml-accordion": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-accordion/-/mjml-accordion-4.14.1.tgz", - "integrity": "sha512-dpNXyjnhYwhM75JSjD4wFUa9JgHm86M2pa0CoTzdv1zOQz67ilc4BoK5mc2S0gOjJpjBShM5eOJuCyVIuAPC6w==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-accordion/-/mjml-accordion-4.15.3.tgz", + "integrity": "sha512-LPNVSj1LyUVYT9G1gWwSw3GSuDzDsQCu0tPB2uDsq4VesYNnU6v3iLCQidMiR6azmIt13OEozG700ygAUuA6Ng==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-body": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-body/-/mjml-body-4.14.1.tgz", - "integrity": "sha512-YpXcK3o2o1U+fhI8f60xahrhXuHmav6BZez9vIN3ZEJOxPFSr+qgr1cT2iyFz50L5+ZsLIVj2ZY+ALQjdsg8ig==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-body/-/mjml-body-4.15.3.tgz", + "integrity": "sha512-7pfUOVPtmb0wC+oUOn4xBsAw4eT5DyD6xqaxj/kssu6RrFXOXgJaVnDPAI9AzIvXJ/5as9QrqRGYAddehwWpHQ==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-button": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-button/-/mjml-button-4.14.1.tgz", - "integrity": "sha512-V1Tl1vQ3lXYvvqHJHvGcc8URr7V1l/ZOsv7iLV4QRrh7kjKBXaRS7uUJtz6/PzEbNsGQCiNtXrODqcijLWlgaw==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-button/-/mjml-button-4.15.3.tgz", + "integrity": "sha512-79qwn9AgdGjJR1vLnrcm2rq2AsAZkKC5JPwffTMG+Nja6zGYpTDZFZ56ekHWr/r1b5WxkukcPj2PdevUug8c+Q==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-carousel": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-carousel/-/mjml-carousel-4.14.1.tgz", - "integrity": "sha512-Ku3MUWPk/TwHxVgKEUtzspy/ePaWtN/3z6/qvNik0KIn0ZUIZ4zvR2JtaVL5nd30LHSmUaNj30XMPkCjYiKkFA==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-carousel/-/mjml-carousel-4.15.3.tgz", + "integrity": "sha512-3ju6I4l7uUhPRrJfN3yK9AMsfHvrYbRkcJ1GRphFHzUj37B2J6qJOQUpzA547Y4aeh69TSb7HFVf1t12ejQxVw==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-cli": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-cli/-/mjml-cli-4.14.1.tgz", - "integrity": "sha512-Gy6MnSygFXs0U1qOXTHqBg2vZX2VL/fAacgQzD4MHq4OuybWaTNSzXRwxBXYCxT3IJB874n2Q0Mxp+Xka+tnZg==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-cli/-/mjml-cli-4.15.3.tgz", + "integrity": "sha512-+V2TDw3tXUVEptFvLSerz125C2ogYl8klIBRY1m5BHd4JvGVf3yhx8N3PngByCzA6PGcv/eydGQN+wy34SHf0Q==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "chokidar": "^3.0.0", - "glob": "^7.1.1", + "glob": "^10.3.10", "html-minifier": "^4.0.0", "js-beautify": "^1.6.14", "lodash": "^4.17.21", - "mjml-core": "4.14.1", - "mjml-migrate": "4.14.1", - "mjml-parser-xml": "4.14.1", - "mjml-validator": "4.13.0", - "yargs": "^16.1.0" + "minimatch": "^9.0.3", + "mjml-core": "4.15.3", + "mjml-migrate": "4.15.3", + "mjml-parser-xml": "4.15.3", + "mjml-validator": "4.15.3", + "yargs": "^17.7.2" }, "bin": { "mjml-cli": "bin/mjml" } }, - "node_modules/mjml-cli/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/mjml-cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "balanced-match": "^1.0.0" } }, "node_modules/mjml-cli/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mjml-cli/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/mjml-cli/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mjml-cli/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "node_modules/mjml-cli/node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" } }, "node_modules/mjml-column": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-column/-/mjml-column-4.14.1.tgz", - "integrity": "sha512-iixVCIX1YJtpQuwG2WbDr7FqofQrlTtGQ4+YAZXGiLThs0En3xNIJFQX9xJ8sgLEGGltyooHiNICBRlzSp9fDg==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-column/-/mjml-column-4.15.3.tgz", + "integrity": "sha512-hYdEFdJGHPbZJSEysykrevEbB07yhJGSwfDZEYDSbhQQFjV2tXrEgYcFD5EneMaowjb55e3divSJxU4c5q4Qgw==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-core": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-core/-/mjml-core-4.14.1.tgz", - "integrity": "sha512-di88rSfX+8r4r+cEqlQCO7CRM4mYZrfe2wSCu2je38i+ujjkLpF72cgLnjBlSG5aOUCZgYvlsZ85stqIz9LQfA==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-core/-/mjml-core-4.15.3.tgz", + "integrity": "sha512-Dmwk+2cgSD9L9GmTbEUNd8QxkTZtW9P7FN/ROZW/fGZD6Hq6/4TB0zEspg2Ow9eYjZXO2ofOJ3PaQEEShKV0kQ==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "cheerio": "1.0.0-rc.12", "detect-node": "^2.0.4", "html-minifier": "^4.0.0", "js-beautify": "^1.6.14", - "juice": "^9.0.0", + "juice": "^10.0.0", "lodash": "^4.17.21", - "mjml-migrate": "4.14.1", - "mjml-parser-xml": "4.14.1", - "mjml-validator": "4.13.0" + "mjml-migrate": "4.15.3", + "mjml-parser-xml": "4.15.3", + "mjml-validator": "4.15.3" } }, "node_modules/mjml-divider": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-divider/-/mjml-divider-4.14.1.tgz", - "integrity": "sha512-agqWY0aW2xaMiUOhYKDvcAAfOLalpbbtjKZAl1vWmNkURaoK4L7MgDilKHSJDFUlHGm2ZOArTrq8i6K0iyThBQ==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-divider/-/mjml-divider-4.15.3.tgz", + "integrity": "sha512-vh27LQ9FG/01y0b9ntfqm+GT5AjJnDSDY9hilss2ixIUh0FemvfGRfsGVeV5UBVPBKK7Ffhvfqc7Rciob9Spzw==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-group": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-group/-/mjml-group-4.14.1.tgz", - "integrity": "sha512-dJt5batgEJ7wxlxzqOfHOI94ABX+8DZBvAlHuddYO4CsLFHYv6XRIArLAMMnAKU76r6p3X8JxYeOjKZXdv49kg==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-group/-/mjml-group-4.15.3.tgz", + "integrity": "sha512-HSu/rKnGZVKFq3ciT46vi1EOy+9mkB0HewO4+P6dP/Y0UerWkN6S3UK11Cxsj0cAp0vFwkPDCdOeEzRdpFEkzA==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-head": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-head/-/mjml-head-4.14.1.tgz", - "integrity": "sha512-KoCbtSeTAhx05Ugn9TB2UYt5sQinSCb7RGRer5iPQ3CrXj8hT5B5Svn6qvf/GACPkWl4auExHQh+XgLB+r3OEA==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head/-/mjml-head-4.15.3.tgz", + "integrity": "sha512-o3mRuuP/MB5fZycjD3KH/uXsnaPl7Oo8GtdbJTKtH1+O/3pz8GzGMkscTKa97l03DAG2EhGrzzLcU2A6eshwFw==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-head-attributes": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-head-attributes/-/mjml-head-attributes-4.14.1.tgz", - "integrity": "sha512-XdUNOp2csK28kBDSistInOyzWNwmu5HDNr4y1Z7vSQ1PfkmiuS6jWG7jHUjdoMhs27e6Leuyyc6a8gWSpqSWrg==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-attributes/-/mjml-head-attributes-4.15.3.tgz", + "integrity": "sha512-2ISo0r5ZKwkrvJgDou9xVPxxtXMaETe2AsAA02L89LnbB2KC0N5myNsHV0sEysTw9+CfCmgjAb0GAI5QGpxKkQ==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-head-breakpoint": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-head-breakpoint/-/mjml-head-breakpoint-4.14.1.tgz", - "integrity": "sha512-Qw9l/W/I5Z9p7I4ShgnEpAL9if4472ejcznbBnp+4Gq+sZoPa7iYoEPsa9UCGutlaCh3N3tIi2qKhl9qD8DFxA==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-breakpoint/-/mjml-head-breakpoint-4.15.3.tgz", + "integrity": "sha512-Eo56FA5C2v6ucmWQL/JBJ2z641pLOom4k0wP6CMZI2utfyiJ+e2Uuinj1KTrgDcEvW4EtU9HrfAqLK9UosLZlg==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-head-font": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-head-font/-/mjml-head-font-4.14.1.tgz", - "integrity": "sha512-oBYm1gaOdEMjE5BoZouRRD4lCNZ1jcpz92NR/F7xDyMaKCGN6T/+r4S5dq1gOLm9zWqClRHaECdFJNEmrDpZqA==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-font/-/mjml-head-font-4.15.3.tgz", + "integrity": "sha512-CzV2aDPpiNIIgGPHNcBhgyedKY4SX3BJoTwOobSwZVIlEA6TAWB4Z9WwFUmQqZOgo1AkkiTHPZQvGcEhFFXH6g==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-head-html-attributes": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-head-html-attributes/-/mjml-head-html-attributes-4.14.1.tgz", - "integrity": "sha512-vlJsJc1Sm4Ml2XvLmp01zsdmWmzm6+jNCO7X3eYi9ngEh8LjMCLIQOncnOgjqm9uGpQu2EgUhwvYFZP2luJOVg==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-html-attributes/-/mjml-head-html-attributes-4.15.3.tgz", + "integrity": "sha512-MDNDPMBOgXUZYdxhosyrA2kudiGO8aogT0/cODyi2Ed9o/1S7W+je11JUYskQbncqhWKGxNyaP4VWa+6+vUC/g==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-head-preview": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-head-preview/-/mjml-head-preview-4.14.1.tgz", - "integrity": "sha512-89gQtt3fhl2dkYpHLF5HDQXz/RLpzecU6wmAIT7Dz6etjLGE1dgq2Ay6Bu/OeHjDcT1gbM131zvBwuXw8OydNw==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-preview/-/mjml-head-preview-4.15.3.tgz", + "integrity": "sha512-J2PxCefUVeFwsAExhrKo4lwxDevc5aKj888HBl/wN4EuWOoOg06iOGCxz4Omd8dqyFsrqvbBuPqRzQ+VycGmaA==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-head-style": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-head-style/-/mjml-head-style-4.14.1.tgz", - "integrity": "sha512-XryOuf32EDuUCBT2k99C1+H87IOM919oY6IqxKFJCDkmsbywKIum7ibhweJdcxiYGONKTC6xjuibGD3fQTTYNQ==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-style/-/mjml-head-style-4.15.3.tgz", + "integrity": "sha512-9J+JuH+mKrQU65CaJ4KZegACUgNIlYmWQYx3VOBR/tyz+8kDYX7xBhKJCjQ1I4wj2Tvga3bykd89Oc2kFZ5WOw==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-head-title": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-head-title/-/mjml-head-title-4.14.1.tgz", - "integrity": "sha512-aIfpmlQdf1eJZSSrFodmlC4g5GudBti2eMyG42M7/3NeLM6anEWoe+UkF/6OG4Zy0tCQ40BDJ5iBZlMsjQICzw==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-head-title/-/mjml-head-title-4.15.3.tgz", + "integrity": "sha512-IM59xRtsxID4DubQ0iLmoCGXguEe+9BFG4z6y2xQDrscIa4QY3KlfqgKGT69ojW+AVbXXJPEVqrAi4/eCsLItQ==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-hero": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-hero/-/mjml-hero-4.14.1.tgz", - "integrity": "sha512-TQJ3yfjrKYGkdEWjHLHhL99u/meKFYgnfJvlo9xeBvRjSM696jIjdqaPHaunfw4CP6d2OpCIMuacgOsvqQMWOA==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-hero/-/mjml-hero-4.15.3.tgz", + "integrity": "sha512-9cLAPuc69yiuzNrMZIN58j+HMK1UWPaq2i3/Fg2ZpimfcGFKRcPGCbEVh0v+Pb6/J0+kf8yIO0leH20opu3AyQ==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-image": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-image/-/mjml-image-4.14.1.tgz", - "integrity": "sha512-jfKLPHXuFq83okwlNM1Um/AEWeVDgs2JXIOsWp2TtvXosnRvGGMzA5stKLYdy1x6UfKF4c1ovpMS162aYGp+xQ==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-image/-/mjml-image-4.15.3.tgz", + "integrity": "sha512-g1OhSdofIytE9qaOGdTPmRIp7JsCtgO0zbsn1Fk6wQh2gEL55Z40j/VoghslWAWTgT2OHFdBKnMvWtN6U5+d2Q==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-migrate": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-migrate/-/mjml-migrate-4.14.1.tgz", - "integrity": "sha512-d+9HKQOhZi3ZFAaFSDdjzJX9eDQGjMf3BArLWNm2okC4ZgfJSpOc77kgCyFV8ugvwc8fFegPnSV60Jl4xtvK2A==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-migrate/-/mjml-migrate-4.15.3.tgz", + "integrity": "sha512-sr/+35RdxZroNQVegjpfRHJ5hda9XCgaS4mK2FGO+Mb1IUevKfeEPII3F/cHDpNwFeYH3kAgyqQ22ClhGLWNBA==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "js-beautify": "^1.6.14", "lodash": "^4.17.21", - "mjml-core": "4.14.1", - "mjml-parser-xml": "4.14.1", - "yargs": "^16.1.0" + "mjml-core": "4.15.3", + "mjml-parser-xml": "4.15.3", + "yargs": "^17.7.2" }, "bin": { "migrate": "lib/cli.js" } }, - "node_modules/mjml-migrate/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/mjml-migrate/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mjml-migrate/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "engines": { - "node": ">=10" - } - }, "node_modules/mjml-navbar": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-navbar/-/mjml-navbar-4.14.1.tgz", - "integrity": "sha512-rNy1Kw8CR3WQ+M55PFBAUDz2VEOjz+sk06OFnsnmNjoMVCjo1EV7OFLDAkmxAwqkC8h4zQWEOFY0MBqqoAg7+A==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-navbar/-/mjml-navbar-4.15.3.tgz", + "integrity": "sha512-VsKH/Jdlf8Yu3y7GpzQV5n7JMdpqvZvTSpF6UQXL0PWOm7k6+LX+sCZimOfpHJ+wCaaybpxokjWZ71mxOoCWoA==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-parser-xml": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-parser-xml/-/mjml-parser-xml-4.14.1.tgz", - "integrity": "sha512-9WQVeukbXfq9DUcZ8wOsHC6BTdhaVwTAJDYMIQglXLwKwN7I4pTCguDDHy5d0kbbzK5OCVxCdZe+bfVI6XANOQ==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-parser-xml/-/mjml-parser-xml-4.15.3.tgz", + "integrity": "sha512-Tz0UX8/JVYICLjT+U8J1f/TFxIYVYjzZHeh4/Oyta0pLpRLeZlxEd71f3u3kdnulCKMP4i37pFRDmyLXAlEuLw==", "dependencies": { - "@babel/runtime": "^7.14.6", - "detect-node": "2.0.4", - "htmlparser2": "^8.0.1", + "@babel/runtime": "^7.23.9", + "detect-node": "2.1.0", + "htmlparser2": "^9.1.0", "lodash": "^4.17.15" } }, - "node_modules/mjml-parser-xml/node_modules/detect-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" + "node_modules/mjml-parser-xml/node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } }, "node_modules/mjml-preset-core": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-4.14.1.tgz", - "integrity": "sha512-uUCqK9Z9d39rwB/+JDV2KWSZGB46W7rPQpc9Xnw1DRP7wD7qAfJwK6AZFCwfTgWdSxw0PwquVNcrUS9yBa9uhw==", - "dependencies": { - "@babel/runtime": "^7.14.6", - "mjml-accordion": "4.14.1", - "mjml-body": "4.14.1", - "mjml-button": "4.14.1", - "mjml-carousel": "4.14.1", - "mjml-column": "4.14.1", - "mjml-divider": "4.14.1", - "mjml-group": "4.14.1", - "mjml-head": "4.14.1", - "mjml-head-attributes": "4.14.1", - "mjml-head-breakpoint": "4.14.1", - "mjml-head-font": "4.14.1", - "mjml-head-html-attributes": "4.14.1", - "mjml-head-preview": "4.14.1", - "mjml-head-style": "4.14.1", - "mjml-head-title": "4.14.1", - "mjml-hero": "4.14.1", - "mjml-image": "4.14.1", - "mjml-navbar": "4.14.1", - "mjml-raw": "4.14.1", - "mjml-section": "4.14.1", - "mjml-social": "4.14.1", - "mjml-spacer": "4.14.1", - "mjml-table": "4.14.1", - "mjml-text": "4.14.1", - "mjml-wrapper": "4.14.1" + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-4.15.3.tgz", + "integrity": "sha512-1zZS8P4O0KweWUqNS655+oNnVMPQ1Rq1GaZq5S9JfwT1Vh/m516lSmiTW9oko6gGHytt5s6Yj6oOeu5Zm8FoLw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "mjml-accordion": "4.15.3", + "mjml-body": "4.15.3", + "mjml-button": "4.15.3", + "mjml-carousel": "4.15.3", + "mjml-column": "4.15.3", + "mjml-divider": "4.15.3", + "mjml-group": "4.15.3", + "mjml-head": "4.15.3", + "mjml-head-attributes": "4.15.3", + "mjml-head-breakpoint": "4.15.3", + "mjml-head-font": "4.15.3", + "mjml-head-html-attributes": "4.15.3", + "mjml-head-preview": "4.15.3", + "mjml-head-style": "4.15.3", + "mjml-head-title": "4.15.3", + "mjml-hero": "4.15.3", + "mjml-image": "4.15.3", + "mjml-navbar": "4.15.3", + "mjml-raw": "4.15.3", + "mjml-section": "4.15.3", + "mjml-social": "4.15.3", + "mjml-spacer": "4.15.3", + "mjml-table": "4.15.3", + "mjml-text": "4.15.3", + "mjml-wrapper": "4.15.3" } }, "node_modules/mjml-raw": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-raw/-/mjml-raw-4.14.1.tgz", - "integrity": "sha512-9+4wzoXnCtfV6QPmjfJkZ50hxFB4Z8QZnl2Ac0D1Cn3dUF46UkmO5NLMu7UDIlm5DdFyycZrMOwvZS4wv9ksPw==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-raw/-/mjml-raw-4.15.3.tgz", + "integrity": "sha512-IGyHheOYyRchBLiAEgw3UM11kFNmBSMupu2BDdejC6ZiDhEAdG+tyERlsCwDPYtXanvFpGWULIu3XlsUPc+RZw==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-section": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-section/-/mjml-section-4.14.1.tgz", - "integrity": "sha512-Ik5pTUhpT3DOfB3hEmAWp8rZ0ilWtIivnL8XdUJRfgYE9D+MCRn+reIO+DAoJHxiQoI6gyeKkIP4B9OrQ7cHQw==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-section/-/mjml-section-4.15.3.tgz", + "integrity": "sha512-JfVPRXH++Hd933gmQfG8JXXCBCR6fIzC3DwiYycvanL/aW1cEQ2EnebUfQkt5QzlYjOkJEH+JpccAsq3ln6FZQ==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-social": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-social/-/mjml-social-4.14.1.tgz", - "integrity": "sha512-G44aOZXgZHukirjkeQWTTV36UywtE2YvSwWGNfo/8d+k5JdJJhCIrlwaahyKEAyH63G1B0Zt8b2lEWx0jigYUw==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-social/-/mjml-social-4.15.3.tgz", + "integrity": "sha512-7sD5FXrESOxpT9Z4Oh36bS6u/geuUrMP1aCg2sjyAwbPcF1aWa2k9OcatQfpRf6pJEhUZ18y6/WBBXmMVmSzXg==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-spacer": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-spacer/-/mjml-spacer-4.14.1.tgz", - "integrity": "sha512-5SfQCXTd3JBgRH1pUy6NVZ0lXBiRqFJPVHBdtC3OFvUS3q1w16eaAXlIUWMKTfy8CKhQrCiE6m65kc662ZpYxA==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-spacer/-/mjml-spacer-4.15.3.tgz", + "integrity": "sha512-3B7Qj+17EgDdAtZ3NAdMyOwLTX1jfmJuY7gjyhS2HtcZAmppW+cxqHUBwCKfvSRgTQiccmEvtNxaQK+tfyrZqA==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-table": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-table/-/mjml-table-4.14.1.tgz", - "integrity": "sha512-aVBdX3WpyKVGh/PZNn2KgRem+PQhWlvnD00DKxDejRBsBSKYSwZ0t3EfFvZOoJ9DzfHsN0dHuwd6Z18Ps44NFQ==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-table/-/mjml-table-4.15.3.tgz", + "integrity": "sha512-FLx7DcRKTdKdcOCbMyBaeudeHaHpwPveRrBm6WyQe3LXx6FfdmOh59i71/16LFQMgBOD3N4/UJkzxLzlTJzMqQ==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-text": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-text/-/mjml-text-4.14.1.tgz", - "integrity": "sha512-yZuvf5z6qUxEo5CqOhCUltJlR6oySKVcQNHwoV5sneMaKdmBiaU4VDnlYFera9gMD9o3KBHIX6kUg7EHnCwBRQ==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-text/-/mjml-text-4.15.3.tgz", + "integrity": "sha512-+C0hxCmw9kg0XzT6vhE5mFkK6y225nC8UEQcN94K0fBCjPKkM+HqZMwGX205fzdGRi+Bxa55b/VhrIVwdv+8vw==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1" + "mjml-core": "4.15.3" } }, "node_modules/mjml-validator": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/mjml-validator/-/mjml-validator-4.13.0.tgz", - "integrity": "sha512-uURYfyQYtHJ6Qz/1A7/+E9ezfcoISoLZhYK3olsxKRViwaA2Mm8gy/J3yggZXnsUXWUns7Qymycm5LglLEIiQg==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-validator/-/mjml-validator-4.15.3.tgz", + "integrity": "sha512-Xb72KdqRwjv/qM2rJpV22syyP2N3cRQ9VVDrN6u2FSzLq02buFNxmSPJ7CKhat3PrUNdVHU75KZwOf/tz4UEhA==", "dependencies": { - "@babel/runtime": "^7.14.6" + "@babel/runtime": "^7.23.9" } }, "node_modules/mjml-wrapper": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/mjml-wrapper/-/mjml-wrapper-4.14.1.tgz", - "integrity": "sha512-aA5Xlq6d0hZ5LY+RvSaBqmVcLkvPvdhyAv3vQf3G41Gfhel4oIPmkLnVpHselWhV14A0KwIOIAKVxHtSAxyOTQ==", + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/mjml-wrapper/-/mjml-wrapper-4.15.3.tgz", + "integrity": "sha512-ditsCijeHJrmBmObtJmQ18ddLxv5oPyMTdPU8Di8APOnD2zPk7Z4UAuJSl7HXB45oFiivr3MJf4koFzMUSZ6Gg==", "dependencies": { - "@babel/runtime": "^7.14.6", + "@babel/runtime": "^7.23.9", "lodash": "^4.17.21", - "mjml-core": "4.14.1", - "mjml-section": "4.14.1" + "mjml-core": "4.15.3", + "mjml-section": "4.15.3" } }, "node_modules/mkdirp": { @@ -9534,14 +9385,6 @@ "pino-http": "^6.4.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -9599,25 +9442,25 @@ "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==" }, "node_modules/nodemailer": { - "version": "6.9.9", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.9.tgz", - "integrity": "sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==", + "version": "6.9.13", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz", + "integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==", "engines": { "node": ">=6.0.0" } }, "node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", + "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", "dependencies": { - "abbrev": "^1.0.0" + "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/normalize-path": { @@ -9993,38 +9836,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pac-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz", - "integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4", - "get-uri": "3", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "5", - "pac-resolver": "^5.0.0", - "raw-body": "^2.2.0", - "socks-proxy-agent": "5" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/pac-resolver": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.1.tgz", - "integrity": "sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==", - "dependencies": { - "degenerator": "^3.0.2", - "ip": "^1.1.5", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", @@ -10174,11 +9985,11 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { @@ -10189,9 +10000,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", - "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "engines": { "node": "14 || >=16.14" } @@ -10313,14 +10124,6 @@ "split2": "^4.1.0" } }, - "node_modules/pick-util": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/pick-util/-/pick-util-1.1.5.tgz", - "integrity": "sha512-H0MaM8T7wpQ/azvB12ChZw7kpSFzjsgv3Z+N7fUWnL1McTGSEeroCngcK4eOPiFQq08rAyKX3hadcAB1kUqfXA==", - "dependencies": { - "@jonkemp/package-utils": "^1.0.8" - } - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -10826,37 +10629,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz", - "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==", - "dependencies": { - "agent-base": "^6.0.0", - "debug": "4", - "http-proxy-agent": "^4.0.0", - "https-proxy-agent": "^5.0.0", - "lru-cache": "^5.1.1", - "pac-proxy-agent": "^5.0.0", - "proxy-from-env": "^1.0.0", - "socks-proxy-agent": "^5.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/proxy-agent/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/proxy-agent/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -10991,6 +10763,14 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "engines": { + "node": ">=6" + } + }, "node_modules/pure-rand": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", @@ -11159,9 +10939,9 @@ "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp.prototype.flags": { "version": "1.5.0", @@ -11188,16 +10968,6 @@ "node": ">= 0.10" } }, - "node_modules/remote-content": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remote-content/-/remote-content-3.0.1.tgz", - "integrity": "sha512-zEMsvb4GgxVKBBTHgy2tte67RYBZx2Kyg9mTYpg+JfATHDqYJqhuC3zG1VoiYhDVP5JaB5+mPKcAvdnT0n3jxA==", - "dependencies": { - "proxy-from-env": "^1.1.0", - "superagent": "^8.0.9", - "superagent-proxy": "^3.0.0" - } - }, "node_modules/repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", @@ -11835,46 +11605,6 @@ "node": "*" } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", - "dependencies": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", - "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "4", - "socks": "^2.3.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/socks/node_modules/ip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", - "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==" - }, "node_modules/sonic-boom": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", @@ -11908,14 +11638,6 @@ "node": ">=0.10.0" } }, - "node_modules/specificity": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", - "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", - "bin": { - "specificity": "bin/specificity" - } - }, "node_modules/split2": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", @@ -12168,62 +11890,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/style-data": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/style-data/-/style-data-2.0.1.tgz", - "integrity": "sha512-frUbteLGDoNEJhbMIWtyNE1VRduZXmZozhct4F+qN++OzIQZNZJ8KToZlDEl3eaedRYlDfKvUoMFMyrZj4x/sg==", - "dependencies": { - "cheerio": "^1.0.0-rc.12", - "mediaquery-text": "^1.2.0", - "pick-util": "^1.1.5" - } - }, - "node_modules/superagent": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", - "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.2", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" - }, - "engines": { - "node": ">=6.4.0 <13 || >=14" - } - }, - "node_modules/superagent-proxy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/superagent-proxy/-/superagent-proxy-3.0.0.tgz", - "integrity": "sha512-wAlRInOeDFyd9pyonrkJspdRAxdLrcsZ6aSnS+8+nu4x1aXbz6FWSTT9M6Ibze+eG60szlL7JA8wEIV7bPWuyQ==", - "dependencies": { - "debug": "^4.3.2", - "proxy-agent": "^5.0.0" - }, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "superagent": ">= 0.15.4 || 1 || 2 || 3" - } - }, - "node_modules/superagent/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12416,9 +12082,9 @@ "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" }, "node_modules/tlds": { - "version": "1.248.0", - "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.248.0.tgz", - "integrity": "sha512-noj0KdpWTBhwsKxMOXk0rN9otg4kTgLm4WohERRHbJ9IY+kSDKr3RmjitaQ3JFzny+DyvBOQKlFZhp0G0qNSfg==", + "version": "1.252.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.252.0.tgz", + "integrity": "sha512-GA16+8HXvqtfEnw/DTcwB0UU354QE1n3+wh08oFjr6Znl7ZLAeUgYzCcK+/CCrOyE0vnHR8/pu3XXG3vDijXpQ==", "bin": { "tlds": "bin.js" } @@ -12755,9 +12421,9 @@ } }, "node_modules/uc.micro": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.0.0.tgz", - "integrity": "sha512-DffL94LsNOccVn4hyfRe5rdKa273swqeA5DJpMOeFmEn1wCDc7nAbbB0gXlgBCL7TNzeTv6G7XVWzan7iJtfig==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" }, "node_modules/uglify-js": { "version": "3.17.4", @@ -13292,14 +12958,6 @@ "node": ">= 10.0.0" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -13384,14 +13042,6 @@ "node": ">= 0.8.0" } }, - "node_modules/xregexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==", - "engines": { - "node": "*" - } - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/web/crux/src/app/image/guards/image.add-to-version.team-access.guard.ts b/web/crux/src/app/image/guards/image.add-to-version.team-access.guard.ts index 1c0ba1f6c..7c6d15602 100644 --- a/web/crux/src/app/image/guards/image.add-to-version.team-access.guard.ts +++ b/web/crux/src/app/image/guards/image.add-to-version.team-access.guard.ts @@ -13,7 +13,7 @@ export default class ImageAddToVersionTeamAccessGuard implements CanActivate { const versionId = req.params.versionId as string const body = req.body as AddImagesDto[] - const regIds = body.map(it => it.registryId) + const regIds = Array.from(new Set(body.map(it => it.registryId))) const registries = await this.prisma.registry.count({ where: { id: { diff --git a/web/crux/src/app/registry/registry.dto.ts b/web/crux/src/app/registry/registry.dto.ts index 7ba04ef06..876992059 100644 --- a/web/crux/src/app/registry/registry.dto.ts +++ b/web/crux/src/app/registry/registry.dto.ts @@ -79,6 +79,9 @@ export class RegistryDto { @IsString() @IsIn(REGISTRY_TYPE_VALUES) type: RegistryType + + @IsUrl() + imageUrlPrefix: string } export class RegistryTokenDto { diff --git a/web/crux/src/app/registry/registry.mapper.ts b/web/crux/src/app/registry/registry.mapper.ts index 3ad05291a..57d3c8333 100644 --- a/web/crux/src/app/registry/registry.mapper.ts +++ b/web/crux/src/app/registry/registry.mapper.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common' import { Registry, RegistryToken, RegistryTypeEnum } from '@prisma/client' import { REGISTRY_EVENT_V2_PULL, REGISTRY_EVENT_V2_PUSH } from 'src/domain/registry' -import { CruxBadRequestException } from 'src/exception/crux-exception' +import { CruxBadRequestException, CruxInternalServerErrorException } from 'src/exception/crux-exception' import EncryptionService from 'src/services/encryption.service' import { REGISTRY_GITLAB_URLS, REGISTRY_HUB_URL } from 'src/shared/const' import { BasicProperties } from '../../shared/dtos/shared.dto' @@ -48,6 +48,7 @@ export default class RegistryMapper { url: it.url, description: it.description, icon: it.icon, + imageUrlPrefix: this.imageUrlOf(it), } } @@ -231,6 +232,25 @@ export default class RegistryMapper { }) } } + + private imageUrlOf(reg: Registry): string { + switch (reg.type) { + case RegistryTypeEnum.hub: + return `${reg.url}/${reg.imageNamePrefix}` + case RegistryTypeEnum.v2: + case RegistryTypeEnum.unchecked: + return reg.url + case RegistryTypeEnum.gitlab: + case RegistryTypeEnum.github: + case RegistryTypeEnum.google: + return `${reg.url}/${reg.imageNamePrefix}` + default: + throw new CruxInternalServerErrorException({ + message: 'Invalid registry type', + value: reg.type, + }) + } + } } type CredentialsDto = { From fceb8e8a49e3bf297aea5a133b1969bce7f4190d Mon Sep 17 00:00:00 2001 From: Mate Vago Date: Tue, 7 May 2024 21:06:11 +0200 Subject: [PATCH 12/19] fix(crux-ui): compose env_file apply (#968) --- .../composer/compose-environment.tsx | 13 ++- .../components/composer/use-composer-state.ts | 109 ++++++++++++------ web/crux-ui/src/models/compose.ts | 59 +++++++++- web/crux-ui/src/pages/composer.tsx | 6 +- web/crux-ui/src/validations/compose.ts | 2 +- 5 files changed, 141 insertions(+), 48 deletions(-) diff --git a/web/crux-ui/src/components/composer/compose-environment.tsx b/web/crux-ui/src/components/composer/compose-environment.tsx index 2a116925f..7b12cdd79 100644 --- a/web/crux-ui/src/components/composer/compose-environment.tsx +++ b/web/crux-ui/src/components/composer/compose-environment.tsx @@ -1,4 +1,5 @@ import DyoWrap from '@app/elements/dyo-wrap' +import { DotEnvironment } from '@app/models' import useTranslation from 'next-translate/useTranslation' import DotEnvFileCard from './dot-env-file-card' import { @@ -21,9 +22,9 @@ const ComposeEnvironment = (props: ComposeEnvironmentProps) => { const { t } = useTranslation('compose') - const onEnvFileChange = (name: string, text: string) => dispatch(convertEnvFile(t, name, text)) - const onEnvNameChange = (from: string, to: string) => dispatch(changeEnvFileName(from, to)) - const onRemoveDotEnv = (name: string) => dispatch(removeEnvFile(name)) + const onEnvFileChange = (target: DotEnvironment, text: string) => dispatch(convertEnvFile(t, target, text)) + const onEnvNameChange = (target: DotEnvironment, to: string) => dispatch(changeEnvFileName(target, to)) + const onRemoveDotEnv = (target: DotEnvironment) => dispatch(removeEnvFile(t, target)) const defaultDotEnv = selectDefaultEnvironment(state) @@ -33,9 +34,9 @@ const ComposeEnvironment = (props: ComposeEnvironmentProps) => { onEnvFileChange(it.name, text)} - onNameChange={it !== defaultDotEnv ? name => onEnvNameChange(it.name, name) : null} - onRemove={it !== defaultDotEnv ? () => onRemoveDotEnv(it.name) : null} + onEnvChange={text => onEnvFileChange(it, text)} + onNameChange={it !== defaultDotEnv ? name => onEnvNameChange(it, name) : null} + onRemove={it !== defaultDotEnv ? () => onRemoveDotEnv(it) : null} /> ))} diff --git a/web/crux-ui/src/components/composer/use-composer-state.ts b/web/crux-ui/src/components/composer/use-composer-state.ts index ff2d85491..fc740d26c 100644 --- a/web/crux-ui/src/components/composer/use-composer-state.ts +++ b/web/crux-ui/src/components/composer/use-composer-state.ts @@ -74,10 +74,17 @@ const applyEnvironments = ( const appliedServices = services.map(entry => { const [key, service] = entry - const dotEnvName = service.env_file ?? DEFAULT_ENVIRONMENT_NAME - const dotEnv = envs.find(it => it.name === dotEnvName) - - const applied = applyDotEnvToComposeService(service, dotEnv.environment) + const envFile: string[] = !service.env_file + ? [DEFAULT_ENVIRONMENT_NAME] + : typeof service.env_file === 'string' + ? [service.env_file] + : service.env_file + const dotEnvs = envFile.map(envName => envs.find(it => it.name === envName)).filter(it => !!it) + + let applied = service + dotEnvs.forEach(it => { + applied = applyDotEnvToComposeService(applied, it.environment) + }) return [key, applied] }) @@ -90,11 +97,15 @@ type ApplyComposeToStateOptions = { envedCompose: Compose t: Translate } -const applyComposeToState = (state: ComposerState, options: ApplyComposeToStateOptions) => { +const applyComposeToState = ( + state: ComposerState, + options: ApplyComposeToStateOptions, + environment: DotEnvironment[], +) => { const { t } = options try { - const newContainers = mapComposeServices(options.envedCompose) + const newContainers = mapComposeServices(options.envedCompose, environment) return { ...state, @@ -170,23 +181,27 @@ export const convertComposeFile = services: applyEnvironments(compose?.services, state.environment), } - return applyComposeToState(state, { - compose: { - text, - yaml: compose, - error: null, + return applyComposeToState( + state, + { + compose: { + text, + yaml: compose, + error: null, + }, + envedCompose, + t, }, - envedCompose, - t, - }) + state.environment, + ) } export const convertEnvFile = - (t: Translate, name: string, text: string): ComposerAction => + (t: Translate, target: DotEnvironment, text: string): ComposerAction => state => { const { environment } = state - const index = environment.findIndex(it => it.name === name) + const index = environment.findIndex(it => it === target) if (index < 0) { return state } @@ -223,19 +238,25 @@ export const convertEnvFile = const newEnv = [...environment] newEnv[index] = dotEnv + let newState = state const { compose } = state + if (compose) { + const envedCompose = { + ...compose.yaml, + services: applyEnvironments(compose?.yaml?.services, newEnv), + } - const envedCompose = { - ...compose.yaml, - services: applyEnvironments(compose?.yaml?.services, newEnv), + newState = applyComposeToState( + state, + { + compose, + envedCompose, + t, + }, + newEnv, + ) } - const newState = applyComposeToState(state, { - compose, - envedCompose, - t, - }) - return { ...newState, environment: newEnv, @@ -243,18 +264,18 @@ export const convertEnvFile = } export const changeEnvFileName = - (from: string, to: string): ComposerAction => + (target: DotEnvironment, name: string): ComposerAction => state => { const { environment } = state - const index = environment.findIndex(it => it.name === from) + const index = environment.findIndex(it => it === target) if (index < 0) { return state } const dotEnv: DotEnvironment = { ...environment[index], - name: to, + name, } const newEnv = [...environment] @@ -285,23 +306,45 @@ export const addEnvFile = (): ComposerAction => state => { } export const removeEnvFile = - (name: string): ComposerAction => + (t: Translate, target: DotEnvironment): ComposerAction => state => { - if (name === DEFAULT_ENVIRONMENT_NAME) { + // eslint-disable-next-line @typescript-eslint/no-use-before-define + const defaultDotEnv = selectDefaultEnvironment(state) + if (defaultDotEnv === target) { return state } const { environment } = state - const index = environment.findIndex(it => it.name === name) + const index = environment.findIndex(it => it === target) if (index < 0) { return state } - return { + const newState = { ...state, - environment: environment.filter(it => it.name !== name), + environment: environment.filter(it => it !== target), + } + + const compose = newState.compose?.yaml + if (!compose) { + return newState + } + + const envedCompose = { + ...compose, + services: applyEnvironments(compose.services, newState.environment), } + + return applyComposeToState( + newState, + { + compose: newState.compose, + envedCompose, + t, + }, + newState.environment, + ) } // selectors diff --git a/web/crux-ui/src/models/compose.ts b/web/crux-ui/src/models/compose.ts index 13ff325cb..cf6b0c8d9 100644 --- a/web/crux-ui/src/models/compose.ts +++ b/web/crux-ui/src/models/compose.ts @@ -12,8 +12,8 @@ import { UniqueKeyValue, VolumeType, } from './container' -import { VersionType } from './version' import { Project } from './project' +import { VersionType } from './version' export const COMPOSE_RESTART_VALUES = ['no', 'always', 'on-failure', 'unless-stopped'] as const export type ComposeRestart = (typeof COMPOSE_RESTART_VALUES)[number] @@ -52,7 +52,7 @@ export type ComposeService = { tty?: boolean working_dir?: string user?: string // we only support numbers, so '0' will work but root won't - env_file?: string + env_file?: string | string[] } export type Compose = { @@ -188,6 +188,29 @@ const mapUser = (user: string): number => { } } +const mapKeyValuesToRecord = (items: string[] | null): Record => + items?.reduce((result, it) => { + const [key, value] = it.split('=') + + result[key] = value + return result + }, {}) + +const mapRecordToKeyValues = (map: Record | null): UniqueKeyValue[] | null => { + if (!map) { + return null + } + + return Object.entries(map).map(entry => { + const [key, value] = entry + return { + id: uuid(), + key, + value, + } + }) +} + const mapKeyValues = (items: string[] | null): UniqueKeyValue[] | null => items?.map(it => { const [key, value] = it.split('=') @@ -210,6 +233,7 @@ const mapStringOrStringArray = (candidate: string | string[]): UniqueKey[] => export const mapComposeServiceToContainerConfig = ( service: ComposeService, serviceKey: string, + envs: DotEnvironment[], ): ContainerConfigData => { const ports: ContainerConfigPort[] = [] const portRanges: ContainerConfigPortRange[] = [] @@ -222,9 +246,34 @@ export const mapComposeServiceToContainerConfig = ( } }) + let environment = mapKeyValuesToRecord(service.environment) + if (service.env_file) { + const envFile = typeof service.env_file === 'string' ? [service.env_file] : service.env_file + + const dotEnvs = envs.filter(it => envFile.includes(it.name)) + if (dotEnvs.length > 0) { + if (!environment) { + environment = {} + } + + const mergedEnvs = dotEnvs.reduce( + (result, it) => ({ + ...result, + ...it.environment, + }), + {}, + ) + + environment = { + ...mergedEnvs, + ...environment, + } + } + } + return { name: service.container_name ?? serviceKey, - environment: mapKeyValues(service.environment), + environment: mapRecordToKeyValues(environment), commands: mapStringOrStringArray(service.entrypoint), args: mapStringOrStringArray(service.command), ports: ports.length > 0 ? ports : null, @@ -263,13 +312,13 @@ export const mapComposeServiceToContainerConfig = ( } } -export const mapComposeServices = (compose: Compose): ConvertedContainer[] => +export const mapComposeServices = (compose: Compose, envs: DotEnvironment[]): ConvertedContainer[] => Object.entries(compose.services).map(entry => { const [key, service] = entry return { image: service.image, - config: mapComposeServiceToContainerConfig(service, key), + config: mapComposeServiceToContainerConfig(service, key, envs), } }) diff --git a/web/crux-ui/src/pages/composer.tsx b/web/crux-ui/src/pages/composer.tsx index edb686973..0d1c135f3 100644 --- a/web/crux-ui/src/pages/composer.tsx +++ b/web/crux-ui/src/pages/composer.tsx @@ -22,7 +22,7 @@ import DyoToggle from '@app/elements/dyo-toggle' import DyoWrap from '@app/elements/dyo-wrap' import useSubmit from '@app/hooks/use-submit' import useTeamRoutes from '@app/hooks/use-team-routes' -import { Project, Registry, VersionDetails, findRegistryByUrl, imageUrlOfImageName } from '@app/models' +import { DotEnvironment, Project, Registry, VersionDetails, findRegistryByUrl, imageUrlOfImageName } from '@app/models' import { appendTeamSlug } from '@app/providers/team-routes' import { ROUTE_COMPOSER, ROUTE_INDEX } from '@app/routes' import { fetcher, redirectTo, teamSlugOrFirstTeam, withContextAuthorization } from '@app/utils' @@ -72,7 +72,7 @@ const ComposerPage = () => { const onComposeFileChange = (text: string) => dispatch(convertComposeFile(t, text)) const onToggleShowDefaultDotEnv = () => dispatch(toggleShowDefaultDotEnv()) - const onEnvFileChange = (name: string, text: string) => dispatch(convertEnvFile(t, name, text)) + const onEnvFileChange = (target: DotEnvironment, text: string) => dispatch(convertEnvFile(t, target, text)) const onAddDotEnv = () => dispatch(addEnvFile()) const onActivateGenerate = () => dispatch(activateUpperSection('generate')) @@ -117,7 +117,7 @@ const ComposerPage = () => { /> {showDefaultEnv && ( - onEnvFileChange(defaultDotEnv.name, text)} /> + onEnvFileChange(defaultDotEnv, text)} /> )}
) : ( diff --git a/web/crux-ui/src/validations/compose.ts b/web/crux-ui/src/validations/compose.ts index 2cbc7bd00..2b751d561 100644 --- a/web/crux-ui/src/validations/compose.ts +++ b/web/crux-ui/src/validations/compose.ts @@ -65,7 +65,7 @@ export const composeServiceSchema = yup.object().shape({ return it }), - env_file: yup.string().optional().nullable(), + env_file: mixedStringOrStringArrayRule.optional().nullable(), }) export const composeSchema = yup From d55f4f5fc86ba1806792c334858371a2d1129daa Mon Sep 17 00:00:00 2001 From: Nandor Magyar Date: Wed, 8 May 2024 14:32:50 +0200 Subject: [PATCH 13/19] chore: bump proto image version to be in sync with golang (#970) --- .github/workflows/builder_image_protobuf.yaml | 2 +- .github/workflows/tester_image_builder.yaml | 2 +- Makefile | 10 +++------- images/builder-golang/Dockerfile | 1 + images/builder-protobuf/Dockerfile | 5 +++-- protobuf/go/agent/agent.pb.go | 2 +- protobuf/go/agent/agent_grpc.pb.go | 2 +- protobuf/go/common/common.pb.go | 2 +- web/crux/src/grpc/google/protobuf/timestamp.ts | 2 +- 9 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/builder_image_protobuf.yaml b/.github/workflows/builder_image_protobuf.yaml index bf5cbb1ce..219da4f50 100644 --- a/.github/workflows/builder_image_protobuf.yaml +++ b/.github/workflows/builder_image_protobuf.yaml @@ -12,7 +12,7 @@ permissions: env: GITHUB_REGISTRY: ghcr.io BUILDER_IMAGE_NAME: dyrector-io/dyrectorio/builder-images/protobuf - VERSION: 1 + VERSION: 2 jobs: build: runs-on: ubuntu-22.04 diff --git a/.github/workflows/tester_image_builder.yaml b/.github/workflows/tester_image_builder.yaml index 0f8479494..af318a26d 100644 --- a/.github/workflows/tester_image_builder.yaml +++ b/.github/workflows/tester_image_builder.yaml @@ -16,7 +16,7 @@ jobs: build: runs-on: ubuntu-22.04 container: - image: ghcr.io/dyrector-io/dyrectorio/builder-images/protobuf:1 + image: ghcr.io/dyrector-io/dyrectorio/builder-images/protobuf:2 steps: - name: Checkout uses: actions/checkout@v3 diff --git a/Makefile b/Makefile index 0060035d0..9b7368535 100644 --- a/Makefile +++ b/Makefile @@ -72,13 +72,13 @@ protogen:| proto-agent proto-crux # Run linting on the Go code .PHONY: go-lint go-lint: - MSYS_NO_PATHCONV=1 docker run --rm -u ${UID}:${GID} -v ${PWD}:/usr/work ghcr.io/dyrector-io/dyrectorio/alpine-proto:3.17-4 ash -c "\ + MSYS_NO_PATHCONV=1 docker run --rm -u ${UID}:${GID} -v ${PWD}:/usr/work ghcr.io/dyrector-io/dyrectorio/builder-images/protobuf:2 ash -c "\ cd golang && make lint" # Generate agent gRPC files .PHONY: proto-agent proto-agent: - MSYS_NO_PATHCONV=1 docker run --rm -u ${UID}:${GID} -v ${PWD}:/usr/work ghcr.io/dyrector-io/dyrectorio/alpine-proto:3.17-4 ash -c "\ + MSYS_NO_PATHCONV=1 docker run --rm -u ${UID}:${GID} -v ${PWD}:/usr/work ghcr.io/dyrector-io/dyrectorio/builder-images/protobuf:2 ash -c "\ mkdir -p protobuf/go && \ protoc -I. \ --go_out /tmp \ @@ -91,7 +91,7 @@ proto-agent: # Generate API grpc files .PHONY: proto-crux proto-crux: - MSYS_NO_PATHCONV=1 docker run --rm -u ${UID}:${GID} -v ${PWD}:/usr/work ghcr.io/dyrector-io/dyrectorio/alpine-proto:3.17-4 ash -c "\ + MSYS_NO_PATHCONV=1 docker run --rm -u ${UID}:${GID} -v ${PWD}:/usr/work ghcr.io/dyrector-io/dyrectorio/builder-images/protobuf:2 ash -c "\ mkdir -p ./web/crux/src/grpc && \ protoc \ --experimental_allow_proto3_optional \ @@ -106,10 +106,6 @@ proto-crux: cd ./web/crux/src/grpc && \ npx prettier -w "./**.ts" -.PHONY: build-proto-image -build-proto-image: - docker build -t ghcr.io/dyrector-io/dyrectorio/alpine-proto:3.17-4 -f images/alpine-proto/Dockerfile --progress plain . - .PHONY: branch-check branch-check: @branch=$$(git rev-parse --abbrev-ref HEAD); \ diff --git a/images/builder-golang/Dockerfile b/images/builder-golang/Dockerfile index c626ad838..4a9511ea9 100644 --- a/images/builder-golang/Dockerfile +++ b/images/builder-golang/Dockerfile @@ -1,3 +1,4 @@ +# note: update this together with protobuf image FROM docker.io/library/golang:1.22-alpine3.19 ENV GOLANGCI_LINT_CACHE $GOPATH/cache diff --git a/images/builder-protobuf/Dockerfile b/images/builder-protobuf/Dockerfile index 982b93a92..02990d6b9 100644 --- a/images/builder-protobuf/Dockerfile +++ b/images/builder-protobuf/Dockerfile @@ -1,6 +1,7 @@ # this image is our official way of generating proto-files # for reproducable builds, anywhere -FROM docker.io/library/golang:1.20-alpine3.17 +# note: update this together with golang image +FROM docker.io/library/golang:1.22-alpine3.19 ENV GOLANGCI_LINT_CACHE $GOPATH/cache ENV GOCACHE $GOPATH/cache @@ -14,7 +15,7 @@ RUN mkdir -p $GOPATH/src && \ mkdir -p $GOPATH/cache && \ mkdir -p $GOPATH/pkg -RUN apk add nodejs npm alpine-sdk protoc protobuf-dev rsync docker git zstd openssl1.1-compat bash jq +RUN apk add nodejs npm alpine-sdk protoc protobuf-dev rsync docker git zstd openssl bash jq RUN npm i -g \ ts-proto@1.138.0 \ diff --git a/protobuf/go/agent/agent.pb.go b/protobuf/go/agent/agent.pb.go index 2212e21d7..fb3337b1f 100644 --- a/protobuf/go/agent/agent.pb.go +++ b/protobuf/go/agent/agent.pb.go @@ -6,7 +6,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v4.24.4 // source: protobuf/proto/agent.proto package agent diff --git a/protobuf/go/agent/agent_grpc.pb.go b/protobuf/go/agent/agent_grpc.pb.go index f489b97ac..59e018dda 100644 --- a/protobuf/go/agent/agent_grpc.pb.go +++ b/protobuf/go/agent/agent_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.2.0 -// - protoc v3.21.9 +// - protoc v4.24.4 // source: protobuf/proto/agent.proto package agent diff --git a/protobuf/go/common/common.pb.go b/protobuf/go/common/common.pb.go index 072dd680d..e929da025 100644 --- a/protobuf/go/common/common.pb.go +++ b/protobuf/go/common/common.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v4.24.4 // source: protobuf/proto/common.proto package common diff --git a/web/crux/src/grpc/google/protobuf/timestamp.ts b/web/crux/src/grpc/google/protobuf/timestamp.ts index c03ffb604..490fef20b 100644 --- a/web/crux/src/grpc/google/protobuf/timestamp.ts +++ b/web/crux/src/grpc/google/protobuf/timestamp.ts @@ -90,7 +90,7 @@ export const protobufPackage = 'google.protobuf' * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with * the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use * the Joda Time's [`ISODateTimeFormat.dateTime()`]( - * http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D + * http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime() * ) to obtain a formatter capable of generating timestamps in this format. */ export interface Timestamp { From a71886e36153d335b541a8fbc42823f73213ed23 Mon Sep 17 00:00:00 2001 From: Mate Vago Date: Wed, 8 May 2024 17:06:57 +0200 Subject: [PATCH 14/19] fix(web): missing composer image config patches (#969) --- .../composer/generate-version-card.tsx | 21 ++++ .../components/composer/use-composer-state.ts | 100 +++++++++++------- web/crux-ui/src/models/compose.ts | 91 ++++++---------- web/crux-ui/src/pages/composer.tsx | 8 +- .../src/app/container/container.mapper.ts | 16 +-- web/crux/src/app/image/image.service.ts | 4 +- 6 files changed, 132 insertions(+), 108 deletions(-) diff --git a/web/crux-ui/src/components/composer/generate-version-card.tsx b/web/crux-ui/src/components/composer/generate-version-card.tsx index 1ac5cec21..ff280522c 100644 --- a/web/crux-ui/src/components/composer/generate-version-card.tsx +++ b/web/crux-ui/src/components/composer/generate-version-card.tsx @@ -16,11 +16,13 @@ import { ConvertedContainer, CreateProject, CreateVersion, + PatchVersionImage, Project, ProjectDetails, Registry, VERSION_TYPE_VALUES, VersionDetails, + VersionImage, findRegistryByUrl, imageUrlOfImageName as imageUrlOfImageTag, } from '@app/models' @@ -112,11 +114,30 @@ const GenerateVersionCard = (props: GenerateVersionCardProps) => { routes.project.versions(project.id).api.images(version.id), addImagesBody, ) + if (!createImagesRes.ok) { await handleApiError(createImagesRes) return } + const images = (await createImagesRes.json()) as VersionImage[] + + const configPatches = images.map((image, index) => { + const container = containers[index] + + const patchImageBody: PatchVersionImage = { + config: container.config, + } + + return sendForm( + 'PATCH', + routes.project.versions(project.id).api.imageDetails(version.id, image.id), + patchImageBody, + ) + }) + + await Promise.all(configPatches) + await onVersionGenerated(project, version) }, }) diff --git a/web/crux-ui/src/components/composer/use-composer-state.ts b/web/crux-ui/src/components/composer/use-composer-state.ts index fc740d26c..2900bd00e 100644 --- a/web/crux-ui/src/components/composer/use-composer-state.ts +++ b/web/crux-ui/src/components/composer/use-composer-state.ts @@ -6,6 +6,7 @@ import { DotEnvironment, applyDotEnvToComposeService, mapComposeServices, + mapKeyValuesToRecord as mapKeyValuesToObject, } from '@app/models' import { composeSchema, getValidationError } from '@app/validations' import { load } from 'js-yaml' @@ -66,6 +67,33 @@ export const toggleShowDefaultDotEnv = (): ComposerAction => state => ({ showDefaultDotEnv: !state.showDefaultDotEnv, }) +const mergeDotEnvsWithServiceEnv = (envs: DotEnvironment[], serviceEnv: string[] | null): string[] | null => { + if (envs.length < 1) { + return serviceEnv + } + + let merged = envs.reduce( + (result, it) => ({ + ...result, + ...it.environment, + }), + {}, + ) + + if (serviceEnv) { + const serviceEnvObj = mapKeyValuesToObject(serviceEnv) + merged = { + ...merged, + ...serviceEnvObj, + } + } + + return Object.entries(merged).map(entry => { + const [key, value] = entry + return `${key}=${value}` + }) +} + const applyEnvironments = ( composeServices: Record, envs: DotEnvironment[], @@ -75,13 +103,23 @@ const applyEnvironments = ( const [key, service] = entry const envFile: string[] = !service.env_file - ? [DEFAULT_ENVIRONMENT_NAME] + ? [] : typeof service.env_file === 'string' ? [service.env_file] : service.env_file - const dotEnvs = envFile.map(envName => envs.find(it => it.name === envName)).filter(it => !!it) + let dotEnvs = envs.filter(it => envFile.includes(it.name)) + + // add explicit envs to environment + let applied: ComposeService = { + ...service, + environment: mergeDotEnvsWithServiceEnv(dotEnvs, service.environment), + } + + const defaultDotEnv = envs.find(it => it.name === DEFAULT_ENVIRONMENT_NAME) + if (dotEnvs.length < 1 && defaultDotEnv) { + dotEnvs = [defaultDotEnv] + } - let applied = service dotEnvs.forEach(it => { applied = applyDotEnvToComposeService(applied, it.environment) }) @@ -97,15 +135,11 @@ type ApplyComposeToStateOptions = { envedCompose: Compose t: Translate } -const applyComposeToState = ( - state: ComposerState, - options: ApplyComposeToStateOptions, - environment: DotEnvironment[], -) => { +const applyComposeToState = (state: ComposerState, options: ApplyComposeToStateOptions) => { const { t } = options try { - const newContainers = mapComposeServices(options.envedCompose, environment) + const newContainers = mapComposeServices(options.envedCompose) return { ...state, @@ -181,19 +215,15 @@ export const convertComposeFile = services: applyEnvironments(compose?.services, state.environment), } - return applyComposeToState( - state, - { - compose: { - text, - yaml: compose, - error: null, - }, - envedCompose, - t, + return applyComposeToState(state, { + compose: { + text, + yaml: compose, + error: null, }, - state.environment, - ) + envedCompose, + t, + }) } export const convertEnvFile = @@ -246,15 +276,11 @@ export const convertEnvFile = services: applyEnvironments(compose?.yaml?.services, newEnv), } - newState = applyComposeToState( - state, - { - compose, - envedCompose, - t, - }, - newEnv, - ) + newState = applyComposeToState(state, { + compose, + envedCompose, + t, + }) } return { @@ -336,15 +362,11 @@ export const removeEnvFile = services: applyEnvironments(compose.services, newState.environment), } - return applyComposeToState( - newState, - { - compose: newState.compose, - envedCompose, - t, - }, - newState.environment, - ) + return applyComposeToState(newState, { + compose: newState.compose, + envedCompose, + t, + }) } // selectors diff --git a/web/crux-ui/src/models/compose.ts b/web/crux-ui/src/models/compose.ts index cf6b0c8d9..27f9b915b 100644 --- a/web/crux-ui/src/models/compose.ts +++ b/web/crux-ui/src/models/compose.ts @@ -188,7 +188,7 @@ const mapUser = (user: string): number => { } } -const mapKeyValuesToRecord = (items: string[] | null): Record => +export const mapKeyValuesToRecord = (items: string[] | null): Record => items?.reduce((result, it) => { const [key, value] = it.split('=') @@ -196,21 +196,6 @@ const mapKeyValuesToRecord = (items: string[] | null): Record => return result }, {}) -const mapRecordToKeyValues = (map: Record | null): UniqueKeyValue[] | null => { - if (!map) { - return null - } - - return Object.entries(map).map(entry => { - const [key, value] = entry - return { - id: uuid(), - key, - value, - } - }) -} - const mapKeyValues = (items: string[] | null): UniqueKeyValue[] | null => items?.map(it => { const [key, value] = it.split('=') @@ -233,7 +218,6 @@ const mapStringOrStringArray = (candidate: string | string[]): UniqueKey[] => export const mapComposeServiceToContainerConfig = ( service: ComposeService, serviceKey: string, - envs: DotEnvironment[], ): ContainerConfigData => { const ports: ContainerConfigPort[] = [] const portRanges: ContainerConfigPortRange[] = [] @@ -246,34 +230,9 @@ export const mapComposeServiceToContainerConfig = ( } }) - let environment = mapKeyValuesToRecord(service.environment) - if (service.env_file) { - const envFile = typeof service.env_file === 'string' ? [service.env_file] : service.env_file - - const dotEnvs = envs.filter(it => envFile.includes(it.name)) - if (dotEnvs.length > 0) { - if (!environment) { - environment = {} - } - - const mergedEnvs = dotEnvs.reduce( - (result, it) => ({ - ...result, - ...it.environment, - }), - {}, - ) - - environment = { - ...mergedEnvs, - ...environment, - } - } - } - return { name: service.container_name ?? serviceKey, - environment: mapRecordToKeyValues(environment), + environment: mapKeyValues(service.environment), commands: mapStringOrStringArray(service.entrypoint), args: mapStringOrStringArray(service.command), ports: ports.length > 0 ? ports : null, @@ -312,35 +271,33 @@ export const mapComposeServiceToContainerConfig = ( } } -export const mapComposeServices = (compose: Compose, envs: DotEnvironment[]): ConvertedContainer[] => +export const mapComposeServices = (compose: Compose): ConvertedContainer[] => Object.entries(compose.services).map(entry => { const [key, service] = entry return { image: service.image, - config: mapComposeServiceToContainerConfig(service, key, envs), + config: mapComposeServiceToContainerConfig(service, key), } }) -class DotEnvApplicator { +export class DotEnvApplicator { constructor(private readonly dotEnv: Record) {} applyToString(candidate: string): string { - if (!candidate) { - return candidate - } + let original = candidate ?? undefined + let applied = this.applyToStringOnce(candidate) ?? undefined - candidate = candidate.replace(/\${[^}]*}/g, subStr => { - const envName = subStr.substring(2, subStr.length - 1) - return this.applyEnvToFoundEnv(envName) - }) + let iterations = 0 + while (original !== applied && iterations < 32) { + original = applied + applied = this.applyToStringOnce(original) ?? undefined + candidate = applied - candidate = candidate.replace(/\$[^{ ]*\s/g, subStr => { - const envName = subStr.substring(1).trim() - return this.applyEnvToFoundEnv(envName) - }) + iterations++ + } - return candidate + return applied } applyToStringArray(candidate: string[]): string[] { @@ -412,6 +369,24 @@ class DotEnvApplicator { return this.dotEnv[key] ?? defaultValue ?? `\${${key}}` } + + private applyToStringOnce(candidate: string): string { + if (!candidate) { + return candidate + } + + candidate = candidate.replace(/\${[^}]*}/g, subStr => { + const envName = subStr.substring(2, subStr.length - 1) + return this.applyEnvToFoundEnv(envName) + }) + + candidate = candidate.replace(/\$[^{][a-zA-Z0-9_]*/g, subStr => { + const envName = subStr.substring(1).trim() + return this.applyEnvToFoundEnv(envName) + }) + + return candidate + } } export const applyDotEnvToComposeService = ( diff --git a/web/crux-ui/src/pages/composer.tsx b/web/crux-ui/src/pages/composer.tsx index 0d1c135f3..f8d94303c 100644 --- a/web/crux-ui/src/pages/composer.tsx +++ b/web/crux-ui/src/pages/composer.tsx @@ -67,6 +67,8 @@ const ComposerPage = () => { [registries], ) + const missingRegistries = !state.containers.every(it => hasRegistry(it.image)) + const submit = useSubmit() const onComposeFileChange = (text: string) => dispatch(convertComposeFile(t, text)) @@ -94,14 +96,16 @@ const ComposerPage = () => { {state.upperSection === 'compose' ? ( - {t('generate')} + + {t('generate')} + ) : ( <> {t('common:discard')} - + {t('generate')} diff --git a/web/crux/src/app/container/container.mapper.ts b/web/crux/src/app/container/container.mapper.ts index 9cbe75220..3131051e2 100644 --- a/web/crux/src/app/container/container.mapper.ts +++ b/web/crux/src/app/container/container.mapper.ts @@ -69,13 +69,13 @@ export default class ContainerMapper { configDataToDb(config: Partial): Omit { return { - name: config.name, - expose: config.expose, + name: config.name ?? undefined, + expose: config.expose ?? undefined, routing: toPrismaJson(config.routing), configContainer: toPrismaJson(config.configContainer), // Set user to the given value, if not null or use 0 if specifically 0, otherwise set to default -1 user: config.user ?? (config.user === 0 ? 0 : -1), - workingDirectory: config.workingDirectory, + workingDirectory: config.workingDirectory ?? undefined, tty: config.tty !== null ? config.tty : false, ports: toPrismaJson(config.ports), portRanges: toPrismaJson(config.portRanges), @@ -86,19 +86,19 @@ export default class ContainerMapper { secrets: toPrismaJson(config.secrets), initContainers: toPrismaJson(config.initContainers), logConfig: toPrismaJson(config.logConfig), - storageSet: config.storageSet, - storageId: config.storageId, + storageSet: config.storageSet ?? undefined, + storageId: config.storageId ?? undefined, storageConfig: toPrismaJson(config.storageConfig), // dagent - restartPolicy: config.restartPolicy, - networkMode: config.networkMode, + restartPolicy: config.restartPolicy ?? undefined, + networkMode: config.networkMode ?? undefined, networks: toPrismaJson(config.networks), dockerLabels: toPrismaJson(config.dockerLabels), expectedState: toPrismaJson(config.expectedState), // crane - deploymentStrategy: config.deploymentStrategy, + deploymentStrategy: config.deploymentStrategy ?? undefined, healthCheckConfig: toPrismaJson(config.healthCheckConfig), resourceConfig: toPrismaJson(config.resourceConfig), proxyHeaders: config.proxyHeaders !== null ? config.proxyHeaders : false, diff --git a/web/crux/src/app/image/image.service.ts b/web/crux/src/app/image/image.service.ts index 430f9c3e5..01cc04b9d 100644 --- a/web/crux/src/app/image/image.service.ts +++ b/web/crux/src/app/image/image.service.ts @@ -238,7 +238,9 @@ export default class ImageService { labels: labels ?? undefined, tag: request.tag ?? undefined, config: { - update: config, + update: { + data: config, + }, }, updatedBy: identity.id, }, From 764b23fe31153355eee7e8771dc5a353160c49c5 Mon Sep 17 00:00:00 2001 From: Nandor Magyar Date: Wed, 8 May 2024 17:47:44 +0200 Subject: [PATCH 15/19] feat(agent): add sysctls option to builder (#966) --- golang/Makefile | 2 ++ .../builder/container/container_builder.go | 20 ++++++++++++++----- web/crux-ui/docker-compose.yaml | 1 - web/crux-ui/e2e/utils/global.teardown.ts | 4 ++-- web/crux-ui/next.config.js | 3 +-- web/crux-ui/src/server/crux-api.ts | 4 ++-- 6 files changed, 22 insertions(+), 12 deletions(-) diff --git a/golang/Makefile b/golang/Makefile index 589bc1ad0..9ffb1593f 100644 --- a/golang/Makefile +++ b/golang/Makefile @@ -21,6 +21,7 @@ GOSEC=v2.19.0 GOLANGCI=v1.57.2 GOFUMPT=v0.6.0 YAMLFMT=v0.11.0 +FIELDALIGN=v0.20.0 # support for: linux darwin windows GOOS?=linux @@ -121,6 +122,7 @@ install-go-tools: go install github.com/securego/gosec/v2/cmd/gosec@${GOSEC} go install mvdan.cc/gofumpt@${GOFUMPT} go install github.com/google/yamlfmt/cmd/yamlfmt@${YAMLFMT} + go install go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@${FIELDALIGN} .PHONY: compile-agents compile-agents: compile-crane compile-dagent diff --git a/golang/pkg/builder/container/container_builder.go b/golang/pkg/builder/container/container_builder.go index 47a5396a3..d048ae9f4 100644 --- a/golang/pkg/builder/container/container_builder.go +++ b/golang/pkg/builder/container/container_builder.go @@ -53,6 +53,7 @@ type Builder interface { WithPullDisplayFunc(imageHelper.PullDisplayFn) Builder WithExtraHosts(hosts []string) Builder WithWorkingDirectory(workingDirectory string) Builder + WithSysctls(sysctls map[string]string) Builder WithPreCreateHooks(hooks ...LifecycleFunc) Builder WithPostCreateHooks(hooks ...LifecycleFunc) Builder WithPreStartHooks(hooks ...LifecycleFunc) Builder @@ -66,21 +67,21 @@ type DockerContainerBuilder struct { logger dogger.LogWriter client client.APIClient ctx context.Context - logConfig *container.LogConfig + user *int64 networkMap map[string]string labels map[string]string pullDisplayFn imageHelper.PullDisplayFn containerID *string - user *int64 + logConfig *container.LogConfig + sysctls map[string]string workingDirectory string containerName string imageWithTag string registryAuth string networkMode string restartPolicy container.RestartPolicyMode - networks []string hooksPostStart []LifecycleFunc - hooksPreStart []LifecycleFunc + portList []PortBinding hooksPreCreate []LifecycleFunc entrypoint []string cmd []string @@ -88,10 +89,11 @@ type DockerContainerBuilder struct { hooksPostCreate []LifecycleFunc mountList []mount.Mount portRanges []PortRangeBinding - portList []PortBinding + hooksPreStart []LifecycleFunc envList []string networkAliases []string extraHosts []string + networks []string imagePriority imageHelper.PullPriority tty bool withoutConflict bool @@ -281,6 +283,13 @@ func (dc *DockerContainerBuilder) WithWorkingDirectory(workingDirectory string) return dc } +// Sets the the sysctl/kernel parameters for containers runtime, +// key is the name of the setting value is the actual value eg. { "net.core.somaxconn": "1024" } +func (dc *DockerContainerBuilder) WithSysctls(sysctls map[string]string) Builder { + dc.sysctls = sysctls + return dc +} + // Sets an array of hooks which runs before the container is created. ContainerID is nil in these hooks. func (dc *DockerContainerBuilder) WithPreCreateHooks(hooks ...LifecycleFunc) Builder { dc.hooksPreCreate = hooks @@ -313,6 +322,7 @@ func builderToDockerConfig(dc *DockerContainerBuilder) (hostConfig *container.Ho PortBindings: portListNat, AutoRemove: dc.remove, ExtraHosts: dc.extraHosts, + Sysctls: dc.sysctls, } containerConfig = &container.Config{ diff --git a/web/crux-ui/docker-compose.yaml b/web/crux-ui/docker-compose.yaml index a7a773e0b..c0b54b395 100644 --- a/web/crux-ui/docker-compose.yaml +++ b/web/crux-ui/docker-compose.yaml @@ -1,4 +1,3 @@ -version: '3.3' services: crux-ui: container_name: crux-ui diff --git a/web/crux-ui/e2e/utils/global.teardown.ts b/web/crux-ui/e2e/utils/global.teardown.ts index 6a0cc509b..61a27487f 100644 --- a/web/crux-ui/e2e/utils/global.teardown.ts +++ b/web/crux-ui/e2e/utils/global.teardown.ts @@ -32,8 +32,8 @@ export const fetchCruxFromBrowser = async (cookie: string, cruxUrl: string, url: let body: any = null try { body = await res.json() - } catch { - console.error('[ERROR]: Crux fetch failed to parse error body of url', url) + } catch (e: any) { + console.error('[ERROR]: Crux fetch failed to parse error body of url', `${cruxUrl}${url}`, e) } if (body && isDyoError(body)) { diff --git a/web/crux-ui/next.config.js b/web/crux-ui/next.config.js index ab76636db..f26013553 100644 --- a/web/crux-ui/next.config.js +++ b/web/crux-ui/next.config.js @@ -4,9 +4,8 @@ const resultOrder = process.env.DNS_DEFAULT_RESULT_ORDER if (resultOrder) { dns.setDefaultResultOrder(resultOrder === 'ipv4first' ? 'ipv4first' : 'verbatim') -} else if (process.env.NODE_ENV !== 'production') { - dns.setDefaultResultOrder('ipv4first') } + const nextTranslate = require('next-translate-plugin') module.exports = { diff --git a/web/crux-ui/src/server/crux-api.ts b/web/crux-ui/src/server/crux-api.ts index 3a6d80e74..b7db9379d 100644 --- a/web/crux-ui/src/server/crux-api.ts +++ b/web/crux-ui/src/server/crux-api.ts @@ -27,8 +27,8 @@ export const fetchCrux = async ( let body: any = null try { body = await res.json() - } catch { - console.error('[ERROR]: Crux fetch failed to parse error body of url', url) + } catch (e: any) { + console.error('[ERROR]: Crux fetch failed to parse error body of url', `${cruxUrl}${url}`, e) } const apiError = fromApiError(res.status, body ?? {}) From 5d1d2df07393ad31c8a1073ccc8958f489f528b3 Mon Sep 17 00:00:00 2001 From: Mate Vago Date: Fri, 10 May 2024 12:34:10 +0200 Subject: [PATCH 16/19] fix(crux-ui): envs split only at the first equals sign (#971) --- .../src/components/composer/use-composer-state.ts | 3 ++- web/crux-ui/src/models/compose.ts | 12 +++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/web/crux-ui/src/components/composer/use-composer-state.ts b/web/crux-ui/src/components/composer/use-composer-state.ts index 2900bd00e..326c0fe7c 100644 --- a/web/crux-ui/src/components/composer/use-composer-state.ts +++ b/web/crux-ui/src/components/composer/use-composer-state.ts @@ -5,6 +5,7 @@ import { ConvertedContainer, DotEnvironment, applyDotEnvToComposeService, + envStringToKeyValue, mapComposeServices, mapKeyValuesToRecord as mapKeyValuesToObject, } from '@app/models' @@ -251,7 +252,7 @@ export const convertEnvFile = line = line.substring(0, commentIndex).trim() } - const [key, value] = line.split('=') + const [key, value] = envStringToKeyValue(line) return [key, value] }) .filter(it => { diff --git a/web/crux-ui/src/models/compose.ts b/web/crux-ui/src/models/compose.ts index 27f9b915b..08d32ab59 100644 --- a/web/crux-ui/src/models/compose.ts +++ b/web/crux-ui/src/models/compose.ts @@ -161,7 +161,7 @@ const mapVolume = (volume: string): ContainerConfigVolume => { id: uuid(), name: path ? name : '', path: path || name, - type: type && CONTAINER_VOLUME_TYPE_VALUES.includes(type as VolumeType) ? (type as VolumeType) : null, + type: type && CONTAINER_VOLUME_TYPE_VALUES.includes(type as VolumeType) ? (type as VolumeType) : 'rwo', } } @@ -188,9 +188,15 @@ const mapUser = (user: string): number => { } } +export const envStringToKeyValue = (env: string): [string, string] => { + const [key, ...rest] = env.split('=') + const value = rest.join('=') + return [key, value] +} + export const mapKeyValuesToRecord = (items: string[] | null): Record => items?.reduce((result, it) => { - const [key, value] = it.split('=') + const [key, value] = envStringToKeyValue(it) result[key] = value return result @@ -198,7 +204,7 @@ export const mapKeyValuesToRecord = (items: string[] | null): Record items?.map(it => { - const [key, value] = it.split('=') + const [key, value] = envStringToKeyValue(it) return { id: uuid(), From 24823109ae092d1d80cebe93275982027d0bebcb Mon Sep 17 00:00:00 2001 From: Nandor Magyar Date: Mon, 13 May 2024 13:32:25 +0200 Subject: [PATCH 17/19] chore: more consistent release generation, adjust to new version of (#972) --- .golangci.yml | 6 ------ Makefile | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 55100744a..ea8348e80 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -39,12 +39,6 @@ linters-settings: min-complexity: 15 goimports: local-prefixes: github.com/golangci/golangci-lint,github.com/dyrector-io/dyrectorio - gomnd: - checks: - - argument - - case - - condition - - return govet: enable-all: true lll: diff --git a/Makefile b/Makefile index 9b7368535..038c47807 100644 --- a/Makefile +++ b/Makefile @@ -129,7 +129,7 @@ release: branch-check $(info Do you want to continue? Version will be: $(version) from branch: $(shell git rev-parse --abbrev-ref HEAD)) read - git pull + git pull --tags @if git checkout -b "release/$(version)"; then \ echo "Branch release/$(version) created and checked out"; \ From d3f89f4e6ce751b9c38d19931ced0e3a3897582b Mon Sep 17 00:00:00 2001 From: Nandor Magyar Date: Mon, 13 May 2024 15:05:17 +0200 Subject: [PATCH 18/19] chore(agent): remove magic numbers, adjust related rules (#973) --- .golangci.yml | 6 ++++++ golang/internal/mapper/grpc.go | 4 ++-- golang/internal/mapper/grpc_test.go | 2 +- golang/pkg/dagent/utils/docker.go | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index ea8348e80..f9fe78380 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -45,6 +45,12 @@ linters-settings: line-length: 140 misspell: locale: US + mnd: + checks: + - argument + - case + - condition + - return nolintlint: allow-unused: false # report any unused nolint directives require-explanation: false # don't require an explanation for nolint directives diff --git a/golang/internal/mapper/grpc.go b/golang/internal/mapper/grpc.go index e78d0503d..10d0d99e5 100644 --- a/golang/internal/mapper/grpc.go +++ b/golang/internal/mapper/grpc.go @@ -114,8 +114,8 @@ func mapContainerConfig(in *agent.DeployRequest) v1.ContainerConfig { } if cc.Expose != nil { - containerConfig.Expose = *cc.Expose > 1 - containerConfig.ExposeTLS = *cc.Expose > 2 + containerConfig.Expose = *cc.Expose >= common.ExposeStrategy_EXPOSE + containerConfig.ExposeTLS = *cc.Expose == common.ExposeStrategy_EXPOSE_WITH_TLS } if cc.Routing != nil { diff --git a/golang/internal/mapper/grpc_test.go b/golang/internal/mapper/grpc_test.go index 271d23217..b4b28a4ae 100644 --- a/golang/internal/mapper/grpc_test.go +++ b/golang/internal/mapper/grpc_test.go @@ -217,7 +217,7 @@ func testDeployRequest() *agent.DeployRequest { upLimit := "5Mi" mntPath := "/path/to/mount" repoPrefix := "repo-prefix" - strategy := common.ExposeStrategy(777) + strategy := common.ExposeStrategy_EXPOSE_WITH_TLS b := true return &agent.DeployRequest{ Id: "testID", diff --git a/golang/pkg/dagent/utils/docker.go b/golang/pkg/dagent/utils/docker.go index 4bd0fbedf..43dfe773b 100644 --- a/golang/pkg/dagent/utils/docker.go +++ b/golang/pkg/dagent/utils/docker.go @@ -48,6 +48,8 @@ import ( const DockerLogHeaderLength = 8 +const RWOwnerROther = 0o644 + type DockerVersion struct { ServerVersion string ClientVersion string @@ -113,7 +115,7 @@ func WriteContainerFile(ctx context.Context, cli *client.Client, tarHeader := &tar.Header{ Name: filename, - Mode: 0o644, + Mode: RWOwnerROther, Size: fileSize, Uid: meta.UID, Gid: meta.GID, From ec2d3d52ab9c14c9b4c7fc2fe03206ff537eadb3 Mon Sep 17 00:00:00 2001 From: Nandor Magyar Date: Mon, 13 May 2024 15:36:51 +0200 Subject: [PATCH 19/19] release: 0.12.0 --- CHANGELOG.md | 67 +++++++++++++++++------------- golang/internal/version/version.go | 2 +- web/crux-ui/package-lock.json | 4 +- web/crux-ui/package.json | 2 +- web/crux/package-lock.json | 4 +- web/crux/package.json | 2 +- web/crux/src/shared/const.ts | 2 +- 7 files changed, 47 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4acc588e2..8d2528d0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,36 @@ # CHANGELOG + +## [0.12.0](https://github.com/dyrector-io/dyrectorio/compare/0.11.7...0.12.0) (2024-05-13) + +### Chore + +* **(agent):** remove magic numbers, adjust related rules ([#973](https://github.com/dyrector-io/dyrectorio/issues/973)) +* more consistent release generation, adjust to new version of ([#972](https://github.com/dyrector-io/dyrectorio/issues/972)) +* bump proto image version to be in sync with golang ([#970](https://github.com/dyrector-io/dyrectorio/issues/970)) +* **(agent):** use new docker types, go upgrade ([#963](https://github.com/dyrector-io/dyrectorio/issues/963)) + +### Feat + +* **(agent):** add sysctls option to builder ([#966](https://github.com/dyrector-io/dyrectorio/issues/966)) +* **(web):** composer ([#967](https://github.com/dyrector-io/dyrectorio/issues/967)) + +### Fix + +* **(crux-ui):** envs split only at the first equals sign ([#971](https://github.com/dyrector-io/dyrectorio/issues/971)) +* **(web):** missing composer image config patches ([#969](https://github.com/dyrector-io/dyrectorio/issues/969)) +* **(crux-ui):** compose env_file apply ([#968](https://github.com/dyrector-io/dyrectorio/issues/968)) +* **(crux-ui):** add IPv6 listen for dual-stack listening ([#965](https://github.com/dyrector-io/dyrectorio/issues/965)) +* crane uses port/targetPort in serviceMonitors for metrics ([#964](https://github.com/dyrector-io/dyrectorio/issues/964)) +* **(agent):** deprecated types.AuthConfig ([#962](https://github.com/dyrector-io/dyrectorio/issues/962)) +* **(crux):** get instance config ([#961](https://github.com/dyrector-io/dyrectorio/issues/961)) + +### Refactor + +* **(crux-ui):** move deployment related components to the root deployments folder ([#960](https://github.com/dyrector-io/dyrectorio/issues/960)) + + ## [0.11.7](https://github.com/dyrector-io/dyrectorio/compare/0.11.6...0.11.7) (2024-04-11) @@ -29,11 +59,7 @@ -## [0.11.5](https://github.com/dyrector-io/dyrectorio/compare/help...0.11.5) (2024-04-05) - - - -## [help](https://github.com/dyrector-io/dyrectorio/compare/0.11.4...help) (2024-04-05) +## [0.11.5](https://github.com/dyrector-io/dyrectorio/compare/0.11.4...0.11.5) (2024-04-05) ### Feat @@ -67,11 +93,16 @@ -## [0.11.3](https://github.com/dyrector-io/dyrectorio/compare/ls...0.11.3) (2024-03-11) +## [0.11.3](https://github.com/dyrector-io/dyrectorio/compare/0.11.2...0.11.3) (2024-03-11) + +### Doc + +* **(crux):** add extra info encrpytion gey generation ([#926](https://github.com/dyrector-io/dyrectorio/issues/926)) ### Feat * container log api ([#931](https://github.com/dyrector-io/dyrectorio/issues/931)) +* **(web):** update kratos to 1.1.0 ([#925](https://github.com/dyrector-io/dyrectorio/issues/925)) ### Fix @@ -79,18 +110,6 @@ * add ENCRYPTION_SECRET_KEY to compose files ([#927](https://github.com/dyrector-io/dyrectorio/issues/927)) - -## [ls](https://github.com/dyrector-io/dyrectorio/compare/0.11.2...ls) (2024-02-26) - -### Doc - -* **(crux):** add extra info encrpytion gey generation ([#926](https://github.com/dyrector-io/dyrectorio/issues/926)) - -### Feat - -* **(web):** update kratos to 1.1.0 ([#925](https://github.com/dyrector-io/dyrectorio/issues/925)) - - ## [0.11.2](https://github.com/dyrector-io/dyrectorio/compare/0.11.1...0.11.2) (2024-02-23) @@ -961,7 +980,7 @@ -## [0.2.1](https://github.com/dyrector-io/dyrectorio/compare/v0.1.1...0.2.1) (2022-09-27) +## [0.2.1](https://github.com/dyrector-io/dyrectorio/compare/0.1.1...0.2.1) (2022-09-27) ### Chore @@ -1114,12 +1133,8 @@ * websocket message routing - -## [v0.1.1](https://github.com/dyrector-io/dyrectorio/compare/0.1.1...v0.1.1) (2022-08-03) - - -## [0.1.1](https://github.com/dyrector-io/dyrectorio/compare/v0.1.0...0.1.1) (2022-08-03) +## [0.1.1](https://github.com/dyrector-io/dyrectorio/compare/0.1.0...0.1.1) (2022-08-03) ### Chore @@ -1135,10 +1150,6 @@ * **(agent):** tests ([#54](https://github.com/dyrector-io/dyrectorio/issues/54)) - -## [v0.1.0](https://github.com/dyrector-io/dyrectorio/compare/0.1.0...v0.1.0) (2022-08-01) - - ## [0.1.0](https://github.com/dyrector-io/dyrectorio/compare/0.0.1...0.1.0) (2022-08-01) diff --git a/golang/internal/version/version.go b/golang/internal/version/version.go index ec7c0f058..15415b493 100644 --- a/golang/internal/version/version.go +++ b/golang/internal/version/version.go @@ -6,7 +6,7 @@ import ( var ( // Version represents the version of the application - Version = "0.11.7" + Version = "0.12.0" // CommitHash is the hash of the commit used for the build CommitHash = "n/a" // BuildTimestamp represents the timestamp when the build was created diff --git a/web/crux-ui/package-lock.json b/web/crux-ui/package-lock.json index 1cb2f02be..96757cf4e 100644 --- a/web/crux-ui/package-lock.json +++ b/web/crux-ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "crux-ui", - "version": "0.11.7", + "version": "0.12.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "crux-ui", - "version": "0.11.7", + "version": "0.12.0", "license": "Apache-2.0", "dependencies": { "@ory/kratos-client": "^1.1.0", diff --git a/web/crux-ui/package.json b/web/crux-ui/package.json index 88645bedb..c318232e2 100644 --- a/web/crux-ui/package.json +++ b/web/crux-ui/package.json @@ -1,6 +1,6 @@ { "name": "crux-ui", - "version": "0.11.7", + "version": "0.12.0", "description": "Open-source delivery platform that helps developers to deliver applications efficiently by simplifying software releases and operations in any environment.", "author": "dyrector.io", "private": true, diff --git a/web/crux/package-lock.json b/web/crux/package-lock.json index cfdf0d58d..83efcb1d6 100644 --- a/web/crux/package-lock.json +++ b/web/crux/package-lock.json @@ -1,12 +1,12 @@ { "name": "crux", - "version": "0.11.7", + "version": "0.12.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "crux", - "version": "0.11.7", + "version": "0.12.0", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.9.0", diff --git a/web/crux/package.json b/web/crux/package.json index dc1f6bcc0..88d38e371 100644 --- a/web/crux/package.json +++ b/web/crux/package.json @@ -1,6 +1,6 @@ { "name": "crux", - "version": "0.11.7", + "version": "0.12.0", "description": "Open-source delivery platform that helps developers to deliver applications efficiently by simplifying software releases and operations in any environment.", "author": "dyrector.io", "private": true, diff --git a/web/crux/src/shared/const.ts b/web/crux/src/shared/const.ts index ee47f40dd..ded908a8e 100644 --- a/web/crux/src/shared/const.ts +++ b/web/crux/src/shared/const.ts @@ -21,7 +21,7 @@ export const AGENT_STREAM_TIMEOUT = 60_000 export const GET_CONTAINER_LOG_DEFAULT_TAKE = 100 // NOTE(@m8vago): This should be incremented, when a new release includes a proto file change -const AGENT_PROTO_COMPATIBILITY_MINIMUM_VERSION = '0.11.7' +const AGENT_PROTO_COMPATIBILITY_MINIMUM_VERSION = '0.12.0' export const AGENT_SUPPORTED_MINIMUM_VERSION = coerce(AGENT_PROTO_COMPATIBILITY_MINIMUM_VERSION) export const API_CREATED_LOCATION_HEADERS = {