Skip to content

Commit cedd58f

Browse files
authored
feat: obviate ghostlinkd (ietf-tools#7336)
* wip: identify whats needed to obviate ghostlinkd * fix: hardlink new charter files to ftp directory * fix: hardlink new charter files to ftp directory (continued) * chore: bring settings comment up to date * chore: add archive and ftp dirs to setup of various environments * fix: test charter submits write to ftp dir * chore: remove debug * fix: test charter approval writes to ftp dir * fix: link review revisions into ftp dir * fix: link to all archive and ftp on submission post * chore: clean comments, move action to github issue * fix: link idindex files to all archive and ftp * chore: deflake * chore: remove TODO comment * fix: use settings * chore: rename new setting
1 parent 370c3b2 commit cedd58f

19 files changed

Lines changed: 172 additions & 54 deletions

dev/deploy-to-container/settings_local.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@
6060
BOFREQ_PATH = '/assets/ietf-ftp/bofreq/'
6161
CONFLICT_REVIEW_PATH = '/assets/ietf-ftp/conflict-reviews/'
6262
STATUS_CHANGE_PATH = '/assets/ietf-ftp/status-changes/'
63-
INTERNET_DRAFT_ARCHIVE_DIR = '/assets/archive/id'
63+
INTERNET_DRAFT_ARCHIVE_DIR = '/assets/collection/draft-archive'
6464
INTERNET_ALL_DRAFTS_ARCHIVE_DIR = '/assets/archive/id'
6565
BIBXML_BASE_PATH = '/assets/ietfdata/derived/bibxml'
6666
IDSUBMIT_REPOSITORY_PATH = INTERNET_DRAFT_PATH
67+
FTP_DIR = '/assets/ftp'
6768

6869
NOMCOM_PUBLIC_KEYS_DIR = 'data/nomcom_keys/public_keys/'
6970
SLIDE_STAGING_PATH = '/test/staging/'

dev/diff/settings_local.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,10 @@
5757
BOFREQ_PATH = '/assets/ietf-ftp/bofreq/'
5858
CONFLICT_REVIEW_PATH = '/assets/ietf-ftp/conflict-reviews/'
5959
STATUS_CHANGE_PATH = '/assets/ietf-ftp/status-changes/'
60-
INTERNET_DRAFT_ARCHIVE_DIR = '/assets/ietf-ftp/internet-drafts/'
60+
INTERNET_DRAFT_ARCHIVE_DIR = '/assets/collection/draft-archive'
6161
INTERNET_ALL_DRAFTS_ARCHIVE_DIR = '/assets/ietf-ftp/internet-drafts/'
6262
BIBXML_BASE_PATH = '/assets/ietfdata/derived/bibxml'
63+
FTP_DIR = '/assets/ftp'
6364

6465
NOMCOM_PUBLIC_KEYS_DIR = 'data/nomcom_keys/public_keys/'
6566
SLIDE_STAGING_PATH = 'test/staging/'

dev/tests/settings_local.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@
5656
BOFREQ_PATH = '/assets/ietf-ftp/bofreq/'
5757
CONFLICT_REVIEW_PATH = '/assets/ietf-ftp/conflict-reviews/'
5858
STATUS_CHANGE_PATH = '/assets/ietf-ftp/status-changes/'
59-
INTERNET_DRAFT_ARCHIVE_DIR = '/assets/ietf-ftp/internet-drafts/'
59+
INTERNET_DRAFT_ARCHIVE_DIR = '/assets/collection/draft-archive'
6060
INTERNET_ALL_DRAFTS_ARCHIVE_DIR = '/assets/ietf-ftp/internet-drafts/'
6161
BIBXML_BASE_PATH = '/assets/ietfdata/derived/bibxml'
62+
FTP_DIR = '/assets/ftp'
6263

6364
NOMCOM_PUBLIC_KEYS_DIR = 'data/nomcom_keys/public_keys/'
6465
SLIDE_STAGING_PATH = 'test/staging/'

docker/configs/settings_local.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@
4646
BOFREQ_PATH = '/assets/ietf-ftp/bofreq/'
4747
CONFLICT_REVIEW_PATH = '/assets/ietf-ftp/conflict-reviews/'
4848
STATUS_CHANGE_PATH = '/assets/ietf-ftp/status-changes/'
49-
INTERNET_DRAFT_ARCHIVE_DIR = '/assets/archive/id'
49+
INTERNET_DRAFT_ARCHIVE_DIR = '/assets/collection/draft-archive'
5050
INTERNET_ALL_DRAFTS_ARCHIVE_DIR = '/assets/archive/id'
5151
BIBXML_BASE_PATH = '/assets/ietfdata/derived/bibxml'
5252
IDSUBMIT_REPOSITORY_PATH = INTERNET_DRAFT_PATH
53+
FTP_DIR = '/assets/ftp'
5354

5455
NOMCOM_PUBLIC_KEYS_DIR = 'data/nomcom_keys/public_keys/'
5556
SLIDE_STAGING_PATH = 'test/staging/'

docker/scripts/app-create-dirs.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ for sub in \
99
test/wiki/ietf \
1010
data/nomcom_keys/public_keys \
1111
/assets/archive/id \
12+
/assets/collection \
13+
/assets/collection/draft-archive \
1214
/assets/ietf-ftp \
1315
/assets/ietf-ftp/bofreq \
1416
/assets/ietf-ftp/charter \
@@ -33,6 +35,10 @@ for sub in \
3335
/assets/www6/iesg \
3436
/assets/www6/iesg/evaluation \
3537
/assets/media/photo \
38+
/assets/ftp \
39+
/assets/ftp/charter \
40+
/assets/ftp/internet-drafts \
41+
/assets/ftp/review \
3642
; do
3743
if [ ! -d "$sub" ]; then
3844
echo "Creating dir $sub"

ietf/doc/expire.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ def move_file(f):
139139

140140
if os.path.exists(src):
141141
try:
142+
# ghostlinkd would keep this in the combined all archive since it would
143+
# be sourced from a different place. But when ghostlinkd is removed, nothing
144+
# new is needed here - the file will already exist in the combined archive
142145
shutil.move(src, dst)
143146
except IOError as e:
144147
if "No such file or directory" in str(e):
@@ -213,6 +216,10 @@ def splitext(fn):
213216
filename, revision = match.groups()
214217

215218
def move_file_to(subdir):
219+
# Similar to move_draft_files_to_archive
220+
# ghostlinkd would keep this in the combined all archive since it would
221+
# be sourced from a different place. But when ghostlinkd is removed, nothing
222+
# new is needed here - the file will already exist in the combined archive
216223
shutil.move(path,
217224
os.path.join(settings.INTERNET_DRAFT_ARCHIVE_DIR, subdir, basename))
218225

@@ -229,4 +236,5 @@ def move_file_to(subdir):
229236
move_file_to("")
230237

231238
except Document.DoesNotExist:
239+
# All uses of this past 2014 seem related to major system failures.
232240
move_file_to("unknown_ids")

ietf/doc/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ def get_file_path(self):
142142
if self.is_dochistory():
143143
self._cached_file_path = settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR
144144
else:
145+
# This could be simplified since anything in INTERNET_DRAFT_PATH is also already in INTERNET_ALL_DRAFTS_ARCHIVE_DIR
145146
draft_state = self.get_state('draft')
146147
if draft_state and draft_state.slug == 'active':
147148
self._cached_file_path = settings.INTERNET_DRAFT_PATH

ietf/doc/tests_charter.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ def test_view_revisions(self):
8787
class EditCharterTests(TestCase):
8888
settings_temp_path_overrides = TestCase.settings_temp_path_overrides + ['CHARTER_PATH']
8989

90+
def setUp(self):
91+
super().setUp()
92+
(Path(settings.FTP_DIR)/"charter").mkdir()
93+
9094
def write_charter_file(self, charter):
9195
(Path(settings.CHARTER_PATH) / f"{charter.name}-{charter.rev}.txt").write_text("This is a charter.")
9296

@@ -506,13 +510,16 @@ def test_submit_charter(self):
506510
self.assertEqual(charter.rev, next_revision(prev_rev))
507511
self.assertTrue("new_revision" in charter.latest_event().type)
508512

509-
file_contents = (
510-
Path(settings.CHARTER_PATH) / (charter.name + "-" + charter.rev + ".txt")
511-
).read_text("utf-8")
513+
charter_path = Path(settings.CHARTER_PATH) / (charter.name + "-" + charter.rev + ".txt")
514+
file_contents = (charter_path).read_text("utf-8")
512515
self.assertEqual(
513516
file_contents,
514517
"Windows line\nMac line\nUnix line\n" + utf_8_snippet.decode("utf-8"),
515518
)
519+
ftp_charter_path = Path(settings.FTP_DIR) / "charter" / charter_path.name
520+
self.assertTrue(ftp_charter_path.exists())
521+
self.assertTrue(charter_path.samefile(ftp_charter_path))
522+
516523

517524
def test_submit_initial_charter(self):
518525
group = GroupFactory(type_id='wg',acronym='mars',list_email='mars-wg@ietf.org')
@@ -808,9 +815,11 @@ def test_approve(self):
808815
self.assertTrue(not charter.ballot_open("approve"))
809816

810817
self.assertEqual(charter.rev, "01")
811-
self.assertTrue(
812-
(Path(settings.CHARTER_PATH) / ("charter-ietf-%s-%s.txt" % (group.acronym, charter.rev))).exists()
813-
)
818+
charter_path = Path(settings.CHARTER_PATH) / ("charter-ietf-%s-%s.txt" % (group.acronym, charter.rev))
819+
charter_ftp_path = Path(settings.FTP_DIR) / "charter" / charter_path.name
820+
self.assertTrue(charter_path.exists())
821+
self.assertTrue(charter_ftp_path.exists())
822+
self.assertTrue(charter_path.samefile(charter_ftp_path))
814823

815824
self.assertEqual(len(outbox), 2)
816825
#

ietf/doc/tests_review.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# -*- coding: utf-8 -*-
33

44

5+
from pathlib import Path
56
import datetime, os, shutil
67
import io
78
import tarfile, tempfile, mailbox
@@ -47,6 +48,7 @@ def setUp(self):
4748
self.review_dir = self.tempdir('review')
4849
self.old_document_path_pattern = settings.DOCUMENT_PATH_PATTERN
4950
settings.DOCUMENT_PATH_PATTERN = self.review_dir + "/{doc.type_id}/"
51+
(Path(settings.FTP_DIR) / "review").mkdir()
5052

5153
self.review_subdir = os.path.join(self.review_dir, "review")
5254
if not os.path.exists(self.review_subdir):
@@ -57,6 +59,13 @@ def tearDown(self):
5759
settings.DOCUMENT_PATH_PATTERN = self.old_document_path_pattern
5860
super().tearDown()
5961

62+
def verify_review_files_were_written(self, assignment, expected_content = "This is a review\nwith two lines"):
63+
review_file = Path(self.review_subdir) / f"{assignment.review.name}.txt"
64+
content = review_file.read_text()
65+
self.assertEqual(content, expected_content)
66+
review_ftp_file = Path(settings.FTP_DIR) / "review" / review_file.name
67+
self.assertTrue(review_file.samefile(review_ftp_file))
68+
6069
def test_request_review(self):
6170
doc = WgDraftFactory(group__acronym='mars',rev='01')
6271
NewRevisionDocEventFactory(doc=doc,rev='01')
@@ -830,8 +839,7 @@ def test_complete_review_upload_content(self):
830839
self.assertTrue(assignment.review_request.team.acronym.lower() in assignment.review.name)
831840
self.assertTrue(assignment.review_request.doc.rev in assignment.review.name)
832841

833-
with io.open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
834-
self.assertEqual(f.read(), "This is a review\nwith two lines")
842+
self.verify_review_files_were_written(assignment)
835843

836844
self.assertEqual(len(outbox), 1)
837845
self.assertIn(assignment.review_request.team.list_email, outbox[0]["To"])
@@ -885,8 +893,7 @@ def test_complete_review_enter_content(self):
885893
completed_time_diff = timezone.now() - assignment.completed_on
886894
self.assertLess(completed_time_diff, datetime.timedelta(seconds=10))
887895

888-
with io.open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
889-
self.assertEqual(f.read(), "This is a review\nwith two lines")
896+
self.verify_review_files_were_written(assignment)
890897

891898
self.assertEqual(len(outbox), 1)
892899
self.assertIn(assignment.review_request.team.list_email, outbox[0]["To"])
@@ -926,8 +933,7 @@ def test_complete_review_enter_content_by_secretary(self):
926933
self.assertLess(event0_time_diff, datetime.timedelta(seconds=10))
927934
self.assertEqual(events[1].time, datetime.datetime(2012, 12, 24, 12, 13, 14, tzinfo=DEADLINE_TZINFO))
928935

929-
with io.open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
930-
self.assertEqual(f.read(), "This is a review\nwith two lines")
936+
self.verify_review_files_were_written(assignment)
931937

932938
self.assertEqual(len(outbox), 1)
933939
self.assertIn(assignment.review_request.team.list_email, outbox[0]["To"])
@@ -1013,8 +1019,7 @@ def test_complete_review_link_to_mailing_list(self, mock):
10131019
assignment = reload_db_objects(assignment)
10141020
self.assertEqual(assignment.state_id, "completed")
10151021

1016-
with io.open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
1017-
self.assertEqual(f.read(), "This is a review\nwith two lines")
1022+
self.verify_review_files_were_written(assignment)
10181023

10191024
self.assertEqual(len(outbox), 0)
10201025
self.assertTrue("http://example.com" in assignment.review.external_url)
@@ -1063,8 +1068,7 @@ def test_complete_unsolicited_review_link_to_mailing_list_by_secretary(self, moc
10631068
self.assertEqual(assignment.reviewer, rev_role.person.role_email('reviewer'))
10641069
self.assertEqual(assignment.state_id, "completed")
10651070

1066-
with io.open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
1067-
self.assertEqual(f.read(), "This is a review\nwith two lines")
1071+
self.verify_review_files_were_written(assignment)
10681072

10691073
self.assertEqual(len(outbox), 0)
10701074
self.assertTrue("http://example.com" in assignment.review.external_url)
@@ -1172,8 +1176,9 @@ def test_revise_review_enter_content(self):
11721176
self.assertLess(event_time_diff, datetime.timedelta(seconds=10))
11731177
self.assertTrue('revised' in event1.desc.lower())
11741178

1175-
with io.open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
1176-
self.assertEqual(f.read(), "This is a review\nwith two lines")
1179+
# See https://github.com/ietf-tools/datatracker/issues/6941
1180+
# These are _not_ getting written as a new version as intended.
1181+
self.verify_review_files_were_written(assignment)
11771182

11781183
self.assertEqual(len(outbox), 0)
11791184

@@ -1200,6 +1205,8 @@ def test_revise_review_enter_content(self):
12001205
# Ensure that a new event was created for the new revision (#2590)
12011206
self.assertNotEqual(event1.id, event2.id)
12021207

1208+
self.verify_review_files_were_written(assignment, "This is a revised review")
1209+
12031210
self.assertEqual(len(outbox), 0)
12041211

12051212
def test_edit_comment(self):

ietf/doc/utils_charter.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,31 @@ def change_group_state_after_charter_approval(group, by):
9292
def fix_charter_revision_after_approval(charter, by):
9393
# according to spec, 00-02 becomes 01, so copy file and record new revision
9494
try:
95-
old = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.name, charter.rev))
96-
new = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.name, next_approved_revision(charter.rev)))
95+
old = os.path.join(
96+
charter.get_file_path(), "%s-%s.txt" % (charter.name, charter.rev)
97+
)
98+
new = os.path.join(
99+
charter.get_file_path(),
100+
"%s-%s.txt" % (charter.name, next_approved_revision(charter.rev)),
101+
)
97102
shutil.copy(old, new)
98103
except IOError:
99104
log("There was an error copying %s to %s" % (old, new))
105+
# Also provide a copy to the legacy ftp source directory, which is served by rsync
106+
# This replaces the hardlink copy that ghostlink has made in the past
107+
# Still using a hardlink as long as these are on the same filesystem.
108+
# Staying with os.path vs pathlib.Path until we get to python>=3.10.
109+
charter_dir = os.path.join(settings.FTP_DIR, "charter")
110+
ftp_filepath = os.path.join(
111+
charter_dir, "%s-%s.txt" % (charter.name, next_approved_revision(charter.rev))
112+
)
113+
try:
114+
os.link(new, ftp_filepath)
115+
except IOError:
116+
log(
117+
"There was an error creating a harlink at %s pointing to %s"
118+
% (ftp_filepath, new)
119+
)
100120

101121
events = []
102122
e = NewRevisionDocEvent(doc=charter, by=by, type="new_revision")
@@ -108,6 +128,7 @@ def fix_charter_revision_after_approval(charter, by):
108128
charter.rev = e.rev
109129
charter.save_with_history(events)
110130

131+
111132
def historic_milestones_for_charter(charter, rev):
112133
"""Return GroupMilestone/GroupMilestoneHistory objects for charter
113134
document at rev by looking through the history."""

0 commit comments

Comments
 (0)