インポート/エクスポート開発ドキュメント
インポート/エクスポート機能の一般的な開発ガイドラインとヒント。
この文書は、YouTubeで公開されているインポート/エクスポート201プレゼンテーションに基づいています。
セキュリティ
インポート/エクスポート機能は常に更新されています(エクスポートする新しいものが追加されています)。しかし、コードは長い間リファクタリングされていません。その動的な性質がセキュリティ上の懸念を増加させないことを確認するために、コード監査を行う必要があります。GitLabチームメンバーはこの機密イシューでより多くの情報を見ることができます:https://gitlab.com/gitlab-org/gitlab/-/issues/20720
.
コードのセキュリティ
これらのクラスの中には、インポート/エクスポートにセキュリティのレイヤーを提供するものがあります。
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
バージョニング
インポート/エクスポートは、1つのGitLabリリース中に頻繁に一定の変更があるため、厳密なSemVerを使用しません。ただし、変更があった場合はアップデートが必要です。
# ImportExport
module Gitlab
module ImportExport
extend self
# For every version update, the version 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.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
- ...
エクスポートによって呼び出されるExporterメソッド:
# 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を返す場合、issueの
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)
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
テストフィクスチャ
インポート/エクスポート仕様で使われるフィクスチャはspec/fixtures/lib/gitlab/import_export
にあります。プロジェクト・フィクスチャとグループ・フィクスチャの両方があります。
これらのフィクスチャにはそれぞれ2つのバージョンがあります:
-
project.json
もしくはgroup.json
と呼ばれる、すべてのオブジェクトを含む、人間が読める単一のJSONファイル。 -
ndjson
フォーマットのファイルのツリーを含むtree
という名前のフォルダ。厳密な必要性がない限り、このフォルダの下のファイルを手動で編集しないでください。
人間が読めるJSONファイルからNDJSONツリーを生成するツールは、gitlab-org/memory-team/team-tools
プロジェクトにあります。
プロジェクト
** legacy-project-json-to-ndjson.sh
、NDJSONツリーを生成してください。**
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
グループ
legacy-group-json-to-ndjson.rb
、NDJSONツリーを生成してください。
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
フォルダの両方を更新することを確認してください。