From 5cbf52377d07eced485d1e3fa3beb4a7dfe4e55d Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sun, 11 Dec 2022 08:58:22 +0200 Subject: [PATCH] feat(system): path to upgrade standalone to BE [EE-4071] (#8095) --- .eslintrc.yml | 5 + api/cmd/portainer/main.go | 20 ++- api/exec/compose_stack.go | 39 ++++-- api/exec/compose_stack_integration_test.go | 8 +- api/filesystem/filesystem.go | 21 +-- api/go.mod | 7 +- api/go.sum | 20 +-- api/http/handler/handler.go | 14 +- api/http/handler/status/handler.go | 41 ------ api/http/handler/status/status_inspect.go | 30 ----- api/http/handler/system/handler.go | 67 ++++++++++ .../nodes_count.go} | 29 +++- api/http/handler/system/status.go | 48 +++++++ .../system_info.go} | 17 ++- api/http/handler/system/system_upgrade.go | 54 ++++++++ .../handler/{status => system}/version.go | 26 +++- api/http/server.go | 12 +- api/internal/upgrade/upgrade.go | 125 ++++++++++++++++++ api/platform/platform.go | 50 +++++-- api/portainer.go | 6 +- app/constants.js | 2 - .../license-management/use-license.service.ts | 19 +-- app/portainer/rest/status.js | 15 --- app/portainer/services/api/status.service.ts | 80 ----------- app/portainer/services/api/statusService.js | 57 +++----- .../FormControl/FormControl.module.css | 0 .../FormControl/FormControl.stories.tsx | 43 +++++- .../FormControl/FormControl.tsx | 15 ++- .../modals/Modal/CloseButton.module.css | 22 +++ .../components/modals/Modal/CloseButton.tsx | 21 +++ .../components/modals/Modal/Modal.module.css | 20 +++ app/react/components/modals/Modal/Modal.tsx | 53 ++++++++ .../modals/Modal/ModalBody.module.css | 4 + .../components/modals/Modal/ModalBody.tsx | 9 ++ .../modals/Modal/ModalFooter.module.css | 5 + .../components/modals/Modal/ModalFooter.tsx | 15 +++ .../modals/Modal/ModalHeader.module.css | 19 +++ .../components/modals/Modal/ModalHeader.tsx | 33 +++++ app/react/components/modals/Modal/index.tsx | 18 +++ app/react/components/modals/Modal/types.ts | 6 + app/react/hooks/withHideOnExtension.tsx | 24 ++++ .../HomeView/LicenseNodePanel.test.tsx | 4 +- .../portainer/HomeView/LicenseNodePanel.tsx | 22 +-- .../environments/queries/useAgentDetails.ts | 4 +- .../feature-flags/useRedirectFeatureFlag.ts | 4 +- .../portainer/feature-flags/withEdition.tsx | 22 +++ .../feature-flags/withFeatureFlag.tsx | 26 ++++ .../portainer/{status => system}/build-url.ts | 2 +- app/react/portainer/system/query-keys.ts | 3 + .../{status => system}/useNodesCount.ts | 5 +- .../{status => system}/useSystemInfo.ts | 16 ++- app/react/portainer/system/useSystemStatus.ts | 46 +++++++ .../portainer/system/useSystemVersion.ts | 38 ++++++ .../system/useUpgradeEditionMutation.ts | 20 +++ app/react/sidebar/Footer/BuildInfoModal.tsx | 12 +- app/react/sidebar/Footer/Footer.tsx | 9 -- .../sidebar/Footer/UpdateNotifications.tsx | 9 +- app/react/sidebar/Sidebar.tsx | 4 +- app/react/sidebar/UpgradeBEBanner.tsx | 63 --------- .../sidebar/UpgradeBEBanner/LoadingDialog.tsx | 52 ++++++++ .../UpgradeBEBanner/NonAdminUpgradeDialog.tsx | 52 ++++++++ .../UpgradeBEBanner/UpgradeBEBanner.tsx | 78 +++++++++++ .../sidebar/UpgradeBEBanner/UpgradeDialog.tsx | 41 ++++++ .../UpgradeBEBanner/UploadLicenseDialog.tsx | 107 +++++++++++++++ app/react/sidebar/UpgradeBEBanner/index.ts | 1 + app/setup-tests/server-handlers.ts | 2 +- build/build_binary.ps1 | 4 + build/build_binary.sh | 4 + build/build_binary_azuredevops.sh | 3 + build/linux/Dockerfile | 2 + build/linux/alpine.Dockerfile | 2 + build/windows/Dockerfile | 2 + .../upgrade-standalone.yml.mustache | 17 +++ 73 files changed, 1374 insertions(+), 421 deletions(-) delete mode 100644 api/http/handler/status/handler.go delete mode 100644 api/http/handler/status/status_inspect.go create mode 100644 api/http/handler/system/handler.go rename api/http/handler/{status/status_nodes_count.go => system/nodes_count.go} (59%) create mode 100644 api/http/handler/system/status.go rename api/http/handler/{status/status_system.go => system/system_info.go} (75%) create mode 100644 api/http/handler/system/system_upgrade.go rename api/http/handler/{status => system}/version.go (80%) create mode 100644 api/internal/upgrade/upgrade.go delete mode 100644 app/portainer/rest/status.js delete mode 100644 app/portainer/services/api/status.service.ts delete mode 100644 app/react/components/form-components/FormControl/FormControl.module.css create mode 100644 app/react/components/modals/Modal/CloseButton.module.css create mode 100644 app/react/components/modals/Modal/CloseButton.tsx create mode 100644 app/react/components/modals/Modal/Modal.module.css create mode 100644 app/react/components/modals/Modal/Modal.tsx create mode 100644 app/react/components/modals/Modal/ModalBody.module.css create mode 100644 app/react/components/modals/Modal/ModalBody.tsx create mode 100644 app/react/components/modals/Modal/ModalFooter.module.css create mode 100644 app/react/components/modals/Modal/ModalFooter.tsx create mode 100644 app/react/components/modals/Modal/ModalHeader.module.css create mode 100644 app/react/components/modals/Modal/ModalHeader.tsx create mode 100644 app/react/components/modals/Modal/index.tsx create mode 100644 app/react/components/modals/Modal/types.ts create mode 100644 app/react/hooks/withHideOnExtension.tsx create mode 100644 app/react/portainer/feature-flags/withEdition.tsx create mode 100644 app/react/portainer/feature-flags/withFeatureFlag.tsx rename app/react/portainer/{status => system}/build-url.ts (82%) create mode 100644 app/react/portainer/system/query-keys.ts rename app/react/portainer/{status => system}/useNodesCount.ts (78%) rename app/react/portainer/{status => system}/useSystemInfo.ts (65%) create mode 100644 app/react/portainer/system/useSystemStatus.ts create mode 100644 app/react/portainer/system/useSystemVersion.ts create mode 100644 app/react/portainer/system/useUpgradeEditionMutation.ts delete mode 100644 app/react/sidebar/UpgradeBEBanner.tsx create mode 100644 app/react/sidebar/UpgradeBEBanner/LoadingDialog.tsx create mode 100644 app/react/sidebar/UpgradeBEBanner/NonAdminUpgradeDialog.tsx create mode 100644 app/react/sidebar/UpgradeBEBanner/UpgradeBEBanner.tsx create mode 100644 app/react/sidebar/UpgradeBEBanner/UpgradeDialog.tsx create mode 100644 app/react/sidebar/UpgradeBEBanner/UploadLicenseDialog.tsx create mode 100644 app/react/sidebar/UpgradeBEBanner/index.ts create mode 100644 mustache-templates/upgrade-standalone.yml.mustache diff --git a/.eslintrc.yml b/.eslintrc.yml index da0d32284..93d5a668d 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -105,6 +105,11 @@ overrides: 'no-await-in-loop': 'off' 'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }] 'regex/invalid': ['error', [{ 'regex': ' getNodesCount(), - { - onError(error) { - notifyError('Failure', error as Error, 'Failed to get nodes count'); - }, - } - ); - - return { nodesCount: data || 0, isLoading }; -} - export function useIntegratedLicenseInfo() { - const { isLoading: isLoadingNodes, nodesCount } = useNodesCounts(); + const { isLoading: isLoadingNodes, data: nodesCount = 0 } = useNodesCount(); const { isLoading: isLoadingLicense, info } = useLicenseInfo(); if ( diff --git a/app/portainer/rest/status.js b/app/portainer/rest/status.js deleted file mode 100644 index 387b524b9..000000000 --- a/app/portainer/rest/status.js +++ /dev/null @@ -1,15 +0,0 @@ -angular.module('portainer.app').factory('Status', [ - '$resource', - 'API_ENDPOINT_STATUS', - function StatusFactory($resource, API_ENDPOINT_STATUS) { - 'use strict'; - return $resource( - API_ENDPOINT_STATUS + '/:action', - {}, - { - get: { method: 'GET' }, - version: { method: 'GET', params: { action: 'version' } }, - } - ); - }, -]); diff --git a/app/portainer/services/api/status.service.ts b/app/portainer/services/api/status.service.ts deleted file mode 100644 index 8e3190d6b..000000000 --- a/app/portainer/services/api/status.service.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { useQuery } from 'react-query'; - -import axios, { parseAxiosError } from '../axios'; - -export interface NodesCountResponse { - nodes: number; -} - -export async function getNodesCount() { - try { - const { data } = await axios.get(buildUrl('nodes')); - return data.nodes; - } catch (error) { - throw parseAxiosError(error as Error); - } -} - -export interface StatusResponse { - Edition: string; - Version: string; - InstanceID: string; -} - -export async function getStatus() { - try { - const { data } = await axios.get(buildUrl()); - - data.Edition = 'Community Edition'; - - return data; - } catch (error) { - throw parseAxiosError(error as Error); - } -} - -export function useStatus( - select?: (status: StatusResponse) => T -) { - return useQuery(['status'], () => getStatus(), { select }); -} - -export interface VersionResponse { - // Whether portainer has an update available - UpdateAvailable: boolean; - // The latest version available - LatestVersion: string; - ServerVersion: string; - DatabaseVersion: string; - Build: { - BuildNumber: string; - ImageTag: string; - NodejsVersion: string; - YarnVersion: string; - WebpackVersion: string; - GoVersion: string; - }; -} - -export async function getVersionStatus() { - try { - const { data } = await axios.get(buildUrl('version')); - return data; - } catch (error) { - throw parseAxiosError(error as Error); - } -} - -export function useVersionStatus() { - return useQuery(['version'], () => getVersionStatus()); -} - -function buildUrl(action?: string) { - let url = '/status'; - - if (action) { - url += `/${action}`; - } - - return url; -} diff --git a/app/portainer/services/api/statusService.js b/app/portainer/services/api/statusService.js index 4042135a9..a91d71f18 100644 --- a/app/portainer/services/api/statusService.js +++ b/app/portainer/services/api/statusService.js @@ -1,42 +1,27 @@ -import { StatusVersionViewModel, StatusViewModel } from '../../models/status'; +import { getSystemStatus } from '@/react/portainer/system/useSystemStatus'; +import { StatusViewModel } from '../../models/status'; -angular.module('portainer.app').factory('StatusService', [ - '$q', - 'Status', - function StatusServiceFactory($q, Status) { - 'use strict'; - var service = {}; +angular.module('portainer.app').factory('StatusService', StatusServiceFactory); - service.status = function () { - var deferred = $q.defer(); +/* @ngInject */ +function StatusServiceFactory($q) { + 'use strict'; + var service = {}; - Status.get() - .$promise.then(function success(data) { - var status = new StatusViewModel(data); - deferred.resolve(status); - }) - .catch(function error(err) { - deferred.reject({ msg: 'Unable to retrieve application status', err: err }); - }); + service.status = function () { + var deferred = $q.defer(); - return deferred.promise; - }; + getSystemStatus() + .then(function success(data) { + var status = new StatusViewModel(data); + deferred.resolve(status); + }) + .catch(function error(err) { + deferred.reject({ msg: 'Unable to retrieve application status', err: err }); + }); - service.version = function () { - var deferred = $q.defer(); + return deferred.promise; + }; - Status.version() - .$promise.then(function success(data) { - var status = new StatusVersionViewModel(data); - deferred.resolve(status); - }) - .catch(function error(err) { - deferred.reject({ msg: 'Unable to retrieve application version info', err: err }); - }); - - return deferred.promise; - }; - - return service; - }, -]); + return service; +} diff --git a/app/react/components/form-components/FormControl/FormControl.module.css b/app/react/components/form-components/FormControl/FormControl.module.css deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/react/components/form-components/FormControl/FormControl.stories.tsx b/app/react/components/form-components/FormControl/FormControl.stories.tsx index 70ae3d7ed..db628381f 100644 --- a/app/react/components/form-components/FormControl/FormControl.stories.tsx +++ b/app/react/components/form-components/FormControl/FormControl.stories.tsx @@ -12,15 +12,31 @@ export default { interface TextFieldProps { label: string; tooltip?: string; + vertical?: boolean; + required?: boolean; + error?: string; } export { TextField, SelectField }; -function TextField({ label, tooltip = '' }: TextFieldProps) { +function TextField({ + label, + tooltip = '', + required, + error, + vertical, +}: TextFieldProps) { const [value, setValue] = useState(''); const inputId = 'input'; return ( - + +