-
Notifications
You must be signed in to change notification settings - Fork 1
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
[FC-20956] batou_ext.fcio: Add components that set an RG in maintenance for the time of the deployment #213
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
- Added `batou_ext.fcio.Maintenance{Start,End}`: with these components it's possible to mark the RG | ||
as "in maintenance". Components can be scheduled between these two by doing | ||
`self.provide("needs-maintenance", self)`. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -138,6 +138,19 @@ def _check_aliases(self): | |
return error, results | ||
|
||
|
||
API_URL = "https://{project}:{api_key}@api.flyingcircus.io/v1" | ||
|
||
|
||
def create_xmlrpc_client(environment: batou.environment.Environment): | ||
rg_name = environment.overrides["provision"]["project"] | ||
api_key = environment.overrides["provision"]["api_key"] | ||
api_url = environment.overrides["provision"].get("api_url", API_URL) | ||
api = xmlrpc.client.ServerProxy( | ||
api_url.format(project=rg_name, api_key=api_key) | ||
) | ||
return api | ||
|
||
|
||
class Provision(batou.component.Component): | ||
"""FCIO provisioning component. | ||
|
||
|
@@ -151,7 +164,6 @@ class Provision(batou.component.Component): | |
location = "rzob" | ||
vm_environment_class = "NixOS" | ||
vm_environment = None | ||
api_url = "https://{project}:{api_key}@api.flyingcircus.io/v1" | ||
|
||
# Passed by the CLI runner, not meant to be set via environment: | ||
env_name: str = None | ||
|
@@ -171,15 +183,7 @@ def load_env(self): | |
return environment | ||
|
||
def get_api(self): | ||
rg_name = self.environment_.overrides["provision"]["project"] | ||
api_key = self.environment_.overrides["provision"]["api_key"] | ||
api_url = self.environment_.overrides["provision"].get("api_url") | ||
if not api_url: | ||
api_url = self.api_url | ||
api = xmlrpc.client.ServerProxy( | ||
api_url.format(project=rg_name, api_key=api_key) | ||
) | ||
return api | ||
return create_xmlrpc_client(self.environment_) | ||
|
||
def get_currently_provisioned_vms(self): | ||
return self.api.query("virtualmachine") | ||
|
@@ -305,6 +309,69 @@ def get_diff(self, old, new): | |
return result | ||
|
||
|
||
class DirectoryXMLRPC(batou.component.Component): | ||
def configure(self): | ||
self.xmlrpc = create_xmlrpc_client(self.environment) | ||
self.provide("directory-xmlrpc", self.xmlrpc) | ||
|
||
|
||
class MaintenanceStart(batou.component.Component): | ||
def configure(self): | ||
self.require("needs-maintenance", strict=False, reverse=True) | ||
self.xmlrpc = self.require_one("directory-xmlrpc") | ||
|
||
def verify(self): | ||
raise batou.UpdateNeeded() | ||
|
||
def update(self): | ||
change_maintenance_state( | ||
self.xmlrpc, self.host.name, desired_state=True | ||
) | ||
|
||
|
||
class MaintenanceEnd(batou.component.Component): | ||
def configure(self): | ||
self.require("needs-maintenance", strict=False) | ||
self.xmlrpc = self.require_one("directory-xmlrpc") | ||
|
||
def verify(self): | ||
raise batou.UpdateNeeded() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still have bad feelings about this: every deployment with no other change will set the RG to maintenance for a short time with this. Unfortunately I don't have a better idea after reading through batou's sources: when Ideas? Or is this good enough? |
||
|
||
def update(self): | ||
change_maintenance_state( | ||
self.xmlrpc, self.host.name, desired_state=False | ||
) | ||
|
||
|
||
def change_maintenance_state( | ||
xmlrpc, host_name, desired_state, predict_only=False | ||
): | ||
rg_name = host_name[:-2] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I effectively assume here that each host in a deployment is in the same RG. |
||
rg = next( | ||
(rg for rg in xmlrpc.query("resourcegroup") if rg["name"] == rg_name), | ||
None, | ||
) | ||
if rg is None: | ||
raise ValueError( | ||
f"Cannot change maintenance state of RG '{rg_name}', not in list of RGs modifyable with the xmlrpc API token." | ||
) | ||
|
||
if desired_state == rg["in_maintenance"]: | ||
raise ValueError( | ||
f"Maintenance state of RG '{rg_name}' is already '{desired_state}'!" | ||
) | ||
|
||
xmlrpc.apply( | ||
[ | ||
{ | ||
"__type__": "resourcegroup", | ||
"in_maintenance": desired_state, | ||
"name": rg_name, | ||
} | ||
] | ||
) | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser() | ||
subparsers = parser.add_subparsers() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has the downside that setting it up is a little verbose: you have to add three additional components.
We may be able to cut it down to two by removing the
DirectoryXMLRPC
and setting up the client in each component.Another idea I had was to use a decorator which injects
Start
endEnd
into a component (and calls the component'sconfigure
in between), but this has the downside that the maintenance is left when the component itself is updated. I.e.Thoughts?