Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: NixOS/nixops
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0c467cda1eca
Choose a base ref
...
head repository: NixOS/nixops
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 958af5ef0015
Choose a head ref
  • 5 commits
  • 4 files changed
  • 1 contributor

Commits on Sep 3, 2020

  1. Diff: Explicitly type depl argument

    grahamc authored and gilligan committed Sep 3, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6e96029 View commit details
  2. Copy the full SHA
    d659af2 View commit details
  3. ResourceState: give a run-time-accessible version of the Definition, …

    …use it for the DiffState to get a definition
    grahamc authored and gilligan committed Sep 3, 2020
    Copy the full SHA
    d9d0a77 View commit details

Commits on Sep 4, 2020

  1. Copy the full SHA
    2f601e6 View commit details

Commits on Sep 9, 2020

  1. Merge pull request #1388 from grahamc/defntype

    ResourceState.definition_type: introduce for run-time accessible definition type validation
    grahamc authored Sep 9, 2020
    Copy the full SHA
    958af5e View commit details
Showing with 78 additions and 33 deletions.
  1. +1 −3 nixops/backends/__init__.py
  2. +21 −0 nixops/deployment.py
  3. +30 −13 nixops/diff.py
  4. +26 −17 nixops/resources/__init__.py
4 changes: 1 addition & 3 deletions nixops/backends/__init__.py
Original file line number Diff line number Diff line change
@@ -66,9 +66,7 @@ def __init__(self, name: str, config: nixops.resources.ResourceEval):
self.provision_ssh_key = config["provisionSSHKey"]


MachineDefinitionType = TypeVar(
"MachineDefinitionType", bound="MachineDefinition", contravariant=True
)
MachineDefinitionType = TypeVar("MachineDefinitionType", bound="MachineDefinition")


@runtime_checkable
21 changes: 21 additions & 0 deletions nixops/deployment.py
Original file line number Diff line number Diff line change
@@ -65,6 +65,7 @@ class UnknownBackend(Exception):
NixosConfigurationType = List[Dict[Tuple[str, ...], Any]]

TypedResource = TypeVar("TypedResource")
TypedDefinition = TypeVar("TypedDefinition")


class Deployment:
@@ -196,6 +197,26 @@ def get_typed_resource(
raise ValueError(f"{res} not of type {type}")
return res

def get_generic_definition(
self, name: str, type_name: str
) -> nixops.resources.ResourceDefinition:
defn = self._definitions().get(name, None)
if not defn:
raise Exception("definition ‘{0}’ does not exist".format(name))
if defn.get_type() != type_name:
raise Exception(
"definition ‘{0}’ is not of type ‘{1}’".format(name, type_name)
)
return defn

def get_typed_definition(
self, name: str, type_name: str, type: Type[TypedDefinition]
) -> TypedDefinition:
defn = self.get_generic_definition(name, type_name)
if not isinstance(defn, type):
raise ValueError(f"{defn} not of type {type}")
return defn

def get_machine(self, name: str, type: Type[TypedResource]) -> TypedResource:
m = self.get_generic_machine(name)
if not isinstance(m, type):
43 changes: 30 additions & 13 deletions nixops/diff.py
Original file line number Diff line number Diff line change
@@ -2,10 +2,29 @@

import itertools

from typing import Any, AnyStr, Callable, Dict, List, Optional, Tuple
from typing import (
Any,
AnyStr,
Callable,
Dict,
List,
Optional,
Tuple,
Generic,
TypeVar,
TYPE_CHECKING,
)
from nixops.logger import MachineLogger
from nixops.state import StateDict

if TYPE_CHECKING:
import nixops.deployment

# Note: redefined to avoid import loops
ResourceDefinitionType = TypeVar(
"ResourceDefinitionType", bound="nixops.resources.ResourceDefinition"
)


class Handler:
def __init__(
@@ -39,7 +58,7 @@ def get_keys(self, *_: AnyStr) -> List[str]:
return self._keys


class Diff:
class Diff(Generic[ResourceDefinitionType]):
"""
Diff engine main class which implements methods for doing diffs between
the state/config and generating a plan: sequence of handlers to be executed.
@@ -51,18 +70,14 @@ class Diff:

def __init__(
self,
# FIXME: type should be 'nixops.deployment.Deployment'
# however we have to upgrade to python3 in order
# to solve the import cycle by forward declaration
depl: Any,
depl: nixops.deployment.Deployment,
logger: MachineLogger,
config: Dict[str, Any],
defn: ResourceDefinitionType,
state: StateDict,
res_type: str,
):
# type: (...) -> None
) -> None:
self.handlers: List[Handler] = []
self._definition = config
self._definition = defn.resource_eval
self._state = state
self._depl = depl
self._type = res_type
@@ -131,8 +146,8 @@ def topological_sort(self, handlers: List[Handler]) -> List[Handler]:
dependencies.
"""
# TODO implement cycle detection
parent = {} # type: Dict[Handler, Optional[Handler]]
sequence = [] # type: List[Handler]
parent: Dict[Handler, Optional[Handler]] = {}
sequence: List[Handler] = []

def visit(handler: Handler) -> None:
for v in handler.get_deps():
@@ -194,7 +209,9 @@ def retrieve_def(d):
try:
d = getattr(res, k)
except AttributeError:
d = res._state[k]
# TODO res._state is private and should not be reached in to.
# Make sure nixops-aws still works when fixing this.
d = res._state[k] # type: ignore
return d

d = self._definition.get(key, None)
43 changes: 26 additions & 17 deletions nixops/resources/__init__.py
Original file line number Diff line number Diff line change
@@ -4,8 +4,8 @@
import re
import nixops.util
from threading import Event
from typing import List, Optional, Dict, Any, TypeVar, Union, TYPE_CHECKING
from nixops.monkey import Protocol
from typing import List, Optional, Dict, Any, Type, TypeVar, Union, TYPE_CHECKING
from nixops.monkey import Protocol, runtime_checkable
from nixops.state import StateDict, RecordId
from nixops.diff import Diff, Handler
from nixops.util import ImmutableMapping, ImmutableValidatedObject
@@ -70,14 +70,15 @@ def show_type(self) -> str:
return self.get_type()


ResourceDefinitionType = TypeVar(
"ResourceDefinitionType", bound="ResourceDefinition", contravariant=True
)
ResourceDefinitionType = TypeVar("ResourceDefinitionType", bound="ResourceDefinition")


@runtime_checkable
class ResourceState(Protocol[ResourceDefinitionType]):
"""Base class for NixOps resource state objects."""

definition_type: Type[ResourceDefinitionType]

name: str

@classmethod
@@ -329,28 +330,37 @@ def next_charge_time(self):
return None


class DiffEngineResourceState(ResourceState):
@runtime_checkable
class DiffEngineResourceState(
ResourceState[ResourceDefinitionType], Protocol[ResourceDefinitionType]
):
_reserved_keys: List[str] = []
_state: StateDict

def __init__(self, depl, name, id):
nixops.resources.ResourceState.__init__(self, depl, name, id)
self._state = StateDict(depl, id)

def create(self, defn, check, allow_reboot, allow_recreate):
def create(
self,
defn: ResourceDefinitionType,
check: bool,
allow_reboot: bool,
allow_recreate: bool,
):
# if --check is true check against the api and update the state
# before firing up the diff engine in order to get the needed
# handlers calls
if check:
self._check()
diff_engine = self.setup_diff_engine(defn.config)
diff_engine = self.setup_diff_engine(defn)

for handler in diff_engine.plan():
handler.handle(allow_recreate)

def plan(self, defn):
def plan(self, defn: ResourceDefinitionType):
if hasattr(self, "_state"):
diff_engine = self.setup_diff_engine(defn.config)
diff_engine = self.setup_diff_engine(defn)
diff_engine.plan(show=True)
else:
self.warn(
@@ -359,11 +369,11 @@ def plan(self, defn):
)
)

def setup_diff_engine(self, config):
def setup_diff_engine(self, defn: ResourceDefinitionType):
diff_engine = Diff(
depl=self.depl,
logger=self.logger,
config=dict(config),
defn=defn,
state=self._state,
res_type=self.get_type(),
)
@@ -376,11 +386,10 @@ def get_handlers(self):
getattr(self, h) for h in dir(self) if isinstance(getattr(self, h), Handler)
]

def get_defn(self):
if self.depl.definitions is not None and self.name in self.depl.definitions:
return self.depl.definitions[self.name].config
else:
return {}
def get_defn(self) -> ResourceDefinitionType:
return self.depl.get_typed_definition(
self.name, self.get_type(), self.definition_type
)


GenericResourceState = ResourceState[ResourceDefinition]