8000 feat: Staging Benches by balamurali27 · Pull Request #167 · frappe/press · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat: Staging Benches #167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Jul 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
90eb71b
feat(Server): Add staging check to doctype
balamurali27 Jun 30, 2021
accb721
refactor(TestSite): create as many test docs in create_test_bench
balamurali27 Jul 2, 2021
453a6bf
feat(DeployCandidate): Add methods to create staging deploy
balamurali27 Jul 2, 2021
a80b4be
feat(DeployCandidate): Add buttons for types of deploys
balamurali27 Jul 2, 2021
9332422
feat(DeployCandidate): Add action options for alternate deploys
balamurali27 Jul 5, 2021
773aab0
feat(Deploy): Add staging check for deploy
balamurali27 Jul 5, 2021
319d89d
fix(DeployCandidate): Get staging servers correctly
balamurali27 Jul 9, 2021
d56fa0e
feat: Add staging check to Bench & Site
balamurali27 Jul 22, 2021
e10a6bd
refactor(Server): Use classmethods for queries
balamurali27 Jul 15, 2021
58307a8
feat: Set staging fields for staging deploy & benches
balamurali27 Jul 19, 2021
53dc272
feat(DeployCandidate): Implement promote to production
balamurali27 Jul 22, 2021
37f6d99
feat(DeployCandidate): Show actions depending on `staged` field
balamurali27 Jul 22, 2021
d63cc25
fix(DeployCandidate): Allow multiple deploys of same candidate
balamurali27 Jul 22, 2021
ae7eb8c
feat(Bench): Create staging site on new staging bench
balamurali27 Jul 23, 2021
755eabe
feat(PressSettings): Add settings for staging site variables
balamurali27 Jul 23, 2021
da2a5e9
feat(StagingSite): Use staging plan for staging sites
balamurali27 Jul 23, 2021
e252fb4
fix(DeployCandidate): Set staged check correctly
balamurali27 Jul 23, 2021
7f344c3
feat: Add staged & staging check in standard filter
balamurali27 Jul 23, 2021
88f657f
fix(Bench): Import StagingSite before usage
balamurali27 Jul 23, 2021
9bed374
Merge branch 'master' into staging-bench
balamurali27 Jul 23, 2021
c29a0d5
feat(Server): Don't make staging servers exclusive for staging benches
balamurali27 Jul 23, 2021
7634cfc
fix(StagingSite): Add fallback value for staging_expiry
balamurali27 Jul 23, 2021
ca57c29
fix(StagingSite): Use expiry as hours instead of days
balamurali27 Jul 28, 2021
78df86b
feat(StagingSite): Handle staging site creation errors
balamurali27 Jul 28, 2021
88052a9
fix(Bench): Remove wrong test
balamurali27 Jul 28, 2021
384f43b
fix(StagingSite): Move StagingSite to bench.py
balamurali27 Jul 28, 2021
1e7cde1
fix(StagingSite): Use log_error correctly
balamurali27 Jul 29, 2021
9956e45
style: black + flake + isort
balamurali27 Jul 30, 2021
522ede4
fix(TestStagingSite): Update staging site creation test
balamurali27 Jul 30, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions press/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
],
"hourly_long": [
"press.press.doctype.bench.bench.archive_obsolete_benches",
"press.press.doctype.bench.bench.archive_staging_sites",
"press.press.doctype.bench.bench.scale_workers",
"press.press.doctype.site.backups.schedule",
"press.press.doctype.subscription.subscription.create_usage_records",
Expand Down
3 changes: 2 additions & 1 deletion press/press/audit.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Functions for automated audit of frappe cloud systems."""
import json
from datetime import datetime, timedelta
from press.press.doctype.server.server import Server
from typing import List

import frappe
Expand Down Expand Up @@ -32,7 +33,7 @@ class BenchFieldCheck(Audit):
audit_type = "Bench Field Check"

def __init__(self):
servers = frappe.get_all("Server", pluck="name")
servers = Server.get_all_prod()
log = {}
status = "Success"
for server in servers:
Expand Down
10 changes: 9 additions & 1 deletion press/press/doctype/bench/bench.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"database_server",
"cluster",
"column_break_3",
"staging",
"group",
"team",
"candidate",
Expand Down Expand Up @@ -175,10 +176,17 @@
"fieldtype": "Check",
"label": "Is Single Container",
"read_only": 1
},
{
"default": "0",
"fieldname": "staging",
"fieldtype": "Check",
"in_standard_filter": 1,
"label": "Staging"
}
],
"links": [],
"modified": "2021-06-07 08:14:43.416549",
"modified": "2021-07-23 10:34:04.517306",
"modified_by": "Administrator",
"module": "Press",
"name": "Bench",
Expand Down
52 changes: 49 additions & 3 deletions press/press/doctype/bench/bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
from __future__ import unicode_literals

import json
from datetime import datetime, timedelta

import frappe
from frappe.model.document import Document
from frappe.model.naming import append_number_if_name_exists
from frappe.model.naming import append_number_if_name_exists, make_autoname

from press.agent import Agent
from press.overrides import get_permission_query_conditions_for_doctype
from press.press.doctype.site.site import Site
from press.utils import log_error


Expand Down Expand Up @@ -207,8 +209,51 @@ def work_load(self) -> float:
)


class StagingSite(Site):
def __init__(self, bench: Bench):
plan = frappe.db.get_value("Press Settings", None, "staging_plan")
if not plan:
frappe.throw("Staging plan not set in settings")
log_error(title="Staging plan not set in settings")
super().__init__(
{
"doctype": "Site",
"subdomain": make_autoname("staging-.########"),
"staging": True,
"bench": bench.name,
"apps": [{"app": app.app} for app in bench.apps],
"team": "Administrator",
"subscription_plan": plan,
}
)

@classmethod
def archive_expired(cls):
expiry = frappe.db.get_value("Press Settings", None, "staging_expiry") or 24
sites = frappe.get_all(
"Site",
{"staging": True, "created_on": ("<", datetime.now() - timedelta(hours=expiry))},
)
for site_name in sites:
site = frappe.doc("Site", site_name)
site.archive()

@classmethod
def create_if_needed(cls, bench: Bench):
if not bench.staging:
return
try:
cls(bench).insert()
except Exception as e:
log_error("Staging Site creation error", exception=e)


def archive_staging_sites():
StagingSite.archive_expired()


def process_new_bench_job_update(job):
bench_status = frappe.get_value("Bench", job.bench, "status")
bench = frappe.get_doc("Bench", job.bench)

updated_status = {
"Pending": "Pending",
Expand All @@ -217,9 +262,10 @@ def process_new_bench_job_update(job):
"Failure": "Broken",
}[job.status]

if updated_status != bench_status:
if updated_status != bench.status:
frappe.db.set_value("Bench", job.bench, "status", updated_status)
if updated_status == "Active":
StagingSite.create_if_needed(bench)
frappe.enqueue(
"press.press.doctype.bench.bench.archive_obsolete_benches",
enqueue_after_commit=True,
Expand Down
23 changes: 21 additions & 2 deletions press/press/doctype/bench/test_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,31 @@
import frappe

from press.press.doctype.agent_job.agent_job import AgentJob
from press.press.doctype.bench.bench import Bench, scale_workers
from press.press.doctype.bench.bench import Bench, StagingSite, scale_workers
from press.press.doctype.plan.test_plan import create_test_plan
from press.press.doctype.site.test_site import create_test_site
from press.press.doctype.site.test_site import create_test_bench, create_test_site
from press.press.doctype.subscription.test_subscription import create_test_subscription


@patch.object(AgentJob, "after_insert", new=Mock())
class TestStagingSite(unittest.TestCase):
def tearDown(self):
frappe.db.rollback()

def test_create_staging_site(self):
bench = create_test_bench() # also creates press settings
frappe.db.set_value(
"Press Settings", None, "staging_plan", create_test_plan("Site").name
)
count_before = frappe.db.count("Site")

site = StagingSite(bench).insert()

self.assertTrue(site.staging)
count_after = frappe.db.count("Site")
self.assertEqual(count_after - count_before, 1)


@patch.object(AgentJob, "after_insert", new=Mock())
class TestBench(unittest.TestCase):
def setUp(self):
Expand Down
10 changes: 9 additions & 1 deletion press/press/doctype/deploy/deploy.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"field_order": [
"group",
"team",
"staging",
"column_break_2",
"candidate",
"section_break_5",
Expand Down Expand Up @@ -57,10 +58,17 @@
"options": "Team",
"read_only": 1,
"reqd": 1
},
{
"default": "0",
"fieldname": "staging",
"fieldtype": "Check",
"in_standard_filter": 1,
"label": "Staging"
}
],
"links": [],
"modified": "2021-02-15 11:16:56.826807",
"modified": "2021-07-23 10:34:15.747070",
"modified_by": "Administrator",
"module": "Press",
"name": "Deploy",
Expand Down
1 change: 1 addition & 0 deletions press/press/doctype/deploy/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def create_benches(self):
"group": self.group,
"candidate": self.candidate,
"workers": 1,
"staging": self.staging,
}
).insert()
bench.bench = new.name
Expand Down
19 changes: 11 additions & 8 deletions press/press/doctype/deploy_candidate/deploy_candidate.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@ frappe.ui.form.on('Deploy Candidate', {
};

[
[__('Build'), 'build'],
[__('Build and Deploy'), 'build_and_deploy']
].forEach(([label, method]) => {
frm.add_custom_button(
label,
() => { frm.call(method).then((r) => frm.refresh()) },
__('Actions')
);
[__('Build'), 'build', true],
[__('Deploy to Staging'), 'deploy_to_staging', true],
[__('Promote to Production'), 'promote_to_production', frm.doc.staged],
[__('Deploy to Production (build and deploy)'), 'deploy_to_production', true],
].forEach(([label, method, show]) => {
if (show)
frm.add_custom_button(
label,
() => { frm.call(method).then((r) => frm.refresh()) },
__('Actions')
);
});

}
Expand Down
11 changes: 10 additions & 1 deletion press/press/doctype/deploy_candidate/deploy_candidate.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"field_order": [
"status",
"is_single_container",
"staged",
"column_break_2",
"group",
"team",
Expand Down Expand Up @@ -171,10 +172,18 @@
"fieldtype": "Check",
"label": "Is Single Container",
"read_only": 1
},
{
"default": "0",
"fieldname": "staged",
"fieldtype": "Check",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Staged"
}
],
"links": [],
"modified": "2021-06-07 08:14:52.486387",
"modified": "2021-07-23 10:34:45.618253",
"modified_by": "Administrator",
"module": "Press",
"name": "Deploy Candidate",
Expand Down
95 changes: 65 additions & 30 deletions press/press/doctype/deploy_candidate/deploy_candidate.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@
import shutil
import subprocess
from subprocess import Popen
from typing import List

# import json

import docker
import dockerfile
import frappe
from frappe.model.document import Document
from frappe.utils import now_datetime as now
from press.utils import log_error
from frappe.core.utils import find
import docker
from frappe.model.document import Document
from frappe.model.naming import make_autoname
from frappe.utils import now_datetime as now

from press.overrides import get_permission_query_conditions_for_doctype
from press.utils import get_current_team
from press.press.doctype.server.server import Server
from press.utils import get_current_team, log_error

# import json


class DeployCandidate(Document):
Expand Down Expand Up @@ -53,7 +55,21 @@ def build(self):
frappe.db.commit()

@frappe.whitelist()
def build_and_deploy(self):
def deploy_to_staging(self):
"""Deploy a bench on staging server and also create a staging site."""
self.build_and_deploy(staging=True)

@frappe.whitelist()
def promote_to_production(self):
if not self.staged:
frappe.throw("Cannot promote unstaged candidate to production")
self._deploy()

@frappe.whitelist()
def deploy_to_production(self):
self.build_and_deploy()

def build_and_deploy(self, staging: bool = False):
self.status = "Pending"
self.add_build_steps()
self.save()
Expand All @@ -64,18 +80,27 @@ def build_and_deploy(self):
)
frappe.set_user(team)
frappe.enqueue_doc(
self.doctype, self.name, "_build_and_deploy", timeout=1200, enqueue_after_commit=True
self.doctype,
self.name,
"_build_and_deploy",
timeout=1200,
enqueue_after_commit=True,
staging=staging,
)
frappe.set_user(user)
frappe.session.data = session_data
frappe.db.commit()

def _build_and_deploy(self):
def _build_and_deploy(self, staging: bool):
self._build()
self._deploy()
self._deploy(staging)

def _deploy(self):
self.create_deploy()
@frappe.whitelist()
def _deploy(self, staging=False):
try:
self.create_deploy(staging)
except Exception:
log_error("Deploy Creation Error", candidate=self.name)

def _build(self):
self.status = "Running"
Expand Down Expand Up @@ -379,27 +404,37 @@ def _push_docker_image(self):
frappe.db.commit()
raise

def create_deploy(self):
try:
def create_deploy(self, staging: bool):
deploy_doc = None
if staging:
servers = [Server.get_one_staging()]
if not servers:
frappe.log_error(title="Staging Server for new benches not found")
else:
servers = frappe.get_doc("Release Group", self.group).servers
servers = [server.server for server in servers]
deploy_doc = frappe.db.exists(
"Deploy", {"group": self.group, "candidate": self.name}
"Deploy", {"group": self.group, "candidate": self.name, "staging": False}
)
servers = frappe.get_doc("Release Group", self.group).servers

if deploy_doc or not servers:
return
if deploy_doc or not servers:
return

deploy_doc = frappe.get_doc(
{
"doctype": "Deploy",
"group": self.group,
"candidate": self.name,
"benches": [{"server": server.server} for server in servers],
}
)
deploy_doc.insert()
except Exception:
log_error("Deploy Creation Error", candidate=self.name)
return self._create_deploy(servers, staging)

def _create_deploy(self, servers: List[str], staging):
deploy = frappe.get_doc(
{
"doctype": "Deploy",
"group": self.group,
"candidate": self.name,
"benches": [{"server": server} for server in servers],
"staging": staging,
}
).insert()
if staging:
self.db_set("staged", True)
return deploy

def on_update(self):
if self.status == "Running":
Expand Down
Loading
0