Skip to content
This repository has been archived by the owner on Jun 11, 2023. It is now read-only.

Commit

Permalink
Add Henforcers for MotherboardUpdateRequest
Browse files Browse the repository at this point in the history
  • Loading branch information
renatomassaro committed Dec 12, 2017
1 parent 43e5c58 commit 6629945
Show file tree
Hide file tree
Showing 9 changed files with 615 additions and 3 deletions.
59 changes: 59 additions & 0 deletions lib/entity/henforcer/entity.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ defmodule Helix.Entity.Henforcer.Entity do

import Helix.Henforcer

alias Helix.Network.Query.Network, as: NetworkQuery
alias Helix.Server.Model.Component
alias Helix.Server.Model.Server
alias Helix.Server.Henforcer.Component, as: ComponentHenforcer
alias Helix.Server.Henforcer.Server, as: ServerHenforcer
alias Helix.Server.Query.Component, as: ComponentQuery
alias Helix.Entity.Model.Entity
alias Helix.Entity.Query.Entity, as: EntityQuery

Expand Down Expand Up @@ -63,4 +67,59 @@ defmodule Helix.Entity.Henforcer.Entity do
end
|> wrap_relay(%{entity: entity, server: server})
end

def owns_component?(entity_id = %Entity.ID{}, component, owned) do
henforce entity_exists?(entity_id) do
owns_component?(relay.entity, component, owned)
end
end

def owns_component?(entity, component_id = %Component.ID{}, owned) do
henforce(ComponentHenforcer.component_exists?(component_id)) do
owns_component?(entity, relay.component, owned)
end
end

def owns_component?(entity = %Entity{}, component = %Component{}, nil) do
owned_components =
entity
|> EntityQuery.get_components()
|> Enum.map(&(ComponentQuery.fetch(&1.component_id)))

owns_component?(entity, component, owned_components)
end

def owns_component?(entity = %Entity{}, component = %Component{}, owned) do
if component in owned do
reply_ok()
else
reply_error({:component, :not_belongs})
end
|> wrap_relay(
%{entity: entity, component: component, owned_components: owned}
)
end

def owns_nip?(entity_id = %Entity.ID{}, network_id, ip, owned) do
henforce entity_exists?(entity_id) do
owns_nip?(relay.entity, network_id, ip, owned)
end
end

def owns_nip?(entity = %Entity{}, network_id, ip, nil) do
owned_nips = NetworkQuery.Connection.get_by_entity(entity.entity_id)

owns_nip?(entity, network_id, ip, owned_nips)
end

def owns_nip?(entity = %Entity{}, network_id, ip, owned) do
nc = Enum.find(owned, &(&1.network_id == network_id and &1.ip == ip))

if nc do
reply_ok(%{network_connection: nc})
else
reply_error({:network_connection, :not_belongs})
end
|> wrap_relay(%{entity_network_connections: owned})
end
end
152 changes: 152 additions & 0 deletions lib/server/henforcer/component.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
defmodule Helix.Server.Henforcer.Component do

import Helix.Henforcer

alias Helix.Entity.Henforcer.Entity, as: EntityHenforcer
alias Helix.Network.Query.Network, as: NetworkQuery
alias Helix.Server.Model.Component
alias Helix.Server.Model.Motherboard
alias Helix.Server.Query.Component, as: ComponentQuery

@internet_id NetworkQuery.internet().network_id

def component_exists?(component_id = %Component.ID{}) do
with component = %Component{} <- ComponentQuery.fetch(component_id) do
reply_ok(%{component: component})
else
_ ->
reply_error({:component, :not_found})
end
end

def is_motherboard?(component = %Component{type: :mobo}),
do: reply_ok(%{component: component})
def is_motherboard?(%Component{}),
do: reply_error({:component, :not_motherboard})
def is_motherboard?(component_id = %Component.ID{}) do
henforce(component_exists?(component_id)) do
is_motherboard?(relay.component)
end
end

def can_link?(
mobo = %Component{type: :mobo},
component = %Component{},
slot_id)
do
with :ok <- Motherboard.check_compatibility(mobo, component, slot_id, []) do
reply_ok()
else
{:error, reason} ->
reply_error({:motherboard, reason})
end
end

def has_initial_components?(components) do
if Motherboard.has_required_initial_components?(components) do
reply_ok()
else
reply_error({:motherboard, :missing_initial_components})
end
end

def has_public_nip?(network_connections) do
if Enum.find(network_connections, &(&1.network_id == @internet_id)) do
reply_ok()
else
reply_error({:motherboard, :missing_public_nip})
end
end

def can_update_mobo?(entity_id, mobo_id, components, network_connections) do
reduce_components = fn mobo ->
init = {{true, %{}}, nil}

components
|> Enum.reduce_while(init, fn {slot_id, comp_id}, {{true, acc}, cache} ->
with \
{true, r1} <- component_exists?(comp_id),
component = r1.component,

{true, r2} <-
EntityHenforcer.owns_component?(entity_id, component, cache),

{true, _} <- can_link?(mobo, component, slot_id)
do
acc_components = Map.get(acc, :components, [])

new_acc =
acc
|> put_in([:components], acc_components ++ [{component, slot_id}])
|> put_in([:entity], r2.entity)
|> put_in([:owned_components], r2.owned_components)

{:cont, {{true, new_acc}, r2.owned_components}}
else
error ->
{:halt, {error, cache}}
end
end)
|> elem(0)
end

reduce_network_connections = fn entity, components ->
init = {{true, %{}}, nil}

network_connections
|> Enum.reduce_while(init, fn {_nic_id, nip}, {{true, acc}, cache} ->
{network_id, ip} = nip

with \
{true, r1} <- EntityHenforcer.owns_nip?(entity, network_id, ip, cache)
do
acc_nc = Map.get(acc, :network_connections, [])

new_acc =
acc
|> put_in([:network_connections], acc_nc ++ [r1.network_connection])
|> put_in(
[:entity_network_connections], r1.entity_network_connections
)

{:cont, {{true, new_acc}, r1.entity_network_connections}}
else
error ->
{:halt, {error, cache}}
end
end)
|> elem(0)
end

with \
{true, r0} <- component_exists?(mobo_id),
mobo = r0.component,

# Make sure user is plugging components into a motherboard
{true, _} <- is_motherboard?(mobo),

# Iterate over components/slots and make the required henforcements
{true, r1} <- reduce_components.(mobo),
components = r1.components,
entity = r1.entity,
owned = r1.owned_components,

# Ensure mobo belongs to entity
{true, _} <- EntityHenforcer.owns_component?(entity, mobo, owned),

# Ensure all required initial components are there
{true, _} <- has_initial_components?(components),

# Iterate over NetworkConnections and make the required henforcements
{true, r2} <- reduce_network_connections.(entity, components),

# The mobo must have at least one public NC assigned to it
{true, _} <- has_public_nip?(r2.network_connections)
do
reply_ok(relay([r1, r2]))
else
error ->
error
end
end
end
1 change: 1 addition & 0 deletions lib/server/henforcer/server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ defmodule Helix.Server.Henforcer.Server do
@doc """
Ensures the requested server exists on the database.
"""
# TODO: REVIEW: Why does it accept `Server.t` as input?
def server_exists?(server = %Server{}),
do: server_exists?(server.server_id)
def server_exists?(server_id = %Server.ID{}) do
Expand Down
2 changes: 1 addition & 1 deletion lib/server/model/motherboard.ex
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ defmodule Helix.Server.Model.Motherboard do
| {:error, :wrong_slot_type}
| {:error, :slot_in_use}
| {:error, :bad_slot}
defp check_compatibility(
def check_compatibility(
mobo = %Component{},
component = %Component{},
slot_id,
Expand Down
2 changes: 1 addition & 1 deletion lib/server/websocket/requests/motherboard_update.ex
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ request Helix.Server.Websocket.Requests.MotherboardUpdate do
{:ok, ip} = IPv4.cast(nc["ip"])
{:ok, network_id} = Network.ID.cast(nc["network_id"])

{nic_id, {ip, network_id}}
{nic_id, {network_id, ip}}
end)

{:ok, ncs}
Expand Down
64 changes: 64 additions & 0 deletions test/entity/henforcer/entity_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ defmodule Helix.Entity.Henforcer.EntityTest do
alias Helix.Entity.Model.Entity
alias Helix.Entity.Henforcer.Entity, as: EntityHenforcer

alias Helix.Test.Network.Helper, as: NetworkHelper
alias Helix.Test.Server.Component.Setup, as: ComponentSetup
alias Helix.Test.Server.Helper, as: ServerHelper
alias Helix.Test.Server.Setup, as: ServerSetup
alias Helix.Test.Entity.Setup, as: EntitySetup

@internet_id NetworkHelper.internet_id()

describe "entity_exists?/1" do
test "accepts when entity exists" do
{entity, _} = EntitySetup.entity()
Expand Down Expand Up @@ -50,4 +55,63 @@ defmodule Helix.Entity.Henforcer.EntityTest do
assert reason == {:server, :not_belongs}
end
end

describe "owns_component?/3" do
test "accepts when entity owns the component" do
{server, %{entity: entity}} = ServerSetup.server()

assert {true, relay} =
EntityHenforcer.owns_component?(
entity.entity_id, server.motherboard_id, nil
)

assert relay.component.component_id == server.motherboard_id
assert relay.entity == entity
assert length(relay.owned_components) >= 4

assert_relay relay, [:component, :entity, :owned_components]
end

test "rejects when entity does not own the component" do
{entity, _} = EntitySetup.entity()
{component, _} = ComponentSetup.component()

assert {false, reason, relay} =
EntityHenforcer.owns_component?(entity, component, nil)

assert reason == {:component, :not_belongs}

assert_relay relay, [:component, :entity, :owned_components]
end
end

describe "owns_nip?/4" do
test "accepts when entity owns the nip" do
{server, %{entity: entity}} = ServerSetup.server()

%{ip: ip, network_id: network_id} = ServerHelper.get_nip(server)

assert {true, relay} =
EntityHenforcer.owns_nip?(entity.entity_id, network_id, ip, nil)

assert relay.entity == entity
assert relay.network_connection.network_id == network_id
assert relay.network_connection.ip == ip
assert length(relay.entity_network_connections) == 1

assert_relay relay,
[:entity, :network_connection, :entity_network_connections]
end

test "rejects when entity doesn't own the nip" do
{entity, _} = EntitySetup.entity()

assert {false, reason, _} =
EntityHenforcer.owns_nip?(
entity.entity_id, @internet_id, "1.2.3.4", nil
)

assert reason == {:network_connection, :not_belongs}
end
end
end
Loading

0 comments on commit 6629945

Please sign in to comment.