Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 5 additions & 5 deletions shard.lock
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ shards:

neuroplastic:
git: https://github.com/spider-gazelle/neuroplastic.git
version: 1.13.1
version: 1.14.2

office365:
git: https://github.com/placeos/office365.git
Expand Down Expand Up @@ -179,15 +179,15 @@ shards:

placeos-driver:
git: https://github.com/placeos/driver.git
version: 7.19.0
version: 7.21.1

placeos-log-backend:
git: https://github.com/place-labs/log-backend.git
version: 0.13.0

placeos-models:
git: https://github.com/placeos/models.git
version: 9.85.0
version: 9.86.2

placeos-resource:
git: https://github.com/place-labs/resource.git
Expand All @@ -207,11 +207,11 @@ shards:

redis-cluster:
git: https://github.com/place-labs/redis-cluster.cr.git
version: 0.8.10
version: 0.8.11

redis_service_manager:
git: https://github.com/place-labs/redis_service_manager.git
version: 3.3.0
version: 3.3.1

rendezvous-hash:
git: https://github.com/caspiano/rendezvous-hash.git
Expand Down
43 changes: 37 additions & 6 deletions spec/api/chaos_spec.cr
Original file line number Diff line number Diff line change
@@ -1,12 +1,43 @@
require "../helper"

# Testing pattern for controllers as follows..
# - Create the controller instance, with intended mocks
# - Create the call instance
# - Check the outcome of the request

module PlaceOS::Core
describe Api::Chaos, tags: "api" do
pending "chaos/terminate"
client = AC::SpecHelper.client
namespace = Api::Chaos::NAMESPACE[0]

after_each do
Services.reset
end

it "chaos/terminate" do
ProcessManager.with_driver do |mod, _driver_path, driver_key, _driver|
module_manager = module_manager_mock
Services.module_manager = module_manager

module_manager.load_module(mod)
pid = module_manager.local_processes.protocol_manager_by_driver?(driver_key).try(&.pid).not_nil!
Process.exists?(pid).should be_true

response = client.post("#{namespace}terminate?path=#{driver_key}")
response.status_code.should eq 200

success = Channel(Nil).new
spawn do
while Process.exists?(pid)
sleep 50.milliseconds
end
success.send nil
end

select
when success.receive
Process.exists?(pid).should be_false
when timeout 2.seconds
raise "timeout waiting for driver terminate"
end
ensure
module_manager.try &.stop
end
end
end
end
29 changes: 13 additions & 16 deletions spec/api/command_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@ module PlaceOS::Core::Api
used_for_place_testing: [] of String,
}.to_json

# allow injecting mock manager during testing
class Command
class_property mock_module_manager : ModuleManager? = nil
property module_manager : ModuleManager { @@mock_module_manager || ModuleManager.instance }
end

describe Command, tags: "api" do
client = AC::SpecHelper.client

Expand All @@ -25,15 +19,16 @@ module PlaceOS::Core::Api
"Content-Type" => "application/json",
}

after_each { Command.mock_module_manager = nil }
after_each { Services.reset }

describe "command/:module_id/execute" do
it "executes a command on a running module" do
_, _, mod, resource_manager = create_resources
mod_id = mod.id.as(String)
module_manager = module_manager_mock
module_manager.load_module(mod)
Command.mock_module_manager = module_manager
Services.module_manager = module_manager
Services.resource_manager = resource_manager

route = File.join(namespace, mod_id, "execute")
response = client.post(route, headers: json_headers, body: EXEC_PAYLOAD)
Expand All @@ -57,7 +52,8 @@ module PlaceOS::Core::Api
module_manager = module_manager_mock
# Register as lazy (don't spawn driver)
module_manager.load_module(mod)
Command.mock_module_manager = module_manager
Services.module_manager = module_manager
Services.resource_manager = resource_manager

# Verify driver is not spawned
module_manager.local_processes.module_loaded?(mod_id).should be_false
Expand All @@ -80,7 +76,7 @@ module PlaceOS::Core::Api

# Don't load the module, but it's not lazy either
module_manager = module_manager_mock
Command.mock_module_manager = module_manager
Services.module_manager = module_manager

route = File.join(namespace, mod_id, "execute")
response = client.post(route, headers: json_headers, body: EXEC_PAYLOAD)
Expand All @@ -100,7 +96,8 @@ module PlaceOS::Core::Api

# Load module
module_manager.load_module(mod)
Command.mock_module_manager = module_manager
Services.module_manager = module_manager
Services.resource_manager = resource_manager

# Create Command controller context
route = File.join(namespace, mod_id, "debugger")
Expand All @@ -117,7 +114,7 @@ module PlaceOS::Core::Api
message_channel.close
raise e
end
Fiber.yield
sleep 100.milliseconds

# Create an execute request
route = File.join(namespace, mod_id, "execute")
Expand All @@ -126,12 +123,14 @@ module PlaceOS::Core::Api

# Wait for messages on the debugger
messages = [] of String
2.times do
deadline = Time.instant + 5.seconds
until Time.instant >= deadline
select
when message = message_channel.receive
messages << message
break if message == %([1,"this will be propagated to backoffice!"])
when timeout 2.seconds
break
next
end
end

Expand All @@ -140,7 +139,5 @@ module PlaceOS::Core::Api
resource_manager.try &.stop
end
end

pending "command/debugger"
end
end
86 changes: 86 additions & 0 deletions spec/api/edge_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
require "../helper"

module PlaceOS::Core::Api
describe Edge, tags: "api" do
client = AC::SpecHelper.client

namespace = Edge::NAMESPACE[0]
json_headers = HTTP::Headers{
"Content-Type" => "application/json",
}

it "returns desired state snapshots for an edge" do
_, _, mod = setup(role: PlaceOS::Model::Driver::Role::Service)
resource_manager = PlaceOS::Core::ResourceManager.new(testing: true)
resource_manager.start { }
edge = PlaceOS::Model::Generator.edge.save!
mod.edge_id = edge.id.as(String)
mod.running = true
mod.save!

route = File.join(namespace, edge.id.as(String), "desired_state")
response = client.get(route, headers: json_headers)
response.status_code.should eq 200

snapshot = PlaceOS::Edge::State::Snapshot.from_json(response.body)
snapshot.edge_id.should eq edge.id
snapshot.modules.map(&.module_id).should contain(mod.id.as(String))
snapshot.drivers.should_not be_empty
ensure
resource_manager.try &.stop
end

it "returns not modified when the desired state is stale" do
_, _, mod = setup(role: PlaceOS::Model::Driver::Role::Service)
resource_manager = PlaceOS::Core::ResourceManager.new(testing: true)
resource_manager.start { }
edge = PlaceOS::Model::Generator.edge.save!
mod.edge_id = edge.id.as(String)
mod.running = true
mod.save!

route = File.join(namespace, edge.id.as(String), "desired_state")
first = client.get(route, headers: json_headers)
first.status_code.should eq 200
snapshot = PlaceOS::Edge::State::Snapshot.from_json(first.body)

headers = json_headers.dup
headers["If-Modified-Since"] = HTTP.format_time(snapshot.last_modified)
second = client.get(route, headers: headers)
second.status_code.should eq 304
ensure
resource_manager.try &.stop
end

it "returns not found for an unknown edge snapshot request" do
response = client.get(File.join(namespace, "edge-missing", "desired_state"), headers: json_headers)
response.status_code.should eq 404
end

it "streams compiled driver binaries for an edge" do
_, driver, mod = setup(role: PlaceOS::Model::Driver::Role::Service)
resource_manager = PlaceOS::Core::ResourceManager.new(testing: true)
resource_manager.start { }
edge = PlaceOS::Model::Generator.edge.save!
mod.edge_id = edge.id.as(String)
mod.running = true
mod.save!

result = PlaceOS::Core::DriverResource.load(driver, PlaceOS::Core::DriverStore.new, true)
route = File.join(namespace, edge.id.as(String), "drivers", File.basename(result.path))
response = client.get(route, headers: json_headers)
response.status_code.should eq 200
response.headers["Content-Type"].should eq "application/octet-stream"
response.body.bytesize.should be > 0
ensure
resource_manager.try &.stop
end

it "returns not found when the binary key does not exist" do
edge = PlaceOS::Model::Generator.edge.save!
route = File.join(namespace, edge.id.as(String), "drivers", "drivers_missing_deadbeef_arm64")
response = client.get(route, headers: json_headers)
response.status_code.should eq 404
end
end
end
84 changes: 80 additions & 4 deletions spec/api/status_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ module PlaceOS::Core::Api
"Content-Type" => "application/json",
}

after_each do
Services.reset
end

describe "status/" do
it "renders data about node" do
_, driver, _, resource_manager = create_resources
Services.resource_manager = resource_manager

driver.reload!

Expand All @@ -27,10 +32,81 @@ module PlaceOS::Core::Api
resource_manager.try &.stop
end

pending "deletes standalone driver binary used for metadata"
end
it "returns local driver status for a running module" do
_, driver, mod, resource_manager = create_resources
module_manager = module_manager_mock
Services.module_manager = module_manager
Services.resource_manager = resource_manager
module_manager.load_module(mod)

driver_path = module_manager.store.driver_binary_path(driver.file_name, driver.commit).to_s
route = "#{namespace}driver?path=#{URI.encode_path(driver_path)}"
response = client.get(route, headers: json_headers)
response.status_code.should eq 200

status = Status::DriverStatus.from_json(response.body)
status.local.should_not be_nil
status.local.not_nil!.running.should be_true
status.edge.should be_empty
ensure
module_manager.try &.stop
resource_manager.try &.stop
end

it "returns machine load for local and edge runtimes" do
_, _, _, resource_manager = create_resources
Services.resource_manager = resource_manager
response = client.get("#{namespace}load", headers: json_headers)
response.status_code.should eq 200

load = Status::MachineLoad.from_json(response.body)
load.local.hostname.should_not be_empty
load.local.cpu_count.should be > 0
load.edge.should be_empty
ensure
resource_manager.try &.stop
end

it "returns loaded module mappings" do
_, _, mod, resource_manager = create_resources
module_manager = module_manager_mock
Services.module_manager = module_manager
Services.resource_manager = resource_manager
module_manager.load_module(mod)

response = client.get("#{namespace}loaded", headers: json_headers)
response.status_code.should eq 200

loaded = Status::LoadedModules.from_json(response.body)
loaded.local.values.flatten.should contain(mod.id.as(String))
loaded.edge.should be_empty
ensure
module_manager.try &.stop
resource_manager.try &.stop
end

it "reports persisted edge connection visibility" do
edge = PlaceOS::Model::Generator.edge.save!
edge.update_fields(
online: true,
last_seen: Time.utc
)

module_manager = module_manager_mock
Services.module_manager = module_manager
response = client.get("#{namespace}edges", headers: json_headers)
response.status_code.should eq 200

pending "status/driver"
pending "status/load"
body = Hash(String, Status::EdgeConnection).from_json(response.body)
body[edge.id.as(String)].online.should be_true
body[edge.id.as(String)].last_seen.should_not be_nil
body[edge.id.as(String)].websocket_connected.should be_false
body[edge.id.as(String)].snapshot_version.should be_nil
body[edge.id.as(String)].pending_updates.should eq 0
body[edge.id.as(String)].pending_events.should eq 0
ensure
module_manager.try &.stop
end
end
end
end
17 changes: 15 additions & 2 deletions spec/driver_manager/driver_cleanup_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,22 @@ module PlaceOS::Core
tracker = DriverCleanup::StaleProcessTracker.new(DriverStore::BINARY_PATH, REDIS_CLIENT)
stale_list = tracker.update_and_find_stale(ENV["STALE_THRESHOLD_DAYS"]?.try &.to_i || 30)
stale_list.size.should eq(0)
driver_file = Path[DriverStore::BINARY_PATH, "drivers_place_private_helper_cce023a_#{Core::ARCH}"].to_s
value = REDIS_CLIENT.hgetall(driver_file)
value = case data = REDIS_CLIENT.hgetall(driver_path)
in Hash
data.transform_keys(&.to_s).transform_values(&.to_s)
in Array
hash = {} of String => String
data.each_slice(2) do |slice|
next unless field = slice[0]?
next unless raw = slice[1]?
hash[field.to_s] = raw.to_s
end
hash
end
value["last_executed_at"].to_i64.should be > 0
ensure
module_manager.try &.stop
resource_manager.try &.stop
end
end
end
Loading
Loading