Import/Export 개발 문서
GitLab v19.4요약
버그와 성능 문제가 유입될 위험을 줄이기 위해 새로 추가하는 관계는 기능 플래그 뒤에 두어야 합니다. Import/Export 기능에 대한 일반적인 개발 지침과 팁입니다. 이 문서는 YouTube에 공개된 Import/Export 201 발표를 바탕으로 작성되었습니다.
버그와 성능 문제가 유입될 위험을 줄이기 위해 새로 추가하는 관계는 기능 플래그 뒤에 두어야 합니다.
Import/Export 기능에 대한 일반적인 개발 지침과 팁입니다.
이 문서는 YouTube에 공개된 Import/Export 201 발표를 바탕으로 작성되었습니다.
더 자세한 배경은 YouTube의 Deep dive on Import / Export Development 영상에서 확인할 수 있습니다.
보안#
Import/Export 기능은 (내보낼 항목이 추가되면서) 계속 업데이트되고 있습니다. 다만
코드는 오랫동안 리팩터링되지 않았습니다. 이 기능의 동적인 성격 때문에 보안 문제가
늘어나지 않도록 코드 감사를 수행해야 합니다.
GitLab 팀원은 다음 비공개 이슈에서 자세한 내용을 확인할 수 있습니다.
https://gitlab.com/gitlab-org/gitlab/-/issues/20720.
코드 내 보안#
다음 클래스 중 일부는 Import/Export에 보안 계층을 제공합니다.
AttributeCleaner는 금지된 키를 모두 제거합니다.
# AttributeCleaner
# Removes all `_ids` and other prohibited keys
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
It looks like #{relation_class}, which is exported using the project Import/Export, has new attributes:
Please add the attribute(s) to SAFE_MODEL_ATTRIBUTES if they can be exported.
Please denylist the attribute(s) in IMPORT_EXPORT_CONFIG by adding it to its corresponding
model in the +excluded_attributes+ section.
SAFE_MODEL_ATTRIBUTES: #{File.expand_path(safe_attributes_file)}
IMPORT_EXPORT_CONFIG: #{Gitlab::ImportExport.config_file}
MSG
ModelConfigurationSpec은 새 모델이 추가되었는지 확인합니다.
# ModelConfigurationSpec
<<-MSG
New model(s) <#{new_models.join(',')}> have been added, related to #{parent_model_name}, which is exported by
the Import/Export feature.
If you think this model should be included in the export, please add it to `#{Gitlab::ImportExport.config_file}`.
Definitely add it to `#{File.expand_path(ce_models_yml)}`
to signal that you've handled this error and to prevent it from showing up in the future.
MSG
ExportFileSpec은 암호화되었거나 민감한 칼럼을 탐지합니다.
# ExportFileSpec
<<-MSG
Found a new sensitive word <#{key_found}>, which is part of the hash #{parent.inspect}
If you think this information shouldn't get exported, please exclude the model or attribute in
IMPORT_EXPORT_CONFIG.
Otherwise, please add the exception to +safe_list+ in CURRENT_SPEC using #{sensitive_word} as the
key and the correspondent hash or model as the value.
Also, if the attribute is a generated unique token, please add it to RelationFactory::TOKEN_RESET_MODELS
if it needs to be reset (to prevent duplicate column problems while importing to the same instance).
IMPORT_EXPORT_CONFIG: #{Gitlab::ImportExport.config_file}
CURRENT_SPEC: #{__FILE__}
MSG
버전 관리#
Import/Export는 하나의 GitLab 릴리스 안에서도 변경이 잦기 때문에 엄격한 SemVer를 따르지 않습니다. 다만 호환성을 깨는 변경이 있을 때는 버전을 올려야 합니다.
# ImportExport
module Gitlab
module ImportExport
extend self
# For every version update, the history in import_export.md has to be kept up to date.
VERSION = '0.2.4'
호환성#
프로젝트를 가져오고 내보낼 때는 호환성을 확인합니다.
버전을 올려야 하는 경우#
모델이나 칼럼의 이름을 바꾸거나 형식을 변경하는 경우, JSON 구조나 아카이브 파일의 파일 구조 변경에 맞춰 버전을 올려야 합니다.
다음 경우에는 버전을 올리지 않아도 됩니다.
- 새 칼럼이나 모델을 추가하는 경우
- 칼럼이나 모델을 제거하는 경우(DB 제약이 없는 경우)
- 새로운 항목을 내보내는 경우(예를 들어 새로운 유형의 업로드)
버전을 올릴 때마다 통합 스펙이 실패하는데, 다음 명령으로 해결할 수 있습니다.
bundle exec rake gitlab:import_export:bump_version
코드 빠르게 살펴보기#
Import/Export 설정 (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
모델 관계의 내보내기 순서는 다음과 같이 커스터마이즈합니다.
# Specify a custom export reordering for a given relationship
# For example for issues we use a custom export reordering by relative_position, so that on import, we can reset the
# relative position value, but still keep the issues order to the order in which issues were in the exported project.
# By default the ordering of relations is done by PK.
# column - specify the column by which to reorder, by default it is relation's PK
# direction - specify the ordering direction :asc or :desc, default :asc
# nulls_position - specify where would null values be positioned. Because custom ordering column can contain nulls we
# need to also specify where would the nulls be placed. It can be :nulls_last or :nulls_first, defaults
# to :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
이 정의는 다음과 같이 동작합니다.
- 이슈의
exportable_association?(:epic_issue, current_user: current_user)메서드를 호출합니다. - 메서드가 true를 반환하면 해당 이슈의
epic_issue연관을 포함합니다.
가져오기#
가져오기 job의 상태는 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 폴더를 함께 업데이트합니다.