diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml index 6e84d4f3914..4b1194d0fbd 100644 --- a/.gitlab/ci/frontend.gitlab-ci.yml +++ b/.gitlab/ci/frontend.gitlab-ci.yml @@ -280,7 +280,9 @@ coverage-frontend: paths: - coverage-frontend/ reports: - cobertura: coverage-frontend/cobertura-coverage.xml + coverage_report: + coverage_format: cobertura + path: coverage-frontend/cobertura-coverage.xml .qa-frontend-node: extends: diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index cbedd83ac95..b4793f91d36 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -610,7 +610,9 @@ rspec:coverage: - coverage/assets/ - coverage/lcov/ reports: - cobertura: coverage/coverage.xml + coverage_report: + coverage_format: cobertura + path: coverage/coverage.xml rspec:undercoverage: extends: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index e9264ffc8d1..14bc169854a 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -162,12 +162,6 @@ Rails/RakeEnvironment: Rails/RedundantForeignKey: Enabled: false -# Offense count: 1144 -# Configuration parameters: ForbiddenMethods, AllowedMethods. -# ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all -Rails/SkipsModelValidations: - Enabled: false - # Offense count: 278 # Cop supports --auto-correct. Rails/SquishedSQLHeredocs: diff --git a/.rubocop_todo/layout/argument_alignment.yml b/.rubocop_todo/layout/argument_alignment.yml index acb8e8157cc..d8d79d9fa90 100644 --- a/.rubocop_todo/layout/argument_alignment.yml +++ b/.rubocop_todo/layout/argument_alignment.yml @@ -688,7 +688,6 @@ Layout/ArgumentAlignment: - 'spec/finders/keys_finder_spec.rb' - 'spec/finders/merge_requests_finder_spec.rb' - 'spec/finders/personal_access_tokens_finder_spec.rb' - - 'spec/finders/projects/serverless/functions_finder_spec.rb' - 'spec/frontend/fixtures/issues.rb' - 'spec/frontend/fixtures/merge_requests.rb' - 'spec/frontend/fixtures/merge_requests_diffs.rb' diff --git a/.rubocop_todo/lint/missing_cop_enable_directive.yml b/.rubocop_todo/lint/missing_cop_enable_directive.yml index a8edf6c85dc..589f98c9e02 100644 --- a/.rubocop_todo/lint/missing_cop_enable_directive.yml +++ b/.rubocop_todo/lint/missing_cop_enable_directive.yml @@ -6,7 +6,6 @@ Lint/MissingCopEnableDirective: Exclude: - 'app/controllers/admin/users_controller.rb' - 'app/controllers/projects/forks_controller.rb' - - 'app/finders/projects/serverless/functions_finder.rb' - 'app/graphql/resolvers/group_issues_resolver.rb' - 'app/graphql/resolvers/issues_resolver.rb' - 'app/graphql/resolvers/project_members_resolver.rb' diff --git a/.rubocop_todo/rails/skips_model_validations.yml b/.rubocop_todo/rails/skips_model_validations.yml new file mode 100644 index 00000000000..9b908b7b997 --- /dev/null +++ b/.rubocop_todo/rails/skips_model_validations.yml @@ -0,0 +1,749 @@ +--- +Rails/SkipsModelValidations: + # Offense count: 1424 + # Temporarily disabled due to too many offenses + Enabled: false + Exclude: + - 'app/controllers/import/github_controller.rb' + - 'app/controllers/projects/environments_controller.rb' + - 'app/controllers/projects/notes_controller.rb' + - 'app/models/alert_management/alert.rb' + - 'app/models/analytics/cycle_analytics/aggregation.rb' + - 'app/models/chat_name.rb' + - 'app/models/ci/build.rb' + - 'app/models/ci/build_trace_chunks/database.rb' + - 'app/models/ci/build_trace_metadata.rb' + - 'app/models/ci/daily_build_group_report_result.rb' + - 'app/models/ci/deleted_object.rb' + - 'app/models/ci/namespace_mirror.rb' + - 'app/models/ci/pending_build.rb' + - 'app/models/ci/pipeline_schedule.rb' + - 'app/models/ci/processable.rb' + - 'app/models/ci/project_mirror.rb' + - 'app/models/ci/resource_group.rb' + - 'app/models/ci/runner.rb' + - 'app/models/ci/running_build.rb' + - 'app/models/ci/unit_test.rb' + - 'app/models/commit_status.rb' + - 'app/models/concerns/batch_nullify_dependent_associations.rb' + - 'app/models/concerns/board_recent_visit.rb' + - 'app/models/concerns/cache_markdown_field.rb' + - 'app/models/concerns/can_move_repository_storage.rb' + - 'app/models/concerns/cascading_namespace_setting_attribute.rb' + - 'app/models/concerns/counter_attribute.rb' + - 'app/models/concerns/deprecated_assignee.rb' + - 'app/models/concerns/file_store_mounter.rb' + - 'app/models/concerns/has_wiki_page_meta_attributes.rb' + - 'app/models/concerns/noteable.rb' + - 'app/models/concerns/packages/debian/distribution.rb' + - 'app/models/concerns/relative_positioning.rb' + - 'app/models/concerns/repository_storage_movable.rb' + - 'app/models/concerns/resolvable_note.rb' + - 'app/models/concerns/schedulable.rb' + - 'app/models/concerns/subscribable.rb' + - 'app/models/container_expiration_policy.rb' + - 'app/models/customer_relations/contact.rb' + - 'app/models/customer_relations/organization.rb' + - 'app/models/deployment.rb' + - 'app/models/diff_note_position.rb' + - 'app/models/environment.rb' + - 'app/models/gpg_key.rb' + - 'app/models/group.rb' + - 'app/models/group_import_state.rb' + - 'app/models/hooks/web_hook.rb' + - 'app/models/internal_id.rb' + - 'app/models/issue.rb' + - 'app/models/jira_import_state.rb' + - 'app/models/loose_foreign_keys/deleted_record.rb' + - 'app/models/merge_request.rb' + - 'app/models/merge_request/diff_commit_user.rb' + - 'app/models/merge_request_diff.rb' + - 'app/models/namespace.rb' + - 'app/models/note.rb' + - 'app/models/project.rb' + - 'app/models/project_authorization.rb' + - 'app/models/project_import_state.rb' + - 'app/models/project_statistics.rb' + - 'app/models/project_wiki.rb' + - 'app/models/projects/ci_feature_usage.rb' + - 'app/models/projects/repository_storage_move.rb' + - 'app/models/projects/topic.rb' + - 'app/models/raw_usage_data.rb' + - 'app/models/remote_mirror.rb' + - 'app/models/route.rb' + - 'app/models/todo.rb' + - 'app/models/u2f_registration.rb' + - 'app/models/user.rb' + - 'app/models/user_custom_attribute.rb' + - 'app/models/user_interacted_project.rb' + - 'app/services/boards/lists/base_destroy_service.rb' + - 'app/services/boards/lists/move_service.rb' + - 'app/services/bulk_create_integration_service.rb' + - 'app/services/bulk_update_integration_service.rb' + - 'app/services/ci/abort_pipelines_service.rb' + - 'app/services/ci/disable_user_pipeline_schedules_service.rb' + - 'app/services/ci/expire_pipeline_cache_service.rb' + - 'app/services/ci/job_artifacts/create_service.rb' + - 'app/services/ci/job_artifacts/destroy_batch_service.rb' + - 'app/services/ci/job_artifacts/expire_project_build_artifacts_service.rb' + - 'app/services/ci/job_artifacts/update_unknown_locked_status_service.rb' + - 'app/services/ci/test_failure_history_service.rb' + - 'app/services/ci/update_build_state_service.rb' + - 'app/services/ci/update_pending_build_service.rb' + - 'app/services/clusters/agent_tokens/track_usage_service.rb' + - 'app/services/clusters/agents/refresh_authorization_service.rb' + - 'app/services/clusters/integrations/prometheus_health_check_service.rb' + - 'app/services/deployments/archive_in_project_service.rb' + - 'app/services/event_create_service.rb' + - 'app/services/groups/transfer_service.rb' + - 'app/services/issuable_base_service.rb' + - 'app/services/issues/move_service.rb' + - 'app/services/issues/set_crm_contacts_service.rb' + - 'app/services/keys/expiry_notification_service.rb' + - 'app/services/keys/last_used_service.rb' + - 'app/services/labels/promote_service.rb' + - 'app/services/labels/transfer_service.rb' + - 'app/services/merge_requests/base_service.rb' + - 'app/services/merge_requests/bulk_remove_attention_requested_service.rb' + - 'app/services/merge_requests/cleanup_refs_service.rb' + - 'app/services/merge_requests/ff_merge_service.rb' + - 'app/services/merge_requests/handle_assignees_change_service.rb' + - 'app/services/merge_requests/merge_service.rb' + - 'app/services/merge_requests/rebase_service.rb' + - 'app/services/merge_requests/reopen_service.rb' + - 'app/services/milestones/promote_service.rb' + - 'app/services/milestones/transfer_service.rb' + - 'app/services/packages/composer/create_package_service.rb' + - 'app/services/packages/debian/generate_distribution_service.rb' + - 'app/services/packages/generic/create_package_file_service.rb' + - 'app/services/packages/mark_package_files_for_destruction_service.rb' + - 'app/services/packages/npm/create_tag_service.rb' + - 'app/services/packages/pypi/create_package_service.rb' + - 'app/services/packages/rubygems/create_dependencies_service.rb' + - 'app/services/personal_access_tokens/last_used_service.rb' + - 'app/services/projects/destroy_service.rb' + - 'app/services/projects/detect_repository_languages_service.rb' + - 'app/services/projects/move_deploy_keys_projects_service.rb' + - 'app/services/projects/move_forks_service.rb' + - 'app/services/projects/move_lfs_objects_projects_service.rb' + - 'app/services/projects/move_notification_settings_service.rb' + - 'app/services/projects/move_project_authorizations_service.rb' + - 'app/services/projects/move_project_group_links_service.rb' + - 'app/services/projects/move_project_members_service.rb' + - 'app/services/projects/move_users_star_projects_service.rb' + - 'app/services/projects/repository_languages_service.rb' + - 'app/services/projects/unlink_fork_service.rb' + - 'app/services/reset_project_cache_service.rb' + - 'app/services/spam/akismet_mark_as_spam_service.rb' + - 'app/services/spam/ham_service.rb' + - 'app/services/suggestions/apply_service.rb' + - 'app/services/suggestions/outdate_service.rb' + - 'app/services/users/activity_service.rb' + - 'app/services/users/migrate_to_ghost_user_service.rb' + - 'app/services/users/respond_to_terms_service.rb' + - 'app/services/users/set_status_service.rb' + - 'app/services/users/upsert_credit_card_validation_service.rb' + - 'app/services/x509_certificate_revoke_service.rb' + - 'app/uploaders/file_mover.rb' + - 'app/uploaders/object_storage.rb' + - 'app/workers/analytics/usage_trends/counter_job_worker.rb' + - 'app/workers/concerns/dependency_proxy/expireable.rb' + - 'app/workers/concerns/packages/cleanup_artifact_worker.rb' + - 'app/workers/container_expiration_policy_worker.rb' + - 'app/workers/namespaceless_project_destroy_worker.rb' + - 'app/workers/packages/helm/extraction_worker.rb' + - 'app/workers/packages/nuget/extraction_worker.rb' + - 'app/workers/packages/rubygems/extraction_worker.rb' + - 'app/workers/personal_access_tokens/expired_notification_worker.rb' + - 'app/workers/personal_access_tokens/expiring_worker.rb' + - 'app/workers/pipeline_metrics_worker.rb' + - 'app/workers/process_commit_worker.rb' + - 'app/workers/repository_check/clear_worker.rb' + - 'app/workers/repository_check/single_repository_worker.rb' + - 'app/workers/stuck_merge_jobs_worker.rb' + - 'app/workers/x509_issuer_crl_check_worker.rb' + - 'db/migrate/20210428151144_update_invalid_web_hooks.rb' + - 'db/migrate/20210629031900_associate_existing_dast_builds_with_variables.rb' + - 'db/migrate/20210630224625_generate_customers_dot_jwt_signing_key.rb' + - 'db/migrate/20210729123101_confirm_security_bot.rb' + - 'db/migrate/20220413054910_backfill_delayed_group_deletion.rb' + - 'db/post_migrate/20210303121224_update_gitlab_subscriptions_start_at_post_eoa.rb' + - 'db/post_migrate/20210303165302_cleanup_cluster_tokens_with_null_name.rb' + - 'db/post_migrate/20210406144743_backfill_total_tuple_count_for_batched_migrations.rb' + - 'db/post_migrate/20210513155546_backfill_nuget_temporary_packages_to_processing_status.rb' + - 'db/post_migrate/20210601073400_fix_total_stage_in_vsa.rb' + - 'db/post_migrate/20210615234935_fix_batched_migrations_old_format_job_arguments.rb' + - 'db/post_migrate/20210722042939_update_issuable_slas_where_issue_closed.rb' + - 'db/post_migrate/20210731132939_backfill_stage_event_hash.rb' + - 'db/post_migrate/20210809123658_orphaned_invite_tokens_cleanup.rb' + - 'db/post_migrate/20210811122206_update_external_project_bots.rb' + - 'db/post_migrate/20210825150212_cleanup_remaining_orphan_invites.rb' + - 'db/post_migrate/20210826171758_initialize_throttle_unauthenticated_api_columns.rb' + - 'db/post_migrate/20210901153324_slice_merge_request_diff_commit_migrations.rb' + - 'db/post_migrate/20210908132335_disable_job_token_scope_when_unused.rb' + - 'db/post_migrate/20210914095310_cleanup_orphan_project_access_tokens.rb' + - 'db/post_migrate/20211217174331_mark_recalculate_finding_signatures_as_completed.rb' + - 'db/post_migrate/20211220123956_update_invalid_member_states.rb' + - 'db/post_migrate/20220305223212_add_security_training_providers.rb' + - 'db/post_migrate/20220307203459_rename_user_email_lookup_limit_setting_to_search_settings_cleanup.rb' + - 'db/post_migrate/20220322132242_update_pages_onboarding_state.rb' + - 'ee/app/controllers/ee/clusters/clusters_controller.rb' + - 'ee/app/models/approval_merge_request_rule.rb' + - 'ee/app/models/ci/minutes/namespace_monthly_usage.rb' + - 'ee/app/models/ci/minutes/project_monthly_usage.rb' + - 'ee/app/models/concerns/deprecated_approvals_before_merge.rb' + - 'ee/app/models/concerns/epic_tree_sorting.rb' + - 'ee/app/models/concerns/geo/replicable_registry.rb' + - 'ee/app/models/concerns/geo/verification_state.rb' + - 'ee/app/models/ee/description_version.rb' + - 'ee/app/models/ee/environment.rb' + - 'ee/app/models/ee/epic.rb' + - 'ee/app/models/ee/event.rb' + - 'ee/app/models/ee/group.rb' + - 'ee/app/models/ee/iteration.rb' + - 'ee/app/models/ee/namespace_setting.rb' + - 'ee/app/models/ee/project_wiki.rb' + - 'ee/app/models/geo/container_repository_registry.rb' + - 'ee/app/models/geo/design_registry.rb' + - 'ee/app/models/geo/project_registry.rb' + - 'ee/app/models/geo_node.rb' + - 'ee/app/models/incident_management/oncall_rotation.rb' + - 'ee/app/models/vulnerabilities/feedback.rb' + - 'ee/app/services/app_sec/dast/profiles/create_associations_service.rb' + - 'ee/app/services/ci/minutes/additional_packs/change_namespace_service.rb' + - 'ee/app/services/ci/minutes/batch_reset_service.rb' + - 'ee/app/services/ci/minutes/refresh_cached_data_service.rb' + - 'ee/app/services/ci/minutes/reset_usage_service.rb' + - 'ee/app/services/ci/minutes/update_project_and_namespace_usage_service.rb' + - 'ee/app/services/ci/sync_reports_to_approval_rules_service.rb' + - 'ee/app/services/ee/issues/move_service.rb' + - 'ee/app/services/ee/labels/promote_service.rb' + - 'ee/app/services/ee/milestones/promote_service.rb' + - 'ee/app/services/ee/projects/transfer_service.rb' + - 'ee/app/services/ee/users/migrate_to_ghost_user_service.rb' + - 'ee/app/services/epics/strategies/due_date_inherited_strategy.rb' + - 'ee/app/services/epics/strategies/start_date_inherited_strategy.rb' + - 'ee/app/services/geo/job_artifact_deleted_event_store.rb' + - 'ee/app/services/geo/repository_verification_reset.rb' + - 'ee/app/services/incident_management/oncall_rotations/edit_service.rb' + - 'ee/app/services/incident_management/oncall_rotations/remove_participant_service.rb' + - 'ee/app/services/iterations/cadences/create_iterations_in_advance_service.rb' + - 'ee/app/services/iterations/cadences/destroy_service.rb' + - 'ee/app/services/iterations/delete_service.rb' + - 'ee/app/services/iterations/roll_over_issues_service.rb' + - 'ee/app/services/ldap_group_reset_service.rb' + - 'ee/app/services/personal_access_tokens/revoke_invalid_tokens.rb' + - 'ee/app/services/security/findings/cleanup_service.rb' + - 'ee/app/services/security/ingestion/mark_as_resolved_service.rb' + - 'ee/app/services/security/store_findings_metadata_service.rb' + - 'ee/app/services/security/store_scan_service.rb' + - 'ee/app/services/security/update_training_service.rb' + - 'ee/app/services/vulnerabilities/starboard_vulnerability_resolve_service.rb' + - 'ee/app/workers/import_software_licenses_worker.rb' + - 'ee/app/workers/iterations_update_status_worker.rb' + - 'ee/app/workers/sync_seat_link_request_worker.rb' + - 'ee/lib/api/geo_replication.rb' + - 'ee/lib/ee/api/protected_branches.rb' + - 'ee/lib/ee/gitlab/auth/ldap/sync/group.rb' + - 'ee/lib/ee/gitlab/background_migration/backfill_iteration_cadence_id_for_boards.rb' + - 'ee/lib/ee/gitlab/background_migration/migrate_job_artifact_registry_to_ssf.rb' + - 'ee/lib/ee/gitlab/background_migration/migrate_requirements_to_work_items.rb' + - 'ee/lib/ee/gitlab/background_migration/populate_resolved_on_default_branch_column.rb' + - 'ee/lib/ee/gitlab/background_migration/populate_uuids_for_security_findings.rb' + - 'ee/lib/gitlab/geo/replicator.rb' + - 'ee/lib/tasks/migrate/ldap.rake' + - 'ee/spec/controllers/admin/geo/projects_controller_spec.rb' + - 'ee/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb' + - 'ee/spec/controllers/groups/ldaps_controller_spec.rb' + - 'ee/spec/controllers/projects/merge_requests_controller_spec.rb' + - 'ee/spec/controllers/trials_controller_spec.rb' + - 'ee/spec/factories/import_states.rb' + - 'ee/spec/features/admin/admin_settings_spec.rb' + - 'ee/spec/features/epic_boards/epic_boards_sidebar_spec.rb' + - 'ee/spec/features/projects/settings/ee/service_desk_setting_spec.rb' + - 'ee/spec/features/projects/settings/issues_settings_spec.rb' + - 'ee/spec/features/projects/settings/protected_environments_spec.rb' + - 'ee/spec/features/projects/user_applies_custom_file_template_spec.rb' + - 'ee/spec/features/trials/select_namespace_spec.rb' + - 'ee/spec/finders/geo/repository_verification_finder_spec.rb' + - 'ee/spec/finders/security/findings_finder_spec.rb' + - 'ee/spec/finders/security/training_urls_finder_spec.rb' + - 'ee/spec/finders/template_finder_spec.rb' + - 'ee/spec/graphql/mutations/issues/set_epic_spec.rb' + - 'ee/spec/graphql/mutations/issues/set_escalation_policy_spec.rb' + - 'ee/spec/graphql/mutations/issues/set_iteration_spec.rb' + - 'ee/spec/graphql/resolvers/boards/epic_lists_resolvers_spec.rb' + - 'ee/spec/helpers/ee/blob_helper_spec.rb' + - 'ee/spec/helpers/push_rules_helper_spec.rb' + - 'ee/spec/lib/banzai/filter/references/epic_reference_filter_spec.rb' + - 'ee/spec/lib/banzai/filter/references/iteration_reference_filter_spec.rb' + - 'ee/spec/lib/banzai/filter/references/vulnerability_reference_filters_spec.rb' + - 'ee/spec/lib/ee/api/helpers_spec.rb' + - 'ee/spec/lib/ee/audit/group_changes_auditor_spec.rb' + - 'ee/spec/lib/ee/audit/project_changes_auditor_spec.rb' + - 'ee/spec/lib/ee/audit/project_ci_cd_setting_changes_auditor_spec.rb' + - 'ee/spec/lib/ee/audit/project_feature_changes_auditor_spec.rb' + - 'ee/spec/lib/ee/audit/protected_branches_changes_auditor_spec.rb' + - 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb' + - 'ee/spec/lib/ee/gitlab/background_migration/fix_incorrect_max_seats_used_spec.rb' + - 'ee/spec/lib/ee/gitlab/checks/push_rules/commit_check_spec.rb' + - 'ee/spec/lib/gitlab/auth/ldap/access_spec.rb' + - 'ee/spec/lib/gitlab/auth/saml/user_spec.rb' + - 'ee/spec/lib/gitlab/custom_file_templates_spec.rb' + - 'ee/spec/lib/gitlab/geo/geo_tasks_spec.rb' + - 'ee/spec/lib/gitlab/geo/jwt_request_decoder_spec.rb' + - 'ee/spec/lib/gitlab/geo/replication/file_downloader_spec.rb' + - 'ee/spec/lib/gitlab/geo/replication/file_transfer_spec.rb' + - 'ee/spec/lib/gitlab/geo/replication/job_artifact_downloader_spec.rb' + - 'ee/spec/lib/gitlab/geo/signed_data_spec.rb' + - 'ee/spec/lib/gitlab/git_access_spec.rb' + - 'ee/spec/models/application_setting_spec.rb' + - 'ee/spec/models/ci/minutes/namespace_monthly_usage_spec.rb' + - 'ee/spec/models/concerns/elastic/note_spec.rb' + - 'ee/spec/models/concerns/geo/verification_state_spec.rb' + - 'ee/spec/models/dast/profile_schedule_spec.rb' + - 'ee/spec/models/ee/group_spec.rb' + - 'ee/spec/models/ee/groups/feature_setting_spec.rb' + - 'ee/spec/models/ee/iteration_spec.rb' + - 'ee/spec/models/ee/iterations/cadence_spec.rb' + - 'ee/spec/models/ee/key_spec.rb' + - 'ee/spec/models/ee/namespace_limit_spec.rb' + - 'ee/spec/models/ee/vulnerability_spec.rb' + - 'ee/spec/models/geo_node_spec.rb' + - 'ee/spec/models/geo_node_status_spec.rb' + - 'ee/spec/models/group_wiki_repository_spec.rb' + - 'ee/spec/models/instance_security_dashboard_spec.rb' + - 'ee/spec/models/merge_request/blocking_spec.rb' + - 'ee/spec/models/merge_train_spec.rb' + - 'ee/spec/models/packages/package_file_spec.rb' + - 'ee/spec/models/project_feature_spec.rb' + - 'ee/spec/models/project_import_state_spec.rb' + - 'ee/spec/models/project_spec.rb' + - 'ee/spec/models/project_team_spec.rb' + - 'ee/spec/models/push_rule_spec.rb' + - 'ee/spec/models/requirements_management/requirement_spec.rb' + - 'ee/spec/models/security/scan_spec.rb' + - 'ee/spec/models/snippet_repository_spec.rb' + - 'ee/spec/models/vulnerabilities/feedback_spec.rb' + - 'ee/spec/models/vulnerabilities/stat_diff_spec.rb' + - 'ee/spec/policies/group_policy_spec.rb' + - 'ee/spec/policies/project_policy_spec.rb' + - 'ee/spec/requests/api/epic_issues_spec.rb' + - 'ee/spec/requests/api/graphql/mutations/issues/promote_to_epic_spec.rb' + - 'ee/spec/requests/api/graphql/mutations/issues/set_epic_spec.rb' + - 'ee/spec/requests/api/groups_spec.rb' + - 'ee/spec/requests/api/internal/app_sec/dast/site_validations_spec.rb' + - 'ee/spec/requests/api/internal/kubernetes_spec.rb' + - 'ee/spec/requests/api/namespaces_spec.rb' + - 'ee/spec/requests/api/project_approvals_spec.rb' + - 'ee/spec/requests/git_http_geo_spec.rb' + - 'ee/spec/requests/projects/merge_requests_controller_spec.rb' + - 'ee/spec/serializers/merge_request_widget_entity_spec.rb' + - 'ee/spec/services/ci/create_pipeline_service_spec.rb' + - 'ee/spec/services/ci/minutes/email_notification_service_spec.rb' + - 'ee/spec/services/ci/register_job_service_spec.rb' + - 'ee/spec/services/ci_cd/setup_project_spec.rb' + - 'ee/spec/services/ee/boards/issues/list_service_spec.rb' + - 'ee/spec/services/ee/notification_service_spec.rb' + - 'ee/spec/services/ee/releases/create_evidence_service_spec.rb' + - 'ee/spec/services/epic_issues/update_service_spec.rb' + - 'ee/spec/services/epics/issue_promote_service_spec.rb' + - 'ee/spec/services/epics/update_service_spec.rb' + - 'ee/spec/services/geo/file_download_service_spec.rb' + - 'ee/spec/services/geo/file_registry_removal_service_spec.rb' + - 'ee/spec/services/geo/hashed_storage_migration_service_spec.rb' + - 'ee/spec/services/groups/create_service_spec.rb' + - 'ee/spec/services/groups/update_service_spec.rb' + - 'ee/spec/services/merge_trains/check_status_service_spec.rb' + - 'ee/spec/services/merge_trains/refresh_merge_request_service_spec.rb' + - 'ee/spec/services/merge_trains/refresh_service_spec.rb' + - 'ee/spec/services/projects/setup_ci_cd_spec.rb' + - 'ee/spec/services/projects/update_mirror_service_spec.rb' + - 'ee/spec/services/security/ingestion/ingest_report_slice_service_spec.rb' + - 'ee/spec/services/security/security_orchestration_policies/create_pipeline_service_spec.rb' + - 'ee/spec/services/vulnerabilities/starboard_vulnerability_resolve_service_spec.rb' + - 'ee/spec/services/vulnerabilities/statistics/adjustment_service_spec.rb' + - 'ee/spec/services/vulnerabilities/statistics/update_service_spec.rb' + - 'ee/spec/support/helpers/ee/geo_helpers.rb' + - 'ee/spec/support/shared_examples/models/requirement_issues_examples.rb' + - 'ee/spec/support/shared_examples/policies/protected_environments_shared_examples.rb' + - 'ee/spec/workers/app_sec/dast/profile_schedule_worker_spec.rb' + - 'ee/spec/workers/ee/repository_check/batch_worker_spec.rb' + - 'ee/spec/workers/geo/repositories_clean_up_worker_spec.rb' + - 'ee/spec/workers/geo/repository_shard_sync_worker_spec.rb' + - 'ee/spec/workers/geo/repository_sync_worker_spec.rb' + - 'ee/spec/workers/geo/repository_verification/primary/batch_worker_spec.rb' + - 'ee/spec/workers/geo/repository_verification/primary/shard_worker_spec.rb' + - 'ee/spec/workers/geo/repository_verification/secondary/scheduler_worker_spec.rb' + - 'ee/spec/workers/geo/repository_verification/secondary/single_worker_spec.rb' + - 'ee/spec/workers/geo/verification_state_backfill_service_spec.rb' + - 'ee/spec/workers/import_software_licenses_worker_spec.rb' + - 'ee/spec/workers/iterations/roll_over_issues_worker_spec.rb' + - 'ee/spec/workers/iterations_update_status_worker_spec.rb' + - 'ee/spec/workers/network_policy_metrics_worker_spec.rb' + - 'ee/spec/workers/security/orchestration_policy_rule_schedule_namespace_worker_spec.rb' + - 'ee/spec/workers/security/orchestration_policy_rule_schedule_worker_spec.rb' + - 'ee/spec/workers/update_all_mirrors_worker_spec.rb' + - 'lib/api/commit_statuses.rb' + - 'lib/api/usage_data.rb' + - 'lib/gitlab/background_migration/add_primary_email_to_emails_if_user_confirmed.rb' + - 'lib/gitlab/background_migration/backfill_artifact_expiry_date.rb' + - 'lib/gitlab/background_migration/backfill_ci_queuing_tables.rb' + - 'lib/gitlab/background_migration/backfill_draft_status_on_merge_requests.rb' + - 'lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2.rb' + - 'lib/gitlab/background_migration/backfill_member_namespace_for_group_members.rb' + - 'lib/gitlab/background_migration/backfill_namespace_id_for_namespace_route.rb' + - 'lib/gitlab/background_migration/backfill_namespace_traversal_ids_roots.rb' + - 'lib/gitlab/background_migration/backfill_projects_with_coverage.rb' + - 'lib/gitlab/background_migration/backfill_topics_title.rb' + - 'lib/gitlab/background_migration/backfill_user_namespace.rb' + - 'lib/gitlab/background_migration/backfill_work_item_type_id_for_issues.rb' + - 'lib/gitlab/background_migration/cleanup_draft_data_from_faulty_regex.rb' + - 'lib/gitlab/background_migration/copy_column_using_background_migration_job.rb' + - 'lib/gitlab/background_migration/legacy_upload_mover.rb' + - 'lib/gitlab/background_migration/merge_topics_with_same_name.rb' + - 'lib/gitlab/background_migration/migrate_merge_request_diff_commit_users.rb' + - 'lib/gitlab/background_migration/migrate_null_private_profile_to_false.rb' + - 'lib/gitlab/background_migration/migrate_personal_namespace_project_maintainer_to_owner.rb' + - 'lib/gitlab/background_migration/migrate_project_taggings_context_from_tags_to_topics.rb' + - 'lib/gitlab/background_migration/migrate_shimo_confluence_integration_category.rb' + - 'lib/gitlab/background_migration/migrate_u2f_webauthn.rb' + - 'lib/gitlab/background_migration/nullify_orphan_runner_id_on_ci_builds.rb' + - 'lib/gitlab/background_migration/project_namespaces/backfill_project_namespaces.rb' + - 'lib/gitlab/background_migration/remove_all_trace_expiration_dates.rb' + - 'lib/gitlab/background_migration/reset_duplicate_ci_runners_token_encrypted_values_on_projects.rb' + - 'lib/gitlab/background_migration/reset_duplicate_ci_runners_token_values_on_projects.rb' + - 'lib/gitlab/bitbucket_import/importer.rb' + - 'lib/gitlab/bitbucket_server_import/importer.rb' + - 'lib/gitlab/ci/tags/bulk_insert.rb' + - 'lib/gitlab/ci/trace.rb' + - 'lib/gitlab/composer/cache.rb' + - 'lib/gitlab/database/background_migration_job.rb' + - 'lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin.rb' + - 'lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb' + - 'lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb' + - 'lib/gitlab/database/schema_migrations.rb' + - 'lib/gitlab/etag_caching/middleware.rb' + - 'lib/gitlab/fogbugz_import/importer.rb' + - 'lib/gitlab/github_import/importer/pull_request_merged_by_importer.rb' + - 'lib/gitlab/github_import/importer/pull_request_review_importer.rb' + - 'lib/gitlab/import/set_async_jid.rb' + - 'lib/gitlab/jira_import/handle_labels_service.rb' + - 'lib/gitlab/job_waiter.rb' + - 'lib/gitlab/legacy_github_import/importer.rb' + - 'lib/gitlab/markdown_cache/active_record/extension.rb' + - 'lib/gitlab/otp_key_rotator.rb' + - 'lib/gitlab/seeder.rb' + - 'lib/tasks/ci/cleanup.rake' + - 'lib/tasks/gitlab/external_diffs.rake' + - 'lib/tasks/gitlab/ldap.rake' + - 'lib/tasks/gitlab/user_management.rake' + - 'lib/tasks/migrate/migrate_iids.rake' + - 'spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb' + - 'spec/controllers/groups_controller_spec.rb' + - 'spec/controllers/import/bitbucket_controller_spec.rb' + - 'spec/controllers/import/gitlab_controller_spec.rb' + - 'spec/controllers/omniauth_callbacks_controller_spec.rb' + - 'spec/controllers/projects/forks_controller_spec.rb' + - 'spec/controllers/projects/graphs_controller_spec.rb' + - 'spec/controllers/projects/jobs_controller_spec.rb' + - 'spec/controllers/projects/merge_requests/content_controller_spec.rb' + - 'spec/controllers/projects/merge_requests_controller_spec.rb' + - 'spec/controllers/projects/notes_controller_spec.rb' + - 'spec/controllers/projects/pipelines/tests_controller_spec.rb' + - 'spec/controllers/projects/repositories_controller_spec.rb' + - 'spec/controllers/projects/settings/ci_cd_controller_spec.rb' + - 'spec/controllers/projects/starrers_controller_spec.rb' + - 'spec/controllers/projects_controller_spec.rb' + - 'spec/controllers/uploads_controller_spec.rb' + - 'spec/factories/alert_management/alerts.rb' + - 'spec/factories/container_expiration_policies.rb' + - 'spec/factories/design_management/versions.rb' + - 'spec/factories/environments.rb' + - 'spec/factories/import_states.rb' + - 'spec/factories/projects.rb' + - 'spec/factories/usage_data.rb' + - 'spec/features/admin/admin_settings_spec.rb' + - 'spec/features/admin/admin_uses_repository_checks_spec.rb' + - 'spec/features/dashboard/projects_spec.rb' + - 'spec/features/groups_spec.rb' + - 'spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb' + - 'spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb' + - 'spec/features/issues/discussion_lock_spec.rb' + - 'spec/features/merge_request/merge_request_discussion_lock_spec.rb' + - 'spec/features/merge_request/user_creates_image_diff_notes_spec.rb' + - 'spec/features/merge_request/user_locks_discussion_spec.rb' + - 'spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb' + - 'spec/features/merge_request/user_sees_diff_spec.rb' + - 'spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb' + - 'spec/features/merge_request/user_sees_merge_widget_spec.rb' + - 'spec/features/merge_request/user_sees_pipelines_spec.rb' + - 'spec/features/merge_request/user_views_merge_request_from_deleted_fork_spec.rb' + - 'spec/features/monitor_sidebar_link_spec.rb' + - 'spec/features/password_reset_spec.rb' + - 'spec/features/profiles/emails_spec.rb' + - 'spec/features/projects/blobs/blob_show_spec.rb' + - 'spec/features/projects/diffs/diff_show_spec.rb' + - 'spec/features/projects/features_visibility_spec.rb' + - 'spec/features/projects/fork_spec.rb' + - 'spec/features/projects/jobs/user_browses_jobs_spec.rb' + - 'spec/features/projects/jobs_spec.rb' + - 'spec/features/projects/members/invite_group_spec.rb' + - 'spec/features/projects/milestones/milestone_spec.rb' + - 'spec/features/projects/pipeline_schedules_spec.rb' + - 'spec/features/projects/pipelines/pipeline_spec.rb' + - 'spec/features/projects/settings/service_desk_setting_spec.rb' + - 'spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb' + - 'spec/features/projects/user_sees_sidebar_spec.rb' + - 'spec/features/projects_spec.rb' + - 'spec/features/refactor_blob_viewer_disabled/projects/blobs/blob_show_spec.rb' + - 'spec/features/u2f_spec.rb' + - 'spec/features/users/show_spec.rb' + - 'spec/features/webauthn_spec.rb' + - 'spec/finders/groups_finder_spec.rb' + - 'spec/finders/notes_finder_spec.rb' + - 'spec/finders/packages/go/package_finder_spec.rb' + - 'spec/finders/packages/maven/package_finder_spec.rb' + - 'spec/finders/packages/npm/package_finder_spec.rb' + - 'spec/finders/packages/nuget/package_finder_spec.rb' + - 'spec/finders/packages/package_finder_spec.rb' + - 'spec/finders/projects_finder_spec.rb' + - 'spec/finders/releases/group_releases_finder_spec.rb' + - 'spec/finders/releases_finder_spec.rb' + - 'spec/finders/user_group_notification_settings_finder_spec.rb' + - 'spec/graphql/mutations/custom_emoji/destroy_spec.rb' + - 'spec/graphql/mutations/issues/set_escalation_status_spec.rb' + - 'spec/graphql/mutations/issues/update_spec.rb' + - 'spec/graphql/resolvers/ci/test_suite_resolver_spec.rb' + - 'spec/graphql/types/project_type_spec.rb' + - 'spec/helpers/auth_helper_spec.rb' + - 'spec/helpers/events_helper_spec.rb' + - 'spec/helpers/groups_helper_spec.rb' + - 'spec/helpers/import_helper_spec.rb' + - 'spec/helpers/members_helper_spec.rb' + - 'spec/helpers/projects_helper_spec.rb' + - 'spec/initializers/active_record_locking_spec.rb' + - 'spec/lib/api/helpers_spec.rb' + - 'spec/lib/backup/repositories_spec.rb' + - 'spec/lib/banzai/filter/references/issue_reference_filter_spec.rb' + - 'spec/lib/banzai/filter/references/merge_request_reference_filter_spec.rb' + - 'spec/lib/banzai/filter/references/snippet_reference_filter_spec.rb' + - 'spec/lib/banzai/reference_parser/merge_request_parser_spec.rb' + - 'spec/lib/banzai/reference_parser/snippet_parser_spec.rb' + - 'spec/lib/gitlab/asciidoc_spec.rb' + - 'spec/lib/gitlab/auth/saml/user_spec.rb' + - 'spec/lib/gitlab/background_migration/backfill_project_repositories_spec.rb' + - 'spec/lib/gitlab/background_migration/batched_migration_job_spec.rb' + - 'spec/lib/gitlab/background_migration/update_timelogs_null_spent_at_spec.rb' + - 'spec/lib/gitlab/ci/variables/builder/group_spec.rb' + - 'spec/lib/gitlab/ci/variables/builder/project_spec.rb' + - 'spec/lib/gitlab/contributions_calendar_spec.rb' + - 'spec/lib/gitlab/cycle_analytics/permissions_spec.rb' + - 'spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb' + - 'spec/lib/gitlab/database/batch_count_spec.rb' + - 'spec/lib/gitlab/database/consistency_checker_spec.rb' + - 'spec/lib/gitlab/database/load_balancing/connection_proxy_spec.rb' + - 'spec/lib/gitlab/database/load_balancing_spec.rb' + - 'spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb' + - 'spec/lib/gitlab/database/query_analyzers/prevent_cross_database_modification_spec.rb' + - 'spec/lib/gitlab/database/schema_migrations/migrations_spec.rb' + - 'spec/lib/gitlab/discussions_diff/file_collection_spec.rb' + - 'spec/lib/gitlab/email/handler/create_note_handler_spec.rb' + - 'spec/lib/gitlab/email/handler/create_note_on_issuable_handler_spec.rb' + - 'spec/lib/gitlab/etag_caching/store_spec.rb' + - 'spec/lib/gitlab/git_access_spec.rb' + - 'spec/lib/gitlab/git_access_wiki_spec.rb' + - 'spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb' + - 'spec/lib/gitlab/middleware/go_spec.rb' + - 'spec/lib/gitlab/middleware/query_analyzer_spec.rb' + - 'spec/lib/gitlab/object_hierarchy_spec.rb' + - 'spec/lib/gitlab/pages_transfer_spec.rb' + - 'spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb' + - 'spec/lib/sidebars/projects/menus/project_information_menu_spec.rb' + - 'spec/mailers/notify_spec.rb' + - 'spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb' + - 'spec/migrations/remove_duplicate_dast_site_tokens_spec.rb' + - 'spec/migrations/schedule_update_timelogs_null_spent_at_spec.rb' + - 'spec/models/application_setting_spec.rb' + - 'spec/models/ci/build_dependencies_spec.rb' + - 'spec/models/ci/build_spec.rb' + - 'spec/models/ci/group_spec.rb' + - 'spec/models/ci/legacy_stage_spec.rb' + - 'spec/models/ci/pipeline_schedule_spec.rb' + - 'spec/models/ci/pipeline_spec.rb' + - 'spec/models/ci/processable_spec.rb' + - 'spec/models/ci/resource_group_spec.rb' + - 'spec/models/ci/runner_spec.rb' + - 'spec/models/ci/stage_spec.rb' + - 'spec/models/commit_signatures/gpg_signature_spec.rb' + - 'spec/models/commit_status_spec.rb' + - 'spec/models/concerns/cache_markdown_field_spec.rb' + - 'spec/models/concerns/deployment_platform_spec.rb' + - 'spec/models/concerns/deprecated_assignee_spec.rb' + - 'spec/models/concerns/each_batch_spec.rb' + - 'spec/models/concerns/pg_full_text_searchable_spec.rb' + - 'spec/models/concerns/project_features_compatibility_spec.rb' + - 'spec/models/concerns/spammable_spec.rb' + - 'spec/models/container_repository_spec.rb' + - 'spec/models/deploy_keys_project_spec.rb' + - 'spec/models/deploy_token_spec.rb' + - 'spec/models/diff_discussion_spec.rb' + - 'spec/models/diff_note_spec.rb' + - 'spec/models/environment_spec.rb' + - 'spec/models/group_spec.rb' + - 'spec/models/guest_spec.rb' + - 'spec/models/integration_spec.rb' + - 'spec/models/issue_spec.rb' + - 'spec/models/loose_foreign_keys/deleted_record_spec.rb' + - 'spec/models/member_spec.rb' + - 'spec/models/members/group_member_spec.rb' + - 'spec/models/members/project_member_spec.rb' + - 'spec/models/merge_request_diff_spec.rb' + - 'spec/models/merge_request_spec.rb' + - 'spec/models/namespace/traversal_hierarchy_spec.rb' + - 'spec/models/namespace_spec.rb' + - 'spec/models/note_spec.rb' + - 'spec/models/project_authorization_spec.rb' + - 'spec/models/project_feature_spec.rb' + - 'spec/models/project_spec.rb' + - 'spec/models/project_statistics_spec.rb' + - 'spec/models/projects/build_artifacts_size_refresh_spec.rb' + - 'spec/models/projects/topic_spec.rb' + - 'spec/models/remote_mirror_spec.rb' + - 'spec/models/repository_spec.rb' + - 'spec/models/route_spec.rb' + - 'spec/models/snippet_repository_spec.rb' + - 'spec/models/user_spec.rb' + - 'spec/policies/ci/build_policy_spec.rb' + - 'spec/policies/custom_emoji_policy_spec.rb' + - 'spec/policies/note_policy_spec.rb' + - 'spec/policies/project_policy_spec.rb' + - 'spec/presenters/ci/build_presenter_spec.rb' + - 'spec/presenters/project_presenter_spec.rb' + - 'spec/requests/api/ci/job_artifacts_spec.rb' + - 'spec/requests/api/ci/runner/jobs_request_post_spec.rb' + - 'spec/requests/api/container_repositories_spec.rb' + - 'spec/requests/api/graphql/container_repository/container_repository_details_spec.rb' + - 'spec/requests/api/graphql/group/dependency_proxy_blobs_spec.rb' + - 'spec/requests/api/graphql/group/dependency_proxy_group_setting_spec.rb' + - 'spec/requests/api/graphql/group/dependency_proxy_image_ttl_policy_spec.rb' + - 'spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb' + - 'spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb' + - 'spec/requests/api/graphql/mutations/snippets/create_spec.rb' + - 'spec/requests/api/graphql/mutations/snippets/destroy_spec.rb' + - 'spec/requests/api/graphql/mutations/snippets/update_spec.rb' + - 'spec/requests/api/helm_packages_spec.rb' + - 'spec/requests/api/issues/get_group_issues_spec.rb' + - 'spec/requests/api/issues/get_project_issues_spec.rb' + - 'spec/requests/api/issues/issues_spec.rb' + - 'spec/requests/api/merge_requests_spec.rb' + - 'spec/requests/api/notes_spec.rb' + - 'spec/requests/api/nuget_group_packages_spec.rb' + - 'spec/requests/api/projects_spec.rb' + - 'spec/requests/api/pypi_packages_spec.rb' + - 'spec/requests/api/releases_spec.rb' + - 'spec/requests/api/rubygem_packages_spec.rb' + - 'spec/requests/api/snippets_spec.rb' + - 'spec/requests/api/tags_spec.rb' + - 'spec/requests/git_http_spec.rb' + - 'spec/requests/groups/settings/access_tokens_controller_spec.rb' + - 'spec/requests/jwt_controller_spec.rb' + - 'spec/requests/lfs_http_spec.rb' + - 'spec/requests/projects/merge_requests_spec.rb' + - 'spec/requests/projects/settings/access_tokens_controller_spec.rb' + - 'spec/services/alert_management/create_alert_issue_service_spec.rb' + - 'spec/services/ci/compare_reports_base_service_spec.rb' + - 'spec/services/ci/compare_test_reports_service_spec.rb' + - 'spec/services/ci/job_artifacts/update_unknown_locked_status_service_spec.rb' + - 'spec/services/ci/register_job_service_spec.rb' + - 'spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb' + - 'spec/services/ci/retry_job_service_spec.rb' + - 'spec/services/ci/retry_pipeline_service_spec.rb' + - 'spec/services/ci/test_failure_history_service_spec.rb' + - 'spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb' + - 'spec/services/container_expiration_policies/cleanup_service_spec.rb' + - 'spec/services/dependency_proxy/find_cached_manifest_service_spec.rb' + - 'spec/services/deployments/update_environment_service_spec.rb' + - 'spec/services/groups/create_service_spec.rb' + - 'spec/services/groups/transfer_service_spec.rb' + - 'spec/services/groups/update_service_spec.rb' + - 'spec/services/incident_management/pager_duty/process_webhook_service_spec.rb' + - 'spec/services/issuable/common_system_notes_service_spec.rb' + - 'spec/services/issues/clone_service_spec.rb' + - 'spec/services/issues/close_service_spec.rb' + - 'spec/services/issues/update_service_spec.rb' + - 'spec/services/members/destroy_service_spec.rb' + - 'spec/services/merge_requests/get_urls_service_spec.rb' + - 'spec/services/merge_requests/merge_service_spec.rb' + - 'spec/services/merge_requests/refresh_service_spec.rb' + - 'spec/services/merge_requests/reopen_service_spec.rb' + - 'spec/services/merge_requests/update_service_spec.rb' + - 'spec/services/notes/update_service_spec.rb' + - 'spec/services/notification_service_spec.rb' + - 'spec/services/packages/maven/metadata/sync_service_spec.rb' + - 'spec/services/packages/nuget/search_service_spec.rb' + - 'spec/services/projects/container_repository/delete_tags_service_spec.rb' + - 'spec/services/projects/create_service_spec.rb' + - 'spec/services/projects/destroy_service_spec.rb' + - 'spec/services/projects/fork_service_spec.rb' + - 'spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb' + - 'spec/services/repositories/destroy_service_spec.rb' + - 'spec/services/spam/ham_service_spec.rb' + - 'spec/services/system_notes/design_management_service_spec.rb' + - 'spec/services/system_notes/issuables_service_spec.rb' + - 'spec/services/system_notes/time_tracking_service_spec.rb' + - 'spec/services/users/repair_ldap_blocked_service_spec.rb' + - 'spec/services/work_items/task_list_reference_replacement_service_spec.rb' + - 'spec/support/helpers/access_matchers_helpers.rb' + - 'spec/support/matchers/access_matchers_for_controller.rb' + - 'spec/support/shared_contexts/email_shared_context.rb' + - 'spec/support/shared_contexts/finders/packages/npm/package_finder_shared_context.rb' + - 'spec/support/shared_contexts/mailers/notify_shared_context.rb' + - 'spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb' + - 'spec/support/shared_examples/ci/stuck_builds_shared_examples.rb' + - 'spec/support/shared_examples/controllers/create_notes_rate_limit_shared_examples.rb' + - 'spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb' + - 'spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb' + - 'spec/support/shared_examples/features/2fa_shared_examples.rb' + - 'spec/support/shared_examples/features/access_tokens_shared_examples.rb' + - 'spec/support/shared_examples/features/sidebar_shared_examples.rb' + - 'spec/support/shared_examples/lib/banzai/reference_parser_shared_examples.rb' + - 'spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb' + - 'spec/support/shared_examples/models/concerns/featurable_shared_examples.rb' + - 'spec/support/shared_examples/models/concerns/ttl_expirable_shared_examples.rb' + - 'spec/support/shared_examples/models/members_notifications_shared_example.rb' + - 'spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb' + - 'spec/support/shared_examples/models/throttled_touch_shared_examples.rb' + - 'spec/support/shared_examples/policies/resource_access_token_shared_examples.rb' + - 'spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb' + - 'spec/support/shared_examples/requests/api/members_shared_examples.rb' + - 'spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb' + - 'spec/support/shared_examples/requests/user_activity_shared_examples.rb' + - 'spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb' + - 'spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb' + - 'spec/support/shared_examples/services/notification_service_shared_examples.rb' + - 'spec/support/shared_examples/views/pipeline_status_changes_email.rb' + - 'spec/support/trace/trace_helpers.rb' + - 'spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb' + - 'spec/tasks/gitlab/artifacts/check_rake_spec.rb' + - 'spec/tasks/gitlab/external_diffs_rake_spec.rb' + - 'spec/tasks/gitlab/uploads/check_rake_spec.rb' + - 'spec/uploaders/job_artifact_uploader_spec.rb' + - 'spec/views/groups/edit.html.haml_spec.rb' + - 'spec/views/projects/environments/terminal.html.haml_spec.rb' + - 'spec/views/projects/tracing/show.html.haml_spec.rb' + - 'spec/workers/auto_devops/disable_worker_spec.rb' + - 'spec/workers/build_finished_worker_spec.rb' + - 'spec/workers/ci/merge_requests/add_todo_when_build_fails_worker_spec.rb' + - 'spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb' + - 'spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb' + - 'spec/workers/container_expiration_policy_worker_spec.rb' + - 'spec/workers/container_registry/migration/guard_worker_spec.rb' + - 'spec/workers/gitlab/github_import/advance_stage_worker_spec.rb' + - 'spec/workers/packages/cleanup_package_file_worker_spec.rb' + - 'spec/workers/packages/cleanup_package_registry_worker_spec.rb' + - 'spec/workers/packages/composer/cache_cleanup_worker_spec.rb' + - 'spec/workers/pipeline_schedule_worker_spec.rb' + - 'spec/workers/remote_mirror_notification_worker_spec.rb' + - 'spec/workers/repository_check/batch_worker_spec.rb' + - 'spec/workers/repository_check/clear_worker_spec.rb' diff --git a/.rubocop_todo/rspec/verified_doubles.yml b/.rubocop_todo/rspec/verified_doubles.yml index aed96909fe3..e272fbc555c 100644 --- a/.rubocop_todo/rspec/verified_doubles.yml +++ b/.rubocop_todo/rspec/verified_doubles.yml @@ -332,7 +332,6 @@ RSpec/VerifiedDoubles: - spec/features/projects/integrations/user_activates_jira_spec.rb - spec/finders/ci/auth_job_finder_spec.rb - spec/finders/merge_requests/oldest_per_commit_finder_spec.rb - - spec/finders/projects/serverless/functions_finder_spec.rb - spec/finders/repositories/changelog_commits_finder_spec.rb - spec/finders/repositories/changelog_tag_finder_spec.rb - spec/graphql/features/authorization_spec.rb diff --git a/Gemfile.lock b/Gemfile.lock index 86e9d18b31d..5f2e4f18ca1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1088,7 +1088,7 @@ GEM rspec-expectations (3.10.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) - rspec-mocks (3.10.2) + rspec-mocks (3.10.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-parameterized (0.5.0) diff --git a/app/assets/javascripts/pages/projects/serverless/index.js b/app/assets/javascripts/pages/projects/serverless/index.js deleted file mode 100644 index 9ae81b327b1..00000000000 --- a/app/assets/javascripts/pages/projects/serverless/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import ServerlessBundle from '~/serverless/serverless_bundle'; - -new ServerlessBundle(); // eslint-disable-line no-new diff --git a/app/assets/javascripts/serverless/components/area.vue b/app/assets/javascripts/serverless/components/area.vue deleted file mode 100644 index 0b158ff3e95..00000000000 --- a/app/assets/javascripts/serverless/components/area.vue +++ /dev/null @@ -1,145 +0,0 @@ - - - diff --git a/app/assets/javascripts/serverless/components/empty_state.vue b/app/assets/javascripts/serverless/components/empty_state.vue deleted file mode 100644 index 6d1cea519c4..00000000000 --- a/app/assets/javascripts/serverless/components/empty_state.vue +++ /dev/null @@ -1,39 +0,0 @@ - - - diff --git a/app/assets/javascripts/serverless/components/environment_row.vue b/app/assets/javascripts/serverless/components/environment_row.vue deleted file mode 100644 index 01030172ea8..00000000000 --- a/app/assets/javascripts/serverless/components/environment_row.vue +++ /dev/null @@ -1,65 +0,0 @@ - - - diff --git a/app/assets/javascripts/serverless/components/function_details.vue b/app/assets/javascripts/serverless/components/function_details.vue deleted file mode 100644 index d2306c2d8bd..00000000000 --- a/app/assets/javascripts/serverless/components/function_details.vue +++ /dev/null @@ -1,94 +0,0 @@ - - - diff --git a/app/assets/javascripts/serverless/components/function_row.vue b/app/assets/javascripts/serverless/components/function_row.vue deleted file mode 100644 index fab9c0a75e7..00000000000 --- a/app/assets/javascripts/serverless/components/function_row.vue +++ /dev/null @@ -1,77 +0,0 @@ - - - diff --git a/app/assets/javascripts/serverless/components/functions.vue b/app/assets/javascripts/serverless/components/functions.vue deleted file mode 100644 index e9461aa3ead..00000000000 --- a/app/assets/javascripts/serverless/components/functions.vue +++ /dev/null @@ -1,139 +0,0 @@ - - - diff --git a/app/assets/javascripts/serverless/components/missing_prometheus.vue b/app/assets/javascripts/serverless/components/missing_prometheus.vue deleted file mode 100644 index d9e6bb5009e..00000000000 --- a/app/assets/javascripts/serverless/components/missing_prometheus.vue +++ /dev/null @@ -1,57 +0,0 @@ - - - diff --git a/app/assets/javascripts/serverless/components/pod_box.vue b/app/assets/javascripts/serverless/components/pod_box.vue deleted file mode 100644 index 04d3641bce3..00000000000 --- a/app/assets/javascripts/serverless/components/pod_box.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - diff --git a/app/assets/javascripts/serverless/components/url.vue b/app/assets/javascripts/serverless/components/url.vue deleted file mode 100644 index b105f49e475..00000000000 --- a/app/assets/javascripts/serverless/components/url.vue +++ /dev/null @@ -1,28 +0,0 @@ - - - diff --git a/app/assets/javascripts/serverless/constants.js b/app/assets/javascripts/serverless/constants.js deleted file mode 100644 index 42c9ee983b4..00000000000 --- a/app/assets/javascripts/serverless/constants.js +++ /dev/null @@ -1,10 +0,0 @@ -export const MAX_REQUESTS = 3; // max number of times to retry - -export const X_INTERVAL = 5; // Reflects the number of verticle bars on the x-axis - -export const CHECKING_INSTALLED = 'checking'; // The backend is still determining whether or not Knative is installed - -export const TIMEOUT = 'timeout'; - -export const DEPRECATION_POST_LINK = - 'https://about.gitlab.com/releases/2021/09/22/gitlab-14-3-released/#gitlab-serverless'; diff --git a/app/assets/javascripts/serverless/event_hub.js b/app/assets/javascripts/serverless/event_hub.js deleted file mode 100644 index e31806ad199..00000000000 --- a/app/assets/javascripts/serverless/event_hub.js +++ /dev/null @@ -1,3 +0,0 @@ -import createEventHub from '~/helpers/event_hub_factory'; - -export default createEventHub(); diff --git a/app/assets/javascripts/serverless/serverless_bundle.js b/app/assets/javascripts/serverless/serverless_bundle.js deleted file mode 100644 index e8d87a40fc7..00000000000 --- a/app/assets/javascripts/serverless/serverless_bundle.js +++ /dev/null @@ -1,67 +0,0 @@ -import Vue from 'vue'; -import FunctionDetails from './components/function_details.vue'; -import Functions from './components/functions.vue'; -import { createStore } from './store'; - -export default class Serverless { - constructor() { - if (document.querySelector('.js-serverless-function-details-page') != null) { - const entryPointData = document.querySelector('.js-serverless-function-details-page').dataset; - const store = createStore(entryPointData); - - const { - serviceName, - serviceDescription, - serviceEnvironment, - serviceUrl, - serviceNamespace, - servicePodcount, - serviceMetricsUrl, - prometheus, - } = entryPointData; - const el = document.querySelector('#js-serverless-function-details'); - - const service = { - name: serviceName, - description: serviceDescription, - environment: serviceEnvironment, - url: serviceUrl, - namespace: serviceNamespace, - podcount: servicePodcount, - metricsUrl: serviceMetricsUrl, - }; - - this.functionDetails = new Vue({ - el, - store, - render(createElement) { - return createElement(FunctionDetails, { - props: { - func: service, - hasPrometheus: prometheus !== undefined, - }, - }); - }, - }); - } else { - const entryPointData = document.querySelector('.js-serverless-functions-page').dataset; - const store = createStore(entryPointData); - - const el = document.querySelector('#js-serverless-functions'); - this.functions = new Vue({ - el, - store, - render(createElement) { - return createElement(Functions); - }, - }); - } - } - - destroy() { - this.destroyed = true; - - this.functions.$destroy(); - this.functionDetails.$destroy(); - } -} diff --git a/app/assets/javascripts/serverless/store/actions.js b/app/assets/javascripts/serverless/store/actions.js deleted file mode 100644 index 166cd796680..00000000000 --- a/app/assets/javascripts/serverless/store/actions.js +++ /dev/null @@ -1,131 +0,0 @@ -import createFlash from '~/flash'; -import axios from '~/lib/utils/axios_utils'; -import { backOff } from '~/lib/utils/common_utils'; -import statusCodes from '~/lib/utils/http_status'; -import { __ } from '~/locale'; -import { MAX_REQUESTS, CHECKING_INSTALLED, TIMEOUT } from '../constants'; -import * as types from './mutation_types'; - -export const requestFunctionsLoading = ({ commit }) => commit(types.REQUEST_FUNCTIONS_LOADING); -export const receiveFunctionsSuccess = ({ commit }, data) => - commit(types.RECEIVE_FUNCTIONS_SUCCESS, data); -export const receiveFunctionsPartial = ({ commit }, data) => - commit(types.RECEIVE_FUNCTIONS_PARTIAL, data); -export const receiveFunctionsTimeout = ({ commit }, data) => - commit(types.RECEIVE_FUNCTIONS_TIMEOUT, data); -export const receiveFunctionsNoDataSuccess = ({ commit }, data) => - commit(types.RECEIVE_FUNCTIONS_NODATA_SUCCESS, data); -export const receiveFunctionsError = ({ commit }, error) => - commit(types.RECEIVE_FUNCTIONS_ERROR, error); - -export const receiveMetricsSuccess = ({ commit }, data) => - commit(types.RECEIVE_METRICS_SUCCESS, data); -export const receiveMetricsNoPrometheus = ({ commit }) => - commit(types.RECEIVE_METRICS_NO_PROMETHEUS); -export const receiveMetricsNoDataSuccess = ({ commit }, data) => - commit(types.RECEIVE_METRICS_NODATA_SUCCESS, data); -export const receiveMetricsError = ({ commit }, error) => - commit(types.RECEIVE_METRICS_ERROR, error); - -export const fetchFunctions = ({ dispatch }, { functionsPath }) => { - let retryCount = 0; - - const functionsPartiallyFetched = (data) => { - if (data.functions !== null && data.functions.length) { - dispatch('receiveFunctionsPartial', data); - } - }; - - dispatch('requestFunctionsLoading'); - - backOff((next, stop) => { - axios - .get(functionsPath) - .then((response) => { - if (response.data.knative_installed === CHECKING_INSTALLED) { - retryCount += 1; - if (retryCount < MAX_REQUESTS) { - functionsPartiallyFetched(response.data); - next(); - } else { - stop(TIMEOUT); - } - } else { - stop(response.data); - } - }) - .catch(stop); - }) - .then((data) => { - if (data === TIMEOUT) { - dispatch('receiveFunctionsTimeout'); - createFlash({ - message: __('Loading functions timed out. Please reload the page to try again.'), - }); - } else if (data.functions !== null && data.functions.length) { - dispatch('receiveFunctionsSuccess', data); - } else { - dispatch('receiveFunctionsNoDataSuccess', data); - } - }) - .catch((error) => { - dispatch('receiveFunctionsError', error); - createFlash({ - message: error, - }); - }); -}; - -export const fetchMetrics = ({ dispatch }, { metricsPath, hasPrometheus }) => { - let retryCount = 0; - - if (!hasPrometheus) { - dispatch('receiveMetricsNoPrometheus'); - return; - } - - backOff((next, stop) => { - axios - .get(metricsPath) - .then((response) => { - if (response.status === statusCodes.NO_CONTENT) { - retryCount += 1; - if (retryCount < MAX_REQUESTS) { - next(); - } else { - dispatch('receiveMetricsNoDataSuccess'); - stop(null); - } - } else { - stop(response.data); - } - }) - .catch(stop); - }) - .then((data) => { - if (data === null) { - return; - } - - const updatedMetric = data.metrics; - const queries = data.metrics.queries.map((query) => ({ - ...query, - result: query.result.map((result) => ({ - ...result, - values: result.values.map(([timestamp, value]) => ({ - time: new Date(timestamp * 1000).toISOString(), - value: Number(value), - })), - })), - })); - - updatedMetric.queries = queries; - dispatch('receiveMetricsSuccess', updatedMetric); - }) - .catch((error) => { - dispatch('receiveMetricsError', error); - createFlash({ - message: error, - }); - }); -}; diff --git a/app/assets/javascripts/serverless/store/getters.js b/app/assets/javascripts/serverless/store/getters.js deleted file mode 100644 index da975c56e5d..00000000000 --- a/app/assets/javascripts/serverless/store/getters.js +++ /dev/null @@ -1,7 +0,0 @@ -import { translate } from '../utils'; - -export const hasPrometheusMissingData = (state) => state.hasPrometheus && !state.hasPrometheusData; - -// Convert the function list into a k/v grouping based on the environment scope - -export const getFunctions = (state) => translate(state.functions); diff --git a/app/assets/javascripts/serverless/store/index.js b/app/assets/javascripts/serverless/store/index.js deleted file mode 100644 index 6f32d85201e..00000000000 --- a/app/assets/javascripts/serverless/store/index.js +++ /dev/null @@ -1,18 +0,0 @@ -import Vue from 'vue'; -import Vuex from 'vuex'; -import * as actions from './actions'; -import * as getters from './getters'; -import mutations from './mutations'; -import createState from './state'; - -Vue.use(Vuex); - -export const createStore = (entryPointData = {}) => - new Vuex.Store({ - actions, - getters, - mutations, - state: createState(entryPointData), - }); - -export default createStore; diff --git a/app/assets/javascripts/serverless/store/mutation_types.js b/app/assets/javascripts/serverless/store/mutation_types.js deleted file mode 100644 index b8fa9ea1a01..00000000000 --- a/app/assets/javascripts/serverless/store/mutation_types.js +++ /dev/null @@ -1,11 +0,0 @@ -export const REQUEST_FUNCTIONS_LOADING = 'REQUEST_FUNCTIONS_LOADING'; -export const RECEIVE_FUNCTIONS_SUCCESS = 'RECEIVE_FUNCTIONS_SUCCESS'; -export const RECEIVE_FUNCTIONS_PARTIAL = 'RECEIVE_FUNCTIONS_PARTIAL'; -export const RECEIVE_FUNCTIONS_TIMEOUT = 'RECEIVE_FUNCTIONS_TIMEOUT'; -export const RECEIVE_FUNCTIONS_NODATA_SUCCESS = 'RECEIVE_FUNCTIONS_NODATA_SUCCESS'; -export const RECEIVE_FUNCTIONS_ERROR = 'RECEIVE_FUNCTIONS_ERROR'; - -export const RECEIVE_METRICS_NO_PROMETHEUS = 'RECEIVE_METRICS_NO_PROMETHEUS'; -export const RECEIVE_METRICS_SUCCESS = 'RECEIVE_METRICS_SUCCESS'; -export const RECEIVE_METRICS_NODATA_SUCCESS = 'RECEIVE_METRICS_NODATA_SUCCESS'; -export const RECEIVE_METRICS_ERROR = 'RECEIVE_METRICS_ERROR'; diff --git a/app/assets/javascripts/serverless/store/mutations.js b/app/assets/javascripts/serverless/store/mutations.js deleted file mode 100644 index 2685a5b11ff..00000000000 --- a/app/assets/javascripts/serverless/store/mutations.js +++ /dev/null @@ -1,49 +0,0 @@ -import * as types from './mutation_types'; - -export default { - [types.REQUEST_FUNCTIONS_LOADING](state) { - state.isLoading = true; - }, - [types.RECEIVE_FUNCTIONS_SUCCESS](state, data) { - state.functions = data.functions; - state.installed = data.knative_installed; - state.isLoading = false; - state.hasFunctionData = true; - }, - [types.RECEIVE_FUNCTIONS_PARTIAL](state, data) { - state.functions = data.functions; - state.installed = true; - state.isLoading = true; - state.hasFunctionData = true; - }, - [types.RECEIVE_FUNCTIONS_TIMEOUT](state) { - state.isLoading = false; - }, - [types.RECEIVE_FUNCTIONS_NODATA_SUCCESS](state, data) { - state.isLoading = false; - state.installed = data.knative_installed; - state.hasFunctionData = false; - }, - [types.RECEIVE_FUNCTIONS_ERROR](state, error) { - state.error = error; - state.hasFunctionData = false; - state.isLoading = false; - }, - [types.RECEIVE_METRICS_SUCCESS](state, data) { - state.isLoading = false; - state.hasPrometheusData = true; - state.graphData = data; - }, - [types.RECEIVE_METRICS_NODATA_SUCCESS](state) { - state.isLoading = false; - state.hasPrometheusData = false; - }, - [types.RECEIVE_METRICS_ERROR](state, error) { - state.hasPrometheusData = false; - state.error = error; - }, - [types.RECEIVE_METRICS_NO_PROMETHEUS](state) { - state.hasPrometheusData = false; - state.hasPrometheus = false; - }, -}; diff --git a/app/assets/javascripts/serverless/store/state.js b/app/assets/javascripts/serverless/store/state.js deleted file mode 100644 index 353bfcf3fed..00000000000 --- a/app/assets/javascripts/serverless/store/state.js +++ /dev/null @@ -1,22 +0,0 @@ -export default ( - initialState = { clustersPath: null, helpPath: null, emptyImagePath: null, statusPath: null }, -) => ({ - clustersPath: initialState.clustersPath, - error: null, - helpPath: initialState.helpPath, - installed: 'checking', - isLoading: true, - - // functions - functions: [], - hasFunctionData: true, - statusPath: initialState.statusPath, - - // function_details - hasPrometheus: true, - hasPrometheusData: false, - graphData: {}, - - // empty_state - emptyImagePath: initialState.emptyImagePath, -}); diff --git a/app/assets/javascripts/serverless/utils.js b/app/assets/javascripts/serverless/utils.js deleted file mode 100644 index e218a9aa3fd..00000000000 --- a/app/assets/javascripts/serverless/utils.js +++ /dev/null @@ -1,20 +0,0 @@ -// Validate that the object coming in has valid query details and results -export const validateGraphData = (data) => - data.queries && - Array.isArray(data.queries) && - data.queries.filter((query) => { - if (Array.isArray(query.result)) { - return query.result.filter((res) => Array.isArray(res.values)).length === query.result.length; - } - - return false; - }).length === data.queries.length; - -export const translate = (functions) => - functions.reduce( - (acc, func) => - Object.assign(acc, { - [func.environment_scope]: (acc[func.environment_scope] || []).concat([func]), - }), - {}, - ); diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_participant.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_participant.vue index 19f588b28be..e9c68008143 100644 --- a/app/assets/javascripts/sidebar/components/assignees/sidebar_participant.vue +++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_participant.vue @@ -3,6 +3,11 @@ import { GlAvatarLabeled, GlAvatarLink, GlIcon } from '@gitlab/ui'; import { IssuableType } from '~/issues/constants'; import { s__, sprintf } from '~/locale'; +const AVAILABILITY_STATUS = { + NOT_SET: 'NOT_SET', + BUSY: 'BUSY', +}; + export default { components: { GlAvatarLabeled, @@ -22,12 +27,17 @@ export default { }, computed: { userLabel() { - if (!this.user.status) { - return this.user.name; + const { name, status } = this.user; + if (!status || status?.availability !== AVAILABILITY_STATUS.BUSY) { + return name; } - return sprintf(s__('UserAvailability|%{author} (Busy)'), { - author: this.user.name, - }); + return sprintf( + s__('UserAvailability|%{author} (Busy)'), + { + author: name, + }, + false, + ); }, hasCannotMergeIcon() { return this.issuableType === IssuableType.MergeRequest && !this.user.canMerge; diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb index c9b6e8923fe..eaa945b0312 100644 --- a/app/controllers/concerns/uploads_actions.rb +++ b/app/controllers/concerns/uploads_actions.rb @@ -143,11 +143,17 @@ module UploadsActions end def bypass_auth_checks_on_uploads? - if ::Feature.enabled?(:enforce_auth_checks_on_uploads, project, default_enabled: :yaml) - false - else - action_name == 'show' && embeddable? + if ::Feature.enabled?(:enforce_auth_checks_on_uploads, target_project, default_enabled: :yaml) + if target_project && !target_project.public? && target_project.enforce_auth_checks_on_uploads? + return false + end end + + action_name == 'show' && embeddable? + end + + def target_project + nil end def find_model diff --git a/app/controllers/projects/serverless/functions_controller.rb b/app/controllers/projects/serverless/functions_controller.rb deleted file mode 100644 index d61694cf40f..00000000000 --- a/app/controllers/projects/serverless/functions_controller.rb +++ /dev/null @@ -1,80 +0,0 @@ -# frozen_string_literal: true - -module Projects - module Serverless - class FunctionsController < Projects::ApplicationController - before_action :ensure_feature_enabled! - before_action :authorize_read_cluster! - - feature_category :not_owned # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned - urgency :low - - def index - respond_to do |format| - format.json do - functions = finder.execute.select do |function| - can?(@current_user, :read_cluster, function.cluster) - end - - serialized_functions = serialize_function(functions) - - render json: { - knative_installed: finder.knative_installed, - functions: serialized_functions - }.to_json - end - - format.html do - render - end - end - end - - def show - function = finder.service(params[:environment_id], params[:id]) - return not_found unless function && can?(@current_user, :read_cluster, function.cluster) - - @service = serialize_function(function) - return not_found if @service.nil? - - @prometheus = finder.has_prometheus?(params[:environment_id]) - - respond_to do |format| - format.json do - render json: @service - end - - format.html - end - end - - def metrics - respond_to do |format| - format.json do - metrics = finder.invocation_metrics(params[:environment_id], params[:id]) - - if metrics.nil? - head :no_content - else - render json: metrics - end - end - end - end - - private - - def finder - Projects::Serverless::FunctionsFinder.new(project) - end - - def serialize_function(function) - Projects::Serverless::ServiceSerializer.new(current_user: @current_user, project: project).represent(function) - end - - def ensure_feature_enabled! - render_404 unless Feature.enabled?(:deprecated_serverless, project, default_enabled: :yaml, type: :ops) - end - end - end -end diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb index e6e91231ba2..a364668ea5f 100644 --- a/app/controllers/projects/uploads_controller.rb +++ b/app/controllers/projects/uploads_controller.rb @@ -23,6 +23,10 @@ class Projects::UploadsController < Projects::ApplicationController FileUploader end + def target_project + model + end + def find_model return @project if @project diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 9957f8c1451..cce02d493f0 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -419,6 +419,7 @@ class ProjectsController < Projects::ApplicationController squash_option mr_default_target_self warn_about_potentially_unwanted_characters + enforce_auth_checks_on_uploads ] end diff --git a/app/finders/projects/serverless/functions_finder.rb b/app/finders/projects/serverless/functions_finder.rb deleted file mode 100644 index f8ccea6b820..00000000000 --- a/app/finders/projects/serverless/functions_finder.rb +++ /dev/null @@ -1,153 +0,0 @@ -# frozen_string_literal: true - -module Projects - module Serverless - class FunctionsFinder - include Gitlab::Utils::StrongMemoize - include ReactiveCaching - - attr_reader :project - - self.reactive_cache_key = ->(finder) { finder.cache_key } - self.reactive_cache_work_type = :external_dependency - self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) } - - MAX_CLUSTERS = 10 - - def initialize(project) - @project = project - end - - def execute - knative_services.flatten.compact - end - - def knative_installed - return knative_installed_from_cluster?(*cache_key) if available_environments.empty? - - states = services_finders.map do |finder| - finder.knative_detected.tap do |state| - return state if state == ::Clusters::KnativeServicesFinder::KNATIVE_STATES['checking'] # rubocop:disable Cop/AvoidReturnFromBlocks - end - end - - states.any? { |state| state == ::Clusters::KnativeServicesFinder::KNATIVE_STATES['installed'] } - end - - def service(environment_scope, name) - knative_service(environment_scope, name)&.first - end - - def invocation_metrics(environment_scope, name) - environment = finders_for_scope(environment_scope).first&.environment - - if environment.present? && environment.prometheus_adapter&.can_query? - func = ::Serverless::Function.new(project, name, environment.deployment_namespace) - environment.prometheus_adapter.query(:knative_invocation, func) - end - end - - def has_prometheus?(environment_scope) - finders_for_scope(environment_scope).any? do |finder| - finder.cluster.integration_prometheus_available? - end - end - - def self.from_cache(project_id) - project = Project.find(project_id) - - new(project) - end - - def cache_key(*args) - [project.id] - end - - def calculate_reactive_cache(*) - # rubocop: disable CodeReuse/ActiveRecord - project.all_clusters.enabled.take(MAX_CLUSTERS).any? do |cluster| - cluster.kubeclient.knative_client.discover - rescue Kubeclient::ResourceNotFoundError - next - end - end - - private - - def knative_installed_from_cluster?(*cache_key) - cached_data = with_reactive_cache_memoized(*cache_key) { |data| data } - - return ::Clusters::KnativeServicesFinder::KNATIVE_STATES['checking'] if cached_data.nil? - - cached_data ? true : false - end - - def with_reactive_cache_memoized(*cache_key) - strong_memoize(:reactive_cache) do - with_reactive_cache(*cache_key) { |data| data } - end - end - - def knative_service(environment_scope, name) - finders_for_scope(environment_scope).map do |finder| - services = finder - .services - .select { |svc| svc["metadata"]["name"] == name } - - attributes = add_metadata(finder, services).first - next unless attributes - - Gitlab::Serverless::Service.new(attributes) - end - end - - def knative_services - services_finders.map do |finder| - attributes = add_metadata(finder, finder.services) - - attributes&.map do |attributes| - Gitlab::Serverless::Service.new(attributes) - end - end - end - - def add_metadata(finder, services) - return if services.nil? - - add_pod_count = services.one? - - services.each do |s| - s["environment_scope"] = finder.cluster.environment_scope - s["environment"] = finder.environment - s["cluster"] = finder.cluster - - if add_pod_count - s["podcount"] = finder - .service_pod_details(s["metadata"]["name"]) - .length - end - end - end - - def services_finders - strong_memoize(:services_finders) do - available_environments.map(&:knative_services_finder).compact - end - end - - def available_environments - @project.environments.available.preload_cluster - end - - def finders_for_scope(environment_scope) - services_finders.select do |finder| - environment_scope == finder.cluster.environment_scope - end - end - - def id - nil - end - end - end -end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index fd91418a742..64254956e49 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -614,6 +614,7 @@ module ProjectsHelper operationsAccessLevel: feature.operations_access_level, showDefaultAwardEmojis: project.show_default_award_emojis?, warnAboutPotentiallyUnwantedCharacters: project.warn_about_potentially_unwanted_characters?, + enforceAuthChecksOnUploads: project.enforce_auth_checks_on_uploads?, securityAndComplianceAccessLevel: project.security_and_compliance_access_level, containerRegistryAccessLevel: feature.container_registry_access_level } diff --git a/app/models/project.rb b/app/models/project.rb index 2b99d6c9d24..eeafb188fc4 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -450,6 +450,7 @@ class Project < ApplicationRecord to: :project_feature, allow_nil: true alias_method :container_registry_enabled, :container_registry_enabled? delegate :show_default_award_emojis, :show_default_award_emojis=, :show_default_award_emojis?, + :enforce_auth_checks_on_uploads, :enforce_auth_checks_on_uploads=, :enforce_auth_checks_on_uploads?, :warn_about_potentially_unwanted_characters, :warn_about_potentially_unwanted_characters=, :warn_about_potentially_unwanted_characters?, to: :project_setting, allow_nil: true delegate :scheduled?, :started?, :in_progress?, :failed?, :finished?, diff --git a/app/views/projects/serverless/functions/index.html.haml b/app/views/projects/serverless/functions/index.html.haml deleted file mode 100644 index 03a1b0ee7bb..00000000000 --- a/app/views/projects/serverless/functions/index.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -- @content_class = "limit-container-width" unless fluid_layout -- breadcrumb_title _('Serverless') -- page_title _('Serverless') -- status_path = project_serverless_functions_path(@project, format: :json) -- clusters_path = project_clusters_path(@project) - -.serverless-functions-page.js-serverless-functions-page{ data: { status_path: status_path, - installed: @installed, - clusters_path: clusters_path, - help_path: help_page_path('user/project/clusters/serverless/index'), - empty_image_path: image_path('illustrations/empty-state/empty-serverless-lg.svg') } } - -.js-serverless-functions-notice - .flash-container - -.top-area.adjust.d-flex.justify-content-center.gl-border-none - .serverless-functions-table#js-serverless-functions diff --git a/app/views/projects/serverless/functions/show.html.haml b/app/views/projects/serverless/functions/show.html.haml deleted file mode 100644 index dd81d957e51..00000000000 --- a/app/views/projects/serverless/functions/show.html.haml +++ /dev/null @@ -1,19 +0,0 @@ -- @content_class = "limit-container-width" unless fluid_layout -- clusters_path = project_clusters_path(@project) -- help_path = help_page_path('user/project/clusters/serverless/index') - -- add_to_breadcrumbs('Serverless', project_serverless_functions_path(@project)) - -- page_title @service[:name] - -.serverless-function-details-page.js-serverless-function-details-page{ data: { service: @service.as_json, - prometheus: @prometheus, - clusters_path: clusters_path, - help_path: help_path } } - -.serverless-function-details#js-serverless-function-details - -.js-serverless-function-notice - .flash-container - -.function-holder.js-function-holder.input-group diff --git a/config/feature_flags/ops/deprecated_serverless.yml b/config/feature_flags/ops/deprecated_serverless.yml deleted file mode 100644 index 0982778f139..00000000000 --- a/config/feature_flags/ops/deprecated_serverless.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: deprecated_serverless -introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81493' -rollout_issue_url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/353901' -milestone: '14.9' -type: ops -group: 'group::configure' -default_enabled: true diff --git a/config/routes/project.rb b/config/routes/project.rb index 0ef891aa4c9..6b2a9700686 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -303,15 +303,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do concerns :clusterable - namespace :serverless do - scope :functions do - get '/:environment_id/:id', to: 'functions#show' - get '/:environment_id/:id/metrics', to: 'functions#metrics', as: :metrics - end - - resources :functions, only: [:index] - end - resources :terraform, only: [:index] resources :google_cloud, only: [:index] diff --git a/data/removals/15_0/15-0-serverless.yml b/data/removals/15_0/15-0-serverless.yml new file mode 100644 index 00000000000..9f6a94037d8 --- /dev/null +++ b/data/removals/15_0/15-0-serverless.yml @@ -0,0 +1,17 @@ +- name: "GitLab Serverless" + announcement_milestone: "14.3" + announcement_date: "2021-09-22" + removal_milestone: "15.0" + removal_date: "2022-05-22" + breaking_change: true + reporter: nagyv-gitlab + body: | # Do not modify this line, instead modify the lines below. + All functionality related to GitLab Serverless was deprecated in GitLab 14.3 and is scheduled for removal in GitLab 15.0. Users who need a replacement for this functionality are encouraged to explore using the following technologies with GitLab CI/CD: + + - [Serverless Framework](https://www.serverless.com) + - [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/deploying-using-gitlab.html) + + For additional context, or to provide feedback regarding this change, please reference our [deprecation issue](https://gitlab.com/groups/gitlab-org/configure/-/epics/6). +# The following items are not published on the docs page, but may be used in the future. + stage: "Configure" + issue_url: https://gitlab.com/groups/gitlab-org/configure/-/epics/6 diff --git a/db/migrate/20220324091224_add_enforce_auth_checks_on_uploads_to_project_settings.rb b/db/migrate/20220324091224_add_enforce_auth_checks_on_uploads_to_project_settings.rb new file mode 100644 index 00000000000..2c86d1d346d --- /dev/null +++ b/db/migrate/20220324091224_add_enforce_auth_checks_on_uploads_to_project_settings.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddEnforceAuthChecksOnUploadsToProjectSettings < Gitlab::Database::Migration[1.0] + enable_lock_retries! + + def change + add_column :project_settings, :enforce_auth_checks_on_uploads, :boolean, null: false, default: true + end +end diff --git a/db/schema_migrations/20220324091224 b/db/schema_migrations/20220324091224 new file mode 100644 index 00000000000..4be0647a91b --- /dev/null +++ b/db/schema_migrations/20220324091224 @@ -0,0 +1 @@ +7418b98f33ada13dedab493ad8a969808a18db2fa0188e428b1c685aabb3bc66 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 6cab0d2bf13..a51fdf660dd 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -19468,6 +19468,7 @@ CREATE TABLE project_settings ( squash_commit_template text, legacy_open_source_license_available boolean DEFAULT true NOT NULL, target_platforms character varying[] DEFAULT '{}'::character varying[] NOT NULL, + enforce_auth_checks_on_uploads boolean DEFAULT true NOT NULL, CONSTRAINT check_3a03e7557a CHECK ((char_length(previous_default_branch) <= 4096)), CONSTRAINT check_b09644994b CHECK ((char_length(squash_commit_template) <= 500)), CONSTRAINT check_bde223416c CHECK ((show_default_award_emojis IS NOT NULL)), diff --git a/doc/administration/git_protocol.md b/doc/administration/git_protocol.md index 29156d9b3e1..3612487f456 100644 --- a/doc/administration/git_protocol.md +++ b/doc/administration/git_protocol.md @@ -111,4 +111,4 @@ URL to use SSH. ### Observe Git protocol version of connections For information on observing the Git protocol versions are being used in a production environment, -see the [relevant documentation](gitaly/index.md#useful-queries). +see the [relevant documentation](gitaly/monitoring.md#useful-queries). diff --git a/doc/administration/gitaly/configure_gitaly.md b/doc/administration/gitaly/configure_gitaly.md index 8746afd60ba..e128682af75 100644 --- a/doc/administration/gitaly/configure_gitaly.md +++ b/doc/administration/gitaly/configure_gitaly.md @@ -2,7 +2,6 @@ stage: Create group: Gitaly info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments -type: reference --- # Configure Gitaly **(FREE SELF)** @@ -710,7 +709,7 @@ To configure Gitaly with TLS: ### Observe type of Gitaly connections For information on observing the type of Gitaly connections being served, see the -[relevant documentation](index.md#useful-queries). +[relevant documentation](monitoring.md#useful-queries). ## `gitaly-ruby` @@ -816,7 +815,7 @@ repository. In the example above: - If the queue grows beyond 10, subsequent requests are rejected with an error. You can observe the behavior of this queue using the Gitaly logs and Prometheus. For more -information, see the [relevant documentation](index.md#monitor-gitaly-concurrency-limiting). +information, see the [relevant documentation](monitoring.md#monitor-gitaly-concurrency-limiting). ## Background Repository Optimization @@ -871,7 +870,7 @@ server" and "Gitaly client" refers to the same machine. ### Verify authentication monitoring Before rotating a Gitaly authentication token, verify that you can -[monitor the authentication behavior](index.md#useful-queries) of your GitLab installation using +[monitor the authentication behavior](monitoring.md#useful-queries) of your GitLab installation using Prometheus. You can then continue the rest of the procedure. @@ -1081,7 +1080,7 @@ closed it. ### Observe the cache -The cache can be observed [using metrics](index.md#pack-objects-cache) and in the following logged +The cache can be observed [using metrics](monitoring.md#pack-objects-cache) and in the following logged information: |Message|Fields|Description| diff --git a/doc/administration/gitaly/faq.md b/doc/administration/gitaly/faq.md index b0a88e8adc9..a5c2c7d1469 100644 --- a/doc/administration/gitaly/faq.md +++ b/doc/administration/gitaly/faq.md @@ -2,7 +2,6 @@ stage: Create group: Gitaly info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments -type: reference --- # Frequently asked questions **(FREE SELF)** diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md index 0316efa92bf..1660269683d 100644 --- a/doc/administration/gitaly/index.md +++ b/doc/administration/gitaly/index.md @@ -2,7 +2,6 @@ stage: Create group: Gitaly info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments -type: reference --- # Gitaly and Gitaly Cluster **(FREE SELF)** @@ -318,7 +317,7 @@ The primary node is chosen to serve the request if: - There are no up to date nodes. - Any other error occurs during node selection. -You can [monitor distribution of reads](#monitor-gitaly-cluster) using Prometheus. +You can [monitor distribution of reads](monitoring.md#monitor-gitaly-cluster) using Prometheus. #### Strong consistency @@ -346,7 +345,7 @@ Strong consistency: - The [13.12 documentation](https://docs.gitlab.com/13.12/ee/administration/gitaly/praefect.html#strong-consistency). - Is unavailable in GitLab 13.0 and earlier. -For more information on monitoring strong consistency, see the Gitaly Cluster [Prometheus metrics documentation](#monitor-gitaly-cluster). +For more information on monitoring strong consistency, see the Gitaly Cluster [Prometheus metrics documentation](monitoring.md#monitor-gitaly-cluster). #### Replication factor @@ -394,196 +393,6 @@ off Gitaly Cluster to a sharded Gitaly instance: 1. [Move the repositories](../operations/moving_repositories.md#move-repositories) to the newly created storage. You can move them by shard or by group, which gives you the opportunity to spread them over multiple Gitaly servers. -## Monitor Gitaly and Gitaly Cluster - -You can use the available logs and [Prometheus metrics](../monitoring/prometheus/index.md) to -monitor Gitaly and Gitaly Cluster (Praefect). - -Metric definitions are available: - -- Directly from Prometheus `/metrics` endpoint configured for Gitaly. -- Using [Grafana Explore](https://grafana.com/docs/grafana/latest/explore/) on a - Grafana instance configured against Prometheus. - -### Monitor Gitaly rate limiting - -Gitaly can be configured to limit requests based on: - -- Concurrency of requests. -- A rate limit. - -Monitor Gitaly request limiting with the `gitaly_requests_dropped_total` Prometheus metric. This metric provides a total count -of requests dropped due to request limiting. The `reason` label indicates why a request was dropped: - -- `rate`, due to rate limiting. -- `max_size`, because the concurrency queue size was reached. -- `max_time`, because the request exceeded the maximum queue wait time as configured in Gitaly. - -### Monitor Gitaly concurrency limiting - -You can observe specific behavior of [concurrency-queued requests](configure_gitaly.md#limit-rpc-concurrency) using -the Gitaly logs and Prometheus: - -- In the [Gitaly logs](../logs.md#gitaly-logs), look for the string (or structured log field) - `acquire_ms`. Messages that have this field are reporting about the concurrency limiter. -- In Prometheus, look for the following metrics: - - `gitaly_concurrency_limiting_in_progress` indicates how many concurrent requests are - being processed. - - `gitaly_concurrency_limiting_queued` indicates how many requests for an RPC for a given - repository are waiting due to the concurrency limit being reached. - - `gitaly_concurrency_limiting_acquiring_seconds` indiciates how long a request has to - wait due to concurrency limits before being processed. - -### `pack-objects` cache - -The following [`pack-objects` cache](configure_gitaly.md#pack-objects-cache) metrics are available: - -- `gitaly_pack_objects_cache_enabled`, a gauge set to `1` when the cache is enabled. Available - labels: `dir` and `max_age`. -- `gitaly_pack_objects_cache_lookups_total`, a counter for cache lookups. Available label: `result`. -- `gitaly_pack_objects_generated_bytes_total`, a counter for the number of bytes written into the - cache. -- `gitaly_pack_objects_served_bytes_total`, a counter for the number of bytes read from the cache. -- `gitaly_streamcache_filestore_disk_usage_bytes`, a gauge for the total size of cache files. - Available label: `dir`. -- `gitaly_streamcache_index_entries`, a gauge for the number of entries in the cache. Available - label: `dir`. - -Some of these metrics start with `gitaly_streamcache` because they are generated by the -`streamcache` internal library package in Gitaly. - -Example: - -```plaintext -gitaly_pack_objects_cache_enabled{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache",max_age="300"} 1 -gitaly_pack_objects_cache_lookups_total{result="hit"} 2 -gitaly_pack_objects_cache_lookups_total{result="miss"} 1 -gitaly_pack_objects_generated_bytes_total 2.618649e+07 -gitaly_pack_objects_served_bytes_total 7.855947e+07 -gitaly_streamcache_filestore_disk_usage_bytes{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache"} 2.6200152e+07 -gitaly_streamcache_filestore_removed_total{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache"} 1 -gitaly_streamcache_index_entries{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache"} 1 -``` - -### Useful queries - -The following are useful queries for monitoring Gitaly: - -- Use the following Prometheus query to observe the - [type of connections](configure_gitaly.md#enable-tls-support) Gitaly is serving a production - environment: - - ```prometheus - sum(rate(gitaly_connections_total[5m])) by (type) - ``` - -- Use the following Prometheus query to monitor the - [authentication behavior](configure_gitaly.md#observe-type-of-gitaly-connections) of your GitLab - installation: - - ```prometheus - sum(rate(gitaly_authentications_total[5m])) by (enforced, status) - ``` - - In a system where authentication is configured correctly and where you have live traffic, you - see something like this: - - ```prometheus - {enforced="true",status="ok"} 4424.985419441742 - ``` - - There may also be other numbers with rate 0, but you only have to take note of the non-zero numbers. - - The only non-zero number should have `enforced="true",status="ok"`. If you have other non-zero - numbers, something is wrong in your configuration. - - The `status="ok"` number reflects your current request rate. In the example above, Gitaly is - handling about 4000 requests per second. - -- Use the following Prometheus query to observe the [Git protocol versions](../git_protocol.md) - being used in a production environment: - - ```prometheus - sum(rate(gitaly_git_protocol_requests_total[1m])) by (grpc_method,git_protocol,grpc_service) - ``` - -### Monitor Gitaly Cluster - -To monitor Gitaly Cluster (Praefect), you can use these Prometheus metrics. There are two separate metrics -endpoints from which metrics can be scraped: - -- The default `/metrics` endpoint. -- `/db_metrics`, which contains metrics that require database queries. - -#### Default Prometheus `/metrics` endpoint - -The following metrics are available from the `/metrics` endpoint: - -- `gitaly_praefect_read_distribution`, a counter to track [distribution of reads](#distributed-reads). - It has two labels: - - - `virtual_storage`. - - `storage`. - - They reflect configuration defined for this instance of Praefect. - -- `gitaly_praefect_replication_latency_bucket`, a histogram measuring the amount of time it takes - for replication to complete after the replication job starts. Available in GitLab 12.10 and later. -- `gitaly_praefect_replication_delay_bucket`, a histogram measuring how much time passes between - when the replication job is created and when it starts. Available in GitLab 12.10 and later. -- `gitaly_praefect_node_latency_bucket`, a histogram measuring the latency in Gitaly returning - health check information to Praefect. This indicates Praefect connection saturation. Available in - GitLab 12.10 and later. - -To monitor [strong consistency](#strong-consistency), you can use the following Prometheus metrics: - -- `gitaly_praefect_transactions_total`, the number of transactions created and voted on. -- `gitaly_praefect_subtransactions_per_transaction_total`, the number of times nodes cast a vote for - a single transaction. This can happen multiple times if multiple references are getting updated in - a single transaction. -- `gitaly_praefect_voters_per_transaction_total`: the number of Gitaly nodes taking part in a - transaction. -- `gitaly_praefect_transactions_delay_seconds`, the server-side delay introduced by waiting for the - transaction to be committed. -- `gitaly_hook_transaction_voting_delay_seconds`, the client-side delay introduced by waiting for - the transaction to be committed. - -To monitor the number of repositories that have no healthy, up-to-date replicas: - -- `gitaly_praefect_unavailable_repositories` - -To monitor [repository verification](praefect.md#repository-verification), use the following Prometheus metrics: - -- `gitaly_praefect_verification_queue_depth`, the total number of replicas pending verification. This - metric is scraped from the database and is only available when Prometheus is scraping the database metrics. -- `gitaly_praefect_verification_jobs_dequeued_total`, the number of verification jobs picked up by the - worker. -- `gitaly_praefect_verification_jobs_completed_total`, the number of verification jobs completed by the - worker. The `result` label indicates the end result of the jobs: - - `valid` indicates the expected replica existed on the storage. - - `invalid` indicates the replica expected to exist did not exist on the storage. - - `error` indicates the job failed and has to be retried. -- `gitaly_praefect_stale_verification_leases_released_total`, the number of stale verification leases - released. - -You can also monitor the [Praefect logs](../logs.md#praefect-logs). - -#### Database metrics `/db_metrics` endpoint - -> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/3286) in GitLab 14.5. - -The following metrics are available from the `/db_metrics` endpoint: - -- `gitaly_praefect_unavailable_repositories`, the number of repositories that have no healthy, up to date replicas. -- `gitaly_praefect_read_only_repositories`, the number of repositories in read-only mode in a virtual storage. - This metric is available for backwards compatibility reasons. `gitaly_praefect_unavailable_repositories` is more - accurate. -- `gitaly_praefect_replication_queue_depth`, the number of jobs in the replication queue. - -## Recover from failure - -Gitaly Cluster can [recover from certain types of failure](recovery.md). - ## Do not bypass Gitaly GitLab doesn't advise directly accessing Gitaly repositories stored on disk with a Git client, diff --git a/doc/administration/gitaly/monitoring.md b/doc/administration/gitaly/monitoring.md new file mode 100644 index 00000000000..7a4f2026f3d --- /dev/null +++ b/doc/administration/gitaly/monitoring.md @@ -0,0 +1,191 @@ +--- +stage: Create +group: Gitaly +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +--- + +# Monitoring Gitaly and Gitaly Cluster + +You can use the available logs and [Prometheus metrics](../monitoring/prometheus/index.md) to +monitor Gitaly and Gitaly Cluster (Praefect). + +Metric definitions are available: + +- Directly from Prometheus `/metrics` endpoint configured for Gitaly. +- Using [Grafana Explore](https://grafana.com/docs/grafana/latest/explore/) on a + Grafana instance configured against Prometheus. + +## Monitor Gitaly rate limiting + +Gitaly can be configured to limit requests based on: + +- Concurrency of requests. +- A rate limit. + +Monitor Gitaly request limiting with the `gitaly_requests_dropped_total` Prometheus metric. This metric provides a total count +of requests dropped due to request limiting. The `reason` label indicates why a request was dropped: + +- `rate`, due to rate limiting. +- `max_size`, because the concurrency queue size was reached. +- `max_time`, because the request exceeded the maximum queue wait time as configured in Gitaly. + +## Monitor Gitaly concurrency limiting + +You can observe specific behavior of [concurrency-queued requests](configure_gitaly.md#limit-rpc-concurrency) using +the Gitaly logs and Prometheus: + +- In the [Gitaly logs](../logs.md#gitaly-logs), look for the string (or structured log field) + `acquire_ms`. Messages that have this field are reporting about the concurrency limiter. +- In Prometheus, look for the following metrics: + - `gitaly_concurrency_limiting_in_progress` indicates how many concurrent requests are + being processed. + - `gitaly_concurrency_limiting_queued` indicates how many requests for an RPC for a given + repository are waiting due to the concurrency limit being reached. + - `gitaly_concurrency_limiting_acquiring_seconds` indicates how long a request has to + wait due to concurrency limits before being processed. + +## `pack-objects` cache + +The following [`pack-objects` cache](configure_gitaly.md#pack-objects-cache) metrics are available: + +- `gitaly_pack_objects_cache_enabled`, a gauge set to `1` when the cache is enabled. Available + labels: `dir` and `max_age`. +- `gitaly_pack_objects_cache_lookups_total`, a counter for cache lookups. Available label: `result`. +- `gitaly_pack_objects_generated_bytes_total`, a counter for the number of bytes written into the + cache. +- `gitaly_pack_objects_served_bytes_total`, a counter for the number of bytes read from the cache. +- `gitaly_streamcache_filestore_disk_usage_bytes`, a gauge for the total size of cache files. + Available label: `dir`. +- `gitaly_streamcache_index_entries`, a gauge for the number of entries in the cache. Available + label: `dir`. + +Some of these metrics start with `gitaly_streamcache` because they are generated by the +`streamcache` internal library package in Gitaly. + +Example: + +```plaintext +gitaly_pack_objects_cache_enabled{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache",max_age="300"} 1 +gitaly_pack_objects_cache_lookups_total{result="hit"} 2 +gitaly_pack_objects_cache_lookups_total{result="miss"} 1 +gitaly_pack_objects_generated_bytes_total 2.618649e+07 +gitaly_pack_objects_served_bytes_total 7.855947e+07 +gitaly_streamcache_filestore_disk_usage_bytes{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache"} 2.6200152e+07 +gitaly_streamcache_filestore_removed_total{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache"} 1 +gitaly_streamcache_index_entries{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache"} 1 +``` + +## Useful queries + +The following are useful queries for monitoring Gitaly: + +- Use the following Prometheus query to observe the + [type of connections](configure_gitaly.md#enable-tls-support) Gitaly is serving a production + environment: + + ```prometheus + sum(rate(gitaly_connections_total[5m])) by (type) + ``` + +- Use the following Prometheus query to monitor the + [authentication behavior](configure_gitaly.md#observe-type-of-gitaly-connections) of your GitLab + installation: + + ```prometheus + sum(rate(gitaly_authentications_total[5m])) by (enforced, status) + ``` + + In a system where authentication is configured correctly and where you have live traffic, you + see something like this: + + ```prometheus + {enforced="true",status="ok"} 4424.985419441742 + ``` + + There may also be other numbers with rate 0, but you only have to take note of the non-zero numbers. + + The only non-zero number should have `enforced="true",status="ok"`. If you have other non-zero + numbers, something is wrong in your configuration. + + The `status="ok"` number reflects your current request rate. In the example above, Gitaly is + handling about 4000 requests per second. + +- Use the following Prometheus query to observe the [Git protocol versions](../git_protocol.md) + being used in a production environment: + + ```prometheus + sum(rate(gitaly_git_protocol_requests_total[1m])) by (grpc_method,git_protocol,grpc_service) + ``` + +## Monitor Gitaly Cluster + +To monitor Gitaly Cluster (Praefect), you can use these Prometheus metrics. There are two separate metrics +endpoints from which metrics can be scraped: + +- The default `/metrics` endpoint. +- `/db_metrics`, which contains metrics that require database queries. + +### Default Prometheus `/metrics` endpoint + +The following metrics are available from the `/metrics` endpoint: + +- `gitaly_praefect_read_distribution`, a counter to track [distribution of reads](index.md#distributed-reads). + It has two labels: + + - `virtual_storage`. + - `storage`. + + They reflect configuration defined for this instance of Praefect. + +- `gitaly_praefect_replication_latency_bucket`, a histogram measuring the amount of time it takes + for replication to complete after the replication job starts. Available in GitLab 12.10 and later. +- `gitaly_praefect_replication_delay_bucket`, a histogram measuring how much time passes between + when the replication job is created and when it starts. Available in GitLab 12.10 and later. +- `gitaly_praefect_node_latency_bucket`, a histogram measuring the latency in Gitaly returning + health check information to Praefect. This indicates Praefect connection saturation. Available in + GitLab 12.10 and later. + +To monitor [strong consistency](index.md#strong-consistency), you can use the following Prometheus metrics: + +- `gitaly_praefect_transactions_total`, the number of transactions created and voted on. +- `gitaly_praefect_subtransactions_per_transaction_total`, the number of times nodes cast a vote for + a single transaction. This can happen multiple times if multiple references are getting updated in + a single transaction. +- `gitaly_praefect_voters_per_transaction_total`: the number of Gitaly nodes taking part in a + transaction. +- `gitaly_praefect_transactions_delay_seconds`, the server-side delay introduced by waiting for the + transaction to be committed. +- `gitaly_hook_transaction_voting_delay_seconds`, the client-side delay introduced by waiting for + the transaction to be committed. + +To monitor the number of repositories that have no healthy, up-to-date replicas: + +- `gitaly_praefect_unavailable_repositories` + +To monitor [repository verification](praefect.md#repository-verification), use the following Prometheus metrics: + +- `gitaly_praefect_verification_queue_depth`, the total number of replicas pending verification. This + metric is scraped from the database and is only available when Prometheus is scraping the database metrics. +- `gitaly_praefect_verification_jobs_dequeued_total`, the number of verification jobs picked up by the + worker. +- `gitaly_praefect_verification_jobs_completed_total`, the number of verification jobs completed by the + worker. The `result` label indicates the end result of the jobs: + - `valid` indicates the expected replica existed on the storage. + - `invalid` indicates the replica expected to exist did not exist on the storage. + - `error` indicates the job failed and has to be retried. +- `gitaly_praefect_stale_verification_leases_released_total`, the number of stale verification leases + released. + +You can also monitor the [Praefect logs](../logs.md#praefect-logs). + +### Database metrics `/db_metrics` endpoint + +> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/3286) in GitLab 14.5. + +The following metrics are available from the `/db_metrics` endpoint: + +- `gitaly_praefect_unavailable_repositories`, the number of repositories that have no healthy, up to date replicas. +- `gitaly_praefect_read_only_repositories`, the number of repositories in read-only mode in a virtual storage. + This metric is available for backwards compatibility reasons. `gitaly_praefect_unavailable_repositories` is more + accurate. +- `gitaly_praefect_replication_queue_depth`, the number of jobs in the replication queue. diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md index 866488f2c56..bbb34a41f84 100644 --- a/doc/administration/gitaly/praefect.md +++ b/doc/administration/gitaly/praefect.md @@ -2,7 +2,6 @@ stage: Create group: Gitaly info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments -type: reference --- # Configure Gitaly Cluster **(FREE SELF)** diff --git a/doc/administration/gitaly/recovery.md b/doc/administration/gitaly/recovery.md index 0d659644bec..6de2acf1792 100644 --- a/doc/administration/gitaly/recovery.md +++ b/doc/administration/gitaly/recovery.md @@ -2,7 +2,6 @@ stage: Create group: Gitaly info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments -type: reference --- # Recovery options diff --git a/doc/administration/gitaly/reference.md b/doc/administration/gitaly/reference.md index 9fe09be10a3..d1e802111cd 100644 --- a/doc/administration/gitaly/reference.md +++ b/doc/administration/gitaly/reference.md @@ -2,7 +2,6 @@ stage: Create group: Gitaly info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments -type: reference --- # Gitaly reference **(FREE SELF)** @@ -71,7 +70,7 @@ Remember to disable `transitioning` when you are done changing your token settings. All authentication attempts are counted in Prometheus under -the [`gitaly_authentications_total` metric](index.md#useful-queries). +the [`gitaly_authentications_total` metric](monitoring.md#useful-queries). ### TLS diff --git a/doc/administration/gitaly/troubleshooting.md b/doc/administration/gitaly/troubleshooting.md index cefddad1b62..085bf399d27 100644 --- a/doc/administration/gitaly/troubleshooting.md +++ b/doc/administration/gitaly/troubleshooting.md @@ -2,7 +2,6 @@ stage: Create group: Gitaly info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments -type: reference --- # Troubleshooting Gitaly and Gitaly Cluster **(FREE SELF)** diff --git a/doc/administration/logs.md b/doc/administration/logs.md index f19a5e056c6..c89c485e7d3 100644 --- a/doc/administration/logs.md +++ b/doc/administration/logs.md @@ -1107,7 +1107,7 @@ in `/var/log/gitlab/gitlab-kas/`. For Omnibus GitLab installations, Praefect logs are in `/var/log/gitlab/praefect/`. -GitLab also tracks [Prometheus metrics for Praefect](gitaly/#monitor-gitaly-cluster). +GitLab also tracks [Prometheus metrics for Praefect](gitaly/monitoring.md#monitor-gitaly-cluster). ## Backup log diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md index 08f7d5095da..4f8fbd0c07e 100644 --- a/doc/administration/monitoring/prometheus/gitlab_metrics.md +++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md @@ -156,7 +156,7 @@ The following metrics can be controlled by feature flags: ## Praefect metrics You can [configure Praefect](../../gitaly/praefect.md#praefect) to report metrics. For information -on available metrics, see the [relevant documentation](../../gitaly/index.md#monitor-gitaly-cluster). +on available metrics, see the [relevant documentation](../../gitaly/monitoring.md#monitor-gitaly-cluster). ## Sidekiq metrics diff --git a/doc/administration/package_information/postgresql_versions.md b/doc/administration/package_information/postgresql_versions.md index c80437221c4..fa23f8069ed 100644 --- a/doc/administration/package_information/postgresql_versions.md +++ b/doc/administration/package_information/postgresql_versions.md @@ -26,6 +26,7 @@ Read more about update policies and warnings in the PostgreSQL | GitLab version | PostgreSQL versions | Default version for fresh installs | Default version for upgrades | Notes | | -------------- | --------------------- | ---------------------------------- | ---------------------------- | ----- | +| 15.0 | 12.7, 13.3 | 13.3 | 13.3 | Users can manually upgrade to 13.3 following the [upgrade docs](https://docs.gitlab.com/omnibus/settings/database.html#gitlab-150-and-later). | | 14.1 | 12.7, 13.3 | 12.7 | 12.7 | PostgreSQL 13 available for fresh installations if not using [Geo](../geo/index.md#requirements-for-running-geo) or [Patroni](../postgresql/index.md#postgresql-replication-and-failover-with-omnibus-gitlab). | 14.0 | 12.7 | 12.7 | 12.7 | HA installations with repmgr are no longer supported and will be prevented from upgrading to Omnibus GitLab 14.0 | | 13.8 | 11.9, 12.4 | 12.4 | 12.4 | Package upgrades automatically performed PostgreSQL upgrade for nodes that are not part of a Geo or HA cluster.). | diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 0981d1e45d9..34fdf710bab 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -2666,7 +2666,7 @@ Input type: `ExternalAuditEventDestinationUpdateInput` | ---- | ---- | ----------- | | `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | `destinationUrl` | [`String`](#string) | Destination URL to change. | -| `id` | [`AuditEventsExternalAuditEventDestinationID!`](#auditeventsexternalauditeventdestinationid) | ID of external audit event destination to destroy. | +| `id` | [`AuditEventsExternalAuditEventDestinationID!`](#auditeventsexternalauditeventdestinationid) | ID of external audit event destination to update. | #### Fields diff --git a/doc/development/deprecation_guidelines/index.md b/doc/development/deprecation_guidelines/index.md index 6187a5a56ca..cafc40ccc68 100644 --- a/doc/development/deprecation_guidelines/index.md +++ b/doc/development/deprecation_guidelines/index.md @@ -35,3 +35,52 @@ For API removals, see the [GraphQL](../../api/graphql/index.md#deprecation-and-r For configuration removals, see the [Omnibus deprecation policy](../../administration/package_information/deprecation_policy.md). For versioning and upgrade details, see our [Release and Maintenance policy](../../policy/maintenance.md). + +## Update the deprecations and removals documentation + +The [deprecations](../../update/deprecations.md) and [removals](../../update/removals.md) +documentation is generated from the YAML files located in +[`gitlab/data/`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/data). + +To update the deprecations and removals pages when an entry is added, +edited, or removed: + +1. From the command line, navigate to your local clone of the [`gitlab-org/gitlab`](https://gitlab.com/gitlab-org/gitlab) project. +1. Create, edit, or remove the YAML file under [deprecations](https://gitlab.com/gitlab-org/gitlab/-/tree/master/data/deprecations) + or [removals](https://gitlab.com/gitlab-org/gitlab/-/tree/master/data/removals). +1. Compile the deprecation or removals documentation with the appropriate command: + + - For deprecations: + + ```shell + bin/rake gitlab:docs:compile_deprecations + ``` + + - For removals: + + ```shell + bin/rake gitlab:docs:compile_removals + ``` + +1. If needed, you can verify the docs are up to date with: + + - For deprecations: + + ```shell + bin/rake gitlab:docs:check_deprecations + ``` + + - For removals: + + ```shell + bin/rake gitlab:docs:check_removals + ``` + +1. Commit the updated documentation and push the changes. +1. Create a merge request using the [Deprecations](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/merge_request_templates/Deprecations.md) + or [Removals](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/merge_request_templates/Removals.md) templates. + +Related Handbook pages: + +- +- diff --git a/doc/update/index.md b/doc/update/index.md index b3c5532aae6..6558a289cdc 100644 --- a/doc/update/index.md +++ b/doc/update/index.md @@ -402,6 +402,12 @@ NOTE: Specific information that follow related to Ruby and Git versions do not apply to [Omnibus installations](https://docs.gitlab.com/omnibus/) and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with appropriate Ruby and Git versions and are not using system binaries for Ruby and Git. There is no need to install Ruby or Git when utilizing these two approaches. +### 14.10.0 + +- The upgrade to GitLab 14.10 executes a [concurrent index drop](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84308) of unneeded + entries from the `ci_job_artifacts` database table. This could potentially run for multiple minutes, especially if the table has a lot of + traffic and the migration is unable to acquire a lock. It is advised to let this process finish as restarting may result in data loss. + ### 14.9.0 - Database changes made by the upgrade to GitLab 14.9 can take hours or days to complete on larger GitLab instances. diff --git a/doc/update/removals.md b/doc/update/removals.md index 266b6e9fa7b..17bdf9f340d 100644 --- a/doc/update/removals.md +++ b/doc/update/removals.md @@ -61,6 +61,21 @@ The Container Registry supports [authentication](https://gitlab.com/gitlab-org/c Since it isn't used in the context of GitLab (the product), `htpasswd` authentication will be deprecated in GitLab 14.9 and removed in GitLab 15.0. +### GitLab Serverless + +WARNING: +This feature was changed or removed in 15.0 +as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes). +Before updating GitLab, review the details carefully to determine if you need to make any +changes to your code, settings, or workflow. + +All functionality related to GitLab Serverless was deprecated in GitLab 14.3 and is scheduled for removal in GitLab 15.0. Users who need a replacement for this functionality are encouraged to explore using the following technologies with GitLab CI/CD: + +- [Serverless Framework](https://www.serverless.com) +- [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/deploying-using-gitlab.html) + +For additional context, or to provide feedback regarding this change, please reference our [deprecation issue](https://gitlab.com/groups/gitlab-org/configure/-/epics/6). + ### GraphQL permissions change for Package settings WARNING: diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md index 73ee156dac1..277ce38aa3c 100644 --- a/doc/user/clusters/applications.md +++ b/doc/user/clusters/applications.md @@ -658,27 +658,12 @@ Here is an example configuration for Knative: domain: 'my.wildcard.A.record.dns' ``` -If you plan to use GitLab Serverless capabilities, be sure to set an `A record` -wildcard domain on your custom configuration. - Support for installing the Knative managed application is provided by the GitLab Configure group. If you run into unknown issues, [open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at least 2 people from the [Configure group](https://about.gitlab.com/handbook/product/categories/#configure-group). -#### Knative Metrics - -GitLab provides [Invocation Metrics](../project/clusters/serverless/index.md#invocation-metrics) -for your functions. To collect these metrics, you must have: - -1. Knative and Prometheus managed applications installed on your cluster. -1. Manually applied the custom metrics on your cluster by running the following command: - - ```shell - kubectl apply -f https://gitlab.com/gitlab-org/cluster-integration/cluster-applications/-/raw/02c8231e30ef5b6725e6ba368bc63863ceb3c07d/src/default-data/knative/istio-metrics.yaml - ``` - #### Uninstall Knative To uninstall Knative, you must first manually remove any custom metrics you have added diff --git a/doc/user/infrastructure/clusters/index.md b/doc/user/infrastructure/clusters/index.md index 114b4096091..238d7004a6f 100644 --- a/doc/user/infrastructure/clusters/index.md +++ b/doc/user/infrastructure/clusters/index.md @@ -53,7 +53,6 @@ the GitLab agent model on the [agent's blueprint documentation](../../../archite - [Cluster cost management](../../clusters/cost_management.md) - [Cluster environments](../../clusters/environments.md) - [Advanced traffic control with Canary Ingress](../../project/canary_deployments.md#advanced-traffic-control-with-canary-ingress-deprecated) -- [Serverless](../../project/clusters/serverless/index.md) - [Deploy Boards](../../project/deploy_boards.md) - [Pod logs](../../project/clusters/kubernetes_pod_logs.md) - [Clusters health](manage/clusters_health.md) diff --git a/doc/user/project/clusters/serverless/aws.md b/doc/user/project/clusters/serverless/aws.md index cf571abbf8a..93bc41dc24c 100644 --- a/doc/user/project/clusters/serverless/aws.md +++ b/doc/user/project/clusters/serverless/aws.md @@ -2,506 +2,10 @@ stage: Configure group: Configure info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +remove_date: '2022-08-22' +redirect_to: '../../../../update/removals.md#gitlab-serverless' --- -# Deploying AWS Lambda function using GitLab CI/CD **(FREE)** +# Deploying AWS Lambda function using GitLab CI/CD (removed) **(FREE)** -GitLab allows users to easily deploy AWS Lambda functions and create rich serverless applications. - -GitLab supports deployment of AWS Lambda functions through GitLab CI/CD using the following Serverless frameworks: - -- [Serverless Framework with AWS](#serverless-framework) -- [AWS' Serverless Application Model (SAM)](#aws-serverless-application-model) - -## Serverless Framework - -The [Serverless Framework can deploy to AWS](https://www.serverless.com/framework/docs/providers/aws/). - -We have prepared an example with a step-by-step guide to create a simple function and deploy it on AWS. - -Additionally, in the [How To section](#how-to), you can read about different use cases like: - -- Running a function locally. -- Working with secrets. -- Setting up CORS. - -Alternatively, you can quickly [create a new project with a template](../../working_with_projects.md#create-a-project). The [`Serverless Framework/JS` template](https://gitlab.com/gitlab-org/project-templates/serverless-framework/) already includes all parts described below. - -### Example - -This example shows you how to: - -1. Create a basic AWS Lambda Node.js function. -1. Link the function to an API Gateway `GET` endpoint. - -#### Steps - -The example consists of the following steps: - -1. Creating a Lambda handler function. -1. Creating a `serverless.yml` file. -1. Crafting the `.gitlab-ci.yml` file. -1. Setting up your AWS credentials with your GitLab account. -1. Deploying your function. -1. Testing the deployed function. - -Lets take it step by step. - -#### Creating a Lambda handler function - -Your Lambda function is the primary handler of requests. In this case, create a very simple Node.js `hello` function: - -```javascript -'use strict'; - -module.exports.hello = async event => { - return { - statusCode: 200, - body: JSON.stringify( - { - message: 'Your function executed successfully!' - }, - null, - 2 - ), - }; -}; -``` - -Place this code in the file `src/handler.js`. - -`src` is the standard location for serverless functions, but is customizable should you desire that. - -In our case, `module.exports.hello` defines the `hello` handler to reference later in the `serverless.yml`. - -You can learn more about the [AWS Lambda Node.js function handler](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html) and all its various options in its documentation. - -#### Creating a `serverless.yml` file - -In the root of your project, create a `serverless.yml` file containing configuration specifics for the Serverless Framework. - -Put the following code in the file: - -```yaml -service: gitlab-example -provider: - name: aws - runtime: nodejs14.x - -functions: - hello: - handler: src/handler.hello - events: - - http: GET hello -``` - -Our function contains a handler and a event. - -The handler definition provisions the Lambda function using the source code located `src/handler.hello`. - -The `events` declaration creates an AWS API Gateway `GET` endpoint to receive external requests and hand them over to the Lambda function via a service integration. - -You can read more about the [available properties and additional configuration possibilities](https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/) of the Serverless Framework. - -#### Crafting the `.gitlab-ci.yml` file - -In a `.gitlab-ci.yml` file in the root of your project, place the following code: - -```yaml -image: node:latest - -stages: - - deploy - -production: - stage: deploy - before_script: - - npm config set prefix /usr/local - - npm install -g serverless - script: - - serverless deploy --stage production --verbose - environment: production -``` - -This example code does the following: - -1. Uses the `node:latest` image for all GitLab CI/CD builds -1. The `deploy` stage: - - Installs the Serverless Framework. - - Deploys the serverless function to your AWS account using the AWS credentials - defined above. - -#### Setting up your AWS credentials with your GitLab account - -In order to interact with your AWS account, the GitLab CI/CD pipelines require both `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` to be defined in your GitLab settings under **Settings > CI/CD > Variables**. -For more information please see [Create a custom variable in the UI](../../../../ci/variables/index.md#custom-variables-validated-by-gitlab). - - The AWS credentials you provide must include IAM policies that provision correct - access control to AWS Lambda, API Gateway, CloudFormation, and IAM resources. - -#### Deploying your function - -`git push` the changes to your GitLab repository and the GitLab build pipeline deploys your function. - -Your GitLab deploy stage log contains output containing your AWS Lambda endpoint URL, -with log lines similar to this: - -```plaintext -endpoints: - GET - https://u768nzby1j.execute-api.us-east-1.amazonaws.com/production/hello -``` - -#### Manually testing your function - -Running the following `curl` command should trigger your function. -Your URL should be the one retrieved from the GitLab deploy stage log: - -```shell -curl "https://u768nzby1j.execute-api.us-east-1.amazonaws.com/production/hello" -``` - -That should output: - -```json -{ - "message": "Your function executed successfully!" -} -``` - -Hooray! You now have a AWS Lambda function deployed via GitLab CI/CD. - -Nice work! - -### How To - -In this section, we show you how to build on the basic example to: - -- Run the function locally. -- Set up secret variables. -- Set up CORS. - -#### Running function locally - -The `serverless-offline` plugin allows to run your code locally. To run your code locally: - -1. Add the following to your `serverless.yml`: - - ```yaml - plugins: - - serverless-offline - ``` - -1. Start the service by running the following command: - - ```shell - serverless offline - ``` - -Running the following `curl` command should trigger your function. - -```shell -curl "http://localhost:3000/hello" -``` - -It should output: - -```json -{ - "message": "Your function executed successfully!" -} -``` - -#### Secret variables - -Secrets are injected into your functions using environment variables. - -By defining variables in the provider section of the `serverless.yml`, you add them to -the environment of the deployed function: - -```yaml -provider: - # Other configuration omitted - # ... - environment: - A_VARIABLE: ${env:A_VARIABLE} -``` - -From there, you can reference them in your functions as well. -Remember to add `A_VARIABLE` to your GitLab CI/CD variables under **Settings > CI/CD > Variables** to be picked up and deployed with your function. - -NOTE: -Anyone with access to the AWS environment may be able to see the values of those -variables persisted in the lambda definition. - -#### Setting up CORS - -If you want to set up a web page that makes calls to your function, like we have done in the [template](https://gitlab.com/gitlab-org/project-templates/serverless-framework/), you need to deal with the Cross-Origin Resource Sharing (CORS). - -The quick way to do that is to add the `cors: true` flag to the HTTP endpoint in your `serverless.yml`: - -```yaml -functions: - hello: - handler: src/handler.hello - events: - - http: # Rewrite this part to enable CORS - path: hello - method: get - cors: true # <-- CORS here -``` - -You also need to return CORS specific headers in your function response: - -```javascript -'use strict'; - -module.exports.hello = async event => { - return { - statusCode: 200, - headers: { - // Uncomment the line below if you need access to cookies or authentication - // 'Access-Control-Allow-Credentials': true, - 'Access-Control-Allow-Origin': '*' - }, - body: JSON.stringify( - { - message: 'Your function executed successfully!' - }, - null, - 2 - ), - }; -}; -``` - -For more information, see the [Your CORS and API Gateway survival guide](https://www.serverless.com/blog/cors-api-gateway-survival-guide/) -blog post written by the Serverless Framework team. - -#### Writing automated tests - -The [Serverless Framework](https://gitlab.com/gitlab-org/project-templates/serverless-framework/) -example project shows how to use Jest, Axios, and `serverless-offline` plugin to do -automated testing of both local and deployed serverless function. - -### Examples and template - -The example code is available: - -- As a [clonable repository](https://gitlab.com/gitlab-org/serverless/examples/serverless-framework-js). -- In a version with [tests and secret variables](https://gitlab.com/gitlab-org/project-templates/serverless-framework/). - -You can also use a [template](../../working_with_projects.md#create-a-project) -(based on the version with tests and secret variables) from within the GitLab UI (see -the `Serverless Framework/JS` template). - -## AWS Serverless Application Model - -AWS Serverless Application Model is an open source framework for building serverless -applications. It makes it easier to build and deploy serverless applications. For more -details, please take a look at AWS documentation on [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/). - -### Deploying AWS Lambda function using AWS SAM and GitLab CI/CD - -GitLab allows developers to build and deploy serverless applications using the combination of: - -- [AWS Serverless Application Model (AWS SAM)](https://aws.amazon.com/serverless/sam/). -- GitLab CI/CD. - -### Example - -This example shows you how to: - -- Install SAM CLI. -- Create a sample SAM application including a Lambda function and API Gateway. -- Build and deploy the application to your AWS account using GitLab CI/CD. - -### Steps - -The example consists of the following steps: - -1. Installing SAM CLI. -1. Creating an AWS SAM application using SAM CLI. -1. Crafting the `.gitlab-ci.yml` file. -1. Setting up your AWS credentials with your GitLab account. -1. Deploying your application. -1. Testing the deployed function. - -### Installing SAM CLI - -AWS SAM provides a CLI called AWS SAM CLI to make it easier to create and manage -applications. - -Some steps in this documentation use SAM CLI. Follow the instructions for -[installing SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) -to install and configure SAM CLI. - -If you use [AWS Cloud9](https://aws.amazon.com/cloud9/) as your integrated development -environment (IDE), the following are installed for you: - -- [AWS Command Line Interface](https://docs.aws.amazon.com/en_pv/cli/latest/userguide/cli-chap-install.html) -- [SAM CLI](https://docs.aws.amazon.com/en_pv/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) -- [Docker](https://docs.docker.com/install/) and necessary Docker images. - -### Creating an AWS SAM application using SAM CLI - -To create a new AWS SAM application: - -1. Create a new GitLab project. -1. `git clone` the project into your local environment. -1. Change to the newly cloned project and create a new SAM app using the following command: - - ```shell - sam init -r python3.8 -n gitlabpoc --app-template "hello-world" - ``` - -1. `git push` the application back to the GitLab project. - -This creates a SAM app named `gitlabpoc` using the default configuration, a single -Python 3.8 function invoked by an [Amazon API Gateway](https://aws.amazon.com/api-gateway/) -endpoint. To see additional runtimes supported by SAM and options for `sam init`, run: - -```shell -sam init -h -``` - -### Setting up your AWS credentials with your GitLab account - -In order to interact with your AWS account, the GitLab CI/CD pipelines require both -`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` to be set in the project's CI/CD variables. - -To set these: - -1. Navigate to the project's **Settings > CI/CD**. -1. Expand the **Variables** section and create entries for `AWS_ACCESS_KEY_ID` and - `AWS_SECRET_ACCESS_KEY`. -1. Mask the credentials so they do not show in logs using the **Masked** toggle. - -The AWS credentials you provide must include IAM policies that provision correct access -control to AWS Lambda, API Gateway, CloudFormation, and IAM resources. - -### Crafting the `.gitlab-ci.yml` file - -In a [`.gitlab-ci.yml`](../../../../ci/yaml/index.md) file in the root of your project, -add the following and replace `` with the name of the S3 bucket where you -want to store your package: - -```yaml -image: python:latest - -stages: - - deploy - -production: - stage: deploy - before_script: - - apt-get update - - apt-get install -y python3-pip - - pip3 install awscli --upgrade - - pip3 install aws-sam-cli --upgrade - script: - - sam build - - sam package --output-template-file packaged.yaml --s3-bucket - - sam deploy --template-file packaged.yaml --stack-name gitlabpoc --s3-bucket --capabilities CAPABILITY_IAM --region us-east-1 - environment: production -``` - -Let's examine the configuration file more closely: - -- `image` specifies the Docker image to use for this build. This is the latest Python - image since the sample application is written in Python. -- AWS CLI and AWS SAM CLI are installed in the `before_script` section. -- SAM build, package, and deploy commands are used to build, package, and deploy the - application. - -### Deploying your application - -Push changes to your GitLab repository and the GitLab build pipeline -deploys your application. If your: - -- Build and deploy are successful, [test your deployed application](#testing-the-deployed-application). -- Build fails, look at the build log to see why the build failed. Some common reasons - the build might fail are: - - - Incompatible versions of software. For example, Python runtime version might be - different from the Python on the build machine. Address this by installing the - required versions of the software. - - You may not be able to access your AWS account from GitLab. Check the CI/CD variables - you set up with AWS credentials. - - You may not have permission to deploy a serverless application. Make sure you - provide all required permissions to deploy a serverless application. - -### Testing the deployed application - -To test the application you deployed, please go to the build log and follow the following steps: - -1. Click on "Show complete raw" on the upper right-hand corner: - - ![SAM complete raw](img/sam-complete-raw.png) - -1. Look for HelloWorldApi – API Gateway endpoint similar to shown below: - - ![SAM API endpoint](img/sam-api-endpoint.png) - -1. Use curl to test the API. For example: - - ```shell - curl "https://py4rg7qtlg.execute-api.us-east-1.amazonaws.com/Prod/hello/" - ``` - -Output should be: - -```json -{"message": "hello world"} -``` - -### Testing Locally - -AWS SAM provides functionality to test your applications locally. You must have AWS SAM -CLI installed locally for you to test locally. - -First, test the function. - -SAM provides a default event in `events/event.json` that includes a message body of: - -```plaintext -{\"message\": \"hello world\"} -``` - -If you pass that event into the `HelloWorldFunction`, it should respond with the same -body. - -Invoke the function by running: - -```shell -sam local invoke HelloWorldFunction -e events/event.json -``` - -Output should be: - -```json -{"message": "hello world"} -``` - -After you confirm that Lambda function is working as expected, test the API Gateway -using following steps. - -Start the API locally by running: - -```shell -sam local start-api -``` - -SAM again launches a Docker container, this time with a mocked Amazon API Gateway -listening on `localhost:3000`. - -Call the `hello` API by running: - -```shell -curl "http://127.0.0.1:3000/hello" -``` - -Output again should be: - -```json -{"message": "hello world"} -``` +This feature was [deprecated](https://gitlab.com/groups/gitlab-org/configure/-/epics/6) in GitLab 14.3 and [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86267) in GitLab 15.0. diff --git a/doc/user/project/clusters/serverless/img/function-details-loaded_v14_0.png b/doc/user/project/clusters/serverless/img/function-details-loaded_v14_0.png deleted file mode 100644 index a19d236fc39..00000000000 Binary files a/doc/user/project/clusters/serverless/img/function-details-loaded_v14_0.png and /dev/null differ diff --git a/doc/user/project/clusters/serverless/img/function-endpoint.png b/doc/user/project/clusters/serverless/img/function-endpoint.png deleted file mode 100644 index a38fe2cb6c2..00000000000 Binary files a/doc/user/project/clusters/serverless/img/function-endpoint.png and /dev/null differ diff --git a/doc/user/project/clusters/serverless/img/function-execution.png b/doc/user/project/clusters/serverless/img/function-execution.png deleted file mode 100644 index f60dd277081..00000000000 Binary files a/doc/user/project/clusters/serverless/img/function-execution.png and /dev/null differ diff --git a/doc/user/project/clusters/serverless/img/function-list_v12_7.png b/doc/user/project/clusters/serverless/img/function-list_v12_7.png deleted file mode 100644 index f2a27ce7b0f..00000000000 Binary files a/doc/user/project/clusters/serverless/img/function-list_v12_7.png and /dev/null differ diff --git a/doc/user/project/clusters/serverless/img/sam-api-endpoint.png b/doc/user/project/clusters/serverless/img/sam-api-endpoint.png deleted file mode 100644 index 3407b2684fd..00000000000 Binary files a/doc/user/project/clusters/serverless/img/sam-api-endpoint.png and /dev/null differ diff --git a/doc/user/project/clusters/serverless/img/sam-complete-raw.png b/doc/user/project/clusters/serverless/img/sam-complete-raw.png deleted file mode 100644 index 1130cd29d56..00000000000 Binary files a/doc/user/project/clusters/serverless/img/sam-complete-raw.png and /dev/null differ diff --git a/doc/user/project/clusters/serverless/img/serverless-page_v14_0.png b/doc/user/project/clusters/serverless/img/serverless-page_v14_0.png deleted file mode 100644 index f88eb4bdcd2..00000000000 Binary files a/doc/user/project/clusters/serverless/img/serverless-page_v14_0.png and /dev/null differ diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md index 29164da307b..432caa8476f 100644 --- a/doc/user/project/clusters/serverless/index.md +++ b/doc/user/project/clusters/serverless/index.md @@ -2,818 +2,10 @@ stage: Configure group: Configure info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +remove_date: '2022-08-22' +redirect_to: '../../../../update/removals.md#gitlab-serverless' --- -# Serverless (DEPRECATED) **(FREE)** +# Serverless (removed) **(FREE)** -> - Introduced in GitLab 11.5. -> - [Deprecated](https://gitlab.com/groups/gitlab-org/configure/-/epics/6) in GitLab 14.3. - -WARNING: -Serverless is currently in [alpha](../../../../policy/alpha-beta-support.md#alpha-features). - -## Overview - -Serverless architectures offer Operators and Developers the ability write highly scalable applications without provisioning a single server. - -GitLab supports several ways deploy Serverless applications in both Kubernetes Environments and also major cloud Function as a Service (FaaS) environments. - -Currently we support: - -- [Knative](#knative): Build Knative applications with Knative and `gitlabktl` on GKE and EKS. -- [AWS Lambda](aws.md): Create serverless applications via the Serverless Framework and GitLab CI/CD. - -## Knative - -Run serverless workloads on Kubernetes using [Knative](https://cloud.google.com/knative/). - -Knative extends Kubernetes to provide a set of middleware components that are useful to build -modern, source-centric, container-based applications. Knative brings some significant benefits out -of the box through its main components: - -- [Serving](https://github.com/knative/serving): Request-driven compute that can scale to zero. -- [Eventing](https://github.com/knative/eventing): Management and delivery of events. - -For more information on Knative, visit the [Knative docs repository](https://github.com/knative/docs). - -With GitLab Serverless, you can deploy both FaaS and serverless applications. - -## Prerequisites - -To run Knative on GitLab, you need: - -1. **Existing GitLab project:** You need a GitLab project to associate all resources. The simplest way to get started: - - If you are planning on [deploying functions](#deploying-functions), - clone the [functions example project](https://gitlab.com/knative-examples/functions) to get - started. - - If you are planning on [deploying a serverless application](#deploying-serverless-applications), - clone the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) to get - started. -1. **Kubernetes Cluster:** An RBAC-enabled Kubernetes cluster is required to deploy Knative. - The simplest way to get started is to add a cluster using the GitLab [GKE integration](../add_remove_clusters.md). - The set of minimum recommended cluster specifications to run Knative is 3 nodes, 6 vCPUs, and 22.50 GB memory. -1. **GitLab Runner:** A runner is required to run the CI jobs that deploy serverless - applications or functions onto your cluster. You can install GitLab Runner - onto the [existing Kubernetes cluster](https://docs.gitlab.com/runner/install/kubernetes.html). -1. **Domain Name:** Knative provides its own load balancer using Istio, and an - external IP address or hostname for all the applications served by Knative. Enter a - wildcard domain to serve your applications. Configure your DNS server to use the - external IP address or hostname for that domain. -1. **`.gitlab-ci.yml`:** GitLab uses [Kaniko](https://github.com/GoogleContainerTools/kaniko) - to build the application. We also use [GitLab Knative tool](https://gitlab.com/gitlab-org/gitlabktl) - CLI to simplify the deployment of services and functions to Knative. -1. **`serverless.yml`** (for [functions only](#deploying-functions)): When using serverless to deploy functions, the `serverless.yml` file - contains the information for all the functions being hosted in the repository as well as a reference - to the runtime being used. -1. **`Dockerfile`** (for [applications only](#deploying-serverless-applications)): Knative requires a - `Dockerfile` in order to build your applications. It should be included at the root of your - project's repository and expose port `8080`. `Dockerfile` is not require if you plan to build serverless functions - using our [runtimes](https://gitlab.com/gitlab-org/serverless/runtimes). -1. **Prometheus** (optional): The [Prometheus cluster integration](../../../clusters/integrations.md#prometheus-cluster-integration) - allows you to monitor the scale and traffic of your serverless function/application. -1. **Logging** (optional): Configuring logging allows you to view and search request logs for your serverless function/application. - See [Configuring logging](#configuring-logging) for more information. - -## Configuring Knative - -> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/58941) in GitLab 12.0. - -1. Follow the steps to - [add a Kubernetes - cluster](../add_remove_clusters.md). - -1. Ensure GitLab can manage Knative: - - For a non-GitLab managed cluster, ensure that the service account for the token - provided can manage resources in the `serving.knative.dev` API group. - - For a GitLab managed cluster, if you added the cluster in [GitLab 12.1 or later](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/30235), - then GitLab already has the required access and you can proceed to the next step. - - Otherwise, you need to manually grant the GitLab service account the ability to manage - resources in the `serving.knative.dev` API group. Since every GitLab service account - has the `edit` cluster role, the simplest way to do this is with an - [aggregated ClusterRole](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#aggregated-clusterroles) - adding rules to the default `edit` cluster role: First, save the following YAML as - `knative-serving-only-role.yaml`: - - ```yaml - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRole - metadata: - name: knative-serving-only-role - labels: - rbac.authorization.k8s.io/aggregate-to-edit: "true" - rules: - - apiGroups: - - serving.knative.dev - resources: - - configurations - - configurationgenerations - - routes - - revisions - - revisionuids - - autoscalers - - services - verbs: - - get - - list - - create - - update - - delete - - patch - - watch - ``` - - Then run the following command: - - ```shell - kubectl apply -f knative-serving-only-role.yaml - ``` - - Alternatively, permissions can be granted on a per-service account basis - using `Role`s and `RoleBinding`s (see the [Kubernetes RBAC - documentation](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) - for more information). - -1. Follow the steps to deploy [functions](#deploying-functions) - or [serverless applications](#deploying-serverless-applications) onto your - cluster. - -1. **Optional:** For invocation metrics to show in GitLab, additional Istio metrics need to be configured in your cluster. For example, with Knative v0.9.0, you can use [this manifest](https://gitlab.com/gitlab-org/charts/knative/-/raw/v0.10.0/vendor/istio-metrics.yml). - -## Supported runtimes - -Serverless functions for GitLab can be run using: - -- [GitLab-managed](#gitlab-managed-runtimes) runtimes. -- [OpenFaaS](#openfaas-runtimes) runtimes. - -If a runtime is not available for the required programming language, consider deploying a -[serverless application](#deploying-serverless-applications). - -### GitLab-managed runtimes - -The following GitLab-managed [runtimes](https://gitlab.com/gitlab-org/serverless/runtimes) -are available: - -- `go` (proof of concept) -- `nodejs` -- `ruby` - -You must provide a `Dockerfile` to run serverless functions if no runtime is specified. - -### OpenFaaS runtimes - -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/29253) in GitLab 12.5. - -[OpenFaaS classic runtimes](https://github.com/openfaas/templates#templates-in-store) can be used with GitLab serverless. - -OpenFaas runtimes are available for the following languages: - -- C# -- Go -- NodeJS -- PHP -- Python -- Ruby - -Runtimes are specified using the pattern: `openfaas/classic/`. The following -example shows how to define a function in `serverless.yml` using an OpenFaaS runtime: - -```yaml -hello: - source: ./hello - runtime: openfaas/classic/ruby - description: "Ruby function using OpenFaaS classic runtime" -``` - -`handler` is not needed for OpenFaaS functions. The location of the handler is defined -by the conventions of the runtime. - -See the [`ruby-openfaas-function`](https://gitlab.com/knative-examples/ruby-openfaas-function) -project for an example of a function using an OpenFaaS runtime. - -## Deploying functions - -> Introduced in GitLab 11.6. - -You can find and import all the files referenced in this doc in the -**[functions example project](https://gitlab.com/knative-examples/functions)**. - -Follow these steps to deploy a function using the Node.js runtime to your -Knative instance (you can skip these steps if you've cloned the example -project): - -1. Create a directory to house the function. In this example we will - create a directory called `echo` at the root of the project. - -1. Create the file to contain the function code. In this example, our file is called `echo.js` and is located inside the `echo` directory. If your project is: - - Public, continue to the next step. - - Private, you must [create a GitLab deploy token](../../deploy_tokens/index.md#creating-a-deploy-token) with `gitlab-deploy-token` as the name and the `read_registry` scope. - -1. `.gitlab-ci.yml`: this defines a pipeline used to deploy your functions. - It must be included at the root of your repository: - - ```yaml - include: - - template: Serverless.gitlab-ci.yml - - functions:build: - extends: .serverless:build:functions - environment: production - - functions:deploy: - extends: .serverless:deploy:functions - environment: production - ``` - - This `.gitlab-ci.yml` creates jobs that invoke some predefined commands to - build and deploy your functions to your cluster. - - `Serverless.gitlab-ci.yml` is a template that allows customization. - You can either import it with `include` parameter and use `extends` to - customize your jobs, or you can inline the entire template by choosing it - from **Apply a template** dropdown when editing the `.gitlab-ci.yml` file through - the user interface. - -1. `serverless.yml`: this file contains the metadata for your functions, - such as name, runtime, and environment. - - It must be included at the root of your repository. - The following is a sample `echo` function which shows the required structure - for the file. - - You can find the relevant files for this project in the [functions example project](https://gitlab.com/knative-examples/functions). - - ```yaml - service: functions - description: "GitLab Serverless functions using Knative" - - provider: - name: triggermesh - envs: - FOO: value - secrets: - - my-secrets - - functions: - echo-js: - handler: echo-js - source: ./echo-js - runtime: gitlab/runtimes/nodejs - description: "node.js runtime function" - envs: - MY_FUNCTION: echo-js - secrets: - - my-secrets - ``` - -Explanation of the fields used above: - -### `service` - -| Parameter | Description | -|-----------|-------------| -| `service` | Name for the Knative service which serves the function. | -| `description` | A short description of the `service`. | - -### `provider` - -| Parameter | Description | -|-----------|-------------| -| `name` | Indicates which provider is used to execute the `serverless.yml` file. In this case, the TriggerMesh middleware. | -| `envs` | Includes the environment variables to be passed as part of function execution for **all** functions in the file, where `FOO` is the variable name and `BAR` are the variable contents. You may replace this with your own variables. | -| `secrets` | Includes the contents of the Kubernetes secret as environment variables accessible to be passed as part of function execution for **all** functions in the file. The secrets are expected in `INI` format. | - -### `functions` - -In the `serverless.yml` example above, the function name is `echo` and the -subsequent lines contain the function attributes. - -| Parameter | Description | -|-----------|-------------| -| `handler` | The function's name. | -| `source` | Directory with sources of a functions. | -| `runtime` (optional)| The runtime to be used to execute the function. This can be a runtime alias (see [Runtime aliases](#runtime-aliases)), or it can be a full URL to a custom runtime repository. When the runtime is not specified, we assume that `Dockerfile` is present in the function directory specified by `source`. | -| `description` | A short description of the function. | -| `envs` | Sets an environment variable for the specific function only. | -| `secrets` | Includes the contents of the Kubernetes secret as environment variables accessible to be passed as part of function execution for the specific function only. The secrets are expected in `INI` format. | - -### Deployment - -#### Runtime aliases - -The optional `runtime` parameter can refer to one of the following runtime aliases (also see [Supported runtimes](#supported-runtimes)): - -| Runtime alias | Maintained by | -|-------------|---------------| -| `gitlab/runtimes/go` | GitLab | -| `gitlab/runtimes/nodejs` | GitLab | -| `gitlab/runtimes/ruby` | GitLab | -| `openfaas/classic/csharp` | OpenFaaS | -| `openfaas/classic/go` | OpenFaaS | -| `openfaas/classic/node` | OpenFaaS | -| `openfaas/classic/php7` | OpenFaaS | -| `openfaas/classic/python` | OpenFaaS | -| `openfaas/classic/python3` | OpenFaaS | -| `openfaas/classic/ruby` | OpenFaaS | - -After the `.gitlab-ci.yml` template has been added and the `serverless.yml` file -has been created, pushing a commit to your project results in a CI pipeline -being executed which deploys each function as a Knative service. After the -deploy stage has finished, additional details for the function display -under **Infrastructure > Serverless platform**. - -![serverless page](img/serverless-page_v14_0.png) - -This page contains all functions available for the project, the description for -accessing the function, and, if available, the function's runtime information. -The details are derived from the Knative installation inside each of the project's -Kubernetes cluster. Click on each function to obtain detailed scale and invocation data. - -The function details can be retrieved directly from Knative on the cluster: - -```shell -kubectl -n "$KUBE_NAMESPACE" get services.serving.knative.dev -``` - -The sample function can now be triggered from any HTTP client using a simple `POST` call: - - 1. Using curl (replace the URL on the last line with the URL of your application): - - ```shell - curl \ - --header "Content-Type: application/json" \ - --request POST \ - --data '{"GitLab":"FaaS"}' \ - "http://functions-echo.functions-1.functions.example.com/" - ``` - - 1. Using a web-based tool (such as Postman or Restlet) - - ![function execution](img/function-execution.png) - -### Secrets - -To access your Kubernetes secrets from within your function, the secrets should be created under the namespace of your serverless deployment and specified in your `serverless.yml` file as above. -You can create secrets in several ways. The following sections show some examples. - -#### CLI example - -```shell -kubectl create secret generic my-secrets -n "$KUBE_NAMESPACE" --from-literal MY_SECRET=imverysecure -``` - -#### Part of deployment job - -You can extend your `.gitlab-ci.yml` to create the secrets during deployment using the [CI/CD variables](../../../../ci/variables/index.md) -stored securely under your GitLab project. - -```yaml -deploy:function: - stage: deploy - environment: production - extends: .serverless:deploy:functions - before_script: - - kubectl create secret generic my-secret - --from-literal MY_SECRET="$GITLAB_SECRET_VARIABLE" - --namespace "$KUBE_NAMESPACE" - --dry-run -o yaml | kubectl apply -f - -``` - -### Running functions locally - -Running a function locally is a good way to quickly verify behavior during development. - -Running functions locally requires: - -- Go 1.12 or newer installed. -- Docker Engine installed and running. -- `gitlabktl` installed using the Go package manager: - - ```shell - GO111MODULE=on go get gitlab.com/gitlab-org/gitlabktl - ``` - -To run a function locally: - -1. Navigate to the root of your GitLab serverless project. -1. Build your function into a Docker image: - - ```shell - gitlabktl serverless build - ``` - -1. Run your function in Docker: - - ```shell - docker run -itp 8080:8080 - ``` - -1. Invoke your function: - - ```shell - curl "http://localhost:8080" - ``` - -## Deploying Serverless applications - -> Introduced in GitLab 11.5. - -Serverless applications are an alternative to [serverless functions](#deploying-functions). -They're useful in scenarios where an existing runtime does not meet the needs of -an application, such as one written in a language that has no runtime available. -Note though that serverless applications should be stateless. - -You can reference and import the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) -to get started. Add the following `.gitlab-ci.yml` to the root of your repository -(you may skip this step if you've previously cloned the previously mentioned, -sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app)): - -```yaml -include: - - template: Serverless.gitlab-ci.yml - -build: - extends: .serverless:build:image - -deploy: - extends: .serverless:deploy:image -``` - -`Serverless.gitlab-ci.yml` is a template that allows customization. -You can either import it with `include` parameter and use `extends` to -customize your jobs, or you can inline the entire template by choosing it -from **Apply a template** dropdown when editing the `.gitlab-ci.yml` file through -the user interface. - -A `serverless.yml` file is not required when deploying serverless applications. - -### Deploy the application with Knative - -With all the pieces in place, the next time a CI pipeline runs the Knative application deploys. Navigate to -**CI/CD > Pipelines** and click the most recent pipeline. - -### Function details - -Go to the **Infrastructure > Serverless platform** page to see the final URL of your functions. - -![function_details](img/function-list_v12_7.png) - -### Invocation metrics - -On the same page as above, click on one of the function -rows to bring up the function details page. - -![function_details](img/function-details-loaded_v14_0.png) - -The pod count gives you the number of pods running the serverless function instances on a given cluster. - -For the Knative function invocations to appear, the -[Prometheus cluster integration must be enabled](../../../clusters/integrations.md#prometheus-cluster-integration). - -Once Prometheus is enabled, a message may appear indicating that the metrics data _is -loading or is not available at this time._ It appears upon the first access of the -page, but should go away after a few seconds. If the message does not disappear, then it -is possible that GitLab is unable to connect to the Prometheus instance running on the -cluster. - -## Configuring logging - -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/33330) in GitLab 12.5. - -### Prerequisites - -- A GitLab-managed cluster. -- `kubectl` installed and working. - -Running `kubectl` commands on your cluster requires setting up access to the -cluster first. For clusters created on: - -- GKE, see [GKE Cluster Access](https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl) -- Other platforms, see [Install and Set Up kubectl](https://kubernetes.io/docs/tasks/tools/). - -### Enable request log template - -Run the following command to enable request logs: - -```shell -kubectl edit cm -n knative-serving config-observability -``` - -Copy the `logging.request-log-template` from the `data._example` field to the data field one level up in the hierarchy. - -### Enable request logs - -Run the following commands to install Elasticsearch, Kibana, and Filebeat into a `kube-logging` namespace and configure all nodes to forward logs using Filebeat: - -```shell -kubectl apply -f https://gitlab.com/gitlab-org/serverless/configurations/knative/raw/v0.7.0/kube-logging-filebeat.yaml -kubectl label nodes --all beta.kubernetes.io/filebeat-ready="true" -``` - -### Viewing request logs - -To view request logs: - -1. Run `kubectl proxy`. -1. Navigate to [Kibana UI](http://localhost:8001/api/v1/namespaces/kube-logging/services/kibana/proxy/app/kibana). - -Or: - -1. Open the [Kibana UI](http://localhost:8001/api/v1/namespaces/kube-logging/services/kibana/proxy/app/kibana). -1. Click on **Discover**, then select `filebeat-*` from the dropdown on the left. -1. Enter `kubernetes.container.name:"queue-proxy" AND message:/httpRequest/` into the search box. - -## Enabling TLS for Knative services - -By default, a GitLab serverless deployment is served over `http`. To serve -over `https`, you must manually obtain and install TLS certificates. - -The simplest way to accomplish this is to use Certbot to -[manually obtain Let's Encrypt certificates](https://knative.dev/docs/serving/using-a-tls-cert/#using-certbot-to-manually-obtain-let-s-encrypt-certificates). -Certbot is a free, open source software tool for automatically using Let's Encrypt -certificates on manually-administered websites to enable HTTPS. - -The following instructions relate to installing and running Certbot on a Linux -server that has Python 3 installed, and may not work on other operating systems -or with other versions of Python. - -1. Install Certbot by running the - [`certbot-auto` wrapper script](https://eff-certbot.readthedocs.io/install.html#certbot-auto). - On the command line of your server, run the following commands: - - ```shell - wget https://dl.eff.org/certbot-auto - sudo mv certbot-auto /usr/local/bin/certbot-auto - sudo chown root /usr/local/bin/certbot-auto - sudo chmod 0755 /usr/local/bin/certbot-auto - /usr/local/bin/certbot-auto --help - ``` - - To check the integrity of the `certbot-auto` script, run: - - ```shell - wget -N https://dl.eff.org/certbot-auto.asc - gpg2 --keyserver ipv4.pool.sks-keyservers.net --recv-key A2CFB51FA275A7286234E7B24D17C995CD9775F2 - gpg2 --trusted-key 4D17C995CD9775F2 --verify certbot-auto.asc /usr/local/bin/certbot-auto - ``` - - The output of the last command should look something like: - - ```shell - gpg: Signature made Mon 10 Jun 2019 06:24:40 PM EDT - gpg: using RSA key A2CFB51FA275A7286234E7B24D17C995CD9775F2 - gpg: key 4D17C995CD9775F2 marked as ultimately trusted - gpg: checking the trustdb - gpg: marginals needed: 3 completes needed: 1 trust model: pgp - gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u - gpg: next trustdb check due at 2027-11-22 - gpg: Good signature from "Let's Encrypt Client Team " [ultimate] - ``` - -1. Run the following command to use Certbot to request a certificate - using DNS challenge during authorization: - - ```shell - /usr/local/bin/certbot-auto certonly --manual --preferred-challenges dns -d '*..example.com' - ``` - - Where `` is the namespace created by GitLab for your serverless project (composed of `--`) and - `example.com` is the domain being used for your project. If you are unsure what the namespace of your project is, navigate - to the **Infrastructure > Serverless platform** page of your project and inspect - the endpoint provided for your function/app. - - ![function_endpoint](img/function-endpoint.png) - - In the above image, the namespace for the project is `node-function-11909507` and the domain is `knative.info`, thus - certificate request line would look like this: - - ```shell - ./certbot-auto certonly --manual --preferred-challenges dns -d '*.node-function-11909507.knative.info' - ``` - - The Certbot tool walks you through the steps of validating that you own each domain that you specify by creating TXT records in those domains. - After this process is complete, the output should look something like this: - - ```shell - IMPORTANT NOTES: - - Congratulations! Your certificate and chain have been saved at: - /etc/letsencrypt/live/namespace.example.com/fullchain.pem - Your key file has been saved at: - /etc/letsencrypt/live/namespace.example/privkey.pem - Your cert will expire on 2019-09-19. To obtain a new or tweaked - version of this certificate in the future, simply run certbot-auto - again. To non-interactively renew *all* of your certificates, run - "certbot-auto renew" - -----BEGIN PRIVATE KEY----- - - Your account credentials have been saved in your Certbot - configuration directory at /etc/letsencrypt. You should make a - secure backup of this folder now. This configuration directory will - also contain certificates and private keys obtained by Certbot so - making regular backups of this folder is ideal. - ``` - -1. Create certificate and private key files. Using the contents of the files - returned by Certbot, create two files in order to create the - Kubernetes secret: - - Run the following command to see the contents of `fullchain.pem`: - - ```shell - sudo cat /etc/letsencrypt/live/node-function-11909507.knative.info/fullchain.pem - ``` - - Output should look like this: - - ```shell - -----BEGIN CERTIFICATE----- - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b4ag== - -----END CERTIFICATE----- - -----BEGIN CERTIFICATE----- - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - K2fcb195768c39e9a94cec2c2e30Qg== - -----END CERTIFICATE----- - ``` - - Create a file with the name `cert.pem` with the contents of the entire output. - - Once `cert.pem` is created, run the following command to see the contents of `privkey.pem`: - - ```shell - sudo cat /etc/letsencrypt/live/namespace.example/privkey.pem - ``` - - Output should look like this: - - ```shell - -----BEGIN PRIVATE KEY----- - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - 2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df - -----BEGIN CERTIFICATE----- - fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6 - 4f294d1eaca42b8692017b4262== - -----END PRIVATE KEY----- - ``` - - Create a new file with the name `cert.pk` with the contents of the entire output. - -1. Create a Kubernetes secret to hold your TLS certificate, `cert.pem`, and - the private key `cert.pk`: - - NOTE: - Running `kubectl` commands on your cluster requires setting up access to the cluster first. - For clusters created on GKE, see - [GKE Cluster Access](https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl). - For other platforms, [install `kubectl`](https://kubernetes.io/docs/tasks/tools/). - - ```shell - kubectl create --namespace istio-system secret tls istio-ingressgateway-certs \ - --key cert.pk \ - --cert cert.pem - ``` - - Where `cert.pem` and `cert.pk` are your certificate and private key files. Note that the `istio-ingressgateway-certs` secret name is required. - -1. Configure Knative to use the new secret that you created for HTTPS - connections. Run the - following command to open the Knative shared `gateway` in edit mode: - - ```shell - kubectl edit gateway knative-ingress-gateway --namespace knative-serving - ``` - - Update the gateway to include the following `tls:` section and configuration: - - ```shell - tls: - mode: SIMPLE - privateKey: /etc/istio/ingressgateway-certs/tls.key - serverCertificate: /etc/istio/ingressgateway-certs/tls.crt - ``` - - Example: - - ```shell - apiVersion: networking.istio.io/v1alpha3 - kind: Gateway - metadata: - # ... skipped ... - spec: - selector: - istio: ingressgateway - servers: - - hosts: - - "*" - port: - name: http - number: 80 - protocol: HTTP - - hosts: - - "*" - port: - name: https - number: 443 - protocol: HTTPS - tls: - mode: SIMPLE - privateKey: /etc/istio/ingressgateway-certs/tls.key - serverCertificate: /etc/istio/ingressgateway-certs/tls.crt - ``` - - After your changes are running on your Knative cluster, you can begin using the HTTPS protocol for secure access your deployed Knative services. - In the event a mistake is made during this process and you need to update the cert, you must edit the gateway `knative-ingress-gateway` - to switch back to `PASSTHROUGH` mode. Once corrections are made, edit the file again so the gateway uses the new certificates. - -## Using an older version of `gitlabktl` - -There may be situations where you want to run an older version of `gitlabktl`. This -requires setting an older version of the `gitlabktl` image in the `.gitlab-ci.yml` file. - -To set an older version, add `image:` to the `functions:deploy` block. For example: - -```yaml -functions:deploy: - extends: .serverless:deploy:functions - environment: production - image: registry.gitlab.com/gitlab-org/gitlabktl:0.5.0 -``` - -Different versions are available by changing the version tag at the end of the registry URL in the -format `registry.gitlab.com/gitlab-org/gitlabktl:`. - -For a full inventory of available `gitlabktl` versions, see the `gitlabktl` project's -[container registry](https://gitlab.com/gitlab-org/gitlabktl/container_registry). +This feature was [deprecated](https://gitlab.com/groups/gitlab-org/configure/-/epics/6) in GitLab 14.3 and [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86267) in GitLab 15.0. diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb index 2a1e6d99297..ebc0e417393 100644 --- a/lib/api/entities/project.rb +++ b/lib/api/entities/project.rb @@ -124,6 +124,7 @@ module API expose :printing_merge_request_link_enabled expose :merge_method expose :squash_option + expose :enforce_auth_checks_on_uploads expose :suggestion_commit_message expose :merge_commit_template expose :squash_commit_template diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb index 3fa20378b19..9a191d98913 100644 --- a/lib/api/helpers/projects_helpers.rb +++ b/lib/api/helpers/projects_helpers.rb @@ -40,6 +40,7 @@ module API optional :emails_disabled, type: Boolean, desc: 'Disable email notifications' optional :show_default_award_emojis, type: Boolean, desc: 'Show default award emojis' optional :warn_about_potentially_unwanted_characters, type: Boolean, desc: 'Warn about Potentially Unwanted Characters' + optional :enforce_auth_checks_on_uploads, type: Boolean, desc: 'Enforce auth check on uploads' optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project' optional :resolve_outdated_diff_discussions, type: Boolean, desc: 'Automatically resolve merge request diffs discussions on lines changed with a push' optional :remove_source_branch_after_merge, type: Boolean, desc: 'Remove the source branch by default after merge' @@ -173,6 +174,7 @@ module API :service_desk_enabled, :keep_latest_artifact, :mr_default_target_self, + :enforce_auth_checks_on_uploads, # TODO: remove in API v5, replaced by *_access_level :issues_enabled, diff --git a/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml b/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml deleted file mode 100644 index 55648437191..00000000000 --- a/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml +++ /dev/null @@ -1,35 +0,0 @@ -# To contribute improvements to CI/CD templates, please follow the Development guide at: -# https://docs.gitlab.com/ee/development/cicd/templates.html -# This specific template is located at: -# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml - -# GitLab Serverless template - -image: alpine:latest - -stages: - - build - - test - - deploy - -.serverless:build:image: - image: registry.gitlab.com/gitlab-org/gitlabktl:latest - stage: build - script: /usr/bin/gitlabktl app build - -.serverless:deploy:image: - image: registry.gitlab.com/gitlab-org/gitlabktl:latest - stage: deploy - environment: development - script: /usr/bin/gitlabktl app deploy - -.serverless:build:functions: - image: registry.gitlab.com/gitlab-org/gitlabktl:latest - stage: build - script: /usr/bin/gitlabktl serverless build - -.serverless:deploy:functions: - image: registry.gitlab.com/gitlab-org/gitlabktl:latest - stage: deploy - environment: development - script: /usr/bin/gitlabktl serverless deploy diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml index 14c7fa1dd26..581254ac860 100644 --- a/lib/gitlab/import_export/project/import_export.yml +++ b/lib/gitlab/import_export/project/import_export.yml @@ -753,6 +753,7 @@ excluded_attributes: - :compliance_framework_setting - :show_default_award_emojis - :warn_about_potentially_unwanted_characters + - :enforce_auth_checks_on_uploads - :services - :exported_protected_branches - :repository_size_limit diff --git a/lib/sidebars/projects/menus/infrastructure_menu.rb b/lib/sidebars/projects/menus/infrastructure_menu.rb index 7bd9ac91efa..a98cc20d51a 100644 --- a/lib/sidebars/projects/menus/infrastructure_menu.rb +++ b/lib/sidebars/projects/menus/infrastructure_menu.rb @@ -9,7 +9,6 @@ module Sidebars return false unless context.project.feature_available?(:operations, context.current_user) add_item(kubernetes_menu_item) - add_item(serverless_menu_item) add_item(terraform_menu_item) add_item(google_cloud_menu_item) @@ -63,19 +62,6 @@ module Sidebars auto_devops_help_path: help_page_path('topics/autodevops/index.md') } } end - def serverless_menu_item - unless Feature.enabled?(:deprecated_serverless, context.project, default_enabled: :yaml, type: :ops) && can?(context.current_user, :read_cluster, context.project) - return ::Sidebars::NilMenuItem.new(item_id: :serverless) - end - - ::Sidebars::MenuItem.new( - title: _('Serverless platform'), - link: project_serverless_functions_path(context.project), - active_routes: { controller: :functions }, - item_id: :serverless - ) - end - def terraform_menu_item unless can?(context.current_user, :read_terraform_state, context.project) return ::Sidebars::NilMenuItem.new(item_id: :terraform) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 8a2f5120111..923d72b9f20 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -21221,9 +21221,6 @@ msgstr "" msgid "Invited" msgstr "" -msgid "Invocations" -msgstr "" - msgid "IrkerService|Channels and users separated by whitespaces. %{recipients_docs_link}" msgstr "" @@ -23157,9 +23154,6 @@ msgstr "" msgid "Loading files, directories, and submodules in the path %{path} for commit reference %{ref}" msgstr "" -msgid "Loading functions timed out. Please reload the page to try again." -msgstr "" - msgid "Loading more" msgstr "" @@ -34746,78 +34740,6 @@ msgstr "" msgid "Server version" msgstr "" -msgid "Serverless" -msgstr "" - -msgid "Serverless platform" -msgstr "" - -msgid "ServerlessDetails|Configure cluster." -msgstr "" - -msgid "ServerlessDetails|Function invocation metrics require the Prometheus cluster integration." -msgstr "" - -msgid "ServerlessDetails|Invocation metrics loading or not available at this time." -msgstr "" - -msgid "ServerlessDetails|Invocations" -msgstr "" - -msgid "ServerlessDetails|Kubernetes Pods" -msgstr "" - -msgid "ServerlessDetails|More information" -msgstr "" - -msgid "ServerlessDetails|No pods loaded at this time." -msgstr "" - -msgid "ServerlessDetails|Number of Kubernetes pods in use over time based on necessity." -msgstr "" - -msgid "ServerlessDetails|pod in use" -msgstr "" - -msgid "ServerlessDetails|pods in use" -msgstr "" - -msgid "ServerlessURL|Copy URL" -msgstr "" - -msgid "Serverless|Getting started with serverless" -msgstr "" - -msgid "Serverless|If you believe none of these apply, please check back later as the function data may be in the process of becoming available." -msgstr "" - -msgid "Serverless|Learn more about Serverless" -msgstr "" - -msgid "Serverless|No functions available" -msgstr "" - -msgid "Serverless|Serverless was %{linkStart}deprecated%{linkEnd} in GitLab 14.3." -msgstr "" - -msgid "Serverless|Serverless was %{postLinkStart}deprecated%{postLinkEnd}. But if you opt to use it, you must install Knative in your Kubernetes cluster first. %{linkStart}Learn more.%{linkEnd}" -msgstr "" - -msgid "Serverless|The deploy job has not finished." -msgstr "" - -msgid "Serverless|The functions listed in the %{startTag}serverless.yml%{endTag} file don't match the namespace of your cluster." -msgstr "" - -msgid "Serverless|There is currently no function data available from Knative. This could be for a variety of reasons including:" -msgstr "" - -msgid "Serverless|Your %{startTag}.gitlab-ci.yml%{endTag} file is not properly configured." -msgstr "" - -msgid "Serverless|Your repository does not have a corresponding %{startTag}serverless.yml%{endTag} file." -msgstr "" - msgid "Service" msgstr "" diff --git a/spec/controllers/groups/uploads_controller_spec.rb b/spec/controllers/groups/uploads_controller_spec.rb index 7dafb813545..8fcc3a7fccf 100644 --- a/spec/controllers/groups/uploads_controller_spec.rb +++ b/spec/controllers/groups/uploads_controller_spec.rb @@ -35,6 +35,169 @@ RSpec.describe Groups::UploadsController do end end + describe "GET #show" do + let(:filename) { "rails_sample.jpg" } + let(:user) { create(:user) } + let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') } + let(:txt) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') } + let(:secret) { FileUploader.generate_secret } + let(:uploader_class) { FileUploader } + + let(:upload_service) do + UploadService.new(model, jpg, uploader_class).execute + end + + let(:show_upload) do + get :show, params: params.merge(secret: secret, filename: filename) + end + + before do + allow(FileUploader).to receive(:generate_secret).and_return(secret) + + allow_next_instance_of(FileUploader) do |instance| + allow(instance).to receive(:image?).and_return(true) + end + + upload_service + end + + context 'when the group is public' do + before do + model.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PUBLIC) + end + + context "when not signed in" do + context "enforce_auth_checks_on_uploads feature flag" do + context "with flag enabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: true) + end + + it "responds with appropriate status" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context "with flag disabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + end + + context "when signed in" do + before do + sign_in(user) + end + + context "when the user doesn't have access to the model" do + context "enforce_auth_checks_on_uploads feature flag" do + context "with flag enabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: true) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + + context "with flag disabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + end + end + + context 'when the group is private' do + before do + model.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PRIVATE) + end + + context "when not signed in" do + context "enforce_auth_checks_on_uploads feature flag" do + context "with flag enabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: true) + end + + it "responds with appropriate status" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context "with flag disabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + end + + context "when signed in" do + before do + sign_in(user) + end + + context "when the user doesn't have access to the model" do + context "enforce_auth_checks_on_uploads feature flag" do + context "with flag enabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: true) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + + context "with flag disabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + end + end + end + def post_authorize(verified: true) request.headers.merge!(workhorse_internal_api_request_header) if verified diff --git a/spec/controllers/projects/serverless/functions_controller_spec.rb b/spec/controllers/projects/serverless/functions_controller_spec.rb deleted file mode 100644 index f8cee09006c..00000000000 --- a/spec/controllers/projects/serverless/functions_controller_spec.rb +++ /dev/null @@ -1,341 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Projects::Serverless::FunctionsController do - include KubernetesHelpers - include ReactiveCachingHelpers - - let(:user) { create(:user) } - let(:project) { create(:project, :repository) } - let(:cluster) { create(:cluster, :project, :provided_by_gcp, projects: [project]) } - let(:service) { cluster.platform_kubernetes } - let(:environment) { create(:environment, project: project) } - let!(:deployment) { create(:deployment, :success, environment: environment, cluster: cluster) } - let(:knative_services_finder) { environment.knative_services_finder } - let(:function_description) { 'A serverless function' } - let(:function_name) { 'some-function-name' } - let(:knative_stub_options) do - { namespace: namespace.namespace, name: function_name, description: function_description } - end - - let(:knative) { create(:clusters_applications_knative, :installed, cluster: cluster) } - - let(:namespace) do - create(:cluster_kubernetes_namespace, - cluster: cluster, - cluster_project: cluster.cluster_project, - project: cluster.cluster_project.project, - environment: environment) - end - - before do - project.add_maintainer(user) - sign_in(user) - end - - def params(opts = {}) - opts.reverse_merge(namespace_id: project.namespace.to_param, - project_id: project.to_param) - end - - shared_examples_for 'behind :deprecated_serverless feature flag' do - before do - stub_feature_flags(deprecated_serverless: false) - end - - it 'returns 404' do - action - expect(response).to have_gitlab_http_status(:not_found) - end - end - - describe 'GET #index' do - let(:expected_json) { { 'knative_installed' => knative_state, 'functions' => functions } } - - it_behaves_like 'behind :deprecated_serverless feature flag' do - let(:action) { get :index, params: params({ format: :json }) } - end - - context 'when cache is being read' do - let(:knative_state) { 'checking' } - let(:functions) { [] } - - before do - get :index, params: params({ format: :json }) - end - - it 'returns checking' do - expect(json_response).to eq expected_json - end - - it { expect(response).to have_gitlab_http_status(:ok) } - end - - context 'when cache is ready' do - let(:knative_state) { true } - - before do - allow(Clusters::KnativeServicesFinder) - .to receive(:new) - .and_return(knative_services_finder) - synchronous_reactive_cache(knative_services_finder) - stub_kubeclient_service_pods( - kube_response({ "kind" => "PodList", "items" => [] }), - namespace: namespace.namespace - ) - end - - context 'when no functions were found' do - let(:functions) { [] } - - before do - stub_kubeclient_knative_services( - namespace: namespace.namespace, - response: kube_response({ "kind" => "ServiceList", "items" => [] }) - ) - get :index, params: params({ format: :json }) - end - - it 'returns checking' do - expect(json_response).to eq expected_json - end - - it { expect(response).to have_gitlab_http_status(:ok) } - end - - context 'when functions were found' do - let(:functions) { [{}, {}] } - - before do - stub_kubeclient_knative_services(namespace: namespace.namespace, cluster_id: cluster.id, name: function_name) - end - - it 'returns functions' do - get :index, params: params({ format: :json }) - expect(json_response["functions"]).not_to be_empty - end - - it 'filters out the functions whose cluster the user does not have permission to read' do - allow(controller).to receive(:can?).and_return(true) - expect(controller).to receive(:can?).with(user, :read_cluster, cluster).and_return(false) - - get :index, params: params({ format: :json }) - - expect(json_response["functions"]).to be_empty - end - - it 'returns a successful response status' do - get :index, params: params({ format: :json }) - expect(response).to have_gitlab_http_status(:ok) - end - - context 'when there is serverless domain for a cluster' do - let!(:serverless_domain_cluster) do - create(:serverless_domain_cluster, clusters_applications_knative_id: knative.id) - end - - it 'returns JSON with function details with serverless domain URL' do - get :index, params: params({ format: :json }) - expect(response).to have_gitlab_http_status(:ok) - - expect(json_response["functions"]).not_to be_empty - - expect(json_response["functions"]).to all( - include( - 'url' => "https://#{function_name}-#{serverless_domain_cluster.uuid[0..1]}a1#{serverless_domain_cluster.uuid[2..-3]}f2#{serverless_domain_cluster.uuid[-2..]}#{"%x" % environment.id}-#{environment.slug}.#{serverless_domain_cluster.domain}" - ) - ) - end - end - - context 'when there is no serverless domain for a cluster' do - it 'keeps function URL as it was' do - expect(::Serverless::Domain).not_to receive(:new) - - get :index, params: params({ format: :json }) - expect(response).to have_gitlab_http_status(:ok) - end - end - end - end - end - - describe 'GET #show' do - it_behaves_like 'behind :deprecated_serverless feature flag' do - let(:action) { get :show, params: params({ format: :json, environment_id: "*", id: "foo" }) } - end - - context 'with function that does not exist' do - it 'returns 404' do - get :show, params: params({ format: :json, environment_id: "*", id: "foo" }) - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'with valid data', :use_clean_rails_memory_store_caching do - shared_examples 'GET #show with valid data' do - context 'when there is serverless domain for a cluster' do - let!(:serverless_domain_cluster) do - create(:serverless_domain_cluster, clusters_applications_knative_id: knative.id) - end - - it 'returns JSON with function details with serverless domain URL' do - get :show, params: params({ format: :json, environment_id: "*", id: function_name }) - expect(response).to have_gitlab_http_status(:ok) - - expect(json_response).to include( - 'url' => "https://#{function_name}-#{serverless_domain_cluster.uuid[0..1]}a1#{serverless_domain_cluster.uuid[2..-3]}f2#{serverless_domain_cluster.uuid[-2..]}#{"%x" % environment.id}-#{environment.slug}.#{serverless_domain_cluster.domain}" - ) - end - - it 'returns 404 when user does not have permission to read the cluster' do - allow(controller).to receive(:can?).and_return(true) - expect(controller).to receive(:can?).with(user, :read_cluster, cluster).and_return(false) - - get :show, params: params({ format: :json, environment_id: "*", id: function_name }) - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'when there is no serverless domain for a cluster' do - it 'keeps function URL as it was' do - get :show, params: params({ format: :json, environment_id: "*", id: function_name }) - expect(response).to have_gitlab_http_status(:ok) - - expect(json_response).to include( - 'url' => "http://#{function_name}.#{namespace.namespace}.example.com" - ) - end - end - - it 'return json with function details' do - get :show, params: params({ format: :json, environment_id: "*", id: function_name }) - expect(response).to have_gitlab_http_status(:ok) - - expect(json_response).to include( - 'name' => function_name, - 'url' => "http://#{function_name}.#{namespace.namespace}.example.com", - 'description' => function_description, - 'podcount' => 0 - ) - end - end - - context 'on Knative 0.5.0' do - before do - prepare_knative_stubs(knative_05_service(**knative_stub_options)) - end - - include_examples 'GET #show with valid data' - end - - context 'on Knative 0.6.0' do - before do - prepare_knative_stubs(knative_06_service(**knative_stub_options)) - end - - include_examples 'GET #show with valid data' - end - - context 'on Knative 0.7.0' do - before do - prepare_knative_stubs(knative_07_service(**knative_stub_options)) - end - - include_examples 'GET #show with valid data' - end - - context 'on Knative 0.9.0' do - before do - prepare_knative_stubs(knative_09_service(**knative_stub_options)) - end - - include_examples 'GET #show with valid data' - end - end - end - - describe 'GET #metrics' do - it_behaves_like 'behind :deprecated_serverless feature flag' do - let(:action) { get :metrics, params: params({ format: :json, environment_id: "*", id: "foo" }) } - end - - context 'invalid data' do - it 'has a bad function name' do - get :metrics, params: params({ format: :json, environment_id: "*", id: "foo" }) - expect(response).to have_gitlab_http_status(:no_content) - end - end - end - - describe 'GET #index with data', :use_clean_rails_memory_store_caching do - shared_examples 'GET #index with data' do - it 'has data' do - get :index, params: params({ format: :json }) - - expect(response).to have_gitlab_http_status(:ok) - - expect(json_response).to match({ - 'knative_installed' => 'checking', - 'functions' => [ - a_hash_including( - 'name' => function_name, - 'url' => "http://#{function_name}.#{namespace.namespace}.example.com", - 'description' => function_description - ) - ] - }) - end - - it 'has data in html' do - get :index, params: params - - expect(response).to have_gitlab_http_status(:ok) - end - end - - context 'on Knative 0.5.0' do - before do - prepare_knative_stubs(knative_05_service(**knative_stub_options)) - end - - include_examples 'GET #index with data' - end - - context 'on Knative 0.6.0' do - before do - prepare_knative_stubs(knative_06_service(**knative_stub_options)) - end - - include_examples 'GET #index with data' - end - - context 'on Knative 0.7.0' do - before do - prepare_knative_stubs(knative_07_service(**knative_stub_options)) - end - - include_examples 'GET #index with data' - end - - context 'on Knative 0.9.0' do - before do - prepare_knative_stubs(knative_09_service(**knative_stub_options)) - end - - include_examples 'GET #index with data' - end - end - - def prepare_knative_stubs(knative_service) - stub_kubeclient_service_pods - stub_reactive_cache(knative_services_finder, - { - services: [knative_service], - pods: kube_knative_pods_body(cluster.project.name, namespace.namespace)["items"] - }, - *knative_services_finder.cache_args) - end -end diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb index c008c7253d8..6d2db25ade2 100644 --- a/spec/controllers/projects/uploads_controller_spec.rb +++ b/spec/controllers/projects/uploads_controller_spec.rb @@ -54,6 +54,241 @@ RSpec.describe Projects::UploadsController do end end + describe "GET #show" do + let(:filename) { "rails_sample.jpg" } + let(:user) { create(:user) } + let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') } + let(:txt) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') } + let(:secret) { FileUploader.generate_secret } + let(:uploader_class) { FileUploader } + + let(:upload_service) do + UploadService.new(model, jpg, uploader_class).execute + end + + let(:show_upload) do + get :show, params: params.merge(secret: secret, filename: filename) + end + + before do + allow(FileUploader).to receive(:generate_secret).and_return(secret) + + allow_next_instance_of(FileUploader) do |instance| + allow(instance).to receive(:image?).and_return(true) + end + + upload_service + end + + context 'when project is private do' do + before do + model.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PRIVATE) + end + + context "when not signed in" do + context "enforce_auth_checks_on_uploads feature flag" do + context "with flag enabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: true) + end + + context 'when the project has setting enforce_auth_checks_on_uploads true' do + before do + model.update!(enforce_auth_checks_on_uploads: true) + end + + it "responds with status 302" do + show_upload + + expect(response).to have_gitlab_http_status(:redirect) + end + end + + context 'when the project has setting enforce_auth_checks_on_uploads false' do + before do + model.update!(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + + context "with flag disabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + end + + context "when signed in" do + before do + sign_in(user) + end + + context "when the user doesn't have access to the model" do + context "enforce_auth_checks_on_uploads feature flag" do + context "with flag enabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: true) + end + + context 'when the project has setting enforce_auth_checks_on_uploads true' do + before do + model.update!(enforce_auth_checks_on_uploads: true) + end + + it "responds with status 404" do + show_upload + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when the project has setting enforce_auth_checks_on_uploads false' do + before do + model.update!(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + end + + context "with flag disabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + end + end + + context 'when project is public' do + before do + model.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PUBLIC) + end + + context "when not signed in" do + context "enforce_auth_checks_on_uploads feature flag" do + context "with flag enabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: true) + end + + context 'when the project has setting enforce_auth_checks_on_uploads true' do + before do + model.update!(enforce_auth_checks_on_uploads: true) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'when the project has setting enforce_auth_checks_on_uploads false' do + before do + model.update!(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + + context "with flag disabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + end + + context "when signed in" do + before do + sign_in(user) + end + + context "when the user doesn't have access to the model" do + context "enforce_auth_checks_on_uploads feature flag" do + context "with flag enabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: true) + end + + context 'when the project has setting enforce_auth_checks_on_uploads true' do + before do + model.update!(enforce_auth_checks_on_uploads: true) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'when the project has setting enforce_auth_checks_on_uploads false' do + before do + model.update!(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + end + + context "with flag disabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + end + end + end + def post_authorize(verified: true) request.headers.merge!(workhorse_internal_api_request_header) if verified diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index f4f3530639b..a69317032ef 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -863,7 +863,8 @@ RSpec.describe ProjectsController do id: project.path, project: { project_setting_attributes: { - show_default_award_emojis: boolean_value + show_default_award_emojis: boolean_value, + enforce_auth_checks_on_uploads: boolean_value } } } @@ -871,6 +872,7 @@ RSpec.describe ProjectsController do project.reload expect(project.show_default_award_emojis?).to eq(result) + expect(project.enforce_auth_checks_on_uploads?).to eq(result) end end end diff --git a/spec/features/monitor_sidebar_link_spec.rb b/spec/features/monitor_sidebar_link_spec.rb index fcef0fa0eff..3c59cd65cdb 100644 --- a/spec/features/monitor_sidebar_link_spec.rb +++ b/spec/features/monitor_sidebar_link_spec.rb @@ -45,7 +45,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project)) expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project)) expect(page).not_to have_link('Product Analytics', href: project_product_analytics_path(project)) - expect(page).not_to have_link('Serverless', href: project_serverless_functions_path(project)) expect(page).not_to have_link('Logs', href: project_logs_path(project)) expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project)) end @@ -79,7 +78,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project)) expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project)) expect(page).not_to have_link('Product Analytics', href: project_product_analytics_path(project)) - expect(page).not_to have_link('Serverless', href: project_serverless_functions_path(project)) expect(page).not_to have_link('Logs', href: project_logs_path(project)) expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project)) end @@ -98,7 +96,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project)) expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project)) - expect(page).not_to have_link('Serverless', href: project_serverless_functions_path(project)) expect(page).not_to have_link('Logs', href: project_logs_path(project)) expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project)) end @@ -117,7 +114,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project)) expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project)) expect(page).to have_link('Logs', href: project_logs_path(project)) - expect(page).to have_link('Serverless', href: project_serverless_functions_path(project)) expect(page).to have_link('Kubernetes', href: project_clusters_path(project)) end @@ -134,7 +130,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do expect(page).to have_link('Environments', href: project_environments_path(project)) expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project)) expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project)) - expect(page).to have_link('Serverless', href: project_serverless_functions_path(project)) expect(page).to have_link('Logs', href: project_logs_path(project)) expect(page).to have_link('Kubernetes', href: project_clusters_path(project)) end diff --git a/spec/features/projects/serverless/functions_spec.rb b/spec/features/projects/serverless/functions_spec.rb deleted file mode 100644 index db8c2a24f2f..00000000000 --- a/spec/features/projects/serverless/functions_spec.rb +++ /dev/null @@ -1,88 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Functions', :js do - include KubernetesHelpers - include ReactiveCachingHelpers - - let(:project) { create(:project, :repository) } - let(:user) { create(:user) } - - before do - project.add_maintainer(user) - gitlab_sign_in(user) - end - - shared_examples "it's missing knative installation" do - before do - functions_finder = Projects::Serverless::FunctionsFinder.new(project) - visit project_serverless_functions_path(project) - allow(Projects::Serverless::FunctionsFinder) - .to receive(:new) - .and_return(functions_finder) - synchronous_reactive_cache(functions_finder) - end - - it 'sees an empty state require Knative installation' do - expect(page).to have_selector('.empty-state') - end - end - - context 'when user does not have a cluster and visits the serverless page' do - it_behaves_like "it's missing knative installation" - end - - context 'when the user does have a cluster and visits the serverless page' do - let(:cluster) { create(:cluster, :project, :provided_by_gcp) } - - it_behaves_like "it's missing knative installation" - end - - context 'when the user has a cluster and knative installed and visits the serverless page', :kubeclient do - let(:cluster) { create(:cluster, :project, :provided_by_gcp, projects: [project]) } - let(:service) { cluster.platform_kubernetes } - let(:environment) { create(:environment, project: project) } - let!(:deployment) { create(:deployment, :success, cluster: cluster, environment: environment) } - let(:knative_services_finder) { environment.knative_services_finder } - let(:namespace) do - create(:cluster_kubernetes_namespace, - cluster: cluster, - project: cluster.cluster_project.project, - environment: environment) - end - - before do - allow(Clusters::KnativeServicesFinder) - .to receive(:new) - .and_return(knative_services_finder) - synchronous_reactive_cache(knative_services_finder) - stub_kubeclient_knative_services(stub_get_services_options) - stub_kubeclient_service_pods(nil, namespace: namespace.namespace) - visit project_serverless_functions_path(project) - end - - context 'when there are no functions' do - let(:stub_get_services_options) do - { - namespace: namespace.namespace, - response: kube_response({ "kind" => "ServiceList", "items" => [] }) - } - end - - it 'sees an empty listing of serverless functions' do - expect(page).to have_selector('.empty-state') - expect(page).not_to have_selector('.content-list') - end - end - - context 'when there are functions' do - let(:stub_get_services_options) { { namespace: namespace.namespace } } - - it 'does not see an empty listing of serverless functions' do - expect(page).not_to have_selector('.empty-state') - expect(page).to have_selector('.content-list') - end - end - end -end diff --git a/spec/finders/projects/serverless/functions_finder_spec.rb b/spec/finders/projects/serverless/functions_finder_spec.rb deleted file mode 100644 index 9b58da2e398..00000000000 --- a/spec/finders/projects/serverless/functions_finder_spec.rb +++ /dev/null @@ -1,185 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Projects::Serverless::FunctionsFinder do - include KubernetesHelpers - include PrometheusHelpers - include ReactiveCachingHelpers - - let(:user) { create(:user) } - let(:project) { create(:project, :repository) } - let(:cluster) { create(:cluster, :project, :provided_by_gcp, projects: [project]) } - let(:service) { cluster.platform_kubernetes } - let(:environment) { create(:environment, project: project) } - let!(:deployment) { create(:deployment, :success, environment: environment, cluster: cluster) } - let(:knative_services_finder) { environment.knative_services_finder } - - let(:namespace) do - create(:cluster_kubernetes_namespace, - cluster: cluster, - project: project, - environment: environment) - end - - before do - project.add_maintainer(user) - end - - describe '#knative_installed' do - context 'when environment does not exist yet' do - shared_examples 'before first deployment' do - let(:service) { cluster.platform_kubernetes } - let(:deployment) { nil } - - it 'returns true if Knative is installed on cluster' do - stub_kubeclient_discover_knative_found(service.api_url) - function_finder = described_class.new(project) - synchronous_reactive_cache(function_finder) - - expect(function_finder.knative_installed).to be true - end - - it 'returns false if Knative is not installed on cluster' do - stub_kubeclient_discover_knative_not_found(service.api_url) - function_finder = described_class.new(project) - synchronous_reactive_cache(function_finder) - - expect(function_finder.knative_installed).to be false - end - end - - context 'when project level cluster is present and enabled' do - it_behaves_like 'before first deployment' do - let(:cluster) { create(:cluster, :project, :provided_by_gcp, enabled: true) } - let(:project) { cluster.project } - end - end - - context 'when group level cluster is present and enabled' do - it_behaves_like 'before first deployment' do - let(:cluster) { create(:cluster, :group, :provided_by_gcp, enabled: true) } - let(:project) { create(:project, group: cluster.groups.first) } - end - end - - context 'when instance level cluster is present and enabled' do - it_behaves_like 'before first deployment' do - let(:project) { create(:project) } - let(:cluster) { create(:cluster, :instance, :provided_by_gcp, enabled: true) } - end - end - - context 'when project level cluster is present, but disabled' do - let(:cluster) { create(:cluster, :project, :provided_by_gcp, enabled: false) } - let(:project) { cluster.project } - let(:service) { cluster.platform_kubernetes } - let(:deployment) { nil } - - it 'returns false even if Knative is installed on cluster' do - stub_kubeclient_discover_knative_found(service.api_url) - function_finder = described_class.new(project) - synchronous_reactive_cache(function_finder) - - expect(function_finder.knative_installed).to be false - end - end - end - - context 'when reactive_caching is still fetching data' do - it 'returns "checking"' do - expect(described_class.new(project).knative_installed).to eq 'checking' - end - end - - context 'when reactive_caching has finished' do - before do - allow(Clusters::KnativeServicesFinder) - .to receive(:new) - .and_return(knative_services_finder) - synchronous_reactive_cache(knative_services_finder) - end - - context 'when knative is not installed' do - it 'returns false' do - stub_kubeclient_discover_knative_not_found(service.api_url) - - expect(described_class.new(project).knative_installed).to eq false - end - end - - context 'reactive_caching is finished and knative is installed' do - it 'returns true' do - stub_kubeclient_knative_services(namespace: namespace.namespace) - stub_kubeclient_service_pods(nil, namespace: namespace.namespace) - - expect(described_class.new(project).knative_installed).to be true - end - end - end - end - - describe 'retrieve data from knative' do - context 'does not have knative installed' do - it { expect(described_class.new(project).execute).to be_empty } - end - - context 'has knative installed' do - let!(:knative) { create(:clusters_applications_knative, :installed, cluster: cluster) } - let(:finder) { described_class.new(project) } - - it 'there are no functions' do - expect(finder.execute).to be_empty - end - - it 'there are functions', :use_clean_rails_memory_store_caching do - stub_kubeclient_service_pods - stub_reactive_cache(knative_services_finder, - { - services: kube_knative_services_body(namespace: namespace.namespace, name: cluster.project.name)["items"], - pods: kube_knative_pods_body(cluster.project.name, namespace.namespace)["items"] - }, - *knative_services_finder.cache_args) - - expect(finder.execute).not_to be_empty - end - - it 'has a function', :use_clean_rails_memory_store_caching do - stub_kubeclient_service_pods - stub_reactive_cache(knative_services_finder, - { - services: kube_knative_services_body(namespace: namespace.namespace, name: cluster.project.name)["items"], - pods: kube_knative_pods_body(cluster.project.name, namespace.namespace)["items"] - }, - *knative_services_finder.cache_args) - - result = finder.service(cluster.environment_scope, cluster.project.name) - expect(result).to be_present - expect(result.name).to be_eql(cluster.project.name) - end - - it 'has metrics', :use_clean_rails_memory_store_caching do - end - end - - context 'has prometheus' do - let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true) } - let!(:knative) { create(:clusters_applications_knative, :installed, cluster: cluster) } - let!(:prometheus) { create(:clusters_integrations_prometheus, cluster: cluster) } - let(:finder) { described_class.new(project) } - - before do - allow(Gitlab::Prometheus::Adapter).to receive(:new).and_return(double(prometheus_adapter: prometheus_adapter)) - allow(prometheus_adapter).to receive(:query).and_return(prometheus_empty_body('matrix')) - end - - it 'is available' do - expect(finder.has_prometheus?("*")).to be true - end - - it 'has query data' do - expect(finder.invocation_metrics("*", cluster.project.name)).not_to be_nil - end - end - end -end diff --git a/spec/frontend/serverless/components/__snapshots__/empty_state_spec.js.snap b/spec/frontend/serverless/components/__snapshots__/empty_state_spec.js.snap deleted file mode 100644 index 0f4dfdf8a75..00000000000 --- a/spec/frontend/serverless/components/__snapshots__/empty_state_spec.js.snap +++ /dev/null @@ -1,22 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`EmptyStateComponent should render content 1`] = ` -"
-
-
\\"\\"
-
-
-
-

- Getting started with serverless -

-

Serverless was deprecated. But if you opt to use it, you must install Knative in your Kubernetes cluster first. Learn more. -

-
- - -
-
-
-
" -`; diff --git a/spec/frontend/serverless/components/area_spec.js b/spec/frontend/serverless/components/area_spec.js deleted file mode 100644 index 05c9ee44307..00000000000 --- a/spec/frontend/serverless/components/area_spec.js +++ /dev/null @@ -1,121 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import Area from '~/serverless/components/area.vue'; -import { mockNormalizedMetrics } from '../mock_data'; - -describe('Area component', () => { - const mockWidgets = 'mockWidgets'; - const mockGraphData = mockNormalizedMetrics; - let areaChart; - - beforeEach(() => { - areaChart = shallowMount(Area, { - propsData: { - graphData: mockGraphData, - containerWidth: 0, - }, - slots: { - default: mockWidgets, - }, - }); - }); - - afterEach(() => { - areaChart.destroy(); - }); - - it('renders chart title', () => { - expect(areaChart.find({ ref: 'graphTitle' }).text()).toBe(mockGraphData.title); - }); - - it('contains graph widgets from slot', () => { - expect(areaChart.find({ ref: 'graphWidgets' }).text()).toBe(mockWidgets); - }); - - describe('methods', () => { - describe('formatTooltipText', () => { - const mockDate = mockNormalizedMetrics.queries[0].result[0].values[0].time; - const generateSeriesData = (type) => ({ - seriesData: [ - { - componentSubType: type, - value: [mockDate, 4], - }, - ], - value: mockDate, - }); - - describe('series is of line type', () => { - beforeEach(() => { - areaChart.vm.formatTooltipText(generateSeriesData('line')); - }); - - it('formats tooltip title', () => { - expect(areaChart.vm.tooltipPopoverTitle).toBe('28 Feb 2019, 11:11AM'); - }); - - it('formats tooltip content', () => { - expect(areaChart.vm.tooltipPopoverContent).toBe('Invocations (requests): 4'); - }); - }); - - it('verify default interval value of 1', () => { - expect(areaChart.vm.getInterval).toBe(1); - }); - }); - - describe('onResize', () => { - const mockWidth = 233; - - beforeEach(() => { - jest.spyOn(Element.prototype, 'getBoundingClientRect').mockImplementation(() => ({ - width: mockWidth, - })); - areaChart.vm.onResize(); - }); - - it('sets area chart width', () => { - expect(areaChart.vm.width).toBe(mockWidth); - }); - }); - }); - - describe('computed', () => { - describe('chartData', () => { - it('utilizes all data points', () => { - expect(Object.keys(areaChart.vm.chartData)).toEqual(['requests']); - expect(areaChart.vm.chartData.requests.length).toBe(2); - }); - - it('creates valid data', () => { - const data = areaChart.vm.chartData.requests; - - expect( - data.filter( - (datum) => new Date(datum.time).getTime() > 0 && typeof datum.value === 'number', - ).length, - ).toBe(data.length); - }); - }); - - describe('generateSeries', () => { - it('utilizes correct time data', () => { - expect(areaChart.vm.generateSeries.data).toEqual([ - ['2019-02-28T11:11:38.756Z', 0], - ['2019-02-28T11:12:38.756Z', 0], - ]); - }); - }); - - describe('xAxisLabel', () => { - it('constructs a label for the chart x-axis', () => { - expect(areaChart.vm.xAxisLabel).toBe('invocations / minute'); - }); - }); - - describe('yAxisLabel', () => { - it('constructs a label for the chart y-axis', () => { - expect(areaChart.vm.yAxisLabel).toBe('Invocations (requests)'); - }); - }); - }); -}); diff --git a/spec/frontend/serverless/components/empty_state_spec.js b/spec/frontend/serverless/components/empty_state_spec.js deleted file mode 100644 index d63882c2a6d..00000000000 --- a/spec/frontend/serverless/components/empty_state_spec.js +++ /dev/null @@ -1,25 +0,0 @@ -import { GlEmptyState, GlSprintf } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import EmptyStateComponent from '~/serverless/components/empty_state.vue'; -import { createStore } from '~/serverless/store'; - -describe('EmptyStateComponent', () => { - let wrapper; - - beforeEach(() => { - const store = createStore({ - clustersPath: '/clusters', - helpPath: '/help', - emptyImagePath: '/image.svg', - }); - wrapper = shallowMount(EmptyStateComponent, { store, stubs: { GlEmptyState, GlSprintf } }); - }); - - afterEach(() => { - wrapper.destroy(); - }); - - it('should render content', () => { - expect(wrapper.html()).toMatchSnapshot(); - }); -}); diff --git a/spec/frontend/serverless/components/environment_row_spec.js b/spec/frontend/serverless/components/environment_row_spec.js deleted file mode 100644 index 944283136d0..00000000000 --- a/spec/frontend/serverless/components/environment_row_spec.js +++ /dev/null @@ -1,68 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import environmentRowComponent from '~/serverless/components/environment_row.vue'; - -import { translate } from '~/serverless/utils'; -import { mockServerlessFunctions, mockServerlessFunctionsDiffEnv } from '../mock_data'; - -const createComponent = (env, envName) => - shallowMount(environmentRowComponent, { - propsData: { env, envName }, - }).vm; - -describe('environment row component', () => { - describe('default global cluster case', () => { - let vm; - - beforeEach(() => { - vm = createComponent(translate(mockServerlessFunctions.functions)['*'], '*'); - }); - - afterEach(() => vm.$destroy()); - - it('has the correct envId', () => { - expect(vm.envId).toEqual('env-global'); - }); - - it('is open by default', () => { - expect(vm.isOpenClass).toEqual({ 'is-open': true }); - }); - - it('generates correct output', () => { - expect(vm.$el.id).toEqual('env-global'); - expect(vm.$el.classList.contains('is-open')).toBe(true); - expect(vm.$el.querySelector('div.title').innerHTML.trim()).toEqual('*'); - }); - - it('opens and closes correctly', () => { - expect(vm.isOpen).toBe(true); - - vm.toggleOpen(); - - expect(vm.isOpen).toBe(false); - }); - }); - - describe('default named cluster case', () => { - let vm; - - beforeEach(() => { - vm = createComponent(translate(mockServerlessFunctionsDiffEnv.functions).test, 'test'); - }); - - afterEach(() => vm.$destroy()); - - it('has the correct envId', () => { - expect(vm.envId).toEqual('env-test'); - }); - - it('is open by default', () => { - expect(vm.isOpenClass).toEqual({ 'is-open': true }); - }); - - it('generates correct output', () => { - expect(vm.$el.id).toEqual('env-test'); - expect(vm.$el.classList.contains('is-open')).toBe(true); - expect(vm.$el.querySelector('div.title').innerHTML.trim()).toEqual('test'); - }); - }); -}); diff --git a/spec/frontend/serverless/components/function_details_spec.js b/spec/frontend/serverless/components/function_details_spec.js deleted file mode 100644 index 0c9b2498589..00000000000 --- a/spec/frontend/serverless/components/function_details_spec.js +++ /dev/null @@ -1,100 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; -import Vuex from 'vuex'; - -import functionDetailsComponent from '~/serverless/components/function_details.vue'; -import { createStore } from '~/serverless/store'; - -describe('functionDetailsComponent', () => { - let component; - let store; - - beforeEach(() => { - Vue.use(Vuex); - - store = createStore({ clustersPath: '/clusters', helpPath: '/help' }); - }); - - afterEach(() => { - component.vm.$destroy(); - }); - - describe('Verify base functionality', () => { - const serviceStub = { - name: 'test', - description: 'a description', - environment: '*', - url: 'http://service.com/test', - namespace: 'test-ns', - podcount: 0, - metricsUrl: '/metrics', - }; - - it('has a name, description, URL, and no pods loaded', () => { - component = shallowMount(functionDetailsComponent, { - store, - propsData: { - func: serviceStub, - hasPrometheus: false, - }, - }); - - expect( - component.vm.$el.querySelector('.serverless-function-name').innerHTML.trim(), - ).toContain('test'); - - expect( - component.vm.$el.querySelector('.serverless-function-description').innerHTML.trim(), - ).toContain('a description'); - - expect(component.vm.$el.querySelector('p').innerHTML.trim()).toContain( - 'No pods loaded at this time.', - ); - }); - - it('has a pods loaded', () => { - serviceStub.podcount = 1; - - component = shallowMount(functionDetailsComponent, { - store, - propsData: { - func: serviceStub, - hasPrometheus: false, - }, - }); - - expect(component.vm.$el.querySelector('p').innerHTML.trim()).toContain('1 pod in use'); - }); - - it('has multiple pods loaded', () => { - serviceStub.podcount = 3; - - component = shallowMount(functionDetailsComponent, { - store, - propsData: { - func: serviceStub, - hasPrometheus: false, - }, - }); - - expect(component.vm.$el.querySelector('p').innerHTML.trim()).toContain('3 pods in use'); - }); - - it('can support a missing description', () => { - serviceStub.description = null; - - component = shallowMount(functionDetailsComponent, { - store, - propsData: { - func: serviceStub, - hasPrometheus: false, - }, - }); - - expect( - component.vm.$el.querySelector('.serverless-function-description').querySelector('div') - .innerHTML.length, - ).toEqual(0); - }); - }); -}); diff --git a/spec/frontend/serverless/components/function_row_spec.js b/spec/frontend/serverless/components/function_row_spec.js deleted file mode 100644 index 081edd33b3b..00000000000 --- a/spec/frontend/serverless/components/function_row_spec.js +++ /dev/null @@ -1,34 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import functionRowComponent from '~/serverless/components/function_row.vue'; -import Timeago from '~/vue_shared/components/time_ago_tooltip.vue'; - -import { mockServerlessFunction } from '../mock_data'; - -describe('functionRowComponent', () => { - let wrapper; - - const createComponent = (func) => { - wrapper = shallowMount(functionRowComponent, { - propsData: { func }, - }); - }; - - afterEach(() => { - wrapper.destroy(); - }); - - it('Parses the function details correctly', () => { - createComponent(mockServerlessFunction); - - expect(wrapper.find('b').text()).toBe(mockServerlessFunction.name); - expect(wrapper.find('span').text()).toBe(mockServerlessFunction.image); - expect(wrapper.find(Timeago).attributes('time')).not.toBe(null); - }); - - it('handles clicks correctly', () => { - createComponent(mockServerlessFunction); - const { vm } = wrapper; - - expect(vm.checkClass(vm.$el.querySelector('p'))).toBe(true); // check somewhere inside the row - }); -}); diff --git a/spec/frontend/serverless/components/functions_spec.js b/spec/frontend/serverless/components/functions_spec.js deleted file mode 100644 index 846fd63e918..00000000000 --- a/spec/frontend/serverless/components/functions_spec.js +++ /dev/null @@ -1,86 +0,0 @@ -import { GlLoadingIcon, GlAlert, GlSprintf } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import Vue, { nextTick } from 'vue'; -import AxiosMockAdapter from 'axios-mock-adapter'; -import Vuex from 'vuex'; -import { TEST_HOST } from 'helpers/test_constants'; -import axios from '~/lib/utils/axios_utils'; -import EmptyState from '~/serverless/components/empty_state.vue'; -import EnvironmentRow from '~/serverless/components/environment_row.vue'; -import functionsComponent from '~/serverless/components/functions.vue'; -import { createStore } from '~/serverless/store'; -import { mockServerlessFunctions } from '../mock_data'; - -describe('functionsComponent', () => { - const statusPath = `${TEST_HOST}/statusPath`; - - let component; - let store; - let axiosMock; - - beforeEach(() => { - axiosMock = new AxiosMockAdapter(axios); - axiosMock.onGet(statusPath).reply(200); - - Vue.use(Vuex); - - store = createStore({}); - component = shallowMount(functionsComponent, { store, stubs: { GlSprintf } }); - }); - - afterEach(() => { - component.destroy(); - axiosMock.restore(); - }); - - it('should render deprecation notice', () => { - expect(component.findComponent(GlAlert).text()).toBe( - 'Serverless was deprecated in GitLab 14.3.', - ); - }); - - it('should render empty state when Knative is not installed', async () => { - await store.dispatch('receiveFunctionsSuccess', { knative_installed: false }); - - expect(component.findComponent(EmptyState).exists()).toBe(true); - }); - - it('should render a loading component', async () => { - await store.dispatch('requestFunctionsLoading'); - - expect(component.findComponent(GlLoadingIcon).exists()).toBe(true); - }); - - it('should render empty state when there is no function data', async () => { - await store.dispatch('receiveFunctionsNoDataSuccess', { knative_installed: true }); - - expect( - component.vm.$el - .querySelector('.empty-state, .js-empty-state') - .classList.contains('js-empty-state'), - ).toBe(true); - - expect(component.vm.$el.querySelector('.state-title, .text-center').innerHTML.trim()).toEqual( - 'No functions available', - ); - }); - - it('should render functions and a loader when functions are partially fetched', async () => { - await store.dispatch('receiveFunctionsPartial', { - ...mockServerlessFunctions, - knative_installed: 'checking', - }); - - expect(component.find('.js-functions-wrapper').exists()).toBe(true); - expect(component.find('.js-functions-loader').exists()).toBe(true); - }); - - it('should render the functions list', async () => { - store = createStore({ clustersPath: 'clustersPath', helpPath: 'helpPath', statusPath }); - - await component.vm.$store.dispatch('receiveFunctionsSuccess', mockServerlessFunctions); - - await nextTick(); - expect(component.findComponent(EnvironmentRow).exists()).toBe(true); - }); -}); diff --git a/spec/frontend/serverless/components/missing_prometheus_spec.js b/spec/frontend/serverless/components/missing_prometheus_spec.js deleted file mode 100644 index 1b93fd784e1..00000000000 --- a/spec/frontend/serverless/components/missing_prometheus_spec.js +++ /dev/null @@ -1,38 +0,0 @@ -import { GlButton } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import missingPrometheusComponent from '~/serverless/components/missing_prometheus.vue'; -import { createStore } from '~/serverless/store'; - -describe('missingPrometheusComponent', () => { - let wrapper; - - const createComponent = (missingData) => { - const store = createStore({ clustersPath: '/clusters', helpPath: '/help' }); - - wrapper = shallowMount(missingPrometheusComponent, { store, propsData: { missingData } }); - }; - - afterEach(() => { - wrapper.destroy(); - }); - - it('should render missing prometheus message', () => { - createComponent(false); - const { vm } = wrapper; - - expect(vm.$el.querySelector('.state-description').innerHTML.trim()).toContain( - 'Function invocation metrics require the Prometheus cluster integration.', - ); - - expect(wrapper.find(GlButton).attributes('variant')).toBe('success'); - }); - - it('should render no prometheus data message', () => { - createComponent(true); - const { vm } = wrapper; - - expect(vm.$el.querySelector('.state-description').innerHTML.trim()).toContain( - 'Invocation metrics loading or not available at this time.', - ); - }); -}); diff --git a/spec/frontend/serverless/components/pod_box_spec.js b/spec/frontend/serverless/components/pod_box_spec.js deleted file mode 100644 index cf0c14a2cac..00000000000 --- a/spec/frontend/serverless/components/pod_box_spec.js +++ /dev/null @@ -1,22 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import podBoxComponent from '~/serverless/components/pod_box.vue'; - -const createComponent = (count) => - shallowMount(podBoxComponent, { - propsData: { - count, - }, - }).vm; - -describe('podBoxComponent', () => { - it('should render three boxes', () => { - const count = 3; - const vm = createComponent(count); - const rects = vm.$el.querySelectorAll('rect'); - - expect(rects.length).toEqual(3); - expect(parseInt(rects[2].getAttribute('x'), 10)).toEqual(40); - - vm.$destroy(); - }); -}); diff --git a/spec/frontend/serverless/components/url_spec.js b/spec/frontend/serverless/components/url_spec.js deleted file mode 100644 index 8c839577aa0..00000000000 --- a/spec/frontend/serverless/components/url_spec.js +++ /dev/null @@ -1,26 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; -import urlComponent from '~/serverless/components/url.vue'; -import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; - -const createComponent = (uri) => - shallowMount(Vue.extend(urlComponent), { - propsData: { - uri, - }, - }); - -describe('urlComponent', () => { - it('should render correctly', () => { - const uri = 'http://testfunc.apps.example.com'; - const wrapper = createComponent(uri); - const { vm } = wrapper; - - expect(vm.$el.classList.contains('clipboard-group')).toBe(true); - expect(wrapper.find(ClipboardButton).attributes('text')).toEqual(uri); - - expect(vm.$el.querySelector('[data-testid="url-text-field"]').innerHTML).toContain(uri); - - vm.$destroy(); - }); -}); diff --git a/spec/frontend/serverless/mock_data.js b/spec/frontend/serverless/mock_data.js deleted file mode 100644 index 1816ad62a04..00000000000 --- a/spec/frontend/serverless/mock_data.js +++ /dev/null @@ -1,145 +0,0 @@ -export const mockServerlessFunctions = { - knative_installed: true, - functions: [ - { - name: 'testfunc1', - namespace: 'tm-example', - environment_scope: '*', - cluster_id: 46, - detail_url: '/testuser/testproj/serverless/functions/*/testfunc1', - podcount: null, - created_at: '2019-02-05T01:01:23Z', - url: 'http://testfunc1.tm-example.apps.example.com', - description: 'A test service', - image: 'knative-test-container-buildtemplate', - }, - { - name: 'testfunc2', - namespace: 'tm-example', - environment_scope: '*', - cluster_id: 46, - detail_url: '/testuser/testproj/serverless/functions/*/testfunc2', - podcount: null, - created_at: '2019-02-05T01:01:23Z', - url: 'http://testfunc2.tm-example.apps.example.com', - description: 'A second test service\nThis one with additional descriptions', - image: 'knative-test-echo-buildtemplate', - }, - ], -}; - -export const mockServerlessFunctionsDiffEnv = { - knative_installed: true, - functions: [ - { - name: 'testfunc1', - namespace: 'tm-example', - environment_scope: '*', - cluster_id: 46, - detail_url: '/testuser/testproj/serverless/functions/*/testfunc1', - podcount: null, - created_at: '2019-02-05T01:01:23Z', - url: 'http://testfunc1.tm-example.apps.example.com', - description: 'A test service', - image: 'knative-test-container-buildtemplate', - }, - { - name: 'testfunc2', - namespace: 'tm-example', - environment_scope: 'test', - cluster_id: 46, - detail_url: '/testuser/testproj/serverless/functions/*/testfunc2', - podcount: null, - created_at: '2019-02-05T01:01:23Z', - url: 'http://testfunc2.tm-example.apps.example.com', - description: 'A second test service\nThis one with additional descriptions', - image: 'knative-test-echo-buildtemplate', - }, - ], -}; - -export const mockServerlessFunction = { - name: 'testfunc1', - namespace: 'tm-example', - environment_scope: '*', - cluster_id: 46, - detail_url: '/testuser/testproj/serverless/functions/*/testfunc1', - podcount: '3', - created_at: '2019-02-05T01:01:23Z', - url: 'http://testfunc1.tm-example.apps.example.com', - description: 'A test service', - image: 'knative-test-container-buildtemplate', -}; - -export const mockMultilineServerlessFunction = { - name: 'testfunc1', - namespace: 'tm-example', - environment_scope: '*', - cluster_id: 46, - detail_url: '/testuser/testproj/serverless/functions/*/testfunc1', - podcount: '3', - created_at: '2019-02-05T01:01:23Z', - url: 'http://testfunc1.tm-example.apps.example.com', - description: 'testfunc1\nA test service line\\nWith additional services', - image: 'knative-test-container-buildtemplate', -}; - -export const mockMetrics = { - success: true, - last_update: '2019-02-28T19:11:38.926Z', - metrics: { - id: 22, - title: 'Knative function invocations', - required_metrics: ['container_memory_usage_bytes', 'container_cpu_usage_seconds_total'], - weight: 0, - y_label: 'Invocations', - queries: [ - { - query_range: - 'floor(sum(rate(istio_revision_request_count{destination_configuration="%{function_name}", destination_namespace="%{kube_namespace}"}[1m])*30))', - unit: 'requests', - label: 'invocations / minute', - result: [ - { - metric: {}, - values: [ - [1551352298.756, '0'], - [1551352358.756, '0'], - ], - }, - ], - }, - ], - }, -}; - -export const mockNormalizedMetrics = { - id: 22, - title: 'Knative function invocations', - required_metrics: ['container_memory_usage_bytes', 'container_cpu_usage_seconds_total'], - weight: 0, - y_label: 'Invocations', - queries: [ - { - query_range: - 'floor(sum(rate(istio_revision_request_count{destination_configuration="%{function_name}", destination_namespace="%{kube_namespace}"}[1m])*30))', - unit: 'requests', - label: 'invocations / minute', - result: [ - { - metric: {}, - values: [ - { - time: '2019-02-28T11:11:38.756Z', - value: 0, - }, - { - time: '2019-02-28T11:12:38.756Z', - value: 0, - }, - ], - }, - ], - }, - ], -}; diff --git a/spec/frontend/serverless/store/actions_spec.js b/spec/frontend/serverless/store/actions_spec.js deleted file mode 100644 index 5fbecf081a6..00000000000 --- a/spec/frontend/serverless/store/actions_spec.js +++ /dev/null @@ -1,80 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import testAction from 'helpers/vuex_action_helper'; -import axios from '~/lib/utils/axios_utils'; -import statusCodes from '~/lib/utils/http_status'; -import { fetchFunctions, fetchMetrics } from '~/serverless/store/actions'; -import { mockServerlessFunctions, mockMetrics } from '../mock_data'; -import { adjustMetricQuery } from '../utils'; - -describe('ServerlessActions', () => { - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - }); - - describe('fetchFunctions', () => { - it('should successfully fetch functions', () => { - const endpoint = '/functions'; - mock.onGet(endpoint).reply(statusCodes.OK, JSON.stringify(mockServerlessFunctions)); - - return testAction( - fetchFunctions, - { functionsPath: endpoint }, - {}, - [], - [ - { type: 'requestFunctionsLoading' }, - { type: 'receiveFunctionsSuccess', payload: mockServerlessFunctions }, - ], - ); - }); - - it('should successfully retry', () => { - const endpoint = '/functions'; - mock - .onGet(endpoint) - .reply(() => new Promise((resolve) => setTimeout(() => resolve(200), Infinity))); - - return testAction( - fetchFunctions, - { functionsPath: endpoint }, - {}, - [], - [{ type: 'requestFunctionsLoading' }], - ); - }); - }); - - describe('fetchMetrics', () => { - it('should return no prometheus', () => { - const endpoint = '/metrics'; - mock.onGet(endpoint).reply(statusCodes.NO_CONTENT); - - return testAction( - fetchMetrics, - { metricsPath: endpoint, hasPrometheus: false }, - {}, - [], - [{ type: 'receiveMetricsNoPrometheus' }], - ); - }); - - it('should successfully fetch metrics', () => { - const endpoint = '/metrics'; - mock.onGet(endpoint).reply(statusCodes.OK, JSON.stringify(mockMetrics)); - - return testAction( - fetchMetrics, - { metricsPath: endpoint, hasPrometheus: true }, - {}, - [], - [{ type: 'receiveMetricsSuccess', payload: adjustMetricQuery(mockMetrics) }], - ); - }); - }); -}); diff --git a/spec/frontend/serverless/store/getters_spec.js b/spec/frontend/serverless/store/getters_spec.js deleted file mode 100644 index e1942bd2759..00000000000 --- a/spec/frontend/serverless/store/getters_spec.js +++ /dev/null @@ -1,43 +0,0 @@ -import * as getters from '~/serverless/store/getters'; -import serverlessState from '~/serverless/store/state'; -import { mockServerlessFunctions } from '../mock_data'; - -describe('Serverless Store Getters', () => { - let state; - - beforeEach(() => { - state = serverlessState; - }); - - describe('hasPrometheusMissingData', () => { - it('should return false if Prometheus is not installed', () => { - state.hasPrometheus = false; - - expect(getters.hasPrometheusMissingData(state)).toEqual(false); - }); - - it('should return false if Prometheus is installed and there is data', () => { - state.hasPrometheusData = true; - - expect(getters.hasPrometheusMissingData(state)).toEqual(false); - }); - - it('should return true if Prometheus is installed and there is no data', () => { - state.hasPrometheus = true; - state.hasPrometheusData = false; - - expect(getters.hasPrometheusMissingData(state)).toEqual(true); - }); - }); - - describe('getFunctions', () => { - it('should translate the raw function array to group the functions per environment scope', () => { - state.functions = mockServerlessFunctions.functions; - - const funcs = getters.getFunctions(state); - - expect(Object.keys(funcs)).toContain('*'); - expect(funcs['*'].length).toEqual(2); - }); - }); -}); diff --git a/spec/frontend/serverless/store/mutations_spec.js b/spec/frontend/serverless/store/mutations_spec.js deleted file mode 100644 index a1a8f9a2ca7..00000000000 --- a/spec/frontend/serverless/store/mutations_spec.js +++ /dev/null @@ -1,86 +0,0 @@ -import * as types from '~/serverless/store/mutation_types'; -import mutations from '~/serverless/store/mutations'; -import { mockServerlessFunctions, mockMetrics } from '../mock_data'; - -describe('ServerlessMutations', () => { - describe('Functions List Mutations', () => { - it('should ensure loading is true', () => { - const state = {}; - - mutations[types.REQUEST_FUNCTIONS_LOADING](state); - - expect(state.isLoading).toEqual(true); - }); - - it('should set proper state once functions are loaded', () => { - const state = {}; - - mutations[types.RECEIVE_FUNCTIONS_SUCCESS](state, mockServerlessFunctions); - - expect(state.isLoading).toEqual(false); - expect(state.hasFunctionData).toEqual(true); - expect(state.functions).toEqual(mockServerlessFunctions.functions); - }); - - it('should ensure loading has stopped and hasFunctionData is false when there are no functions available', () => { - const state = {}; - - mutations[types.RECEIVE_FUNCTIONS_NODATA_SUCCESS](state, { knative_installed: true }); - - expect(state.isLoading).toEqual(false); - expect(state.hasFunctionData).toEqual(false); - expect(state.functions).toBe(undefined); - }); - - it('should ensure loading has stopped, and an error is raised', () => { - const state = {}; - - mutations[types.RECEIVE_FUNCTIONS_ERROR](state, 'sample error'); - - expect(state.isLoading).toEqual(false); - expect(state.hasFunctionData).toEqual(false); - expect(state.functions).toBe(undefined); - expect(state.error).not.toBe(undefined); - }); - }); - - describe('Function Details Metrics Mutations', () => { - it('should ensure isLoading and hasPrometheus data flags indicate data is loaded', () => { - const state = {}; - - mutations[types.RECEIVE_METRICS_SUCCESS](state, mockMetrics); - - expect(state.isLoading).toEqual(false); - expect(state.hasPrometheusData).toEqual(true); - expect(state.graphData).toEqual(mockMetrics); - }); - - it('should ensure isLoading and hasPrometheus data flags are cleared indicating no functions available', () => { - const state = {}; - - mutations[types.RECEIVE_METRICS_NODATA_SUCCESS](state); - - expect(state.isLoading).toEqual(false); - expect(state.hasPrometheusData).toEqual(false); - expect(state.graphData).toBe(undefined); - }); - - it('should properly indicate an error', () => { - const state = {}; - - mutations[types.RECEIVE_METRICS_ERROR](state, 'sample error'); - - expect(state.hasPrometheusData).toEqual(false); - expect(state.error).not.toBe(undefined); - }); - - it('should properly indicate when prometheus is installed', () => { - const state = {}; - - mutations[types.RECEIVE_METRICS_NO_PROMETHEUS](state); - - expect(state.hasPrometheus).toEqual(false); - expect(state.hasPrometheusData).toEqual(false); - }); - }); -}); diff --git a/spec/frontend/serverless/utils.js b/spec/frontend/serverless/utils.js deleted file mode 100644 index 7caf7da231e..00000000000 --- a/spec/frontend/serverless/utils.js +++ /dev/null @@ -1,17 +0,0 @@ -export const adjustMetricQuery = (data) => { - const updatedMetric = data.metrics; - - const queries = data.metrics.queries.map((query) => ({ - ...query, - result: query.result.map((result) => ({ - ...result, - values: result.values.map(([timestamp, value]) => ({ - time: new Date(timestamp * 1000).toISOString(), - value: Number(value), - })), - })), - })); - - updatedMetric.queries = queries; - return updatedMetric; -}; diff --git a/spec/lib/gitlab/legacy_github_import/importer_spec.rb b/spec/lib/gitlab/legacy_github_import/importer_spec.rb index 9a4d7bd996e..e69edbe6dc0 100644 --- a/spec/lib/gitlab/legacy_github_import/importer_spec.rb +++ b/spec/lib/gitlab/legacy_github_import/importer_spec.rb @@ -274,8 +274,7 @@ RSpec.describe Gitlab::LegacyGithubImport::Importer do it 'instantiates a Client' do allow(project).to receive(:import_data).and_return(double(credentials: credentials)) expect(Gitlab::LegacyGithubImport::Client).to receive(:new).with( - credentials[:user], - **{} + credentials[:user] ) subject.client diff --git a/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb b/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb index 81114f5a0b3..2da7d324708 100644 --- a/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb @@ -39,27 +39,17 @@ RSpec.describe Sidebars::Projects::Menus::InfrastructureMenu do subject.renderable_items.delete(find_menu_item(:kubernetes)) end - it 'menu link points to Serverless page' do - expect(subject.link).to eq find_menu_item(:serverless).link + it 'menu link points to Terraform page' do + expect(subject.link).to eq find_menu_item(:terraform).link end - context 'when Serverless menu is not visible' do + context 'when Terraform menu is not visible' do before do - subject.renderable_items.delete(find_menu_item(:serverless)) + subject.renderable_items.delete(find_menu_item(:terraform)) end - it 'menu link points to Terraform page' do - expect(subject.link).to eq find_menu_item(:terraform).link - end - - context 'when Terraform menu is not visible' do - before do - subject.renderable_items.delete(find_menu_item(:terraform)) - end - - it 'menu link points to Google Cloud page' do - expect(subject.link).to eq find_menu_item(:google_cloud).link - end + it 'menu link points to Google Cloud page' do + expect(subject.link).to eq find_menu_item(:google_cloud).link end end end @@ -88,20 +78,6 @@ RSpec.describe Sidebars::Projects::Menus::InfrastructureMenu do it_behaves_like 'access rights checks' end - describe 'Serverless' do - let(:item_id) { :serverless } - - it_behaves_like 'access rights checks' - - context 'when feature :deprecated_serverless is disabled' do - before do - stub_feature_flags(deprecated_serverless: false) - end - - it { is_expected.to be_nil } - end - end - describe 'Terraform' do let(:item_id) { :terraform } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 6066822f4f1..dc0fa6eff66 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -845,6 +845,9 @@ RSpec.describe Project, factory_default: :keep do warn_about_potentially_unwanted_characters warn_about_potentially_unwanted_characters= warn_about_potentially_unwanted_characters? + enforce_auth_checks_on_uploads + enforce_auth_checks_on_uploads= + enforce_auth_checks_on_uploads? ).each do |method| it { is_expected.to delegate_method(method).to(:project_setting).with_arguments(allow_nil: true) } end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 5f623eda28a..20d371bdd4b 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -3563,6 +3563,20 @@ RSpec.describe API::Projects do expect(json_response['topics']).to eq(%w[topic2]) end + it 'updates enforce_auth_checks_on_uploads' do + project3.update!(enforce_auth_checks_on_uploads: false) + + project_param = { enforce_auth_checks_on_uploads: true } + + expect { put api("/projects/#{project3.id}", user), params: project_param } + .to change { project3.reload.enforce_auth_checks_on_uploads } + .from(false) + .to(true) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['enforce_auth_checks_on_uploads']).to eq(true) + end + it 'updates squash_option' do project3.update!(squash_option: 'always') diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb index b472819a00b..ef6ff7be840 100644 --- a/spec/support/shared_contexts/navbar_structure_context.rb +++ b/spec/support/shared_contexts/navbar_structure_context.rb @@ -76,7 +76,6 @@ RSpec.shared_context 'project navbar structure' do nav_item: _('Infrastructure'), nav_sub_items: [ _('Kubernetes clusters'), - _('Serverless platform'), _('Terraform') ] }, diff --git a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb index 22e925e22ae..3943355bffd 100644 --- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb +++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb @@ -537,24 +537,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do end describe 'Infrastructure' do - describe 'Serverless platform' do - it 'has a link to the serverless page' do - render - - expect(rendered).to have_link('Serverless platform', href: project_serverless_functions_path(project)) - end - - describe 'when the user does not have access' do - let(:user) { nil } - - it 'does not have a link to the serverless page' do - render - - expect(rendered).not_to have_link('Serverless platform') - end - end - end - describe 'Terraform' do it 'has a link to the terraform page' do render