Commit 4f9f1d07 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch...

Merge branch '352079-fix-tags-destruction-error-message-when-container-repository-is-importing' into 'master'

Update error message with an importing container repository

See merge request gitlab-org/gitlab!82730
parents 378b3d0d fbe5608f
<script> <script>
import { GlSprintf, GlAlert, GlLink } from '@gitlab/ui'; import { GlSprintf, GlAlert, GlLink } from '@gitlab/ui';
import { ALERT_MESSAGES, ADMIN_GARBAGE_COLLECTION_TIP } from '../../constants/index'; import {
ALERT_MESSAGES,
ADMIN_GARBAGE_COLLECTION_TIP,
ALERT_DANGER_IMPORTING,
} from '../../constants/index';
export default { export default {
components: { components: {
...@@ -23,6 +27,7 @@ export default { ...@@ -23,6 +27,7 @@ export default {
}, },
}, },
garbageCollectionHelpPagePath: { type: String, required: false, default: '' }, garbageCollectionHelpPagePath: { type: String, required: false, default: '' },
containerRegistryImportingHelpPagePath: { type: String, required: false, default: '' },
isAdmin: { isAdmin: {
type: Boolean, type: Boolean,
default: false, default: false,
...@@ -48,6 +53,11 @@ export default { ...@@ -48,6 +53,11 @@ export default {
} }
return config; return config;
}, },
alertHref() {
return this.deleteAlertType === ALERT_DANGER_IMPORTING
? this.containerRegistryImportingHelpPagePath
: this.garbageCollectionHelpPagePath;
},
}, },
}; };
</script> </script>
...@@ -61,7 +71,7 @@ export default { ...@@ -61,7 +71,7 @@ export default {
> >
<gl-sprintf :message="deleteAlertConfig.message"> <gl-sprintf :message="deleteAlertConfig.message">
<template #docLink="{ content }"> <template #docLink="{ content }">
<gl-link :href="garbageCollectionHelpPagePath" target="_blank"> <gl-link :href="alertHref" target="_blank">
{{ content }} {{ content }}
</gl-link> </gl-link>
</template> </template>
......
...@@ -93,6 +93,10 @@ export const DETAILS_DELETE_IMAGE_ERROR_MESSAGE = s__( ...@@ -93,6 +93,10 @@ export const DETAILS_DELETE_IMAGE_ERROR_MESSAGE = s__(
'ContainerRegistry|Something went wrong while scheduling the image for deletion.', 'ContainerRegistry|Something went wrong while scheduling the image for deletion.',
); );
export const DETAILS_IMPORTING_ERROR_MESSAGE = s__(
'ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}.',
);
export const DELETE_IMAGE_CONFIRMATION_TITLE = s__('ContainerRegistry|Delete image repository?'); export const DELETE_IMAGE_CONFIRMATION_TITLE = s__('ContainerRegistry|Delete image repository?');
export const DELETE_IMAGE_CONFIRMATION_TEXT = s__( export const DELETE_IMAGE_CONFIRMATION_TEXT = s__(
'ContainerRegistry|Deleting the image repository will delete all images and tags inside. This action cannot be undone. Please type the following to confirm: %{code}', 'ContainerRegistry|Deleting the image repository will delete all images and tags inside. This action cannot be undone. Please type the following to confirm: %{code}',
...@@ -133,6 +137,7 @@ export const ALERT_DANGER_TAG = 'danger_tag'; ...@@ -133,6 +137,7 @@ export const ALERT_DANGER_TAG = 'danger_tag';
export const ALERT_SUCCESS_TAGS = 'success_tags'; export const ALERT_SUCCESS_TAGS = 'success_tags';
export const ALERT_DANGER_TAGS = 'danger_tags'; export const ALERT_DANGER_TAGS = 'danger_tags';
export const ALERT_DANGER_IMAGE = 'danger_image'; export const ALERT_DANGER_IMAGE = 'danger_image';
export const ALERT_DANGER_IMPORTING = 'danger_importing';
export const DELETE_SCHEDULED = 'DELETE_SCHEDULED'; export const DELETE_SCHEDULED = 'DELETE_SCHEDULED';
export const DELETE_FAILED = 'DELETE_FAILED'; export const DELETE_FAILED = 'DELETE_FAILED';
...@@ -143,6 +148,7 @@ export const ALERT_MESSAGES = { ...@@ -143,6 +148,7 @@ export const ALERT_MESSAGES = {
[ALERT_SUCCESS_TAGS]: DELETE_TAGS_SUCCESS_MESSAGE, [ALERT_SUCCESS_TAGS]: DELETE_TAGS_SUCCESS_MESSAGE,
[ALERT_DANGER_TAGS]: DELETE_TAGS_ERROR_MESSAGE, [ALERT_DANGER_TAGS]: DELETE_TAGS_ERROR_MESSAGE,
[ALERT_DANGER_IMAGE]: DETAILS_DELETE_IMAGE_ERROR_MESSAGE, [ALERT_DANGER_IMAGE]: DETAILS_DELETE_IMAGE_ERROR_MESSAGE,
[ALERT_DANGER_IMPORTING]: DETAILS_IMPORTING_ERROR_MESSAGE,
}; };
export const UNFINISHED_STATUS = 'UNFINISHED'; export const UNFINISHED_STATUS = 'UNFINISHED';
......
...@@ -20,6 +20,7 @@ import { ...@@ -20,6 +20,7 @@ import {
ALERT_SUCCESS_TAGS, ALERT_SUCCESS_TAGS,
ALERT_DANGER_TAGS, ALERT_DANGER_TAGS,
ALERT_DANGER_IMAGE, ALERT_DANGER_IMAGE,
ALERT_DANGER_IMPORTING,
FETCH_IMAGES_LIST_ERROR_MESSAGE, FETCH_IMAGES_LIST_ERROR_MESSAGE,
UNFINISHED_STATUS, UNFINISHED_STATUS,
MISSING_OR_DELETED_IMAGE_BREADCRUMB, MISSING_OR_DELETED_IMAGE_BREADCRUMB,
...@@ -32,6 +33,8 @@ import deleteContainerRepositoryTagsMutation from '../graphql/mutations/delete_c ...@@ -32,6 +33,8 @@ import deleteContainerRepositoryTagsMutation from '../graphql/mutations/delete_c
import getContainerRepositoryDetailsQuery from '../graphql/queries/get_container_repository_details.query.graphql'; import getContainerRepositoryDetailsQuery from '../graphql/queries/get_container_repository_details.query.graphql';
import getContainerRepositoryTagsQuery from '../graphql/queries/get_container_repository_tags.query.graphql'; import getContainerRepositoryTagsQuery from '../graphql/queries/get_container_repository_tags.query.graphql';
const REPOSITORY_IMPORTING_ERROR_MESSAGE = 'repository importing';
export default { export default {
name: 'RegistryDetailsPage', name: 'RegistryDetailsPage',
components: { components: {
...@@ -147,12 +150,17 @@ export default { ...@@ -147,12 +150,17 @@ export default {
}); });
if (data?.destroyContainerRepositoryTags?.errors[0]) { if (data?.destroyContainerRepositoryTags?.errors[0]) {
throw new Error(); throw new Error(data.destroyContainerRepositoryTags.errors[0]);
} }
this.deleteAlertType = this.deleteAlertType =
itemsToBeDeleted.length === 0 ? ALERT_SUCCESS_TAG : ALERT_SUCCESS_TAGS; itemsToBeDeleted.length === 0 ? ALERT_SUCCESS_TAG : ALERT_SUCCESS_TAGS;
} catch (e) { } catch (e) {
this.deleteAlertType = itemsToBeDeleted.length === 0 ? ALERT_DANGER_TAG : ALERT_DANGER_TAGS; if (e.message === REPOSITORY_IMPORTING_ERROR_MESSAGE) {
this.deleteAlertType = ALERT_DANGER_IMPORTING;
} else {
this.deleteAlertType =
itemsToBeDeleted.length === 0 ? ALERT_DANGER_TAG : ALERT_DANGER_TAGS;
}
} }
this.mutationLoading = false; this.mutationLoading = false;
...@@ -188,6 +196,7 @@ export default { ...@@ -188,6 +196,7 @@ export default {
<delete-alert <delete-alert
v-model="deleteAlertType" v-model="deleteAlertType"
:garbage-collection-help-page-path="config.garbageCollectionHelpPagePath" :garbage-collection-help-page-path="config.garbageCollectionHelpPagePath"
:container-registry-importing-help-page-path="config.containerRegistryImportingHelpPagePath"
:is-admin="config.isAdmin" :is-admin="config.isAdmin"
class="gl-my-2" class="gl-my-2"
/> />
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
"registry_host_url_with_port" => escape_once(registry_config.host_port), "registry_host_url_with_port" => escape_once(registry_config.host_port),
"garbage_collection_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'container-registry-garbage-collection'), "garbage_collection_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'container-registry-garbage-collection'),
"run_cleanup_policies_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'run-the-cleanup-policy-now'), "run_cleanup_policies_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'run-the-cleanup-policy-now'),
"container_registry_importing_help_page_path" => help_page_path('user/packages/container_registry/index', anchor: 'tags-temporarily-cannot-be-marked-for-deletion'),
"is_admin": current_user&.admin.to_s, "is_admin": current_user&.admin.to_s,
is_group_page: "true", is_group_page: "true",
"group_path": @group.full_path, "group_path": @group.full_path,
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
"expiration_policy_help_page_path" => help_page_path('user/packages/container_registry/reduce_container_registry_storage', anchor: 'cleanup-policy'), "expiration_policy_help_page_path" => help_page_path('user/packages/container_registry/reduce_container_registry_storage', anchor: 'cleanup-policy'),
"garbage_collection_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'container-registry-garbage-collection'), "garbage_collection_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'container-registry-garbage-collection'),
"run_cleanup_policies_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'run-the-cleanup-policy-now'), "run_cleanup_policies_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'run-the-cleanup-policy-now'),
"container_registry_importing_help_page_path" => help_page_path('user/packages/container_registry/index', anchor: 'tags-temporarily-cannot-be-marked-for-deletion'),
"project_path": @project.full_path, "project_path": @project.full_path,
"gid_prefix": container_repository_gid_prefix, "gid_prefix": container_repository_gid_prefix,
"is_admin": current_user&.admin.to_s, "is_admin": current_user&.admin.to_s,
......
...@@ -695,3 +695,10 @@ There may be some errors not properly cached. Follow these steps to investigate ...@@ -695,3 +695,10 @@ There may be some errors not properly cached. Follow these steps to investigate
Once adjusted, trigger another tag deletion. You should be able to successfully delete tags. Once adjusted, trigger another tag deletion. You should be able to successfully delete tags.
Follow [this issue](https://gitlab.com/gitlab-org/container-registry/-/issues/551) for details. Follow [this issue](https://gitlab.com/gitlab-org/container-registry/-/issues/551) for details.
### Tags temporarily cannot be marked for deletion
GitLab is [migrating to the next generation of the Container Registry](https://gitlab.com/groups/gitlab-org/-/epics/5523).
During the migration, you may encounter difficulty deleting tags.
If you encounter an error, it's likely that your image repository is in the process of being migrated.
Please wait a few minutes and try again.
...@@ -9723,6 +9723,9 @@ msgstr "" ...@@ -9723,6 +9723,9 @@ msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion." msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr "" msgstr ""
msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
msgstr ""
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept." msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
msgstr "" msgstr ""
......
...@@ -97,6 +97,8 @@ RSpec.describe 'Container Registry', :js do ...@@ -97,6 +97,8 @@ RSpec.describe 'Container Registry', :js do
expect(find('.modal .modal-title')).to have_content _('Remove tag') expect(find('.modal .modal-title')).to have_content _('Remove tag')
find('.modal .modal-footer .btn-danger').click find('.modal .modal-footer .btn-danger').click
end end
it_behaves_like 'rejecting tags destruction for an importing repository on', tags: ['latest']
end end
end end
......
...@@ -103,6 +103,8 @@ RSpec.describe 'Container Registry', :js do ...@@ -103,6 +103,8 @@ RSpec.describe 'Container Registry', :js do
find('.modal .modal-footer .btn-danger').click find('.modal .modal-footer .btn-danger').click
end end
it_behaves_like 'rejecting tags destruction for an importing repository on', tags: ['1']
it('pagination navigate to the second page') do it('pagination navigate to the second page') do
visit_next_page visit_next_page
......
...@@ -6,6 +6,7 @@ import { ...@@ -6,6 +6,7 @@ import {
DELETE_TAG_ERROR_MESSAGE, DELETE_TAG_ERROR_MESSAGE,
DELETE_TAGS_SUCCESS_MESSAGE, DELETE_TAGS_SUCCESS_MESSAGE,
DELETE_TAGS_ERROR_MESSAGE, DELETE_TAGS_ERROR_MESSAGE,
DETAILS_IMPORTING_ERROR_MESSAGE,
ADMIN_GARBAGE_COLLECTION_TIP, ADMIN_GARBAGE_COLLECTION_TIP,
} from '~/packages_and_registries/container_registry/explorer/constants'; } from '~/packages_and_registries/container_registry/explorer/constants';
...@@ -76,6 +77,7 @@ describe('Delete alert', () => { ...@@ -76,6 +77,7 @@ describe('Delete alert', () => {
}); });
}); });
}); });
describe('error states', () => { describe('error states', () => {
describe.each` describe.each`
deleteAlertType | message deleteAlertType | message
...@@ -105,6 +107,25 @@ describe('Delete alert', () => { ...@@ -105,6 +107,25 @@ describe('Delete alert', () => {
}); });
}); });
describe('importing repository error state', () => {
beforeEach(() => {
mountComponent({
deleteAlertType: 'danger_importing',
containerRegistryImportingHelpPagePath: 'https://foobar',
});
});
it('alert exist and text is appropriate', () => {
expect(findAlert().text()).toMatchInterpolatedText(DETAILS_IMPORTING_ERROR_MESSAGE);
});
it('alert body contains link', () => {
const alertLink = findLink();
expect(alertLink.exists()).toBe(true);
expect(alertLink.attributes('href')).toBe('https://foobar');
});
});
describe('dismissing alert', () => { describe('dismissing alert', () => {
it('GlAlert dismiss event triggers a change event', () => { it('GlAlert dismiss event triggers a change event', () => {
mountComponent({ deleteAlertType: 'success_tags' }); mountComponent({ deleteAlertType: 'success_tags' });
......
...@@ -239,6 +239,15 @@ export const graphQLDeleteImageRepositoryTagsMock = { ...@@ -239,6 +239,15 @@ export const graphQLDeleteImageRepositoryTagsMock = {
}, },
}; };
export const graphQLDeleteImageRepositoryTagImportingErrorMock = {
data: {
destroyContainerRepositoryTags: {
errors: ['repository importing'],
__typename: 'DestroyContainerRepositoryTagsPayload',
},
},
};
export const dockerCommands = { export const dockerCommands = {
dockerBuildCommand: 'foofoo', dockerBuildCommand: 'foofoo',
dockerPushCommand: 'barbar', dockerPushCommand: 'barbar',
......
...@@ -18,6 +18,7 @@ import { ...@@ -18,6 +18,7 @@ import {
UNFINISHED_STATUS, UNFINISHED_STATUS,
DELETE_SCHEDULED, DELETE_SCHEDULED,
ALERT_DANGER_IMAGE, ALERT_DANGER_IMAGE,
ALERT_DANGER_IMPORTING,
MISSING_OR_DELETED_IMAGE_BREADCRUMB, MISSING_OR_DELETED_IMAGE_BREADCRUMB,
ROOT_IMAGE_TEXT, ROOT_IMAGE_TEXT,
MISSING_OR_DELETED_IMAGE_TITLE, MISSING_OR_DELETED_IMAGE_TITLE,
...@@ -33,6 +34,7 @@ import Tracking from '~/tracking'; ...@@ -33,6 +34,7 @@ import Tracking from '~/tracking';
import { import {
graphQLImageDetailsMock, graphQLImageDetailsMock,
graphQLDeleteImageRepositoryTagsMock, graphQLDeleteImageRepositoryTagsMock,
graphQLDeleteImageRepositoryTagImportingErrorMock,
containerRepositoryMock, containerRepositoryMock,
graphQLEmptyImageDetailsMock, graphQLEmptyImageDetailsMock,
tagsMock, tagsMock,
...@@ -329,6 +331,7 @@ describe('Details Page', () => { ...@@ -329,6 +331,7 @@ describe('Details Page', () => {
const config = { const config = {
isAdmin: true, isAdmin: true,
garbageCollectionHelpPagePath: 'baz', garbageCollectionHelpPagePath: 'baz',
containerRegistryImportingHelpPagePath: 'https://foobar',
}; };
const deleteAlertType = 'success_tag'; const deleteAlertType = 'success_tag';
...@@ -353,6 +356,35 @@ describe('Details Page', () => { ...@@ -353,6 +356,35 @@ describe('Details Page', () => {
expect(findDeleteAlert().props()).toEqual({ ...config, deleteAlertType }); expect(findDeleteAlert().props()).toEqual({ ...config, deleteAlertType });
}); });
describe('importing repository error', () => {
let mutationResolver;
let tagsResolver;
beforeEach(async () => {
mutationResolver = jest
.fn()
.mockResolvedValue(graphQLDeleteImageRepositoryTagImportingErrorMock);
tagsResolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock(imageTagsMock));
mountComponent({ mutationResolver, tagsResolver });
await waitForApolloRequestRender();
});
it('displays the proper alert', async () => {
findTagsList().vm.$emit('delete', [cleanTags[0]]);
await nextTick();
findDeleteModal().vm.$emit('confirmDelete');
await waitForPromises();
expect(tagsResolver).toHaveBeenCalled();
const deleteAlert = findDeleteAlert();
expect(deleteAlert.exists()).toBe(true);
expect(deleteAlert.props('deleteAlertType')).toBe(ALERT_DANGER_IMPORTING);
});
});
}); });
describe('Partial Cleanup Alert', () => { describe('Partial Cleanup Alert', () => {
......
...@@ -7,3 +7,20 @@ RSpec.shared_examples 'handling feature network errors with the container regist ...@@ -7,3 +7,20 @@ RSpec.shared_examples 'handling feature network errors with the container regist
expect(page).to have_content 'We are having trouble connecting to the Container Registry' expect(page).to have_content 'We are having trouble connecting to the Container Registry'
end end
end end
RSpec.shared_examples 'rejecting tags destruction for an importing repository on' do |tags: []|
it 'rejects the tag destruction operation' do
service = instance_double('Projects::ContainerRepository::DeleteTagsService')
expect(service).to receive(:execute).with(container_repository) { { status: :error, message: 'repository importing' } }
expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(container_repository.project, user, tags: tags) { service }
first('[data-testid="additional-actions"]').click
first('[data-testid="single-delete-button"]').click
expect(find('.modal .modal-title')).to have_content _('Remove tag')
find('.modal .modal-footer .btn-danger').click
alert_body = find('.gl-alert-body')
expect(alert_body).to have_content('Tags temporarily cannot be marked for deletion. Please try again in a few minutes.')
expect(alert_body).to have_link('More details', href: help_page_path('user/packages/container_registry/index', anchor: 'tags-temporarily-cannot-be-marked-for-deletion'))
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment