가져오기/내보내기 개발 문서

가져오기/내보내기 기능에 대한 일반 개발 지침 및 팁.

이 문서는 원래 YouTube에 있는 가져오기/내보내기 201 프레젠테이션을 기반으로 합니다.

더 많은 맥락을 원하신다면 YouTube에서 가져오기/내보내기 개발에 대한 심층 분석을 시청하시기 바랍니다.

보안

가져오기/내보내기 기능은 지속적으로 업데이트되고 있습니다(내보낼 수 있는 새로운 항목 추가). 그러나,

코드는 오랫동안 리팩토링되지 않았습니다. 보안 문제의 수를 증가시키지 않도록 코드 감사를 수행해야 합니다.

GitLab 팀원들은 이 기밀 문제에서 더 많은 정보를 확인할 수 있습니다:

https://gitlab.com/gitlab-org/gitlab/-/issues/20720.

코드 내 보안

이 클래스들 중 일부는 가져오기/내보내기에 대한 보안 계층을 제공합니다.

AttributeCleaner는 금지된 키를 제거합니다:

# AttributeCleaner
# 모든 `_ids` 및 기타 금지된 키 제거
    class AttributeCleaner
      ALLOWED_REFERENCES = RelationFactory::PROJECT_REFERENCES + RelationFactory::USER_REFERENCES + ['group_id']

      def clean
        @relation_hash.reject do |key, _value|
          prohibited_key?(key) || !@relation_class.attribute_method?(key) || excluded_key?(key)
        end.except('id')
      end

      ...

AttributeConfigurationSpec는 새로운 열의 추가를 확인합니다:

# AttributeConfigurationSpec
<<-MSG
  #{relation_class}는 프로젝트 가져오기/내보내기를 통해 내보낼 수 있도록
  새로운 속성이 추가된 것 같습니다:

  내보낼 수 있는 경우 속성을 SAFE_MODEL_ATTRIBUTES에 추가하세요.

  해당 속성은 +excluded_attributes+ 섹션의 해당 모델에 추가하여
  가져오기/내보내기 구성에서 제외 목록으로 설정하세요.

  SAFE_MODEL_ATTRIBUTES: #{File.expand_path(safe_attributes_file)}
  IMPORT_EXPORT_CONFIG: #{Gitlab::ImportExport.config_file}
MSG

ModelConfigurationSpec는 새로운 모델의 추가를 확인합니다:

# ModelConfigurationSpec
<<-MSG
  새로운 모델(들) <#{new_models.join(',')}>가 #{parent_model_name}과 관련하여 추가되었으며,
  이는 가져오기/내보내기 기능에 의해 내보내집니다.

  이 모델이 내보내기에 포함되어야 한다고 생각되면, `#{Gitlab::ImportExport.config_file}`에
  추가하세요.

  이 오류를 처리했다고 신호를 보내기 위해 반드시 `#{File.expand_path(ce_models_yml)}`
 에 추가하여 앞으로 다시 나타나지 않도록 하세요.
MSG

ExportFileSpec는 암호화되거나 민감한 열을 감지합니다:

# ExportFileSpec
<<-MSG
  새로운 민감한 단어 <#{key_found}>가 발견되었습니다. 이는 해시 #{parent.inspect}의 일부입니다.

  이 정보가 내보내지 않아야 한다고 생각되면, IMPORT_EXPORT_CONFIG에서 모델 또는 속성을 제외하세요.

  그렇지 않으면, CURRENT_SPEC에서 +safe_list+에 예외를 추가하고 #{sensitive_word}를 키로,
  해당 해시나 모델을 값으로 사용하세요.

  또한, 속성이 생성된 고유 토큰인 경우, 동일한 인스턴스로 가져오는 동안 중복 열 문제를 방지하기 위해
  RelationFactory::TOKEN_RESET_MODELS에 추가하세요.

  IMPORT_EXPORT_CONFIG: #{Gitlab::ImportExport.config_file}
  CURRENT_SPEC: #{__FILE__}
MSG

버전 관리

가져오기/내보내기는 단일 GitLab 릴리스 동안 자주 변경이 발생하기 때문에 엄격한 SemVer를 사용하지 않습니다.

하지만, 파괴적인 변경이 발생할 경우 업데이트가 필요합니다.

# ImportExport
module Gitlab
  module ImportExport
    extend self

    # 모든 버전 업데이트에 대해 import_export.md의 기록이 최신 상태로 유지되어야 합니다.
    VERSION = '0.2.4'

호환성

프로젝트를 가져오고 내보낼 때 호환성을 확인하세요.

버전을 올려야 하는 경우

모델/열 이름을 변경하거나 형식을 수행하는 경우, JSON 구조나 아카이브 파일의 파일 구조에서 버전 수정을 올려야 합니다.

다음 경우에는 버전을 올릴 필요가 없습니다:

  • 새로운 열이나 모델 추가
  • 열이나 모델 제거(데이터베이스 제약 조건이 없는 경우)
  • 새로운 것 내보내기(예: 새로운 업로드 유형)

버전을 올릴 때마다 통합 사양이 실패하며, 다음 명령어로 수정할 수 있습니다:

bundle exec rake gitlab:import_export:bump_version

코드 간략 살펴보기

가져오기/내보내기 구성 (import_export.yml)

주요 구성 파일 import_export.yml은 어떤 모델이 내보내기/가져오기가 가능한지를 정의합니다.

프로젝트 가져오기/내보내기에 포함될 모델 관계:

project_tree:
  - labels:
    - :priorities
  - milestones:
    - events:
      - :push_event_payload
  - issues:
    - events:
    # ...

지정된 모델에 대해 다음 속성만 포함하세요:

included_attributes:
  user:
    - :id
    - :public_email
  # ...

지정된 모델에 대해 다음 속성은 포함하지 마세요:

excluded_attributes:
  project:
    - :name
    - :path
    - ...

내보내기에 의해 호출되는 추가 메서드:

# Methods
methods:
  labels:
    - :type
  label:
    - :type

모델 관계의 내보내기 순서를 사용자 정의하세요:

# 주어진 관계에 대해 사용자 정의 내보내기 순서를 지정합니다.
# 예를 들어, 문제의 경우 상대 위치에 의해 사용자 정의 내보내기 순서를 사용하여,
# 가져올 때 상대 위치 값을 재설정할 수 있지만, 
# 여전히 문제의 순서를 내보내진 프로젝트에서의 문제 순서로 유지할 수 있습니다.
# 기본적으로 관계 순서는 PK에 의해 결정됩니다.
# column - 재배치할 열을 지정합니다. 기본값은 관계의 PK입니다.
# direction - 정렬 방향을 지정합니다 :asc 또는 :desc, 기본값 :asc
# nulls_position - null 값이 어느 위치에 배치될지를 지정합니다. 
#                  사용자 정의 정렬 열이 null을 포함할 수 있기 때문에 
#                  null의 위치를 것도 지정해야 합니다. 기본값은 :nulls_last입니다.

export_reorders:
  project:
    issues:
      column: :relative_position
      direction: :asc
      nulls_position: :nulls_last

조건부 내보내기

연관된 리소스가 프로젝트 외부에서 오는 경우, 프로젝트나 그룹을 내보내는 사용자가 이러한 연관에 접근할 수 있는지 검증해야 할 수도 있습니다.

include_if_exportable은 리소스에 대한 연관 배열을 수용합니다. 내보내기 과정에서 exportable_association? 메서드가 연관 이름과 사용자와 함께 호출되어 연관 리소스가 내보내기에 포함될 수 있는지를 검증합니다.

예를 들어:

include_if_exportable:
  project:
    issues:
      - epic_issue

이 정의는:

  1. 문제의 exportable_association?(:epic_issue, current_user: current_user) 메서드를 호출합니다.
  2. 메서드가 true를 반환하면 문제의 epic_issue 연관을 포함합니다.

가져오기

가져오기 작업 상태는 none에서 finished 또는 failed로 다양한 상태로 이동합니다:

import_status: none -> scheduled -> started -> finished/failed

상태가 started인 동안 Importer 코드는 가져오기에 필요한 각 단계를 처리합니다.

# ImportExport::Importer
module Gitlab
  module ImportExport
    class Importer
      def execute
        if import_file && check_version! && restorers.all?(&:restore) && overwrite_project
          project
        else
          raise Projects::ImportService::Error.new(@shared.errors.join(', '))
        end
      rescue => e
        raise Projects::ImportService::Error.new(e.message)
      ensure
        remove_import_file
      end

      def restorers
        [repo_restorer, wiki_restorer, project_tree, avatar_restorer,
         uploads_restorer, lfs_restorer, statistics_restorer]
      end

내보내기 서비스는 Importer와 유사하며, 데이터를 저장하는 대신 복원합니다.

내보내기

# ImportExport::ExportService
module Projects
  module ImportExport
    class ExportService < BaseService

      def save_all!
        if save_services
          Gitlab::ImportExport::Saver.save(project: project, shared: @shared, user: user)
          notify_success
        else
          cleanup_and_notify_error!
        end
      end

      def save_services
        [version_saver, avatar_saver, project_tree_saver, uploads_saver, repo_saver,
           wiki_repo_saver, lfs_saver].all?(&:save)
      end

테스트 픽스처

Import/Export 사양에 사용되는 픽스처는 spec/fixtures/lib/gitlab/import_export에 있습니다. 프로젝트 및 그룹 픽스처가 모두 포함되어 있습니다.

각 픽스처는 두 가지 버전이 있습니다:

  • 모든 객체가 포함된 사람이 읽을 수 있는 단일 JSON 파일로, project.json 또는 group.json이라고 합니다.
  • ndjson 형식의 파일 트리가 포함된 tree라는 이름의 폴더입니다. 이 폴더 아래의 파일은 꼭 필요할 경우를 제외하고 수동으로 편집하지 마십시오.

사람이 읽을 수 있는 JSON 파일에서 NDJSON 트리를 생성하는 도구는 gitlab-org/cloud-connector-team/team-tools 프로젝트에 있습니다.

프로젝트

NDJSON 트리를 생성하려면 legacy-project-json-to-ndjson.sh를 사용하십시오.

NDJSON 트리는 다음과 같습니다:

tree
├── project
│   ├── auto_devops.ndjson
│   ├── boards.ndjson
│   ├── ci_cd_settings.ndjson
│   ├── ci_pipelines.ndjson
│   ├── container_expiration_policy.ndjson
│   ├── custom_attributes.ndjson
│   ├── error_tracking_setting.ndjson
│   ├── external_pull_requests.ndjson
│   ├── issues.ndjson
│   ├── labels.ndjson
│   ├── merge_requests.ndjson
│   ├── milestones.ndjson
│   ├── pipeline_schedules.ndjson
│   ├── project_badges.ndjson
│   ├── project_feature.ndjson
│   ├── project_members.ndjson
│   ├── protected_branches.ndjson
│   ├── protected_tags.ndjson
│   ├── releases.ndjson
│   ├── services.ndjson
│   ├── snippets.ndjson
│   └── triggers.ndjson
└── project.json

그룹

NDJSON 트리를 생성하려면 legacy-group-json-to-ndjson.rb를 사용하십시오.

NDJSON 트리는 다음과 같습니다:

tree
└── groups
    ├── 4351
    │   ├── badges.ndjson
    │   ├── boards.ndjson
    │   ├── epics.ndjson
    │   ├── labels.ndjson
    │   ├── members.ndjson
    │   └── milestones.ndjson
    ├── 4352
    │   ├── badges.ndjson
    │   ├── boards.ndjson
    │   ├── epics.ndjson
    │   ├── labels.ndjson
    │   ├── members.ndjson
    │   └── milestones.ndjson
    ├── _all.ndjson
    ├── 4351.json
    └── 4352.json

경고: 이 픽스처를 업데이트할 때는 json 파일과 tree 폴더를 모두 업데이트해야 하며, 테스트는 둘 다 적용됩니다.