Skip to content

Commit 77f87b2

Browse files
committed
refactor: [PPT-2430] refactor edge
1 parent 602731b commit 77f87b2

54 files changed

Lines changed: 3872 additions & 945 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

shard.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ shards:
143143

144144
neuroplastic:
145145
git: https://github.com/spider-gazelle/neuroplastic.git
146-
version: 1.13.1
146+
version: 1.14.2
147147

148148
office365:
149149
git: https://github.com/placeos/office365.git
@@ -179,15 +179,15 @@ shards:
179179

180180
placeos-driver:
181181
git: https://github.com/placeos/driver.git
182-
version: 7.19.0
182+
version: 7.21.1
183183

184184
placeos-log-backend:
185185
git: https://github.com/place-labs/log-backend.git
186186
version: 0.13.0
187187

188188
placeos-models:
189189
git: https://github.com/placeos/models.git
190-
version: 9.85.0
190+
version: 9.86.2
191191

192192
placeos-resource:
193193
git: https://github.com/place-labs/resource.git
@@ -207,11 +207,11 @@ shards:
207207

208208
redis-cluster:
209209
git: https://github.com/place-labs/redis-cluster.cr.git
210-
version: 0.8.10
210+
version: 0.8.11
211211

212212
redis_service_manager:
213213
git: https://github.com/place-labs/redis_service_manager.git
214-
version: 3.3.0
214+
version: 3.3.1
215215

216216
rendezvous-hash:
217217
git: https://github.com/caspiano/rendezvous-hash.git

spec/api/chaos_spec.cr

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,43 @@
11
require "../helper"
22

3-
# Testing pattern for controllers as follows..
4-
# - Create the controller instance, with intended mocks
5-
# - Create the call instance
6-
# - Check the outcome of the request
7-
83
module PlaceOS::Core
94
describe Api::Chaos, tags: "api" do
10-
pending "chaos/terminate"
5+
client = AC::SpecHelper.client
6+
namespace = Api::Chaos::NAMESPACE[0]
7+
8+
after_each do
9+
Services.reset
10+
end
11+
12+
it "chaos/terminate" do
13+
ProcessManager.with_driver do |mod, _driver_path, driver_key, _driver|
14+
module_manager = module_manager_mock
15+
Services.module_manager = module_manager
16+
17+
module_manager.load_module(mod)
18+
pid = module_manager.local_processes.protocol_manager_by_driver?(driver_key).try(&.pid).not_nil!
19+
Process.exists?(pid).should be_true
20+
21+
response = client.post("#{namespace}terminate?path=#{driver_key}")
22+
response.status_code.should eq 200
23+
24+
success = Channel(Nil).new
25+
spawn do
26+
while Process.exists?(pid)
27+
sleep 50.milliseconds
28+
end
29+
success.send nil
30+
end
31+
32+
select
33+
when success.receive
34+
Process.exists?(pid).should be_false
35+
when timeout 2.seconds
36+
raise "timeout waiting for driver terminate"
37+
end
38+
ensure
39+
module_manager.try &.stop
40+
end
41+
end
1142
end
1243
end

spec/api/command_spec.cr

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,6 @@ module PlaceOS::Core::Api
1111
used_for_place_testing: [] of String,
1212
}.to_json
1313

14-
# allow injecting mock manager during testing
15-
class Command
16-
class_property mock_module_manager : ModuleManager? = nil
17-
property module_manager : ModuleManager { @@mock_module_manager || ModuleManager.instance }
18-
end
19-
2014
describe Command, tags: "api" do
2115
client = AC::SpecHelper.client
2216

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

28-
after_each { Command.mock_module_manager = nil }
22+
after_each { Services.reset }
2923

3024
describe "command/:module_id/execute" do
3125
it "executes a command on a running module" do
3226
_, _, mod, resource_manager = create_resources
3327
mod_id = mod.id.as(String)
3428
module_manager = module_manager_mock
3529
module_manager.load_module(mod)
36-
Command.mock_module_manager = module_manager
30+
Services.module_manager = module_manager
31+
Services.resource_manager = resource_manager
3732

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

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

8177
# Don't load the module, but it's not lazy either
8278
module_manager = module_manager_mock
83-
Command.mock_module_manager = module_manager
79+
Services.module_manager = module_manager
8480

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

10197
# Load module
10298
module_manager.load_module(mod)
103-
Command.mock_module_manager = module_manager
99+
Services.module_manager = module_manager
100+
Services.resource_manager = resource_manager
104101

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

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

127124
# Wait for messages on the debugger
128125
messages = [] of String
129-
2.times do
126+
deadline = Time.instant + 5.seconds
127+
until Time.instant >= deadline
130128
select
131129
when message = message_channel.receive
132130
messages << message
131+
break if message == %([1,"this will be propagated to backoffice!"])
133132
when timeout 2.seconds
134-
break
133+
next
135134
end
136135
end
137136

@@ -140,7 +139,5 @@ module PlaceOS::Core::Api
140139
resource_manager.try &.stop
141140
end
142141
end
143-
144-
pending "command/debugger"
145142
end
146143
end

spec/api/edge_spec.cr

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
require "../helper"
2+
3+
module PlaceOS::Core::Api
4+
describe Edge, tags: "api" do
5+
client = AC::SpecHelper.client
6+
7+
namespace = Edge::NAMESPACE[0]
8+
json_headers = HTTP::Headers{
9+
"Content-Type" => "application/json",
10+
}
11+
12+
it "returns desired state snapshots for an edge" do
13+
_, _, mod = setup(role: PlaceOS::Model::Driver::Role::Service)
14+
resource_manager = PlaceOS::Core::ResourceManager.new(testing: true)
15+
resource_manager.start { }
16+
edge = PlaceOS::Model::Generator.edge.save!
17+
mod.edge_id = edge.id.as(String)
18+
mod.running = true
19+
mod.save!
20+
21+
route = File.join(namespace, edge.id.as(String), "desired_state")
22+
response = client.get(route, headers: json_headers)
23+
response.status_code.should eq 200
24+
25+
snapshot = PlaceOS::Edge::State::Snapshot.from_json(response.body)
26+
snapshot.edge_id.should eq edge.id
27+
snapshot.modules.map(&.module_id).should contain(mod.id.as(String))
28+
snapshot.drivers.should_not be_empty
29+
ensure
30+
resource_manager.try &.stop
31+
end
32+
33+
it "returns not modified when the desired state is stale" do
34+
_, _, mod = setup(role: PlaceOS::Model::Driver::Role::Service)
35+
resource_manager = PlaceOS::Core::ResourceManager.new(testing: true)
36+
resource_manager.start { }
37+
edge = PlaceOS::Model::Generator.edge.save!
38+
mod.edge_id = edge.id.as(String)
39+
mod.running = true
40+
mod.save!
41+
42+
route = File.join(namespace, edge.id.as(String), "desired_state")
43+
first = client.get(route, headers: json_headers)
44+
first.status_code.should eq 200
45+
snapshot = PlaceOS::Edge::State::Snapshot.from_json(first.body)
46+
47+
headers = json_headers.dup
48+
headers["If-Modified-Since"] = HTTP.format_time(snapshot.last_modified)
49+
second = client.get(route, headers: headers)
50+
second.status_code.should eq 304
51+
ensure
52+
resource_manager.try &.stop
53+
end
54+
55+
it "returns not found for an unknown edge snapshot request" do
56+
response = client.get(File.join(namespace, "edge-missing", "desired_state"), headers: json_headers)
57+
response.status_code.should eq 404
58+
end
59+
60+
it "streams compiled driver binaries for an edge" do
61+
_, driver, mod = setup(role: PlaceOS::Model::Driver::Role::Service)
62+
resource_manager = PlaceOS::Core::ResourceManager.new(testing: true)
63+
resource_manager.start { }
64+
edge = PlaceOS::Model::Generator.edge.save!
65+
mod.edge_id = edge.id.as(String)
66+
mod.running = true
67+
mod.save!
68+
69+
result = PlaceOS::Core::DriverResource.load(driver, PlaceOS::Core::DriverStore.new, true)
70+
route = File.join(namespace, edge.id.as(String), "drivers", File.basename(result.path))
71+
response = client.get(route, headers: json_headers)
72+
response.status_code.should eq 200
73+
response.headers["Content-Type"].should eq "application/octet-stream"
74+
response.body.bytesize.should be > 0
75+
ensure
76+
resource_manager.try &.stop
77+
end
78+
79+
it "returns not found when the binary key does not exist" do
80+
edge = PlaceOS::Model::Generator.edge.save!
81+
route = File.join(namespace, edge.id.as(String), "drivers", "drivers_missing_deadbeef_arm64")
82+
response = client.get(route, headers: json_headers)
83+
response.status_code.should eq 404
84+
end
85+
end
86+
end

spec/api/status_spec.cr

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@ module PlaceOS::Core::Api
99
"Content-Type" => "application/json",
1010
}
1111

12+
after_each do
13+
Services.reset
14+
end
15+
1216
describe "status/" do
1317
it "renders data about node" do
1418
_, driver, _, resource_manager = create_resources
19+
Services.resource_manager = resource_manager
1520

1621
driver.reload!
1722

@@ -27,10 +32,81 @@ module PlaceOS::Core::Api
2732
resource_manager.try &.stop
2833
end
2934

30-
pending "deletes standalone driver binary used for metadata"
31-
end
35+
it "returns local driver status for a running module" do
36+
_, driver, mod, resource_manager = create_resources
37+
module_manager = module_manager_mock
38+
Services.module_manager = module_manager
39+
Services.resource_manager = resource_manager
40+
module_manager.load_module(mod)
41+
42+
driver_path = module_manager.store.driver_binary_path(driver.file_name, driver.commit).to_s
43+
route = "#{namespace}driver?path=#{URI.encode_path(driver_path)}"
44+
response = client.get(route, headers: json_headers)
45+
response.status_code.should eq 200
46+
47+
status = Status::DriverStatus.from_json(response.body)
48+
status.local.should_not be_nil
49+
status.local.not_nil!.running.should be_true
50+
status.edge.should be_empty
51+
ensure
52+
module_manager.try &.stop
53+
resource_manager.try &.stop
54+
end
55+
56+
it "returns machine load for local and edge runtimes" do
57+
_, _, _, resource_manager = create_resources
58+
Services.resource_manager = resource_manager
59+
response = client.get("#{namespace}load", headers: json_headers)
60+
response.status_code.should eq 200
61+
62+
load = Status::MachineLoad.from_json(response.body)
63+
load.local.hostname.should_not be_empty
64+
load.local.cpu_count.should be > 0
65+
load.edge.should be_empty
66+
ensure
67+
resource_manager.try &.stop
68+
end
69+
70+
it "returns loaded module mappings" do
71+
_, _, mod, resource_manager = create_resources
72+
module_manager = module_manager_mock
73+
Services.module_manager = module_manager
74+
Services.resource_manager = resource_manager
75+
module_manager.load_module(mod)
76+
77+
response = client.get("#{namespace}loaded", headers: json_headers)
78+
response.status_code.should eq 200
79+
80+
loaded = Status::LoadedModules.from_json(response.body)
81+
loaded.local.values.flatten.should contain(mod.id.as(String))
82+
loaded.edge.should be_empty
83+
ensure
84+
module_manager.try &.stop
85+
resource_manager.try &.stop
86+
end
87+
88+
it "reports persisted edge connection visibility" do
89+
edge = PlaceOS::Model::Generator.edge.save!
90+
edge.update_fields(
91+
online: true,
92+
last_seen: Time.utc
93+
)
94+
95+
module_manager = module_manager_mock
96+
Services.module_manager = module_manager
97+
response = client.get("#{namespace}edges", headers: json_headers)
98+
response.status_code.should eq 200
3299

33-
pending "status/driver"
34-
pending "status/load"
100+
body = Hash(String, Status::EdgeConnection).from_json(response.body)
101+
body[edge.id.as(String)].online.should be_true
102+
body[edge.id.as(String)].last_seen.should_not be_nil
103+
body[edge.id.as(String)].websocket_connected.should be_false
104+
body[edge.id.as(String)].snapshot_version.should be_nil
105+
body[edge.id.as(String)].pending_updates.should eq 0
106+
body[edge.id.as(String)].pending_events.should eq 0
107+
ensure
108+
module_manager.try &.stop
109+
end
110+
end
35111
end
36112
end

spec/driver_manager/driver_cleanup_spec.cr

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,22 @@ module PlaceOS::Core
2424
tracker = DriverCleanup::StaleProcessTracker.new(DriverStore::BINARY_PATH, REDIS_CLIENT)
2525
stale_list = tracker.update_and_find_stale(ENV["STALE_THRESHOLD_DAYS"]?.try &.to_i || 30)
2626
stale_list.size.should eq(0)
27-
driver_file = Path[DriverStore::BINARY_PATH, "drivers_place_private_helper_cce023a_#{Core::ARCH}"].to_s
28-
value = REDIS_CLIENT.hgetall(driver_file)
27+
value = case data = REDIS_CLIENT.hgetall(driver_path)
28+
in Hash
29+
data.transform_keys(&.to_s).transform_values(&.to_s)
30+
in Array
31+
hash = {} of String => String
32+
data.each_slice(2) do |slice|
33+
next unless field = slice[0]?
34+
next unless raw = slice[1]?
35+
hash[field.to_s] = raw.to_s
36+
end
37+
hash
38+
end
2939
value["last_executed_at"].to_i64.should be > 0
40+
ensure
41+
module_manager.try &.stop
42+
resource_manager.try &.stop
3043
end
3144
end
3245
end

0 commit comments

Comments
 (0)