fix #3403 feature(nimbus): support mobile remote settings collection (#3875)

Because

* We need to be able to send experiments to the relevant remote settings bucket for desktop and mobile experiments

This commit

* Moves the kinto bucket/collection configuration out of environment and into settings because it's consistent across all deployments
* Adds the mobile collection constant
* Modifies the kinto tasks to push to the relevant collection and monitor all collections for live/complete changes
This commit is contained in:
Jared Lockhart 2020-11-03 12:37:42 -05:00 коммит произвёл GitHub
Родитель ffa8f629a3
Коммит 9c42664be6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 180 добавлений и 100 удалений

Просмотреть файл

@ -24,10 +24,6 @@ FEATURE_ANALYSIS=False
FEATURE_MESSAGE_TYPE=False
GOOGLE_APPLICATION_CREDENTIALS=
HOSTNAME=localhost
KINTO_BUCKET_MAIN=main
KINTO_BUCKET=main-workspace
KINTO_COLLECTION=messaging-experiments
KINTO_COLLECTION_NIMBUS=nimbus-desktop-experiments
KINTO_HOST=http://kinto:8888/v1
KINTO_PASS=experimenter
KINTO_USER=experimenter

Просмотреть файл

@ -9,9 +9,11 @@ REVIEW_USER = REVIEW_PASS = "review"
EXPERIMENTER_USER = os.environ["KINTO_USER"]
EXPERIMENTER_PASS = os.environ["KINTO_PASS"]
KINTO_HOST = os.environ["KINTO_HOST"]
KINTO_BUCKET = os.environ["KINTO_BUCKET"]
KINTO_COLLECTION_LEGACY = (os.environ["KINTO_COLLECTION"],)
KINTO_COLLECTION_NIMBUS = os.environ["KINTO_COLLECTION_NIMBUS"]
KINTO_BUCKET = "main-workspace"
KINTO_BUCKET_MAIN = "main"
KINTO_COLLECTION_LEGACY = "messaging-experiments"
KINTO_COLLECTION_NIMBUS_DESKTOP = "nimbus-desktop-experiments"
KINTO_COLLECTION_NIMBUS_MOBILE = "nimbus-mobile-experiments"
def create_user(user, passw):
@ -40,7 +42,11 @@ print(
)
for collection in [KINTO_COLLECTION_LEGACY, KINTO_COLLECTION_NIMBUS]:
for collection in [
KINTO_COLLECTION_LEGACY,
KINTO_COLLECTION_NIMBUS_DESKTOP,
KINTO_COLLECTION_NIMBUS_MOBILE,
]:
print(">>>> Creating kinto group: editors")
print(
client.create_group(

Просмотреть файл

@ -3455,8 +3455,7 @@
"application": {
"enum": [
"firefox-desktop",
"fenix",
"reference-browser"
"fenix"
],
"type": "string",
"nullable": true

Просмотреть файл

@ -3467,8 +3467,7 @@
"application": {
"enum": [
"firefox-desktop",
"fenix",
"reference-browser"
"fenix"
],
"type": "string",
"nullable": true

Просмотреть файл

@ -1,5 +1,6 @@
from dataclasses import dataclass
from django.conf import settings
from django.db import models
@ -60,7 +61,11 @@ class NimbusConstants(object):
class Application(models.TextChoices):
DESKTOP = "firefox-desktop"
FENIX = "fenix"
REFERENCE = "reference-browser"
KINTO_APPLICATION_COLLECTION = {
Application.DESKTOP: settings.KINTO_COLLECTION_NIMBUS_DESKTOP,
Application.FENIX: settings.KINTO_COLLECTION_NIMBUS_MOBILE,
}
class Channel(models.TextChoices):
DESKTOP_BETA = "Beta"
@ -82,7 +87,6 @@ class NimbusConstants(object):
Channel.FENIX_BETA,
Channel.FENIX_RELEASE,
],
Application.REFERENCE: [Channel.REFERENCE_RELEASE],
}
class Version(models.TextChoices):

Просмотреть файл

@ -0,0 +1,33 @@
# Generated by Django 3.0.7 on 2020-11-03 17:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("experiments", "0131_nimbus_remove_probe_set_m2m"),
]
operations = [
migrations.AlterField(
model_name="nimbusexperiment",
name="application",
field=models.CharField(
blank=True,
choices=[("firefox-desktop", "Desktop"), ("fenix", "Fenix")],
max_length=255,
null=True,
),
),
migrations.AlterField(
model_name="nimbusfeatureconfig",
name="application",
field=models.CharField(
blank=True,
choices=[("firefox-desktop", "Desktop"), ("fenix", "Fenix")],
max_length=255,
null=True,
),
),
]

Просмотреть файл

@ -34,7 +34,6 @@ def nimbus_push_experiment_to_kinto(experiment_id):
and push its data to the configured collection. If it fails for any reason, log the
error and reraise it so it will be forwarded to sentry.
"""
kinto_client = KintoClient(settings.KINTO_COLLECTION_NIMBUS)
metrics.incr("push_experiment_to_kinto.started")
@ -42,6 +41,10 @@ def nimbus_push_experiment_to_kinto(experiment_id):
experiment = NimbusExperiment.objects.get(id=experiment_id)
logger.info(f"Pushing {experiment} to Kinto")
kinto_client = KintoClient(
NimbusExperiment.KINTO_APPLICATION_COLLECTION[experiment.application]
)
if not NimbusBucketRange.objects.filter(experiment=experiment).exists():
NimbusIsolationGroup.request_isolation_group_buckets(
experiment.slug,
@ -81,37 +84,38 @@ def nimbus_check_kinto_push_queue():
- Gets the list of all experiments ready to be pushed to kinto and pushes the first
one
"""
kinto_client = KintoClient(settings.KINTO_COLLECTION_NIMBUS)
metrics.incr("check_kinto_push_queue.started")
rejected_collection_data = kinto_client.get_rejected_collection_data()
if rejected_collection_data:
rejected_slug = kinto_client.get_rejected_record()
experiment = NimbusExperiment.objects.get(slug=rejected_slug)
experiment.status = NimbusExperiment.Status.DRAFT
experiment.save()
for application, collection in NimbusExperiment.KINTO_APPLICATION_COLLECTION.items():
kinto_client = KintoClient(collection)
generate_nimbus_changelog(
experiment,
get_kinto_user(),
message=f'Rejected: {rejected_collection_data["last_reviewer_comment"]}',
rejected_collection_data = kinto_client.get_rejected_collection_data()
if rejected_collection_data:
rejected_slug = kinto_client.get_rejected_record()
experiment = NimbusExperiment.objects.get(slug=rejected_slug)
experiment.status = NimbusExperiment.Status.DRAFT
experiment.save()
generate_nimbus_changelog(
experiment,
get_kinto_user(),
message=f'Rejected: {rejected_collection_data["last_reviewer_comment"]}',
)
kinto_client.delete_rejected_record(rejected_slug)
if kinto_client.has_pending_review():
metrics.incr("check_kinto_push_queue.{collection}_pending_review")
return
queued_experiments = NimbusExperiment.objects.filter(
status=NimbusExperiment.Status.REVIEW, application=application
)
kinto_client.delete_rejected_record(rejected_slug)
if kinto_client.has_pending_review():
metrics.incr("check_kinto_push_queue.pending_review")
return
queued_experiments = NimbusExperiment.objects.filter(
status=NimbusExperiment.Status.REVIEW
)
if queued_experiments.exists():
nimbus_push_experiment_to_kinto.delay(queued_experiments.first().id)
metrics.incr("check_kinto_push_queue.queued_experiment_selected")
else:
metrics.incr("check_kinto_push_queue.no_experiments_queued")
if queued_experiments.exists():
nimbus_push_experiment_to_kinto.delay(queued_experiments.first().id)
metrics.incr("check_kinto_push_queue.{collection}_queued_experiment_selected")
else:
metrics.incr("check_kinto_push_queue.{collection}_no_experiments_queued")
metrics.incr("check_kinto_push_queue.completed")
@ -124,31 +128,32 @@ def nimbus_check_experiments_are_live():
present in the collection but are not yet marked as live in the database and marks
them as live.
"""
kinto_client = KintoClient(settings.KINTO_COLLECTION_NIMBUS)
metrics.incr("check_experiments_are_live.started")
accepted_experiments = NimbusExperiment.objects.filter(
status=NimbusExperiment.Status.ACCEPTED
)
for collection in NimbusExperiment.KINTO_APPLICATION_COLLECTION.values():
kinto_client = KintoClient(collection)
records = kinto_client.get_main_records()
record_ids = [r.get("id") for r in records]
accepted_experiments = NimbusExperiment.objects.filter(
status=NimbusExperiment.Status.ACCEPTED
)
for experiment in accepted_experiments:
if experiment.slug in record_ids:
logger.info(
"{experiment} status is being updated to live".format(
experiment=experiment
records = kinto_client.get_main_records()
record_ids = [r.get("id") for r in records]
for experiment in accepted_experiments:
if experiment.slug in record_ids:
logger.info(
"{experiment} status is being updated to live".format(
experiment=experiment
)
)
)
experiment.status = NimbusExperiment.Status.LIVE
experiment.save()
experiment.status = NimbusExperiment.Status.LIVE
experiment.save()
generate_nimbus_changelog(experiment, get_kinto_user())
generate_nimbus_changelog(experiment, get_kinto_user())
logger.info("{experiment} status is set to Live")
logger.info("{experiment} status is set to Live")
metrics.incr("check_experiments_are_live.completed")
@ -161,38 +166,39 @@ def nimbus_check_experiments_are_complete():
marked as live in the database but missing from the collection, indicating that they
are no longer live and can be marked as complete.
"""
kinto_client = KintoClient(settings.KINTO_COLLECTION_NIMBUS)
metrics.incr("check_experiments_are_complete.started")
live_experiments = NimbusExperiment.objects.filter(
status=NimbusExperiment.Status.LIVE
)
for collection in NimbusExperiment.KINTO_APPLICATION_COLLECTION.values():
kinto_client = KintoClient(collection)
records = kinto_client.get_main_records()
record_ids = [r.get("id") for r in records]
live_experiments = NimbusExperiment.objects.filter(
status=NimbusExperiment.Status.LIVE
)
for experiment in live_experiments:
if (
experiment.should_end
and not experiment.emails.filter(
type=NimbusExperiment.EmailType.EXPERIMENT_END
).exists()
):
nimbus_send_experiment_ending_email(experiment)
records = kinto_client.get_main_records()
record_ids = [r.get("id") for r in records]
if experiment.slug not in record_ids:
logger.info(
"{experiment} status is being updated to complete".format(
experiment=experiment
for experiment in live_experiments:
if (
experiment.should_end
and not experiment.emails.filter(
type=NimbusExperiment.EmailType.EXPERIMENT_END
).exists()
):
nimbus_send_experiment_ending_email(experiment)
if experiment.slug not in record_ids:
logger.info(
"{experiment} status is being updated to complete".format(
experiment=experiment
)
)
)
experiment.status = NimbusExperiment.Status.COMPLETE
experiment.save()
experiment.status = NimbusExperiment.Status.COMPLETE
experiment.save()
generate_nimbus_changelog(experiment, get_kinto_user())
generate_nimbus_changelog(experiment, get_kinto_user())
logger.info("{experiment} status is set to Complete")
logger.info("{experiment} status is set to Complete")
metrics.incr("check_experiments_are_complete.completed")

Просмотреть файл

@ -18,31 +18,56 @@ from experimenter.kinto.tests.mixins import MockKintoClientMixin
class TestPushExperimentToKintoTask(MockKintoClientMixin, TestCase):
def setUp(self):
super().setUp()
self.experiment = NimbusExperimentFactory.create_with_status(
def test_push_experiment_to_kinto_sends_desktop_experiment_data(self):
experiment = NimbusExperimentFactory.create_with_status(
NimbusExperiment.Status.DRAFT,
application=NimbusExperiment.Application.DESKTOP,
)
def test_push_experiment_to_kinto_sends_experiment_data(self):
tasks.nimbus_push_experiment_to_kinto(self.experiment.id)
tasks.nimbus_push_experiment_to_kinto(experiment.id)
data = NimbusExperimentSerializer(self.experiment).data
data = NimbusExperimentSerializer(experiment).data
self.assertTrue(
NimbusBucketRange.objects.filter(experiment=self.experiment).exists()
)
self.assertTrue(NimbusBucketRange.objects.filter(experiment=experiment).exists())
self.mock_kinto_client.create_record.assert_called_with(
data=data,
collection=settings.KINTO_COLLECTION_NIMBUS,
collection=settings.KINTO_COLLECTION_NIMBUS_DESKTOP,
bucket=settings.KINTO_BUCKET,
if_not_exists=True,
)
self.assertTrue(
NimbusChangeLog.objects.filter(
experiment=self.experiment,
experiment=experiment,
changed_by__email=settings.KINTO_DEFAULT_CHANGELOG_USER,
old_status=NimbusExperiment.Status.DRAFT,
new_status=NimbusExperiment.Status.ACCEPTED,
).exists()
)
def test_push_experiment_to_kinto_sends_fenix__experiment_data(self):
experiment = NimbusExperimentFactory.create_with_status(
NimbusExperiment.Status.DRAFT,
application=NimbusExperiment.Application.FENIX,
)
tasks.nimbus_push_experiment_to_kinto(experiment.id)
data = NimbusExperimentSerializer(experiment).data
self.assertTrue(NimbusBucketRange.objects.filter(experiment=experiment).exists())
self.mock_kinto_client.create_record.assert_called_with(
data=data,
collection=settings.KINTO_COLLECTION_NIMBUS_MOBILE,
bucket=settings.KINTO_BUCKET,
if_not_exists=True,
)
self.assertTrue(
NimbusChangeLog.objects.filter(
experiment=experiment,
changed_by__email=settings.KINTO_DEFAULT_CHANGELOG_USER,
old_status=NimbusExperiment.Status.DRAFT,
new_status=NimbusExperiment.Status.ACCEPTED,
@ -50,10 +75,12 @@ class TestPushExperimentToKintoTask(MockKintoClientMixin, TestCase):
)
def test_push_experiment_to_kinto_reraises_exception(self):
experiment = NimbusExperimentFactory.create_with_status(
NimbusExperiment.Status.DRAFT,
)
self.mock_kinto_client.create_record.side_effect = Exception
with self.assertRaises(Exception):
tasks.nimbus_push_experiment_to_kinto(self.experiment.id)
tasks.nimbus_push_experiment_to_kinto(experiment.id)
class TestCheckKintoPushQueue(MockKintoClientMixin, TestCase):
@ -106,10 +133,12 @@ class TestCheckKintoPushQueue(MockKintoClientMixin, TestCase):
def test_check_with_reject_review(self):
experiment = NimbusExperimentFactory.create_with_status(
NimbusExperiment.Status.ACCEPTED,
application=NimbusExperiment.Application.DESKTOP,
)
self.mock_kinto_client.delete_record.return_value = {}
self.mock_kinto_client.get_collection.side_effect = [
# Desktop responses
{
"data": {
"status": KINTO_REJECTED_STATUS,
@ -117,13 +146,20 @@ class TestCheckKintoPushQueue(MockKintoClientMixin, TestCase):
}
},
{"data": {"status": "anything"}},
# Fenix responses
{"data": {"status": "anything"}},
{"data": {"status": "anything"}},
]
self.mock_kinto_client.get_records.side_effect = [
# Desktop responses
[{"id": "another-experiment"}],
[
{"id": "another-experiment"},
{"id": experiment.slug},
],
# Fenix responses
[],
[],
]
tasks.nimbus_check_kinto_push_queue()

Просмотреть файл

@ -406,10 +406,11 @@ FEATURE_ANALYSIS = config("FEATURE_ANALYSIS", default=False, cast=bool)
KINTO_HOST = config("KINTO_HOST")
KINTO_USER = config("KINTO_USER")
KINTO_PASS = config("KINTO_PASS")
KINTO_BUCKET = config("KINTO_BUCKET")
KINTO_BUCKET_MAIN = config("KINTO_BUCKET_MAIN")
KINTO_COLLECTION = config("KINTO_COLLECTION")
KINTO_COLLECTION_NIMBUS = config("KINTO_COLLECTION_NIMBUS")
KINTO_BUCKET = "main-workspace"
KINTO_BUCKET_MAIN = "main"
KINTO_COLLECTION = "messaging-experiments"
KINTO_COLLECTION_NIMBUS_DESKTOP = "nimbus-desktop-experiments"
KINTO_COLLECTION_NIMBUS_MOBILE = "nimbus-mobile-experiments"
# Jetstream GCS Bucket data