From 15dbf739c2bc2d5e204299010d99f5436a269358 Mon Sep 17 00:00:00 2001 From: Nate Shoemaker Date: Wed, 20 Nov 2024 09:06:06 -0800 Subject: [PATCH] Managed Deployments: Groups and Releases --- .dialyzer_ignore.exs | 2 +- lib/nerves_hub/accounts/remove_account.ex | 4 +- lib/nerves_hub/application.ex | 2 +- lib/nerves_hub/audit_logs.ex | 6 +- lib/nerves_hub/audit_logs/templates.ex | 22 ++-- .../deployments/inflight_deployment_check.ex | 15 --- lib/nerves_hub/deployments/supervisor.ex | 19 --- lib/nerves_hub/devices.ex | 46 ++++---- lib/nerves_hub/devices/device.ex | 8 +- lib/nerves_hub/devices/inflight_update.ex | 4 +- lib/nerves_hub/devices/update_payload.ex | 8 +- lib/nerves_hub/firmwares/firmware.ex | 10 +- ...{deployments.ex => managed_deployments.ex} | 109 +++++++++--------- .../deployment_group.ex} | 28 ++--- .../managed_deployments/deployment_release.ex | 43 +++++++ .../inflight_deployment_check.ex | 15 +++ .../monitor.ex | 28 ++--- .../orchestrator.ex | 31 ++--- .../managed_deployments/supervisor.ex | 20 ++++ lib/nerves_hub/products/product.ex | 4 +- lib/nerves_hub/types.ex | 2 + .../workers/firmware_delta_builder.ex | 6 +- .../channels/console_channel.ex | 5 +- lib/nerves_hub_web/channels/device_channel.ex | 28 +++-- lib/nerves_hub_web/components/navigation.ex | 4 +- ...ller.ex => deployment_group_controller.ex} | 32 ++--- .../controllers/api/device_controller.ex | 9 +- .../controllers/api/fallback_controller.ex | 2 +- .../controllers/deployment_controller.ex | 6 +- lib/nerves_hub_web/helpers/authorization.ex | 8 +- lib/nerves_hub_web/live/dashboard/index.ex | 4 +- .../edit.ex | 30 ++--- .../edit.html.heex | 2 +- .../live/deployment_groups/index.ex | 35 ++++++ .../index.html.heex | 12 +- .../{deployments => deployment_groups}/new.ex | 31 ++--- .../new.html.heex | 4 +- .../show.ex | 45 ++++---- .../show.html.heex | 59 ++++++---- lib/nerves_hub_web/live/deployments/index.ex | 35 ------ lib/nerves_hub_web/live/devices/show.ex | 24 ++-- .../live/devices/show.html.heex | 8 +- lib/nerves_hub_web/router.ex | 26 +++-- .../views/api/deployment_group_view.ex | 23 ++++ .../views/api/deployment_view.ex | 23 ---- lib/nerves_hub_web/views/api/device_view.ex | 15 ++- lib/nerves_hub_web/views/layout_view.ex | 2 +- priv/repo/add_org_id.exs | 2 +- ...41120211103_create_deployment_releases.exs | 15 +++ ...e_deployment_release_id_to_deployments.exs | 9 ++ ...914_add_deployment_conflict_to_devices.exs | 9 ++ test/nerves_hub/devices_test.exs | 21 ++-- ..._test.exs => managed_deployments_test.exs} | 41 ++++--- .../channels/websocket_test.exs | 27 +++-- .../api/deployment_controller_test.exs | 59 +++++++--- .../api/device_controller_test.exs | 2 +- .../edit_test.exs | 18 +-- .../index_test.exs | 16 +-- .../new_test.exs | 22 ++-- .../show_test.exs | 24 ++-- .../nerves_hub_web/live/devices/show_test.exs | 6 +- test/support/fixtures.ex | 6 +- 62 files changed, 669 insertions(+), 512 deletions(-) delete mode 100644 lib/nerves_hub/deployments/inflight_deployment_check.ex delete mode 100644 lib/nerves_hub/deployments/supervisor.ex rename lib/nerves_hub/{deployments.ex => managed_deployments.ex} (78%) rename lib/nerves_hub/{deployments/deployment.ex => managed_deployments/deployment_group.ex} (82%) create mode 100644 lib/nerves_hub/managed_deployments/deployment_release.ex create mode 100644 lib/nerves_hub/managed_deployments/inflight_deployment_check.ex rename lib/nerves_hub/{deployments => managed_deployments}/monitor.ex (56%) rename lib/nerves_hub/{deployments => managed_deployments}/orchestrator.ex (78%) create mode 100644 lib/nerves_hub/managed_deployments/supervisor.ex rename lib/nerves_hub_web/controllers/api/{deployment_controller.ex => deployment_group_controller.ex} (68%) rename lib/nerves_hub_web/live/{deployments => deployment_groups}/edit.ex (82%) rename lib/nerves_hub_web/live/{deployments => deployment_groups}/edit.html.heex (99%) create mode 100644 lib/nerves_hub_web/live/deployment_groups/index.ex rename lib/nerves_hub_web/live/{deployments => deployment_groups}/index.html.heex (89%) rename lib/nerves_hub_web/live/{deployments => deployment_groups}/new.ex (79%) rename lib/nerves_hub_web/live/{deployments => deployment_groups}/new.html.heex (96%) rename lib/nerves_hub_web/live/{deployments => deployment_groups}/show.ex (71%) rename lib/nerves_hub_web/live/{deployments => deployment_groups}/show.html.heex (75%) delete mode 100644 lib/nerves_hub_web/live/deployments/index.ex create mode 100644 lib/nerves_hub_web/views/api/deployment_group_view.ex delete mode 100644 lib/nerves_hub_web/views/api/deployment_view.ex create mode 100644 priv/repo/migrations/20241120211103_create_deployment_releases.exs create mode 100644 priv/repo/migrations/20241120212950_add_active_deployment_release_id_to_deployments.exs create mode 100644 priv/repo/migrations/20241126000914_add_deployment_conflict_to_devices.exs rename test/nerves_hub/{deployments_test.exs => managed_deployments_test.exs} (84%) rename test/nerves_hub_web/live/{deployments => deployment_groups}/edit_test.exs (74%) rename test/nerves_hub_web/live/{deployments => deployment_groups}/index_test.exs (71%) rename test/nerves_hub_web/live/{deployments => deployment_groups}/new_test.exs (79%) rename test/nerves_hub_web/live/{deployments => deployment_groups}/show_test.exs (79%) diff --git a/.dialyzer_ignore.exs b/.dialyzer_ignore.exs index 11bb9da67..ebcccd6aa 100644 --- a/.dialyzer_ignore.exs +++ b/.dialyzer_ignore.exs @@ -1,7 +1,7 @@ [ {"lib/x509/certificate.ex", "Unknown type: X509.ASN1.record/1."}, {"lib/x509/certificate/extension.ex", "Unknown type: X509.ASN1.record/1."}, - {"lib/nerves_hub/deployments/orchestrator.ex", :unmatched_return, 1}, + {"lib/nerves_hub/managed_deployments/orchestrator.ex", :unmatched_return, 1}, {"lib/nerves_hub_web/channels/device_channel.ex", :unmatched_return, 1}, {"lib/nerves_hub_web/channels/device_socket.ex", :unmatched_return, 1} ] diff --git a/lib/nerves_hub/accounts/remove_account.ex b/lib/nerves_hub/accounts/remove_account.ex index 10d02d39e..76dab09c6 100644 --- a/lib/nerves_hub/accounts/remove_account.ex +++ b/lib/nerves_hub/accounts/remove_account.ex @@ -10,7 +10,6 @@ defmodule NervesHub.Accounts.RemoveAccount do alias NervesHub.Accounts.OrgMetric alias NervesHub.Accounts.OrgUser alias NervesHub.Accounts.User - alias NervesHub.Deployments.Deployment alias NervesHub.Devices.CACertificate alias NervesHub.Devices.Device alias NervesHub.Devices.DeviceCertificate @@ -18,6 +17,7 @@ defmodule NervesHub.Accounts.RemoveAccount do alias NervesHub.Firmwares.Firmware alias NervesHub.Firmwares.FirmwareDelta alias NervesHub.Firmwares.FirmwareTransfer + alias NervesHub.ManagedDeployments.DeploymentGroup alias NervesHub.Products.Product alias NervesHub.Repo @@ -30,7 +30,7 @@ defmodule NervesHub.Accounts.RemoveAccount do |> Multi.delete_all(:invites, &query_by_org_id(Invite, &1)) |> Multi.delete_all(:device_certificates, &query_by_org_id(DeviceCertificate, &1)) |> Multi.delete_all(:ca_certificates, &query_by_org_id(CACertificate, &1)) - |> Multi.delete_all(:deployments, &query_by_org_id(Deployment, &1)) + |> Multi.delete_all(:deployment_groups, &query_by_org_id(DeploymentGroup, &1)) |> Multi.delete_all(:firmware_deltas, &query_firmware_deltas/1) |> Multi.delete_all(:firmware_transfers, &query_by_org_id(FirmwareTransfer, &1)) |> Multi.merge(&delete_firmwares/1) diff --git a/lib/nerves_hub/application.ex b/lib/nerves_hub/application.ex index 3ee3e1fab..0b1ebb486 100644 --- a/lib/nerves_hub/application.ex +++ b/lib/nerves_hub/application.ex @@ -76,7 +76,7 @@ defmodule NervesHub.Application do defp deployments_supervisor("test"), do: [] defp deployments_supervisor(_) do - [NervesHub.Deployments.Supervisor] + [NervesHub.ManagedDeployments.Supervisor] end defp endpoints("test") do diff --git a/lib/nerves_hub/audit_logs.ex b/lib/nerves_hub/audit_logs.ex index b8e7409ba..3a3deeb61 100644 --- a/lib/nerves_hub/audit_logs.ex +++ b/lib/nerves_hub/audit_logs.ex @@ -2,7 +2,7 @@ defmodule NervesHub.AuditLogs do import Ecto.Query alias NervesHub.AuditLogs.AuditLog - alias NervesHub.Deployments.Deployment + alias NervesHub.ManagedDeployments.DeploymentGroup alias NervesHub.Repo alias NimbleCSV.RFC4180, as: CSV @@ -58,8 +58,8 @@ defmodule NervesHub.AuditLogs do |> Flop.run(flop) end - defp query_for_feed(%Deployment{id: id}) do - resource_type = to_string(Deployment) + defp query_for_feed(%DeploymentGroup{id: id}) do + resource_type = to_string(DeploymentGroup) from(al in AuditLog, where: [resource_type: ^resource_type, resource_id: ^id]) |> order_by(desc: :inserted_at) diff --git a/lib/nerves_hub/audit_logs/templates.ex b/lib/nerves_hub/audit_logs/templates.ex index 0215e26a0..e3bef8b26 100644 --- a/lib/nerves_hub/audit_logs/templates.ex +++ b/lib/nerves_hub/audit_logs/templates.ex @@ -2,15 +2,15 @@ defmodule NervesHub.AuditLogs.Templates do alias NervesHub.Accounts.User alias NervesHub.AuditLogs alias NervesHub.AuditLogs.AuditLog - alias NervesHub.Deployments.Deployment alias NervesHub.Devices.Device + alias NervesHub.ManagedDeployments.DeploymentGroup require Logger def audit_resolve_changed_deployment(device, reference_id) do description = if device.deployment_id do - "device #{device.identifier} reloaded deployment and is attached to deployment #{device.deployment.name}" + "device #{device.identifier} reloaded deployment and is attached to deployment #{device.deployment_group.name}" else "device #{device.identifier} reloaded deployment and is no longer attached to a deployment" end @@ -19,18 +19,18 @@ defmodule NervesHub.AuditLogs.Templates do end def audit_device_deployment_update_triggered(device, reference_id) do - deployment = device.deployment - firmware = deployment.firmware + deployment_group = device.deployment_group + firmware = deployment_group.firmware description = - "deployment #{deployment.name} update triggered device #{device.identifier} to update firmware #{firmware.uuid}" + "deployment #{deployment_group.name} update triggered device #{device.identifier} to update firmware #{firmware.uuid}" - AuditLogs.audit_with_ref!(deployment, device, description, reference_id) + AuditLogs.audit_with_ref!(deployment_group, device, description, reference_id) end def audit_device_assigned(device, reference_id) do description = - "device #{device.identifier} reloaded deployment and is attached to deployment #{device.deployment.name}" + "device #{device.identifier} reloaded deployment and is attached to deployment #{device.deployment_group.name}" AuditLogs.audit_with_ref!(device, device, description, reference_id) end @@ -43,7 +43,7 @@ defmodule NervesHub.AuditLogs.Templates do Logger.info("[DeviceChannel] #{description}") end - @spec audit_device_deployment_update(User.t(), Device.t(), Deployment.t()) :: AuditLog.t() + @spec audit_device_deployment_update(User.t(), Device.t(), DeploymentGroup.t()) :: AuditLog.t() def audit_device_deployment_update(user, device, deployment) do AuditLogs.audit!( user, @@ -52,7 +52,11 @@ defmodule NervesHub.AuditLogs.Templates do ) end - @spec audit_device_deployment_update(Device.t(), Deployment.t(), :one_found | :multiple_found) :: + @spec audit_device_deployment_update( + Device.t(), + DeploymentGroup.t(), + :one_found | :multiple_found + ) :: AuditLog.t() def audit_set_deployment(device, deployment, :one_found) do AuditLogs.audit!( diff --git a/lib/nerves_hub/deployments/inflight_deployment_check.ex b/lib/nerves_hub/deployments/inflight_deployment_check.ex deleted file mode 100644 index 340402405..000000000 --- a/lib/nerves_hub/deployments/inflight_deployment_check.ex +++ /dev/null @@ -1,15 +0,0 @@ -defmodule NervesHub.Deployments.InflightDeploymentCheck do - use Ecto.Schema - - @timestamps_opts updated_at: false - - alias NervesHub.Deployments.Deployment - alias NervesHub.Devices.Device - - schema "inflight_deployment_checks" do - belongs_to(:device, Device) - belongs_to(:deployment, Deployment) - - timestamps() - end -end diff --git a/lib/nerves_hub/deployments/supervisor.ex b/lib/nerves_hub/deployments/supervisor.ex deleted file mode 100644 index 1121b6b9e..000000000 --- a/lib/nerves_hub/deployments/supervisor.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule NervesHub.Deployments.Supervisor do - @moduledoc false - - use Supervisor - - def start_link(_) do - Supervisor.start_link(__MODULE__, [], name: __MODULE__) - end - - def init(_) do - children = [ - {Registry, keys: :unique, name: NervesHub.Deployments}, - NervesHub.Deployments.Monitor, - {DynamicSupervisor, strategy: :one_for_one, name: NervesHub.DeploymentDynamicSupervisor} - ] - - Supervisor.init(children, strategy: :one_for_one) - end -end diff --git a/lib/nerves_hub/devices.ex b/lib/nerves_hub/devices.ex index 23304634c..ec02a73fd 100644 --- a/lib/nerves_hub/devices.ex +++ b/lib/nerves_hub/devices.ex @@ -9,8 +9,6 @@ defmodule NervesHub.Devices do alias NervesHub.Accounts.User alias NervesHub.AuditLogs alias NervesHub.Certificate - alias NervesHub.Deployments.Deployment - alias NervesHub.Deployments.Orchestrator alias NervesHub.Devices.CACertificate alias NervesHub.Devices.Connections alias NervesHub.Devices.Device @@ -24,6 +22,8 @@ defmodule NervesHub.Devices do alias NervesHub.Firmwares alias NervesHub.Firmwares.Firmware alias NervesHub.Firmwares.FirmwareMetadata + alias NervesHub.ManagedDeployments.DeploymentGroup + alias NervesHub.ManagedDeployments.Orchestrator alias NervesHub.Products alias NervesHub.Products.Product alias NervesHub.Repo @@ -86,12 +86,12 @@ defmodule NervesHub.Devices do |> where([d], d.product_id == ^product_id) |> join(:left, [d], o in assoc(d, :org)) |> join(:left, [d, o], p in assoc(d, :product)) - |> join(:left, [d, o, p], dp in assoc(d, :deployment)) + |> join(:left, [d, o, p], dp in assoc(d, :deployment_group)) |> join(:left, [d, o, p, dp], f in assoc(dp, :firmware)) |> Repo.exclude_deleted() |> order_by(^sort_devices(sorting)) |> Filtering.build_filters(filters) - |> preload([d, o, p, dp, f], org: o, product: p, deployment: {dp, firmware: f}) + |> preload([d, o, p, dp, f], org: o, product: p, deployment_group: {dp, firmware: f}) |> Connections.preload_latest_connection() |> Flop.run(flop) end @@ -256,7 +256,7 @@ defmodule NervesHub.Devices do {:error, :not_found} device -> - {:ok, Repo.preload(device, [:org, :product, deployment: [:firmware]])} + {:ok, Repo.preload(device, [:org, :product, deployment_group: [:firmware]])} end end @@ -283,9 +283,9 @@ defmodule NervesHub.Devices do |> where(identifier: ^identifier) |> where(org_id: ^org_id) |> join(:left, [d], o in assoc(d, :org)) - |> join(:left, [d], dp in assoc(d, :deployment)) + |> join(:left, [d], dp in assoc(d, :deployment_group)) |> join_and_preload(preload_assoc) - |> preload([d, o, dp], org: o, deployment: dp) + |> preload([d, o, dp], org: o, deployment_group: dp) end defp join_and_preload(query, nil), do: query @@ -357,7 +357,7 @@ defmodule NervesHub.Devices do def get_eligible_deployments(%Device{firmware_metadata: meta} = device) do from( - d in Deployment, + d in DeploymentGroup, join: p in assoc(d, :product), on: [org_id: ^device.org_id, name: ^meta.product], join: f in assoc(d, :firmware), @@ -627,7 +627,7 @@ defmodule NervesHub.Devices do @spec get_device_firmware_for_delta_generation_by_product(binary()) :: list({source_firmware_id(), target_firmware_id()}) def get_device_firmware_for_delta_generation_by_product(product_id) do - Deployment + DeploymentGroup |> where([dep], dep.product_id == ^product_id) |> join(:inner, [dep], dev in Device, on: dev.deployment_id == dep.id) |> join(:inner, [dep, dev], f in Firmware, @@ -642,7 +642,7 @@ defmodule NervesHub.Devices do @spec get_device_firmware_for_delta_generation_by_deployment(binary()) :: list({source_firmware_id(), target_firmware_id()}) def get_device_firmware_for_delta_generation_by_deployment(deployment_id) do - Deployment + DeploymentGroup |> where([dep], dep.id == ^deployment_id) |> join(:inner, [dep], dev in Device, on: dev.deployment_id == dep.id) |> join(:inner, [dep, dev], f in Firmware, @@ -708,7 +708,7 @@ defmodule NervesHub.Devices do end def resolve_update(device) do - deployment = Repo.preload(device.deployment, [:firmware]) + deployment = Repo.preload(device.deployment_group, [:firmware]) case verify_update_eligibility(device, deployment) do {:ok, _device} -> @@ -719,7 +719,7 @@ defmodule NervesHub.Devices do update_available: true, firmware_url: url, firmware_meta: meta, - deployment: deployment, + deployment_group: deployment, deployment_id: deployment.id } @@ -754,7 +754,7 @@ defmodule NervesHub.Devices do """ def matches_deployment?( %Device{tags: tags, firmware_metadata: %FirmwareMetadata{version: version}}, - %Deployment{conditions: %{"version" => requirement, "tags" => dep_tags}} + %DeploymentGroup{conditions: %{"version" => requirement, "tags" => dep_tags}} ) do if version_match?(version, requirement) and tags_match?(tags, dep_tags) do true @@ -765,7 +765,7 @@ defmodule NervesHub.Devices do def matches_deployment?(_, _), do: false - @spec update_deployment(Device.t(), Deployment.t()) :: Device.t() + @spec update_deployment(Device.t(), DeploymentGroup.t()) :: Device.t() def update_deployment(device, deployment) do device |> Ecto.Changeset.change() @@ -781,13 +781,13 @@ defmodule NervesHub.Devices do |> Repo.update!() end - @spec failure_threshold_met?(Device.t(), Deployment.t()) :: boolean() - def failure_threshold_met?(%Device{} = device, %Deployment{} = deployment) do + @spec failure_threshold_met?(Device.t(), DeploymentGroup.t()) :: boolean() + def failure_threshold_met?(%Device{} = device, %DeploymentGroup{} = deployment) do Enum.count(device.update_attempts) >= deployment.device_failure_threshold end - @spec failure_rate_met?(Device.t(), Deployment.t()) :: boolean() - def failure_rate_met?(%Device{} = device, %Deployment{} = deployment) do + @spec failure_rate_met?(Device.t(), DeploymentGroup.t()) :: boolean() + def failure_rate_met?(%Device{} = device, %DeploymentGroup{} = deployment) do seconds_ago = Timex.shift(DateTime.utc_now(), seconds: -deployment.device_failure_rate_seconds) @@ -918,7 +918,7 @@ defmodule NervesHub.Devices do if inflight_update != nil do Orchestrator.device_updated(inflight_update.deployment_id) - Deployment + DeploymentGroup |> where([d], d.id == ^inflight_update.deployment_id) |> Repo.update_all(inc: [current_updated_devices: 1]) @@ -983,7 +983,7 @@ defmodule NervesHub.Devices do |> Repo.transaction() |> case do {:ok, %{move: updated}} -> - _ = broadcast(updated, "moved") + # Deployments.set_deployment(updated) {:ok, updated} err -> @@ -1056,7 +1056,7 @@ defmodule NervesHub.Devices do {:ok, %{updated: non_neg_integer(), ignored: non_neg_integer()}} def move_many_to_deployment(device_ids, deployment_id) do %{firmware: firmware} = - Deployment |> where(id: ^deployment_id) |> preload(:firmware) |> Repo.one() + DeploymentGroup |> where(id: ^deployment_id) |> preload(:firmware) |> Repo.one() {devices_updated_count, _} = Device @@ -1300,14 +1300,14 @@ defmodule NervesHub.Devices do |> Repo.update!() end - def inflight_updates_for(%Deployment{} = deployment) do + def inflight_updates_for(%DeploymentGroup{} = deployment) do InflightUpdate |> where([iu], iu.deployment_id == ^deployment.id) |> preload([:device]) |> Repo.all() end - def count_inflight_updates_for(%Deployment{} = deployment) do + def count_inflight_updates_for(%DeploymentGroup{} = deployment) do InflightUpdate |> select([iu], count(iu)) |> where([iu], iu.deployment_id == ^deployment.id) diff --git a/lib/nerves_hub/devices/device.ex b/lib/nerves_hub/devices/device.ex index cb17b1399..333299230 100644 --- a/lib/nerves_hub/devices/device.ex +++ b/lib/nerves_hub/devices/device.ex @@ -4,12 +4,12 @@ defmodule NervesHub.Devices.Device do import Ecto.Changeset alias NervesHub.Accounts.Org - alias NervesHub.Deployments.Deployment alias NervesHub.Devices.DeviceCertificate alias NervesHub.Devices.DeviceConnection alias NervesHub.Devices.DeviceMetric alias NervesHub.Extensions.DeviceExtensionsSetting alias NervesHub.Firmwares.FirmwareMetadata + alias NervesHub.ManagedDeployments.DeploymentGroup alias NervesHub.Products.Product alias __MODULE__ @@ -40,7 +40,7 @@ defmodule NervesHub.Devices.Device do schema "devices" do belongs_to(:org, Org, where: [deleted_at: nil]) belongs_to(:product, Product, where: [deleted_at: nil]) - belongs_to(:deployment, Deployment) + belongs_to(:deployment_group, DeploymentGroup, foreign_key: :deployment_id) embeds_one(:firmware_metadata, FirmwareMetadata, on_replace: :update) has_many(:device_certificates, DeviceCertificate, on_delete: :delete_all) has_many(:device_connections, DeviceConnection, on_delete: :delete_all) @@ -65,6 +65,10 @@ defmodule NervesHub.Devices.Device do field(:connecting_code, :string) field(:connection_metadata, :map, default: %{}) + field(:deployment_conflict, Ecto.Enum, + values: [:bad_architecture, :bad_platform, :bad_architecture_and_platform] + ) + timestamps() # Deprecated fields, replaced with device_connections table. diff --git a/lib/nerves_hub/devices/inflight_update.ex b/lib/nerves_hub/devices/inflight_update.ex index bd90aece1..8fc89a9a2 100644 --- a/lib/nerves_hub/devices/inflight_update.ex +++ b/lib/nerves_hub/devices/inflight_update.ex @@ -3,16 +3,16 @@ defmodule NervesHub.Devices.InflightUpdate do import Ecto.Changeset - alias NervesHub.Deployments.Deployment alias NervesHub.Devices.Device alias NervesHub.Devices.InflightUpdate alias NervesHub.Firmwares.Firmware + alias NervesHub.ManagedDeployments.DeploymentGroup @required_params [:device_id, :deployment_id, :firmware_id, :firmware_uuid, :expires_at] schema "inflight_updates" do belongs_to(:device, Device) - belongs_to(:deployment, Deployment) + belongs_to(:deployment_group, DeploymentGroup, foreign_key: :deployment_id) belongs_to(:firmware, Firmware) field(:firmware_uuid, Ecto.UUID) diff --git a/lib/nerves_hub/devices/update_payload.ex b/lib/nerves_hub/devices/update_payload.ex index 0c4948693..e9bbd750e 100644 --- a/lib/nerves_hub/devices/update_payload.ex +++ b/lib/nerves_hub/devices/update_payload.ex @@ -3,8 +3,8 @@ defmodule NervesHub.Devices.UpdatePayload do This struct represents the payload that gets dispatched to devices """ - alias NervesHub.Deployments.Deployment alias NervesHub.Firmwares.FirmwareMetadata + alias NervesHub.ManagedDeployments.DeploymentGroup @derive {Jason.Encoder, only: [ @@ -17,7 +17,7 @@ defmodule NervesHub.Devices.UpdatePayload do defstruct update_available: false, firmware_url: nil, firmware_meta: nil, - deployment: nil, + deployment_group: nil, deployment_id: nil @type t :: @@ -25,14 +25,14 @@ defmodule NervesHub.Devices.UpdatePayload do update_available: false, firmware_meta: nil, firmware_url: nil, - deployment: nil, + deployment_group: nil, deployment_id: nil } | %__MODULE__{ update_available: true, firmware_meta: FirmwareMetadata.t(), firmware_url: String.t(), - deployment: Deployment.t(), + deployment_group: DeploymentGroup.t(), deployment_id: non_neg_integer() } end diff --git a/lib/nerves_hub/firmwares/firmware.ex b/lib/nerves_hub/firmwares/firmware.ex index 4755cce30..2706bb15d 100644 --- a/lib/nerves_hub/firmwares/firmware.ex +++ b/lib/nerves_hub/firmwares/firmware.ex @@ -5,7 +5,7 @@ defmodule NervesHub.Firmwares.Firmware do alias NervesHub.Accounts.Org alias NervesHub.Accounts.OrgKey - alias NervesHub.Deployments.Deployment + alias NervesHub.ManagedDeployments.DeploymentGroup alias NervesHub.Products.Product alias __MODULE__ @@ -46,7 +46,7 @@ defmodule NervesHub.Firmwares.Firmware do belongs_to(:org, Org, where: [deleted_at: nil]) belongs_to(:product, Product, where: [deleted_at: nil]) belongs_to(:org_key, OrgKey) - has_many(:deployments, Deployment) + has_many(:deployment_groups, DeploymentGroup) field(:architecture, :string) field(:author, :string) @@ -68,7 +68,7 @@ defmodule NervesHub.Firmwares.Firmware do |> cast(params, @required_params ++ @optional_params) |> validate_required(@required_params) |> unique_constraint(:uuid, name: :firmwares_product_id_uuid_index) - |> foreign_key_constraint(:deployments, name: :deployments_firmware_id_fkey) + |> foreign_key_constraint(:deployment_groups, name: :deployment_groups_firmware_id_fkey) end def update_changeset(%Firmware{} = firmware, params) do @@ -76,12 +76,12 @@ defmodule NervesHub.Firmwares.Firmware do |> cast(params, @required_params ++ @optional_params) |> validate_required(@required_params) |> unique_constraint(:uuid, name: :firmwares_product_id_uuid_index) - |> foreign_key_constraint(:deployments, name: :deployments_firmware_id_fkey) + |> foreign_key_constraint(:deployment_groups, name: :deployment_groups_firmware_id_fkey) end def delete_changeset(%Firmware{} = firmware, params) do firmware |> cast(params, @required_params ++ @optional_params) - |> no_assoc_constraint(:deployments, message: "Firmware has associated deployments") + |> no_assoc_constraint(:deployment_groups, message: "Firmware has associated deployments") end end diff --git a/lib/nerves_hub/deployments.ex b/lib/nerves_hub/managed_deployments.ex similarity index 78% rename from lib/nerves_hub/deployments.ex rename to lib/nerves_hub/managed_deployments.ex index ed50ba733..69d683845 100644 --- a/lib/nerves_hub/deployments.ex +++ b/lib/nerves_hub/managed_deployments.ex @@ -1,14 +1,14 @@ -defmodule NervesHub.Deployments do +defmodule NervesHub.ManagedDeployments do import Ecto.Query require Logger alias NervesHub.AuditLogs alias NervesHub.AuditLogs.Templates - alias NervesHub.Deployments.Deployment - alias NervesHub.Deployments.InflightDeploymentCheck alias NervesHub.Devices alias NervesHub.Devices.Device + alias NervesHub.ManagedDeployments.DeploymentGroup + alias NervesHub.ManagedDeployments.InflightDeploymentCheck alias NervesHub.Products.Product alias NervesHub.Workers.FirmwareDeltaBuilder @@ -16,15 +16,15 @@ defmodule NervesHub.Deployments do alias Ecto.Changeset - @spec all() :: [Deployment.t()] + @spec all() :: [DeploymentGroup.t()] def all() do - Repo.all(Deployment) + Repo.all(DeploymentGroup) end - @spec get_deployments_by_product(Product.t()) :: [Deployment.t()] + @spec get_deployments_by_product(Product.t()) :: [DeploymentGroup.t()] def get_deployments_by_product(%Product{id: product_id}) do from( - d in Deployment, + d in DeploymentGroup, join: f in assoc(d, :firmware), where: f.product_id == ^product_id, preload: [{:firmware, :product}] @@ -43,24 +43,24 @@ defmodule NervesHub.Deployments do |> Map.new() end - @spec get_device_count(Deployment.t()) :: term() | nil - def get_device_count(%Deployment{id: id}) do + @spec get_device_count(DeploymentGroup.t()) :: term() | nil + def get_device_count(%DeploymentGroup{id: id}) do Device |> where([d], d.deployment_id == ^id) |> Repo.exclude_deleted() |> Repo.aggregate(:count) end - @spec get_deployments_by_firmware(integer()) :: [Deployment.t()] + @spec get_deployments_by_firmware(integer()) :: [DeploymentGroup.t()] def get_deployments_by_firmware(firmware_id) do - Deployment + DeploymentGroup |> where([d], d.firmware_id == ^firmware_id) |> Repo.all() end - @spec get(integer()) :: {:ok, Deployment.t()} | {:error, :not_found} + @spec get(integer()) :: {:ok, DeploymentGroup.t()} | {:error, :not_found} def get(id) when is_integer(id) do - case Repo.get(Deployment, id) do + case Repo.get(DeploymentGroup, id) do nil -> {:error, :not_found} @@ -69,16 +69,17 @@ defmodule NervesHub.Deployments do end end - @spec get_deployment(Product.t(), String.t()) :: {:ok, Deployment.t()} | {:error, :not_found} + @spec get_deployment(Product.t(), String.t()) :: + {:ok, DeploymentGroup.t()} | {:error, :not_found} def get_deployment(%Product{id: product_id}, deployment_id) do from( - d in Deployment, + d in DeploymentGroup, where: d.id == ^deployment_id, join: f in assoc(d, :firmware), where: f.product_id == ^product_id, preload: [{:firmware, :product}] ) - |> Deployment.with_firmware() + |> DeploymentGroup.with_firmware() |> Repo.one() |> case do nil -> @@ -89,16 +90,16 @@ defmodule NervesHub.Deployments do end end - def get_deployment!(deployment_id), do: Repo.get!(Deployment, deployment_id) + def get_deployment!(deployment_id), do: Repo.get!(DeploymentGroup, deployment_id) - @spec get_by_product_and_name!(Product.t(), String.t()) :: Deployment.t() + @spec get_by_product_and_name!(Product.t(), String.t()) :: DeploymentGroup.t() def get_by_product_and_name!(product, name) do get_by_product_and_name_query(product, name) |> Repo.one!() end @spec get_deployment_by_name(Product.t(), String.t()) :: - {:ok, Deployment.t()} | {:error, :not_found} + {:ok, DeploymentGroup.t()} | {:error, :not_found} def get_deployment_by_name(product, name) do get_by_product_and_name_query(product, name) |> Repo.one() @@ -112,7 +113,7 @@ defmodule NervesHub.Deployments do end defp get_by_product_and_name_query(%Product{id: product_id}, name) do - Deployment + DeploymentGroup |> where(name: ^name) |> where(product_id: ^product_id) |> join(:left, [d], f in assoc(d, :firmware)) @@ -120,14 +121,15 @@ defmodule NervesHub.Deployments do |> preload([d, f, p], firmware: f, product: p) end - @spec delete_deployment(Deployment.t()) :: {:ok, Deployment.t()} | {:error, :not_found} - def delete_deployment(%Deployment{id: deployment_id}) do - case Repo.delete(Repo.get!(Deployment, deployment_id)) do + @spec delete_deployment(DeploymentGroup.t()) :: + {:ok, DeploymentGroup.t()} | {:error, :not_found} + def delete_deployment(%DeploymentGroup{id: deployment_id}) do + case Repo.delete(Repo.get!(DeploymentGroup, deployment_id)) do {:error, _changeset} -> {:error, :not_found} {:ok, deployment} -> - _ = broadcast(:monitor, "deployments/delete", %{deployment_id: deployment.id}) + _ = broadcast(:monitor, "deployment_groups/delete", %{deployment_id: deployment.id}) {:ok, deployment} end @@ -138,7 +140,8 @@ defmodule NervesHub.Deployments do - Records audit logs depending on changes """ - @spec update_deployment(Deployment.t(), map) :: {:ok, Deployment.t()} | {:error, Changeset.t()} + @spec update_deployment(DeploymentGroup.t(), map) :: + {:ok, DeploymentGroup.t()} | {:error, Changeset.t()} def update_deployment(deployment, params) do result = Repo.transaction(fn -> @@ -150,8 +153,8 @@ defmodule NervesHub.Deployments do changeset = deployment - |> Deployment.with_firmware() - |> Deployment.changeset(params) + |> DeploymentGroup.with_firmware() + |> DeploymentGroup.changeset(params) |> Ecto.Changeset.put_change(:total_updating_devices, device_count) case Repo.update(changeset) do @@ -170,7 +173,7 @@ defmodule NervesHub.Deployments do case result do {:ok, {deployment, changeset}} -> _ = maybe_trigger_delta_generation(deployment, changeset) - :ok = broadcast(deployment, "deployments/update") + :ok = broadcast(deployment, "deployment_groups/update") {:ok, deployment} @@ -186,15 +189,15 @@ defmodule NervesHub.Deployments do payload = %{archive_id: archive_id} _ = broadcast(deployment, "archives/updated", payload) - description = "deployment #{deployment.name} has a new archive" + description = "deployment group #{deployment.name} has a new archive" AuditLogs.audit!(deployment, deployment, description) {:conditions, _new_conditions} -> - description = "deployment #{deployment.name} conditions changed" + description = "deployment group #{deployment.name} conditions changed" AuditLogs.audit!(deployment, deployment, description) {:is_active, is_active} when is_active != true -> - description = "deployment #{deployment.name} is inactive" + description = "deployment group #{deployment.name} is inactive" AuditLogs.audit!(deployment, deployment, description) _ -> @@ -224,7 +227,7 @@ defmodule NervesHub.Deployments do @doc """ Delete any matching inflight deployment checks for devices """ - @spec delete_inflight_checks(Deployment.t()) :: :ok + @spec delete_inflight_checks(DeploymentGroup.t()) :: :ok def delete_inflight_checks(deployment) do _ = InflightDeploymentCheck @@ -234,18 +237,18 @@ defmodule NervesHub.Deployments do :ok end - @spec change_deployment(Deployment.t(), map()) :: Changeset.t() + @spec change_deployment(DeploymentGroup.t(), map()) :: Changeset.t() def change_deployment(deployment, params) do - Deployment.changeset(deployment, params) + DeploymentGroup.changeset(deployment, params) end - @spec create_deployment(map()) :: {:ok, Deployment.t()} | {:error, Changeset.t()} + @spec create_deployment(map()) :: {:ok, DeploymentGroup.t()} | {:error, Changeset.t()} def create_deployment(params) do - changeset = Deployment.creation_changeset(%Deployment{}, params) + changeset = DeploymentGroup.creation_changeset(%DeploymentGroup{}, params) case Repo.insert(changeset) do {:ok, deployment} -> - _ = broadcast(:monitor, "deployments/new", %{deployment_id: deployment.id}) + _ = broadcast(:monitor, "deployment_groups/new", %{deployment_id: deployment.id}) {:ok, deployment} @@ -254,35 +257,35 @@ defmodule NervesHub.Deployments do end end - @spec broadcast(Deployment.t() | atom(), String.t(), map()) :: :ok | {:error, term()} + @spec broadcast(DeploymentGroup.t() | atom(), String.t(), map()) :: :ok | {:error, term()} def broadcast(deployment, event, payload \\ %{}) def broadcast(:none, event, payload) do message = %Phoenix.Socket.Broadcast{ - topic: "deployment:none", + topic: "deployment_group:none", event: event, payload: payload } - Phoenix.PubSub.broadcast(NervesHub.PubSub, "deployment:none", message) + Phoenix.PubSub.broadcast(NervesHub.PubSub, "deployment_group:none", message) end def broadcast(:monitor, event, payload) do Phoenix.PubSub.broadcast( NervesHub.PubSub, - "deployment:monitor", + "deployment_group:monitor", %Phoenix.Socket.Broadcast{event: event, payload: payload} ) end - def broadcast(%Deployment{id: id}, event, payload) do + def broadcast(%DeploymentGroup{id: id}, event, payload) do message = %Phoenix.Socket.Broadcast{ - topic: "deployment:#{id}", + topic: "deployment_group:#{id}", event: event, payload: payload } - Phoenix.PubSub.broadcast(NervesHub.PubSub, "deployment:#{id}", message) + Phoenix.PubSub.broadcast(NervesHub.PubSub, "deployment_group:#{id}", message) end @doc """ @@ -313,7 +316,7 @@ defmodule NervesHub.Deployments do @spec verify_deployment_membership(Device.t()) :: Device.t() def verify_deployment_membership(%Device{deployment_id: deployment_id} = device) when not is_nil(deployment_id) do - %{deployment: deployment} = device = Repo.preload(device, deployment: :firmware) + %{deployment_group: deployment} = device = Repo.preload(device, deployment_group: :firmware) bad_architecture = device.firmware_metadata.architecture != deployment.firmware.architecture bad_platform = device.firmware_metadata.platform != deployment.firmware.platform @@ -363,7 +366,7 @@ defmodule NervesHub.Deployments do [] -> set_deployment_telemetry(:none_found, device) - %{device | deployment: nil} + %{device | deployment_group: nil} [deployment] -> set_deployment_telemetry(:one_found, device, deployment) @@ -406,14 +409,14 @@ defmodule NervesHub.Deployments do ) end - @spec preload_firmware_and_archive(Deployment.t()) :: Deployment.t() + @spec preload_firmware_and_archive(DeploymentGroup.t()) :: DeploymentGroup.t() def preload_firmware_and_archive(deployment) do - %Deployment{} = Repo.preload(deployment, [:archive, :firmware]) + %DeploymentGroup{} = Repo.preload(deployment, [:archive, :firmware]) end @spec preload_with_firmware_and_archive(Device.t(), boolean()) :: Device.t() def preload_with_firmware_and_archive(device, force \\ false) do - %Device{} = Repo.preload(device, [deployment: [:archive, :firmware]], force: force) + %Device{} = Repo.preload(device, [deployment_group: [:archive, :firmware]], force: force) end @doc """ @@ -421,12 +424,12 @@ defmodule NervesHub.Deployments do Based on the product, firmware platform, firmware architecture, and device tags """ - @spec matching_deployments(Device.t(), [boolean()]) :: [Deployment.t()] + @spec matching_deployments(Device.t(), [boolean()]) :: [DeploymentGroup.t()] def matching_deployments(device, active \\ [true, false]) def matching_deployments(%Device{firmware_metadata: nil}, _active), do: [] def matching_deployments(device, active) do - Deployment + DeploymentGroup |> join(:inner, [d], assoc(d, :firmware), as: :firmware) |> preload([_, firmware: f], firmware: f) |> where([d], d.product_id == ^device.product_id) @@ -462,11 +465,11 @@ defmodule NervesHub.Deployments do This is purposefully less-strict then Deployments.matching_deployments/2 and should only be used when a human is choosing the deployment for a device. """ - @spec eligible_deployments(Device.t()) :: [Deployment.t()] + @spec eligible_deployments(Device.t()) :: [DeploymentGroup.t()] def eligible_deployments(%Device{firmware_metadata: nil}), do: [] def eligible_deployments(device) do - Deployment + DeploymentGroup |> join(:inner, [d], assoc(d, :firmware), as: :firmware) |> preload([_, firmware: f], firmware: f) |> where([d, _], d.product_id == ^device.product_id) diff --git a/lib/nerves_hub/deployments/deployment.ex b/lib/nerves_hub/managed_deployments/deployment_group.ex similarity index 82% rename from lib/nerves_hub/deployments/deployment.ex rename to lib/nerves_hub/managed_deployments/deployment_group.ex index 5f888967a..224839705 100644 --- a/lib/nerves_hub/deployments/deployment.ex +++ b/lib/nerves_hub/managed_deployments/deployment_group.ex @@ -1,4 +1,4 @@ -defmodule NervesHub.Deployments.Deployment do +defmodule NervesHub.ManagedDeployments.DeploymentGroup do use Ecto.Schema import Ecto.Changeset @@ -48,7 +48,7 @@ defmodule NervesHub.Deployments.Deployment do belongs_to(:org, Org, where: [deleted_at: nil]) belongs_to(:archive, Archive) - has_many(:inflight_updates, InflightUpdate) + has_many(:inflight_updates, InflightUpdate, foreign_key: :deployment_id) field(:conditions, :map) field(:device_failure_threshold, :integer, default: 3) @@ -66,13 +66,13 @@ defmodule NervesHub.Deployments.Deployment do field(:total_updating_devices, :integer, default: 0) field(:current_updated_devices, :integer, default: 0) field(:inflight_update_expiration_minutes, :integer, default: 60) - # TODO: (nshoes) this column is unused, remove after Managed Deploys is done + # TODO: (nshoes) this column is unused, remove after Managed Deploys is finished field(:recalculation_type, Ecto.Enum, values: [:device, :calculator_queue], default: :device) timestamps() end - def creation_changeset(%Deployment{} = deployment, params) do + def creation_changeset(%DeploymentGroup{} = deployment, params) do # set product_id by getting it from firmware with_product_id = handle_product_id(deployment, params) @@ -93,27 +93,27 @@ defmodule NervesHub.Deployments.Deployment do end end - defp handle_product_id(%Deployment{}, %{firmware: %Firmware{product_id: p_id}} = params) do + defp handle_product_id(%DeploymentGroup{}, %{firmware: %Firmware{product_id: p_id}} = params) do params |> Map.put(:product_id, p_id) end - defp handle_product_id(%Deployment{firmware: %Firmware{product_id: p_id}}, params) do + defp handle_product_id(%DeploymentGroup{firmware: %Firmware{product_id: p_id}}, params) do params |> Map.put(:product_id, p_id) end - defp handle_product_id(%Deployment{} = d, %{firmware_id: f_id} = params) do + defp handle_product_id(%DeploymentGroup{} = d, %{firmware_id: f_id} = params) do handle_product_id(d, params |> Map.put(:firmware, Firmware |> Repo.get!(f_id))) end - defp handle_product_id(%Deployment{firmware_id: nil}, params) do + defp handle_product_id(%DeploymentGroup{firmware_id: nil}, params) do params end - defp handle_product_id(%Deployment{} = d, params) do + defp handle_product_id(%DeploymentGroup{} = d, params) do handle_product_id(d |> with_firmware(), params) end - def changeset(%Deployment{} = deployment, params) do + def changeset(%DeploymentGroup{} = deployment, params) do deployment |> cast(params, @required_fields ++ @optional_fields) |> validate_required(@required_fields) @@ -128,9 +128,9 @@ defmodule NervesHub.Deployments.Deployment do |> validate_conditions() end - def with_firmware(%Deployment{firmware: %Firmware{}} = d), do: d + def with_firmware(%DeploymentGroup{firmware: %Firmware{}} = d), do: d - def with_firmware(%Deployment{} = d) do + def with_firmware(%DeploymentGroup{} = d) do d |> Repo.preload(:firmware) end @@ -140,9 +140,9 @@ defmodule NervesHub.Deployments.Deployment do |> preload(:firmware) end - def with_product(%Deployment{product: %Product{}} = d), do: d + def with_product(%DeploymentGroup{product: %Product{}} = d), do: d - def with_product(%Deployment{} = d) do + def with_product(%DeploymentGroup{} = d) do d |> Repo.preload(:product) end diff --git a/lib/nerves_hub/managed_deployments/deployment_release.ex b/lib/nerves_hub/managed_deployments/deployment_release.ex new file mode 100644 index 000000000..991ee1a7c --- /dev/null +++ b/lib/nerves_hub/managed_deployments/deployment_release.ex @@ -0,0 +1,43 @@ +defmodule NervesHub.ManagedDeployments.DeploymentRelease do + use Ecto.Schema + + import Ecto.Changeset + + alias NervesHub.Archives.Archive + alias NervesHub.ManagedDeployments.DeploymentGroup + alias NervesHub.Firmwares.Firmware + alias NervesHub.Accounts.User + + alias __MODULE__ + + @type t :: %__MODULE__{} + + @required_fields [ + :deployment_group_id, + :firmware_id, + :created_by_id, + :status + ] + + @optional_fields [:archive_id] + + schema "deployment_releases" do + belongs_to(:deployment_group, DeploymentGroup) + belongs_to(:firmware, Firmware) + belongs_to(:archive, Archive) + belongs_to(:user, User, foreign_key: :created_by_id) + + field(:status, Ecto.Enum, + values: [:inactive, :active, :error, :paused, :complete], + default: :inactive + ) + + timestamps() + end + + def changeset(%DeploymentRelease{} = deployment_release, params) do + deployment_release + |> cast(params, @required_fields ++ @optional_fields) + |> validate_required(@required_fields) + end +end diff --git a/lib/nerves_hub/managed_deployments/inflight_deployment_check.ex b/lib/nerves_hub/managed_deployments/inflight_deployment_check.ex new file mode 100644 index 000000000..0defe76e5 --- /dev/null +++ b/lib/nerves_hub/managed_deployments/inflight_deployment_check.ex @@ -0,0 +1,15 @@ +defmodule NervesHub.ManagedDeployments.InflightDeploymentCheck do + use Ecto.Schema + + @timestamps_opts updated_at: false + + alias NervesHub.ManagedDeployments.DeploymentGroup + alias NervesHub.Devices.Device + + schema "inflight_deployment_checks" do + belongs_to(:device, Device) + belongs_to(:deployment_group, DeploymentGroup, foreign_key: :deployment_id) + + timestamps() + end +end diff --git a/lib/nerves_hub/deployments/monitor.ex b/lib/nerves_hub/managed_deployments/monitor.ex similarity index 56% rename from lib/nerves_hub/deployments/monitor.ex rename to lib/nerves_hub/managed_deployments/monitor.ex index 116702ce4..251d82437 100644 --- a/lib/nerves_hub/deployments/monitor.ex +++ b/lib/nerves_hub/managed_deployments/monitor.ex @@ -1,4 +1,4 @@ -defmodule NervesHub.Deployments.Monitor do +defmodule NervesHub.ManagedDeployments.Monitor do @moduledoc """ Deployment Monitor starts a deployment orchestrator per deployment @@ -7,9 +7,9 @@ defmodule NervesHub.Deployments.Monitor do use GenServer - alias NervesHub.DeploymentDynamicSupervisor - alias NervesHub.Deployments - alias NervesHub.Deployments.Orchestrator + alias NervesHub.ManagedDeployments + alias NervesHub.ManagedDeploymentDynamicSupervisor + alias NervesHub.ManagedDeployments.Orchestrator alias Phoenix.PubSub alias Phoenix.Socket.Broadcast @@ -22,18 +22,18 @@ defmodule NervesHub.Deployments.Monitor do end def init(_) do - _ = PubSub.subscribe(NervesHub.PubSub, "deployment:monitor") + _ = PubSub.subscribe(NervesHub.PubSub, "deployment_group:monitor") {:ok, %State{}, {:continue, :boot}} end def handle_continue(:boot, state) do deployments = - Enum.into(Deployments.all(), %{}, fn deployment -> + Enum.into(ManagedDeployments.all(), %{}, fn deployment -> {:ok, orchestrator_pid} = DynamicSupervisor.start_child( - DeploymentDynamicSupervisor, - {Deployments.Orchestrator, deployment} + ManagedDeploymentDynamicSupervisor, + {ManagedDeployments.Orchestrator, deployment} ) {deployment.id, %{orchestrator_pid: orchestrator_pid}} @@ -42,13 +42,13 @@ defmodule NervesHub.Deployments.Monitor do {:noreply, %{state | deployments: deployments}} end - def handle_info(%Broadcast{event: "deployments/new", payload: payload}, state) do - {:ok, deployment} = Deployments.get(payload.deployment_id) + def handle_info(%Broadcast{event: "deployment_groups/new", payload: payload}, state) do + {:ok, deployment} = ManagedDeployments.get(payload.deployment_id) {:ok, orchestrator_pid} = DynamicSupervisor.start_child( - DeploymentDynamicSupervisor, - {Deployments.Orchestrator, deployment} + ManagedDeploymentDynamicSupervisor, + {ManagedDeployments.Orchestrator, deployment} ) deployments = @@ -57,9 +57,9 @@ defmodule NervesHub.Deployments.Monitor do {:noreply, %{state | deployments: deployments}} end - def handle_info(%Broadcast{event: "deployments/delete", payload: payload}, state) do + def handle_info(%Broadcast{event: "deployment_groups/delete", payload: payload}, state) do pid = GenServer.whereis(Orchestrator.name(payload.deployment_id)) - _ = DynamicSupervisor.terminate_child(DeploymentDynamicSupervisor, pid) + _ = DynamicSupervisor.terminate_child(ManagedDeploymentDynamicSupervisor, pid) deployments = Map.delete(state.deployments, payload.deployment_id) {:noreply, %{state | deployments: deployments}} end diff --git a/lib/nerves_hub/deployments/orchestrator.ex b/lib/nerves_hub/managed_deployments/orchestrator.ex similarity index 78% rename from lib/nerves_hub/deployments/orchestrator.ex rename to lib/nerves_hub/managed_deployments/orchestrator.ex index 866fa22e1..18df403b4 100644 --- a/lib/nerves_hub/deployments/orchestrator.ex +++ b/lib/nerves_hub/managed_deployments/orchestrator.ex @@ -1,4 +1,4 @@ -defmodule NervesHub.Deployments.Orchestrator do +defmodule NervesHub.ManagedDeployments.Orchestrator do @moduledoc """ Orchestration process to handle passing out updates to devices @@ -13,10 +13,10 @@ defmodule NervesHub.Deployments.Orchestrator do require Logger - alias NervesHub.Deployments - alias NervesHub.Deployments.Deployment alias NervesHub.Devices alias NervesHub.Devices.Device + alias NervesHub.ManagedDeployments + alias NervesHub.ManagedDeployments.DeploymentGroup alias NervesHub.Repo @@ -28,7 +28,7 @@ defmodule NervesHub.Deployments.Orchestrator do end def name(deployment_id) when is_integer(deployment_id) do - {:via, Registry, {Deployments, deployment_id}} + {:via, Registry, {ManagedDeployments, deployment_id}} end def name(deployment), do: name(deployment.id) @@ -53,14 +53,14 @@ defmodule NervesHub.Deployments.Orchestrator do As devices update and reconnect, the new orchestrator is told that the update was successful, and the process is repeated. """ - @decorate with_span("Deployments.Orchestrator.trigger_update#noop") - def trigger_update(%Deployment{is_active: false}) do + @decorate with_span("ManagedDeployments.Orchestrator.trigger_update#noop") + def trigger_update(%DeploymentGroup{is_active: false}) do :ok end - @decorate with_span("Deployments.Orchestrator.trigger_update") + @decorate with_span("ManagedDeployments.Orchestrator.trigger_update") def trigger_update(deployment) do - :telemetry.execute([:nerves_hub, :deployment, :trigger_update], %{count: 1}) + :telemetry.execute([:nerves_hub, :deployment_group, :trigger_update], %{count: 1}) match_conditions = [ {:and, {:==, {:map_get, :deployment_id, :"$1"}, deployment.id}, @@ -91,7 +91,7 @@ defmodule NervesHub.Deployments.Orchestrator do devices |> Enum.take(count) |> Enum.each(fn %{device_id: device_id, pid: pid} -> - :telemetry.execute([:nerves_hub, :deployment, :trigger_update, :device], %{count: 1}) + :telemetry.execute([:nerves_hub, :deployment_group, :trigger_update, :device], %{count: 1}) device = %Device{id: device_id} @@ -99,7 +99,7 @@ defmodule NervesHub.Deployments.Orchestrator do if Devices.count_inflight_updates_for(deployment) < deployment.concurrent_updates do case Devices.told_to_update(device, deployment) do {:ok, inflight_update} -> - send(pid, {"deployments/update", inflight_update}) + send(pid, {"deployment_groups/update", inflight_update}) :error -> Logger.error( @@ -117,9 +117,9 @@ defmodule NervesHub.Deployments.Orchestrator do {:ok, deployment, {:continue, :boot}} end - @decorate with_span("Deployments.Orchestrator.boot") + @decorate with_span("ManagedDeployments.Orchestrator.boot") def handle_continue(:boot, deployment) do - _ = PubSub.subscribe(NervesHub.PubSub, "deployment:#{deployment.id}") + _ = PubSub.subscribe(NervesHub.PubSub, "deployment_group:#{deployment.id}") # trigger every 10 minutes, plus a jitter between 1 and 5 seconds, as a back up interval = (10 + :rand.uniform(10)) * 60 * 1000 @@ -138,8 +138,8 @@ defmodule NervesHub.Deployments.Orchestrator do {:noreply, deployment} end - @decorate with_span("Deployments.Orchestrator.handle_info:deployments/update") - def handle_info(%Broadcast{event: "deployments/update"}, deployment) do + @decorate with_span("ManagedDeployments.Orchestrator.handle_info:deployment_groups/update") + def handle_info(%Broadcast{event: "deployment_groups/update"}, deployment) do deployment = deployment |> Repo.reload() @@ -151,7 +151,8 @@ defmodule NervesHub.Deployments.Orchestrator do end # Catch all for unknown broadcasts on a deployment - def handle_info(%Broadcast{topic: "deployment:" <> _}, deployment), do: {:noreply, deployment} + def handle_info(%Broadcast{topic: "deployment_group:" <> _}, deployment), + do: {:noreply, deployment} def handle_info(:trigger, deployment) do trigger_update(deployment) diff --git a/lib/nerves_hub/managed_deployments/supervisor.ex b/lib/nerves_hub/managed_deployments/supervisor.ex new file mode 100644 index 000000000..ee94eed48 --- /dev/null +++ b/lib/nerves_hub/managed_deployments/supervisor.ex @@ -0,0 +1,20 @@ +defmodule NervesHub.ManagedDeployments.Supervisor do + @moduledoc false + + use Supervisor + + def start_link(_) do + Supervisor.start_link(__MODULE__, [], name: __MODULE__) + end + + def init(_) do + children = [ + {Registry, keys: :unique, name: NervesHub.ManagedDeployments}, + NervesHub.ManagedDeployments.Monitor, + {DynamicSupervisor, + strategy: :one_for_one, name: NervesHub.ManagedDeploymentDynamicSupervisor} + ] + + Supervisor.init(children, strategy: :one_for_one) + end +end diff --git a/lib/nerves_hub/products/product.ex b/lib/nerves_hub/products/product.ex index 107712872..be38c1cec 100644 --- a/lib/nerves_hub/products/product.ex +++ b/lib/nerves_hub/products/product.ex @@ -4,11 +4,11 @@ defmodule NervesHub.Products.Product do alias NervesHub.Accounts.Org alias NervesHub.Archives.Archive - alias NervesHub.Deployments.Deployment alias NervesHub.Devices.CACertificate alias NervesHub.Devices.Device alias NervesHub.Extensions.ProductExtensionsSetting alias NervesHub.Firmwares.Firmware + alias NervesHub.ManagedDeployments.DeploymentGroup alias NervesHub.Products.SharedSecretAuth alias NervesHub.Scripts.Script @@ -23,7 +23,7 @@ defmodule NervesHub.Products.Product do has_many(:jitp, CACertificate.JITP) has_many(:archives, Archive) has_many(:scripts, Script) - has_many(:deployments, Deployment) + has_many(:deployments, DeploymentGroup) has_many(:shared_secret_auths, SharedSecretAuth, preload_order: [desc: :deactivated_at, asc: :id] diff --git a/lib/nerves_hub/types.ex b/lib/nerves_hub/types.ex index 89f64d073..9f0fc5447 100644 --- a/lib/nerves_hub/types.ex +++ b/lib/nerves_hub/types.ex @@ -73,6 +73,8 @@ defmodule NervesHub.Types do [ "Elixir.NervesHub.Accounts.Org", "Elixir.NervesHub.Accounts.User", + "Elixir.NervesHub.ManagedDeployments.DeploymentGroup", + # TODO: (nshoes) remove after migrating audit_logs actor/resource type "Elixir.NervesHub.Deployments.Deployment", "Elixir.NervesHub.Devices.Device", "Elixir.NervesHub.Firmwares.Firmware", diff --git a/lib/nerves_hub/workers/firmware_delta_builder.ex b/lib/nerves_hub/workers/firmware_delta_builder.ex index 6572eda2c..a1ce9f1ed 100644 --- a/lib/nerves_hub/workers/firmware_delta_builder.ex +++ b/lib/nerves_hub/workers/firmware_delta_builder.ex @@ -7,7 +7,7 @@ defmodule NervesHub.Workers.FirmwareDeltaBuilder do states: [:available, :scheduled, :executing] ] - alias NervesHub.Deployments + alias NervesHub.ManagedDeployments alias NervesHub.Firmwares @impl Oban.Worker @@ -17,8 +17,8 @@ defmodule NervesHub.Workers.FirmwareDeltaBuilder do {:ok, _firmware_delta} = maybe_create_firmware_delta(source, target) - Enum.each(Deployments.get_deployments_by_firmware(target_id), fn deployment -> - Deployments.broadcast(deployment, "deployments/update") + Enum.each(ManagedDeployments.get_deployments_by_firmware(target_id), fn deployment -> + ManagedDeployments.broadcast(deployment, "deployment_groups/update") end) :ok diff --git a/lib/nerves_hub_web/channels/console_channel.ex b/lib/nerves_hub_web/channels/console_channel.ex index 4ae5dd1d7..21e669d0b 100644 --- a/lib/nerves_hub_web/channels/console_channel.ex +++ b/lib/nerves_hub_web/channels/console_channel.ex @@ -73,10 +73,9 @@ defmodule NervesHubWeb.ConsoleChannel do # now that the console is connected, push down the device's elixir, line by line device = socket.assigns.device device = Repo.preload(device, [:deployment]) - deployment = device.deployment - if deployment && deployment.connecting_code do - device.deployment.connecting_code + if device.deployment_group && device.deployment_group.connecting_code do + device.deployment_group.connecting_code |> String.graphemes() |> Enum.each(fn character -> push(socket, "dn", %{"data" => character}) diff --git a/lib/nerves_hub_web/channels/device_channel.ex b/lib/nerves_hub_web/channels/device_channel.ex index 29ed69a45..d0f973729 100644 --- a/lib/nerves_hub_web/channels/device_channel.ex +++ b/lib/nerves_hub_web/channels/device_channel.ex @@ -12,11 +12,11 @@ defmodule NervesHubWeb.DeviceChannel do alias NervesHub.Archives alias NervesHub.AuditLogs.Templates - alias NervesHub.Deployments alias NervesHub.Devices alias NervesHub.Devices.Device alias NervesHub.Firmwares alias NervesHub.Helpers.Logging + alias NervesHub.ManagedDeployments alias NervesHub.Repo alias Phoenix.Socket.Broadcast @@ -38,8 +38,8 @@ defmodule NervesHubWeb.DeviceChannel do def handle_info({:after_join, params}, %{assigns: %{device: device}} = socket) do device = device - |> Deployments.verify_deployment_membership() - |> Deployments.set_deployment() + |> ManagedDeployments.verify_deployment_membership() + |> ManagedDeployments.set_deployment() maybe_send_public_keys(device, socket, params) @@ -54,6 +54,9 @@ defmodule NervesHubWeb.DeviceChannel do send(self(), :device_registration) + # Get device extension capabilities + push(socket, "extensions:get", %{}) + socket = socket |> assign(:device, device) @@ -118,12 +121,15 @@ defmodule NervesHubWeb.DeviceChannel do {:noreply, socket} end - def handle_info(%Broadcast{event: "deployments/update"}, socket) do + def handle_info(%Broadcast{event: "deployment_groups/update"}, socket) do {:noreply, socket} end - @decorate with_span("Channels.DeviceChannel.handle_info:deployments/update") - def handle_info({"deployments/update", inflight_update}, %{assigns: %{device: device}} = socket) do + @decorate with_span("Channels.DeviceChannel.handle_info:deployment_groups/update") + def handle_info( + {"deployment_groups/update", inflight_update}, + %{assigns: %{device: device}} = socket + ) do device = deployment_preload(device) payload = Devices.resolve_update(device) @@ -449,14 +455,14 @@ defmodule NervesHubWeb.DeviceChannel do defp deployment_channel(device) do if device.deployment_id do - "deployment:#{device.deployment_id}" + "deployment_group:#{device.deployment_id}" else - "deployment:none" + "deployment_group:none" end end defp deployment_preload(device) do - Repo.preload(device, [deployment: [:archive, :firmware]], force: true) + Repo.preload(device, [deployment_group: [:archive, :firmware]], force: true) end defp maybe_send_archive(socket) do @@ -466,8 +472,8 @@ defmodule NervesHubWeb.DeviceChannel do version_match = Version.match?(socket.assigns.device_api_version, ">= 2.0.0") if updates_enabled && version_match do - if device.deployment && device.deployment.archive do - archive = device.deployment.archive + if device.deployment_group && device.deployment_group.archive do + archive = device.deployment_group.archive push(socket, "archive", %{ size: archive.size, diff --git a/lib/nerves_hub_web/components/navigation.ex b/lib/nerves_hub_web/components/navigation.ex index ec4c8ba94..668e66147 100644 --- a/lib/nerves_hub_web/components/navigation.ex +++ b/lib/nerves_hub_web/components/navigation.ex @@ -27,7 +27,7 @@ defmodule NervesHubWeb.Components.Navigation do stroke-linejoin="round" /> - <.nav_link label="Deployments" path={~p"/org/#{@org.name}/#{@product.name}/deployments"} current={@current}> + <.nav_link label="Deployments" path={~p"/org/#{@org.name}/#{@product.name}/deployment_groups"} current={@current}> put_status(:created) |> put_resp_header( "location", - Routes.api_deployment_path(conn, :show, org.name, product.name, deployment.name) + Routes.api_deployment_group_path(conn, :show, org.name, product.name, deployment.name) ) - |> render("show.json", deployment: %{deployment | firmware: firmware}) + |> render("show.json", deployment_group: %{deployment | firmware: firmware}) end end end def show(%{assigns: %{org: _org, product: product}} = conn, %{"name" => name}) do - with {:ok, deployment} <- Deployments.get_deployment_by_name(product, name) do - render(conn, "show.json", deployment: deployment) + with {:ok, deployment} <- ManagedDeployments.get_deployment_by_name(product, name) do + render(conn, "show.json", deployment_group: deployment) end end @@ -56,24 +56,24 @@ defmodule NervesHubWeb.API.DeploymentController do "name" => name, "deployment" => deployment_params }) do - with {:ok, deployment} <- Deployments.get_deployment_by_name(product, name), + with {:ok, deployment} <- ManagedDeployments.get_deployment_by_name(product, name), {:ok, deployment_params} <- update_params(product, deployment_params), deployment_params <- whitelist(deployment_params, @whitelist_fields), - {:ok, %Deployment{} = updated_deployment} <- - Deployments.update_deployment(deployment, deployment_params) do + {:ok, %DeploymentGroup{} = updated_deployment} <- + ManagedDeployments.update_deployment(deployment, deployment_params) do AuditLogs.audit!( user, deployment, "#{user.name} updated deployment #{deployment.name}" ) - render(conn, "show.json", deployment: updated_deployment) + render(conn, "show.json", deployment_group: updated_deployment) end end def delete(%{assigns: %{product: product}} = conn, %{"name" => name}) do - with {:ok, deployment} <- Deployments.get_deployment_by_name(product, name), - {:ok, _deployment} <- Deployments.delete_deployment(deployment) do + with {:ok, deployment} <- ManagedDeployments.get_deployment_by_name(product, name), + {:ok, _deployment} <- ManagedDeployments.delete_deployment(deployment) do send_resp(conn, :no_content, "") end end diff --git a/lib/nerves_hub_web/controllers/api/device_controller.ex b/lib/nerves_hub_web/controllers/api/device_controller.ex index e2c25845d..e5946ee9a 100644 --- a/lib/nerves_hub_web/controllers/api/device_controller.ex +++ b/lib/nerves_hub_web/controllers/api/device_controller.ex @@ -40,7 +40,7 @@ defmodule NervesHubWeb.API.DeviceController do |> Map.put("product_id", product.id) with {:ok, device} <- Devices.create_device(params) do - device = Repo.preload(device, [:org, :product, deployment: [:firmware]]) + device = Repo.preload(device, [:org, :product, deployment_group: [:firmware]]) conn |> put_status(:created) @@ -85,7 +85,8 @@ defmodule NervesHubWeb.API.DeviceController do def update(%{assigns: %{org: org}} = conn, %{"identifier" => identifier} = params) do with {:ok, device} <- Devices.get_device_by_identifier(org, identifier), {:ok, updated_device} <- Devices.update_device(device, params) do - updated_device = Repo.preload(updated_device, [:org, :product, deployment: [:firmware]]) + updated_device = + Repo.preload(updated_device, [:org, :product, deployment_group: [:firmware]]) conn |> put_status(201) @@ -99,7 +100,7 @@ defmodule NervesHubWeb.API.DeviceController do {:ok, %DeviceCertificate{device_id: device_id}} <- Devices.get_device_certificate_by_x509(cert), {:ok, device} <- Devices.get_device_by_org(org, device_id) do - device = Repo.preload(device, [:org, :product, deployment: [:firmware]]) + device = Repo.preload(device, [:org, :product, deployment_group: [:firmware]]) conn |> put_status(200) @@ -219,7 +220,7 @@ defmodule NervesHubWeb.API.DeviceController do _ = NervesHubWeb.Endpoint.broadcast( "device:#{device.id}", - "deployments/update", + "deployment_groups/update", payload ) diff --git a/lib/nerves_hub_web/controllers/api/fallback_controller.ex b/lib/nerves_hub_web/controllers/api/fallback_controller.ex index e3caa123f..a6945f4e0 100644 --- a/lib/nerves_hub_web/controllers/api/fallback_controller.ex +++ b/lib/nerves_hub_web/controllers/api/fallback_controller.ex @@ -51,6 +51,6 @@ defmodule NervesHubWeb.API.FallbackController do end defp conflict_error?(error) do - error in [:deployments, :firmwares, :devices] + error in [:deployment_groups, :firmwares, :devices] end end diff --git a/lib/nerves_hub_web/controllers/deployment_controller.ex b/lib/nerves_hub_web/controllers/deployment_controller.ex index 22a166637..b2ea20330 100644 --- a/lib/nerves_hub_web/controllers/deployment_controller.ex +++ b/lib/nerves_hub_web/controllers/deployment_controller.ex @@ -2,7 +2,7 @@ defmodule NervesHubWeb.DeploymentController do use NervesHubWeb, :controller alias NervesHub.AuditLogs - alias NervesHub.Deployments + alias NervesHub.ManagedDeployments plug(:validate_role, org: :view) @@ -10,13 +10,13 @@ defmodule NervesHubWeb.DeploymentController do %{assigns: %{org: org, product: product}} = conn, %{"name" => deployment_name} ) do - {:ok, deployment} = Deployments.get_deployment_by_name(product, deployment_name) + {:ok, deployment} = ManagedDeployments.get_deployment_by_name(product, deployment_name) case AuditLogs.logs_for(deployment) do [] -> conn |> put_flash(:error, "No audit logs exist for this deployment.") - |> redirect(to: ~p"/org/#{org.name}/#{product.name}/deployments") + |> redirect(to: ~p"/org/#{org.name}/#{product.name}/deployment_groups") audit_logs -> audit_logs = AuditLogs.format_for_csv(audit_logs) diff --git a/lib/nerves_hub_web/helpers/authorization.ex b/lib/nerves_hub_web/helpers/authorization.ex index 3969fc319..f5d24de3f 100644 --- a/lib/nerves_hub_web/helpers/authorization.ex +++ b/lib/nerves_hub_web/helpers/authorization.ex @@ -46,10 +46,10 @@ defmodule NervesHubWeb.Helpers.Authorization do def authorized?(:"archive:upload", %OrgUser{role: role}), do: role_check(:manage, role) def authorized?(:"archive:delete", %OrgUser{role: role}), do: role_check(:manage, role) - def authorized?(:"deployment:create", %OrgUser{role: role}), do: role_check(:manage, role) - def authorized?(:"deployment:update", %OrgUser{role: role}), do: role_check(:manage, role) - def authorized?(:"deployment:toggle", %OrgUser{role: role}), do: role_check(:manage, role) - def authorized?(:"deployment:delete", %OrgUser{role: role}), do: role_check(:manage, role) + def authorized?(:"deployment_group:create", %OrgUser{role: role}), do: role_check(:manage, role) + def authorized?(:"deployment_group:update", %OrgUser{role: role}), do: role_check(:manage, role) + def authorized?(:"deployment_group:toggle", %OrgUser{role: role}), do: role_check(:manage, role) + def authorized?(:"deployment_group:delete", %OrgUser{role: role}), do: role_check(:manage, role) def authorized?(:"support_script:create", %OrgUser{role: role}), do: role_check(:manage, role) def authorized?(:"support_script:update", %OrgUser{role: role}), do: role_check(:manage, role) diff --git a/lib/nerves_hub_web/live/dashboard/index.ex b/lib/nerves_hub_web/live/dashboard/index.ex index a3b3d17fd..bc7795563 100644 --- a/lib/nerves_hub_web/live/dashboard/index.ex +++ b/lib/nerves_hub_web/live/dashboard/index.ex @@ -1,9 +1,9 @@ defmodule NervesHubWeb.Live.Dashboard.Index do use NervesHubWeb, :updated_live_view - alias NervesHub.Deployments alias NervesHub.Devices alias NervesHub.Devices.Device + alias NervesHub.ManagedDeployments alias Phoenix.Socket.Broadcast @@ -93,7 +93,7 @@ defmodule NervesHubWeb.Live.Dashboard.Index do Devices.get_minimal_device_location_by_org_id_and_product_id(org.id, product.id) latest_firmwares = - Deployments.get_deployments_by_product(product) + ManagedDeployments.get_deployments_by_product(product) |> Enum.reduce(%{}, fn deployment, acc -> Map.put(acc, deployment.firmware.uuid, deployment.firmware.platform) end) diff --git a/lib/nerves_hub_web/live/deployments/edit.ex b/lib/nerves_hub_web/live/deployment_groups/edit.ex similarity index 82% rename from lib/nerves_hub_web/live/deployments/edit.ex rename to lib/nerves_hub_web/live/deployment_groups/edit.ex index 56d9e2e1d..dc826d017 100644 --- a/lib/nerves_hub_web/live/deployments/edit.ex +++ b/lib/nerves_hub_web/live/deployment_groups/edit.ex @@ -1,10 +1,10 @@ -defmodule NervesHubWeb.Live.Deployments.Edit do +defmodule NervesHubWeb.Live.DeploymentGroup.Edit do use NervesHubWeb, :updated_live_view alias NervesHub.Archives alias NervesHub.AuditLogs - alias NervesHub.Deployments - alias NervesHub.Deployments.Deployment + alias NervesHub.ManagedDeployments + alias NervesHub.ManagedDeployments.DeploymentGroup alias NervesHub.Firmwares alias NervesHub.Firmwares.Firmware @@ -13,18 +13,18 @@ defmodule NervesHubWeb.Live.Deployments.Edit do %{"name" => name} = params %{product: product} = socket.assigns - deployment = Deployments.get_by_product_and_name!(product, name) + deployment = ManagedDeployments.get_by_product_and_name!(product, name) - current_device_count = Deployments.get_device_count(deployment) + current_device_count = ManagedDeployments.get_device_count(deployment) archives = Archives.all_by_product(deployment.product) firmwares = Firmwares.get_firmwares_for_deployment(deployment) - changeset = Deployment.changeset(deployment, %{}) |> tags_to_string() + changeset = DeploymentGroup.changeset(deployment, %{}) |> tags_to_string() socket |> assign(:archives, archives) - |> assign(:deployment, deployment) + |> assign(:deployment_group, deployment) |> assign(:current_device_count, current_device_count) |> assign(:firmware, deployment.firmware) |> assign(:firmwares, firmwares) @@ -33,15 +33,15 @@ defmodule NervesHubWeb.Live.Deployments.Edit do end @impl Phoenix.LiveView - def handle_event("update-deployment", %{"deployment" => params}, socket) do - %{org_user: org_user, org: org, product: product, user: user, deployment: deployment} = + def handle_event("update-deployment", %{"deployment_group" => params}, socket) do + %{org_user: org_user, org: org, product: product, user: user, deployment_group: deployment} = socket.assigns - authorized!(:"deployment:update", org_user) + authorized!(:"deployment_group:update", org_user) params = inject_conditions_map(params) - case Deployments.update_deployment(deployment, params) do + case ManagedDeployments.update_deployment(deployment, params) do {:ok, updated} -> # Use original deployment so changes will get # marked in audit log @@ -53,7 +53,9 @@ defmodule NervesHubWeb.Live.Deployments.Edit do socket |> put_flash(:info, "Deployment updated") - |> push_navigate(to: ~p"/org/#{org.name}/#{product.name}/deployments/#{updated.name}") + |> push_navigate( + to: ~p"/org/#{org.name}/#{product.name}/deployment_groups/#{updated.name}" + ) |> noreply() {:error, changeset} -> @@ -100,8 +102,8 @@ defmodule NervesHubWeb.Live.Deployments.Edit do |> Enum.map(&[value: &1.id, key: firmware_display_name(&1)]) end - def archive_dropdown_options(acrhives) do - acrhives + def archive_dropdown_options(archives) do + archives |> Enum.sort_by( fn archive -> case Version.parse(archive.version) do diff --git a/lib/nerves_hub_web/live/deployments/edit.html.heex b/lib/nerves_hub_web/live/deployment_groups/edit.html.heex similarity index 99% rename from lib/nerves_hub_web/live/deployments/edit.html.heex rename to lib/nerves_hub_web/live/deployment_groups/edit.html.heex index 0dc467353..b9e9e30cd 100644 --- a/lib/nerves_hub_web/live/deployments/edit.html.heex +++ b/lib/nerves_hub_web/live/deployment_groups/edit.html.heex @@ -1,4 +1,4 @@ -<.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployments/#{@deployment.name}"} class="back-link"> +<.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployment_groups/#{@deployment_group.name}"} class="back-link"> Back to Deployment

Edit Deployment

diff --git a/lib/nerves_hub_web/live/deployment_groups/index.ex b/lib/nerves_hub_web/live/deployment_groups/index.ex new file mode 100644 index 000000000..019ef4e79 --- /dev/null +++ b/lib/nerves_hub_web/live/deployment_groups/index.ex @@ -0,0 +1,35 @@ +defmodule NervesHubWeb.Live.DeploymentGroup.Index do + use NervesHubWeb, :updated_live_view + + alias NervesHub.Firmwares.Firmware + alias NervesHub.ManagedDeployments + alias NervesHub.ManagedDeployments.DeploymentGroup + + @impl Phoenix.LiveView + def mount(_params, _session, %{assigns: %{product: product}} = socket) do + deployments = ManagedDeployments.get_deployments_by_product(product) + counts = ManagedDeployments.get_device_counts_by_product(product) + + deployments = + deployments + |> Enum.sort_by(& &1.name) + |> Enum.group_by(fn deployment -> + deployment.firmware.platform + end) + + socket + |> page_title("Deployments - #{product.name}") + |> assign(:deployment_groups, deployments) + |> assign(:counts, counts) + |> ok() + end + + defp firmware_simple_display_name(%Firmware{} = f) do + "#{f.version} #{f.uuid}" + end + + defp version(%DeploymentGroup{conditions: %{"version" => ""}}), do: "-" + defp version(%DeploymentGroup{conditions: %{"version" => version}}), do: version + + defp tags(%DeploymentGroup{conditions: %{"tags" => tags}}), do: tags +end diff --git a/lib/nerves_hub_web/live/deployments/index.html.heex b/lib/nerves_hub_web/live/deployment_groups/index.html.heex similarity index 89% rename from lib/nerves_hub_web/live/deployments/index.html.heex rename to lib/nerves_hub_web/live/deployment_groups/index.html.heex index 97a7ce632..4f0e6f2a7 100644 --- a/lib/nerves_hub_web/live/deployments/index.html.heex +++ b/lib/nerves_hub_web/live/deployment_groups/index.html.heex @@ -1,9 +1,9 @@ -<%= if Enum.empty?(@deployments) do %> +<%= if Enum.empty?(@deployment_groups) do %>
No deployments

<%= @product.name %> doesn’t have any deployments configured

- <.link class="btn btn-outline-light btn-action" aria-label="Setup a deployment" navigate={~p"/org/#{@org.name}/#{@product.name}/deployments/new"}> + <.link class="btn btn-outline-light btn-action" aria-label="Setup a deployment" navigate={~p"/org/#{@org.name}/#{@product.name}/deployment_groups/new"}>
Setup your first Deployment @@ -12,13 +12,13 @@ <% else %>

Deployments

- <.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployments/new"} class="btn btn-outline-light btn-action" aria-label="Create deployment"> + <.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployment_groups/new"} class="btn btn-outline-light btn-action" aria-label="Create deployment"> Create Deployment
- <%= for {platform, deployments} <- @deployments do %> + <%= for {platform, deployments} <- @deployment_groups do %>

<%= platform %>

@@ -36,7 +36,7 @@
Name
- <.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployments/#{deployment.name}"}><%= deployment.name %> + <.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployment_groups/#{deployment.name}"}><%= deployment.name %>
@@ -87,7 +87,7 @@ options
- <.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployments"} class="btn btn-secondary"> + <.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployment_groups"} class="btn btn-secondary"> Cancel
@@ -69,7 +69,7 @@
- <.link class="btn btn-secondary" navigate={~p"/org/#{@org.name}/#{@product.name}/deployments"}>Back + <.link class="btn btn-secondary" navigate={~p"/org/#{@org.name}/#{@product.name}/deployment_groups"}>Back <%= submit("Create Deployment", class: "btn btn-primary") %>
diff --git a/lib/nerves_hub_web/live/deployments/show.ex b/lib/nerves_hub_web/live/deployment_groups/show.ex similarity index 71% rename from lib/nerves_hub_web/live/deployments/show.ex rename to lib/nerves_hub_web/live/deployment_groups/show.ex index b921537ea..c56ee1b8a 100644 --- a/lib/nerves_hub_web/live/deployments/show.ex +++ b/lib/nerves_hub_web/live/deployment_groups/show.ex @@ -1,9 +1,9 @@ -defmodule NervesHubWeb.Live.Deployments.Show do +defmodule NervesHubWeb.Live.DeploymentGroup.Show do use NervesHubWeb, :updated_live_view alias NervesHub.AuditLogs - alias NervesHub.Deployments - alias NervesHub.Deployments.Deployment + alias NervesHub.ManagedDeployments + alias NervesHub.ManagedDeployments.DeploymentGroup alias NervesHub.Devices alias NervesHub.Firmwares.Firmware @@ -14,7 +14,7 @@ defmodule NervesHubWeb.Live.Deployments.Show do %{"name" => name} = params %{product: product} = socket.assigns - deployment = Deployments.get_by_product_and_name!(product, name) + deployment = ManagedDeployments.get_by_product_and_name!(product, name) {logs, audit_pager} = AuditLogs.logs_for_feed(deployment, %{ @@ -30,11 +30,11 @@ defmodule NervesHubWeb.Live.Deployments.Show do |> Map.put(:anchor, "latest-activity") inflight_updates = Devices.inflight_updates_for(deployment) - current_device_count = Deployments.get_device_count(deployment) + current_device_count = ManagedDeployments.get_device_count(deployment) socket |> page_title("Deployment - #{deployment.name} - #{product.name}") - |> assign(:deployment, deployment) + |> assign(:deployment_group, deployment) |> assign(:audit_logs, logs) |> assign(:audit_pager, audit_pager) |> assign(:inflight_updates, inflight_updates) @@ -55,12 +55,12 @@ defmodule NervesHubWeb.Live.Deployments.Show do @impl Phoenix.LiveView def handle_event("toggle", _params, socket) do - authorized!(:"deployment:toggle", socket.assigns.org_user) + authorized!(:"deployment_group:toggle", socket.assigns.org_user) - %{deployment: deployment, user: user} = socket.assigns + %{deployment_group: deployment, user: user} = socket.assigns value = !deployment.is_active - {:ok, deployment} = Deployments.update_deployment(deployment, %{is_active: value}) + {:ok, deployment} = ManagedDeployments.update_deployment(deployment, %{is_active: value}) active_str = if value, do: "active", else: "inactive" description = "#{user.name} marked deployment #{deployment.name} #{active_str}" @@ -68,24 +68,24 @@ defmodule NervesHubWeb.Live.Deployments.Show do socket |> put_flash(:info, "Deployment set #{active_str}") - |> assign(:deployment, deployment) + |> assign(:deployment_group, deployment) |> noreply() end def handle_event("delete", _params, socket) do - authorized!(:"deployment:delete", socket.assigns.org_user) + authorized!(:"deployment_group:delete", socket.assigns.org_user) - %{deployment: deployment, org: org, product: product, user: user} = socket.assigns + %{deployment_group: deployment, org: org, product: product, user: user} = socket.assigns description = "#{user.name} deleted deployment #{deployment.name}" AuditLogs.audit!(user, deployment, description) - {:ok, _} = Deployments.delete_deployment(deployment) + {:ok, _} = ManagedDeployments.delete_deployment(deployment) socket |> put_flash(:info, "Deployment successfully deleted") - |> push_navigate(to: ~p"/org/#{org.name}/#{product.name}/deployments") + |> push_navigate(to: ~p"/org/#{org.name}/#{product.name}/deployment_groups") |> noreply() end @@ -123,8 +123,8 @@ defmodule NervesHubWeb.Live.Deployments.Show do end end - defp version(%Deployment{conditions: %{"version" => ""}}), do: "-" - defp version(%Deployment{conditions: %{"version" => version}}), do: version + defp version(%DeploymentGroup{conditions: %{"version" => ""}}), do: "-" + defp version(%DeploymentGroup{conditions: %{"version" => version}}), do: version defp firmware_summary(%Firmware{version: nil}) do ["Unknown"] @@ -134,19 +134,18 @@ defmodule NervesHubWeb.Live.Deployments.Show do ["#{firmware_display_name(f)}"] end - defp firmware_summary(%Deployment{firmware: %Firmware{} = f}) do + defp firmware_summary(%DeploymentGroup{firmware: %Firmware{} = f}) do firmware_summary(f) end - defp firmware_summary(%Deployment{firmware: %Ecto.Association.NotLoaded{}} = deployment) do - Deployments.preload_firmware_and_archive(deployment) - |> firmware_summary() + defp firmware_summary(%DeploymentGroup{firmware: %Ecto.Association.NotLoaded{}} = deployment) do + ManagedDeployments.preload_firmware_and_archive(deployment) end - defp tags(%Deployment{conditions: %{"tags" => tags}}), do: tags + defp tags(%DeploymentGroup{conditions: %{"tags" => tags}}), do: tags - defp opposite_status(%Deployment{is_active: true}), do: "Off" - defp opposite_status(%Deployment{is_active: false}), do: "On" + defp opposite_status(%DeploymentGroup{is_active: true}), do: "Off" + defp opposite_status(%DeploymentGroup{is_active: false}), do: "On" defp firmware_display_name(%Firmware{} = f) do "#{f.version} #{f.platform} #{f.architecture} #{f.uuid}" diff --git a/lib/nerves_hub_web/live/deployments/show.html.heex b/lib/nerves_hub_web/live/deployment_groups/show.html.heex similarity index 75% rename from lib/nerves_hub_web/live/deployments/show.html.heex rename to lib/nerves_hub_web/live/deployment_groups/show.html.heex index a32eb4030..9831978b3 100644 --- a/lib/nerves_hub_web/live/deployments/show.html.heex +++ b/lib/nerves_hub_web/live/deployment_groups/show.html.heex @@ -1,14 +1,14 @@
- <.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployments"} class="back-link"> + <.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployment_groups"} class="back-link"> All Deployments
- <.link phx-click="toggle" class="btn btn-outline-light btn-action" aria-label={"Turn #{opposite_status(@deployment)} deployment"}> + <.link phx-click="toggle" class="btn btn-outline-light btn-action" aria-label={"Turn #{opposite_status(@deployment_group)} deployment"}> - Turn <%= opposite_status(@deployment) %> + Turn <%= opposite_status(@deployment_group) %> - <.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployments/#{@deployment.name}/edit"} class="btn btn-outline-light btn-action" aria-label="Edit"> + <.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployment_groups/#{@deployment_group.name}/edit"} class="btn btn-outline-light btn-action" aria-label="Edit"> Edit @@ -18,20 +18,25 @@ Delete - <.link href={~p"/org/#{@org.name}/#{@product.name}/deployments/#{@deployment.name}/audit_logs/download"} class="btn btn-outline-light btn-action" aria-label="Download Audit Logs" download> + <.link + href={~p"/org/#{@org.name}/#{@product.name}/deployment_groups/#{@deployment_group.name}/audit_logs/download"} + class="btn btn-outline-light btn-action" + aria-label="Download Audit Logs" + download + >
Download Audit Logs
-

<%= @deployment.name %>

+

<%= @deployment_group.name %>

State
-

- <%= if @deployment.is_active, do: "On", else: "Off" %> +

+ <%= if @deployment_group.is_active, do: "On", else: "Off" %>

@@ -61,6 +66,16 @@
+
Distributed to
+ <%= if Enum.count(tags(@deployment_group)) > 0 do %> + <%= for tag <- tags(@deployment_group) do %> + + <%= tag %> + + <% end %> + <% else %> +

No tags selected, not being distributed

+ <% end %>
Firmware version
@@ -76,7 +91,7 @@ The number of devices that will update at any given time. This is a soft limit and concurrent updates may be slightly above this number.
-

<%= @deployment.concurrent_updates %>

+

<%= @deployment_group.concurrent_updates %>

@@ -84,7 +99,7 @@ The number of minutes before an inflight update expires to clear the queue
-

<%= @deployment.inflight_update_expiration_minutes %>

+

<%= @deployment_group.inflight_update_expiration_minutes %>

@@ -95,7 +110,7 @@ <%= help_message_for(:failure_rate) %>
-

<%= @deployment.failure_rate_amount %> devices per <%= @deployment.failure_rate_seconds %> seconds

+

<%= @deployment_group.failure_rate_amount %> devices per <%= @deployment_group.failure_rate_seconds %> seconds

@@ -105,7 +120,7 @@ <%= help_message_for(:failure_threshold) %>
-

<%= @deployment.failure_threshold %>

+

<%= @deployment_group.failure_threshold %>

@@ -117,7 +132,7 @@ <%= help_message_for(:device_failure_rate) %> -

<%= @deployment.device_failure_rate_amount %> failures per <%= @deployment.device_failure_rate_seconds %> seconds

+

<%= @deployment_group.device_failure_rate_amount %> failures per <%= @deployment_group.device_failure_rate_seconds %> seconds

@@ -127,7 +142,7 @@ <%= help_message_for(:device_failure_threshold) %>
-

<%= @deployment.device_failure_threshold %>

+

<%= @deployment_group.device_failure_threshold %>

@@ -139,14 +154,14 @@ <%= help_message_for(:penalty_timeout_minutes) %> -

<%= @deployment.penalty_timeout_minutes %> minutes

+

<%= @deployment_group.penalty_timeout_minutes %> minutes

Code sent on Device Connect
- <%= if @deployment.connecting_code do %> -
<%= @deployment.connecting_code %>
+ <%= if @deployment_group.connecting_code do %> +
<%= @deployment_group.connecting_code %>
<% else %>

-

<% end %> @@ -164,8 +179,8 @@
Distributed to
- <%= if Enum.count(tags(@deployment)) > 0 do %> - <%= for tag <- tags(@deployment) do %> + <%= if Enum.count(tags(@deployment_group)) > 0 do %> + <%= for tag <- tags(@deployment_group) do %> <%= tag %> @@ -176,7 +191,7 @@
Version requirement
-

<%= version(@deployment) %>

+

<%= version(@deployment_group) %>

@@ -189,8 +204,8 @@
Progress
-
- <%= deployment_percentage(@deployment) %>% +
+ <%= deployment_percentage(@deployment_group) %>%
diff --git a/lib/nerves_hub_web/live/deployments/index.ex b/lib/nerves_hub_web/live/deployments/index.ex deleted file mode 100644 index a356a28b5..000000000 --- a/lib/nerves_hub_web/live/deployments/index.ex +++ /dev/null @@ -1,35 +0,0 @@ -defmodule NervesHubWeb.Live.Deployments.Index do - use NervesHubWeb, :updated_live_view - - alias NervesHub.Deployments - alias NervesHub.Deployments.Deployment - alias NervesHub.Firmwares.Firmware - - @impl Phoenix.LiveView - def mount(_params, _session, %{assigns: %{product: product}} = socket) do - deployments = Deployments.get_deployments_by_product(product) - counts = Deployments.get_device_counts_by_product(product) - - deployments = - deployments - |> Enum.sort_by(& &1.name) - |> Enum.group_by(fn deployment -> - deployment.firmware.platform - end) - - socket - |> page_title("Deployments - #{product.name}") - |> assign(:deployments, deployments) - |> assign(:counts, counts) - |> ok() - end - - defp firmware_simple_display_name(%Firmware{} = f) do - "#{f.version} #{f.uuid}" - end - - defp version(%Deployment{conditions: %{"version" => ""}}), do: "-" - defp version(%Deployment{conditions: %{"version" => version}}), do: version - - defp tags(%Deployment{conditions: %{"tags" => tags}}), do: tags -end diff --git a/lib/nerves_hub_web/live/devices/show.ex b/lib/nerves_hub_web/live/devices/show.ex index d222bf442..fa907d477 100644 --- a/lib/nerves_hub_web/live/devices/show.ex +++ b/lib/nerves_hub_web/live/devices/show.ex @@ -5,7 +5,6 @@ defmodule NervesHubWeb.Live.Devices.Show do alias NervesHub.AuditLogs alias NervesHub.AuditLogs.Templates - alias NervesHub.Deployments alias NervesHub.Devices alias NervesHub.Devices.Alarms alias NervesHub.Devices.Connections @@ -13,6 +12,7 @@ defmodule NervesHubWeb.Live.Devices.Show do alias NervesHub.Devices.UpdatePayload alias NervesHub.Extensions.Health alias NervesHub.Firmwares + alias NervesHub.ManagedDeployments alias NervesHub.Repo alias NervesHub.Scripts alias NervesHub.Tracker @@ -49,7 +49,7 @@ defmodule NervesHubWeb.Live.Devices.Show do |> schedule_health_check_timer() |> assign(:fwup_progress, nil) |> audit_log_assigns(1) - |> assign(:eligible_deployments, Deployments.eligible_deployments(device)) + |> assign(:eligible_deployments, ManagedDeployments.eligible_deployments(device)) |> ok() end @@ -76,7 +76,7 @@ defmodule NervesHubWeb.Live.Devices.Show do device = device |> Repo.reload() - |> Repo.preload(:deployment) + |> Repo.preload(:deployment_group) {:noreply, general_assigns(socket, device)} end @@ -276,7 +276,7 @@ defmodule NervesHubWeb.Live.Devices.Show do socket |> assign(:device, device) - |> assign(:deployment, deployment) + |> assign(:deployment_group, deployment) |> put_flash(:info, "Deployment successfully updated") |> noreply() end @@ -313,7 +313,7 @@ defmodule NervesHubWeb.Live.Devices.Show do def handle_event("push-available-update", _, socket) do authorized!(:"device:push-update", socket.assigns.org_user) - %{device: device, deployment: deployment, user: user} = socket.assigns + %{device: device, deployment_group: deployment, user: user} = socket.assigns deployment = NervesHub.Repo.preload(deployment, :firmware) @@ -327,7 +327,7 @@ defmodule NervesHubWeb.Live.Devices.Show do _ = NervesHubWeb.Endpoint.broadcast( "device:#{device.id}", - "deployments/update", + "deployment_groups/update", inflight_update ) @@ -382,12 +382,12 @@ defmodule NervesHubWeb.Live.Devices.Show do device = device |> Devices.clear_deployment() - |> Repo.preload(:deployment) + |> Repo.preload(:deployment_group) socket |> assign(:device, device) - |> assign(:deployment, nil) - |> assign(:eligible_deployments, Deployments.eligible_deployments(device)) + |> assign(:deployment_group, nil) + |> assign(:eligible_deployments, ManagedDeployments.eligible_deployments(device)) |> put_flash(:info, "Device successfully removed from the deployment") |> noreply() end @@ -465,9 +465,9 @@ defmodule NervesHubWeb.Live.Devices.Show do end defp connecting_code(device) do - if device.deployment && device.deployment.connecting_code do + if device.deployment_group && device.deployment_group.connecting_code do """ - #{device.deployment.connecting_code} + #{device.deployment_group.connecting_code} #{device.connecting_code} """ else @@ -512,7 +512,7 @@ defmodule NervesHubWeb.Live.Devices.Show do |> assign(:firmwares, Firmwares.get_firmware_for_device(device)) |> assign(:alarms, Alarms.get_current_alarms_for_device(device)) |> assign(:latest_metrics, Metrics.get_latest_metric_set(device.id)) - |> assign(:deployment, device.deployment) + |> assign(:deployment_group, device.deployment_group) |> assign(:device_connection, device_connection(device)) |> assign(:device, device) end diff --git a/lib/nerves_hub_web/live/devices/show.html.heex b/lib/nerves_hub_web/live/devices/show.html.heex index 289fc59e9..de4db1d63 100644 --- a/lib/nerves_hub_web/live/devices/show.html.heex +++ b/lib/nerves_hub_web/live/devices/show.html.heex @@ -221,10 +221,10 @@
Assigned Deployment
-
+
- <.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployments/#{@deployment.name}"} class="badge"> -
<%= @deployment.name %>
+ <.link navigate={~p"/org/#{@org.name}/#{@product.name}/deployment_groups/#{@deployment_group.name}"} class="badge"> +
<%= @deployment_group.name %>
@@ -238,7 +238,7 @@
- No Assigned Deployment + No Assigned Deployment
diff --git a/lib/nerves_hub_web/router.ex b/lib/nerves_hub_web/router.ex index cd9a4782c..9146dc6a4 100644 --- a/lib/nerves_hub_web/router.ex +++ b/lib/nerves_hub_web/router.ex @@ -167,12 +167,12 @@ defmodule NervesHubWeb.Router do delete("/:uuid", FirmwareController, :delete) end - scope "/deployments" do - get("/", DeploymentController, :index) - post("/", DeploymentController, :create) - get("/:name", DeploymentController, :show) - put("/:name", DeploymentController, :update) - delete("/:name", DeploymentController, :delete) + scope "/deployment_groups" do + get("/", DeploymentGroupController, :index) + post("/", DeploymentGroupController, :create) + get("/:name", DeploymentGroupController, :show) + put("/:name", DeploymentGroupController, :update) + delete("/:name", DeploymentGroupController, :delete) end end end @@ -216,7 +216,7 @@ defmodule NervesHubWeb.Router do get("/archives/:uuid/download", DownloadController, :archive) get("/firmware/:uuid/download", DownloadController, :firmware) - get("/deployments/:name/audit_logs/download", DeploymentController, :export_audit_logs) + get("/deployment_groups/:name/audit_logs/download", DeploymentController, :export_audit_logs) end scope "/", NervesHubWeb do @@ -299,10 +299,14 @@ defmodule NervesHubWeb.Router do live("/org/:org_name/:product_name/archives/upload", Live.Archives, :upload) live("/org/:org_name/:product_name/archives/:archive_uuid", Live.Archives, :show) - live("/org/:org_name/:product_name/deployments", Live.Deployments.Index) - live("/org/:org_name/:product_name/deployments/new", Live.Deployments.New) - live("/org/:org_name/:product_name/deployments/:name", Live.Deployments.Show) - live("/org/:org_name/:product_name/deployments/:name/edit", Live.Deployments.Edit) + live("/org/:org_name/:product_name/deployment_groups", Live.DeploymentGroup.Index) + live("/org/:org_name/:product_name/deployment_groups/new", Live.DeploymentGroup.New) + live("/org/:org_name/:product_name/deployment_groups/:name", Live.DeploymentGroup.Show) + + live( + "/org/:org_name/:product_name/deployment_groups/:name/edit", + Live.DeploymentGroup.Edit + ) live("/org/:org_name/:product_name/scripts", Live.SupportScripts.Index) live("/org/:org_name/:product_name/scripts/new", Live.SupportScripts.New) diff --git a/lib/nerves_hub_web/views/api/deployment_group_view.ex b/lib/nerves_hub_web/views/api/deployment_group_view.ex new file mode 100644 index 000000000..cbcfb1905 --- /dev/null +++ b/lib/nerves_hub_web/views/api/deployment_group_view.ex @@ -0,0 +1,23 @@ +defmodule NervesHubWeb.API.DeploymentGroupView do + use NervesHubWeb, :api_view + + alias NervesHubWeb.API.DeploymentGroupView + + def render("index.json", %{deployment_groups: deployment_groups}) do + %{data: render_many(deployment_groups, DeploymentGroupView, "deployment_group.json")} + end + + def render("show.json", %{deployment_group: deployment_group}) do + %{data: render_one(deployment_group, DeploymentGroupView, "deployment_group.json")} + end + + def render("deployment_group.json", %{deployment_group: deployment_group}) do + %{ + name: deployment_group.name, + is_active: deployment_group.is_active, + state: if(deployment_group.is_active, do: "on", else: "off"), + firmware_uuid: deployment_group.firmware.uuid, + conditions: deployment_group.conditions + } + end +end diff --git a/lib/nerves_hub_web/views/api/deployment_view.ex b/lib/nerves_hub_web/views/api/deployment_view.ex deleted file mode 100644 index f5c07a13b..000000000 --- a/lib/nerves_hub_web/views/api/deployment_view.ex +++ /dev/null @@ -1,23 +0,0 @@ -defmodule NervesHubWeb.API.DeploymentView do - use NervesHubWeb, :api_view - - alias NervesHubWeb.API.DeploymentView - - def render("index.json", %{deployments: deployments}) do - %{data: render_many(deployments, DeploymentView, "deployment.json")} - end - - def render("show.json", %{deployment: deployment}) do - %{data: render_one(deployment, DeploymentView, "deployment.json")} - end - - def render("deployment.json", %{deployment: deployment}) do - %{ - name: deployment.name, - is_active: deployment.is_active, - state: if(deployment.is_active, do: "on", else: "off"), - firmware_uuid: deployment.firmware.uuid, - conditions: deployment.conditions - } - end -end diff --git a/lib/nerves_hub_web/views/api/device_view.ex b/lib/nerves_hub_web/views/api/device_view.ex index ea483bf5f..a5b32f8ef 100644 --- a/lib/nerves_hub_web/views/api/device_view.ex +++ b/lib/nerves_hub_web/views/api/device_view.ex @@ -28,7 +28,10 @@ defmodule NervesHubWeb.API.DeviceView do last_communication: connection_last_seen_at(device), description: device.description, firmware_metadata: device.firmware_metadata, - deployment: render_one(device.deployment, __MODULE__, "deployment.json", as: :deployment), + deployment_group: + render_one(device.deployment_group, __MODULE__, "deployment_group.json", + as: :deployment_group + ), updates_enabled: device.updates_enabled, updates_blocked_until: device.updates_blocked_until, org_name: device.org.name, @@ -36,12 +39,12 @@ defmodule NervesHubWeb.API.DeviceView do } end - def render("deployment.json", %{deployment: deployment}) do + def render("deployment_group.json", %{deployment_group: deployment_group}) do %{ - firmware_uuid: deployment.firmware.uuid, - firmware_version: deployment.firmware.version, - is_active: deployment.is_active, - name: deployment.name + firmware_uuid: deployment_group.firmware.uuid, + firmware_version: deployment_group.firmware.version, + is_active: deployment_group.is_active, + name: deployment_group.name } end diff --git a/lib/nerves_hub_web/views/layout_view.ex b/lib/nerves_hub_web/views/layout_view.ex index 79a396092..1bdb272f2 100644 --- a/lib/nerves_hub_web/views/layout_view.ex +++ b/lib/nerves_hub_web/views/layout_view.ex @@ -232,7 +232,7 @@ defmodule NervesHubWeb.LayoutView do %{ title: "Deployments", active: "", - href: ~p"/org/#{conn.assigns.org.name}/#{conn.assigns.product.name}/deployments" + href: ~p"/org/#{conn.assigns.org.name}/#{conn.assigns.product.name}/deployment_groups" }, %{ title: "Scripts", diff --git a/priv/repo/add_org_id.exs b/priv/repo/add_org_id.exs index ec56fadac..1780aac38 100644 --- a/priv/repo/add_org_id.exs +++ b/priv/repo/add_org_id.exs @@ -1,7 +1,7 @@ alias NervesHub.Repo alias NervesHub.Firmwares.Firmware alias NervesHub.Devices.DeviceCertificate -alias NervesHub.Deployments.Deployment +alias NervesHub.ManagedDeployments.DeploymentGroup alias NervesHub.AuditLogs.AuditLog import Ecto.Query diff --git a/priv/repo/migrations/20241120211103_create_deployment_releases.exs b/priv/repo/migrations/20241120211103_create_deployment_releases.exs new file mode 100644 index 000000000..b865fd238 --- /dev/null +++ b/priv/repo/migrations/20241120211103_create_deployment_releases.exs @@ -0,0 +1,15 @@ +defmodule NervesHub.Repo.Migrations.CreateDeploymentReleases do + use Ecto.Migration + + def change do + create table(:deployment_releases) do + add(:deployment_group_id, references(:deployments), null: false) + add(:firmware_id, references(:firmwares), null: false) + add(:archive_id, references(:archives)) + add(:created_by_id, references(:users), null: false) + add(:status, :text, default: "inactive", null: false) + + timestamps() + end + end +end diff --git a/priv/repo/migrations/20241120212950_add_active_deployment_release_id_to_deployments.exs b/priv/repo/migrations/20241120212950_add_active_deployment_release_id_to_deployments.exs new file mode 100644 index 000000000..e230a89f0 --- /dev/null +++ b/priv/repo/migrations/20241120212950_add_active_deployment_release_id_to_deployments.exs @@ -0,0 +1,9 @@ +defmodule NervesHub.Repo.Migrations.AddActiveDeploymentReleaseIdToDeployments do + use Ecto.Migration + + def change do + alter table(:deployments) do + add(:active_deployment_release_id, references(:deployment_releases)) + end + end +end diff --git a/priv/repo/migrations/20241126000914_add_deployment_conflict_to_devices.exs b/priv/repo/migrations/20241126000914_add_deployment_conflict_to_devices.exs new file mode 100644 index 000000000..3aa9459b9 --- /dev/null +++ b/priv/repo/migrations/20241126000914_add_deployment_conflict_to_devices.exs @@ -0,0 +1,9 @@ +defmodule NervesHub.Repo.Migrations.AddDeploymentConflictToDevices do + use Ecto.Migration + + def change do + alter table(:devices) do + add(:deployment_conflict, :text) + end + end +end diff --git a/test/nerves_hub/devices_test.exs b/test/nerves_hub/devices_test.exs index b30da5ba9..d260fc731 100644 --- a/test/nerves_hub/devices_test.exs +++ b/test/nerves_hub/devices_test.exs @@ -4,7 +4,7 @@ defmodule NervesHub.DevicesTest do alias Ecto.Changeset alias NervesHub.AuditLogs - alias NervesHub.Deployments + alias NervesHub.ManagedDeployments alias NervesHub.Devices alias NervesHub.Devices.CACertificate alias NervesHub.Devices.DeviceCertificate @@ -387,13 +387,13 @@ defmodule NervesHub.DevicesTest do } {:ok, deployment} = - Deployments.create_deployment(params) + ManagedDeployments.create_deployment(params) |> elem(1) - |> Deployments.update_deployment(%{is_active: true}) + |> ManagedDeployments.update_deployment(%{is_active: true}) {:ok, device_with_firmware} = Devices.get_device_by_org(org, device.id) - [%Deployments.Deployment{id: dep_id} | _] = + [%ManagedDeployments.DeploymentGroup{id: dep_id} | _] = Devices.get_eligible_deployments(device_with_firmware) assert dep_id == deployment.id @@ -428,9 +428,9 @@ defmodule NervesHub.DevicesTest do } {:ok, _deployment} = - Deployments.create_deployment(params) + ManagedDeployments.create_deployment(params) |> elem(1) - |> Deployments.update_deployment(%{is_active: true}) + |> ManagedDeployments.update_deployment(%{is_active: true}) {:ok, device_with_firmware} = Devices.get_device_by_org(org, device.id) @@ -453,7 +453,10 @@ defmodule NervesHub.DevicesTest do firmware1 = Fixtures.firmware_fixture(org_key, product, %{version: "2.0.0"}) - Deployments.update_deployment(old_deployment, %{firmware_id: firmware1.id, is_active: true}) + ManagedDeployments.update_deployment(old_deployment, %{ + firmware_id: firmware1.id, + is_active: true + }) device = Fixtures.device_fixture(org, product, firmware, %{ @@ -476,9 +479,9 @@ defmodule NervesHub.DevicesTest do } {:ok, _deployment2} = - Deployments.create_deployment(params) + ManagedDeployments.create_deployment(params) |> elem(1) - |> Deployments.update_deployment(%{is_active: true}) + |> ManagedDeployments.update_deployment(%{is_active: true}) {:ok, device_with_firmware} = Devices.get_device_by_org(org, device.id) diff --git a/test/nerves_hub/deployments_test.exs b/test/nerves_hub/managed_deployments_test.exs similarity index 84% rename from test/nerves_hub/deployments_test.exs rename to test/nerves_hub/managed_deployments_test.exs index 2dfc3959b..039516677 100644 --- a/test/nerves_hub/deployments_test.exs +++ b/test/nerves_hub/managed_deployments_test.exs @@ -1,10 +1,10 @@ -defmodule NervesHub.DeploymentsTest do +defmodule NervesHub.ManagedDeploymentsTest do use NervesHub.DataCase, async: false import Phoenix.ChannelTest - alias NervesHub.Deployments alias NervesHub.Devices.Device + alias NervesHub.ManagedDeployments alias NervesHub.Fixtures alias Ecto.Changeset @@ -55,7 +55,8 @@ defmodule NervesHub.DeploymentsTest do is_active: false } - {:ok, %Deployments.Deployment{} = deployment} = Deployments.create_deployment(params) + {:ok, %ManagedDeployments.DeploymentGroup{} = deployment} = + ManagedDeployments.create_deployment(params) for key <- Map.keys(params) do assert Map.get(deployment, key) == Map.get(params, key) @@ -79,7 +80,7 @@ defmodule NervesHub.DeploymentsTest do } assert {:error, %Ecto.Changeset{errors: [name: {"has already been taken", _}]}} = - Deployments.create_deployment(params) + ManagedDeployments.create_deployment(params) end test "create_deployment with invalid parameters" do @@ -92,7 +93,7 @@ defmodule NervesHub.DeploymentsTest do is_active: true } - assert {:error, %Changeset{}} = Deployments.create_deployment(params) + assert {:error, %Changeset{}} = ManagedDeployments.create_deployment(params) end end @@ -118,13 +119,13 @@ defmodule NervesHub.DeploymentsTest do is_active: false } - {:ok, deployment} = Deployments.create_deployment(params) + {:ok, deployment} = ManagedDeployments.create_deployment(params) - Phoenix.PubSub.subscribe(NervesHub.PubSub, "deployment:#{deployment.id}") + Phoenix.PubSub.subscribe(NervesHub.PubSub, "deployment_group:#{deployment.id}") - {:ok, _deployment} = Deployments.update_deployment(deployment, %{is_active: true}) + {:ok, _deployment} = ManagedDeployments.update_deployment(deployment, %{is_active: true}) - assert_broadcast("deployments/update", %{}, 500) + assert_broadcast("deployment_groups/update", %{}, 500) end end @@ -154,7 +155,7 @@ defmodule NervesHub.DeploymentsTest do assert [ %{id: ^beta_deployment_id}, %{id: ^rpi_deployment_id} - ] = Deployments.matching_deployments(device) + ] = ManagedDeployments.matching_deployments(device) end test "finds matching deployments including the platform", state do @@ -176,7 +177,7 @@ defmodule NervesHub.DeploymentsTest do device = Fixtures.device_fixture(org, product, rpi_firmware, %{tags: ["beta", "rpi"]}) - assert [%{id: ^rpi_deployment_id}] = Deployments.matching_deployments(device) + assert [%{id: ^rpi_deployment_id}] = ManagedDeployments.matching_deployments(device) end test "finds matching deployments including the architecture", state do @@ -198,7 +199,7 @@ defmodule NervesHub.DeploymentsTest do device = Fixtures.device_fixture(org, product, rpi_firmware, %{tags: ["beta", "rpi"]}) - assert [%{id: ^rpi_deployment_id}] = Deployments.matching_deployments(device) + assert [%{id: ^rpi_deployment_id}] = ManagedDeployments.matching_deployments(device) end test "finds matching deployments including the version", state do @@ -217,7 +218,7 @@ defmodule NervesHub.DeploymentsTest do device = Fixtures.device_fixture(org, product, firmware, %{tags: ["beta", "rpi"]}) - assert [%{id: ^low_deployment_id}] = Deployments.matching_deployments(device) + assert [%{id: ^low_deployment_id}] = ManagedDeployments.matching_deployments(device) end test "finds matching deployments including pre versions", state do @@ -238,7 +239,7 @@ defmodule NervesHub.DeploymentsTest do device = Fixtures.device_fixture(org, product, firmware, %{tags: ["beta", "rpi"]}) - assert [%{id: ^low_deployment_id}] = Deployments.matching_deployments(device) + assert [%{id: ^low_deployment_id}] = ManagedDeployments.matching_deployments(device) end test "finds the newest firmware version including pre-releases", state do @@ -285,13 +286,17 @@ defmodule NervesHub.DeploymentsTest do %{id: ^v100_deployment_id}, %{id: ^v100rc2_deployment_id}, %{id: ^v100rc1_deployment_id} - ] = Deployments.matching_deployments(device) + ] = ManagedDeployments.matching_deployments(device) end test "ignores device without firmware metadata" do - assert [] == Deployments.matching_deployments(%Device{firmware_metadata: nil}) - assert [] == Deployments.matching_deployments(%Device{firmware_metadata: nil}, [true]) - assert [] == Deployments.matching_deployments(%Device{firmware_metadata: nil}, [false]) + assert [] == ManagedDeployments.matching_deployments(%Device{firmware_metadata: nil}) + + assert [] == + ManagedDeployments.matching_deployments(%Device{firmware_metadata: nil}, [true]) + + assert [] == + ManagedDeployments.matching_deployments(%Device{firmware_metadata: nil}, [false]) end end end diff --git a/test/nerves_hub_web/channels/websocket_test.exs b/test/nerves_hub_web/channels/websocket_test.exs index 70fac4342..43daee3f5 100644 --- a/test/nerves_hub_web/channels/websocket_test.exs +++ b/test/nerves_hub_web/channels/websocket_test.exs @@ -7,13 +7,13 @@ defmodule NervesHubWeb.WebsocketTest do alias NervesHub.AuditLogs alias NervesHub.AuditLogs.AuditLog - alias NervesHub.Deployments - alias NervesHub.Deployments.Orchestrator alias NervesHub.Devices alias NervesHub.Devices.Connections alias NervesHub.Devices.Device alias NervesHub.Devices.DeviceConnection alias NervesHub.Fixtures + alias NervesHub.ManagedDeployments + alias NervesHub.ManagedDeployments.Orchestrator alias NervesHub.Products alias NervesHub.Repo alias NervesHubWeb.DeviceEndpoint @@ -661,7 +661,7 @@ defmodule NervesHubWeb.WebsocketTest do "tags" => ["beta"] } }) - |> Deployments.update_deployment(%{is_active: true}) + |> ManagedDeployments.update_deployment(%{is_active: true}) device = Fixtures.device_fixture( @@ -683,7 +683,7 @@ defmodule NervesHubWeb.WebsocketTest do Fixtures.firmware_fixture(org_key, firmware.product, %{version: "0.0.2", dir: tmp_dir}) {:ok, deployment} = - Deployments.update_deployment(deployment, %{ + ManagedDeployments.update_deployment(deployment, %{ firmware_id: new_firmware.id }) @@ -735,7 +735,7 @@ defmodule NervesHubWeb.WebsocketTest do SocketClient.clean_close(socket) end - test "removes device from deployment and creates audit log if firmware doesn't match", + test "removes device from deployment and sets reason if firmware doesn't match", %{ user: user, tmp_dir: tmp_dir @@ -758,7 +758,7 @@ defmodule NervesHubWeb.WebsocketTest do "tags" => ["beta", "beta-edge"] } }) - |> Deployments.update_deployment(%{is_active: true}) + |> ManagedDeployments.update_deployment(%{is_active: true}) device = Fixtures.device_fixture( @@ -824,7 +824,7 @@ defmodule NervesHubWeb.WebsocketTest do "tags" => ["beta", "beta-edge"] } }) - |> Deployments.update_deployment(%{is_active: true}) + |> ManagedDeployments.update_deployment(%{is_active: true}) device = Fixtures.device_fixture( @@ -855,8 +855,6 @@ defmodule NervesHubWeb.WebsocketTest do device = Repo.reload(device) assert device.deployment_id - - SocketClient.clean_close(socket) end end @@ -909,7 +907,7 @@ defmodule NervesHubWeb.WebsocketTest do SocketClient.clean_close(socket) end - test "vaild certificate expired signer can connect", %{user: user, tmp_dir: tmp_dir} do + test "valid certificate expired signer can connect", %{user: user, tmp_dir: tmp_dir} do org = Fixtures.org_fixture(user, %{name: "custom_ca_test"}) {device, _firmware} = device_fixture(tmp_dir, user, %{identifier: @valid_serial}, org) @@ -1044,7 +1042,7 @@ defmodule NervesHubWeb.WebsocketTest do }, archive_id: archive.id }) - |> Deployments.update_deployment(%{is_active: true}) + |> ManagedDeployments.update_deployment(%{is_active: true}) device = Fixtures.device_fixture( @@ -1092,7 +1090,7 @@ defmodule NervesHubWeb.WebsocketTest do }, archive_id: archive.id }) - |> Deployments.update_deployment(%{is_active: true}) + |> ManagedDeployments.update_deployment(%{is_active: true}) device = Fixtures.device_fixture( @@ -1143,7 +1141,7 @@ defmodule NervesHubWeb.WebsocketTest do "tags" => ["beta"] } }) - |> Deployments.update_deployment(%{is_active: true}) + |> ManagedDeployments.update_deployment(%{is_active: true}) device = Fixtures.device_fixture( @@ -1164,7 +1162,8 @@ defmodule NervesHubWeb.WebsocketTest do assert_connection_change() - {:ok, _deployment} = Deployments.update_deployment(deployment, %{archive_id: archive.id}) + {:ok, _deployment} = + ManagedDeployments.update_deployment(deployment, %{archive_id: archive.id}) archive = SocketClient.wait_archive(socket) assert %{"url" => _, "version" => _} = archive diff --git a/test/nerves_hub_web/controllers/api/deployment_controller_test.exs b/test/nerves_hub_web/controllers/api/deployment_controller_test.exs index 45a7012cc..cf63de96c 100644 --- a/test/nerves_hub_web/controllers/api/deployment_controller_test.exs +++ b/test/nerves_hub_web/controllers/api/deployment_controller_test.exs @@ -2,12 +2,12 @@ defmodule NervesHubWeb.API.DeploymentControllerTest do use NervesHubWeb.APIConnCase, async: true alias NervesHub.AuditLogs - alias NervesHub.Deployments.Deployment + alias NervesHub.ManagedDeployments.DeploymentGroup alias NervesHub.Fixtures describe "index" do test "lists all deployments", %{conn: conn, org: org, product: product} do - conn = get(conn, Routes.api_deployment_path(conn, :index, org.name, product.name)) + conn = get(conn, Routes.api_deployment_group_path(conn, :index, org.name, product.name)) assert json_response(conn, 200)["data"] == [] end end @@ -38,11 +38,20 @@ defmodule NervesHubWeb.API.DeploymentControllerTest do params: params, product: product } do - conn = post(conn, Routes.api_deployment_path(conn, :create, org.name, product.name), params) + conn = + post( + conn, + Routes.api_deployment_group_path(conn, :create, org.name, product.name), + params + ) + assert json_response(conn, 201)["data"] conn = - get(conn, Routes.api_deployment_path(conn, :show, org.name, product.name, params.name)) + get( + conn, + Routes.api_deployment_group_path(conn, :show, org.name, product.name, params.name) + ) assert json_response(conn, 200)["data"]["name"] == params.name end @@ -54,15 +63,21 @@ defmodule NervesHubWeb.API.DeploymentControllerTest do product: product, user: user } do - conn = post(conn, Routes.api_deployment_path(conn, :create, org.name, product.name), params) + conn = + post( + conn, + Routes.api_deployment_group_path(conn, :create, org.name, product.name), + params + ) + assert json_response(conn, 201)["data"] [audit_log] = AuditLogs.logs_by(user) - assert audit_log.resource_type == Deployment + assert audit_log.resource_type == DeploymentGroup end test "renders errors when data is invalid", %{conn: conn, org: org, product: product} do - conn = post(conn, Routes.api_deployment_path(conn, :create, org.name, product.name)) + conn = post(conn, Routes.api_deployment_group_path(conn, :create, org.name, product.name)) assert json_response(conn, 500)["errors"] != %{} end end @@ -76,11 +91,15 @@ defmodule NervesHubWeb.API.DeploymentControllerTest do org: org, product: product } do - path = Routes.api_deployment_path(conn, :update, org.name, product.name, deployment.name) + path = + Routes.api_deployment_group_path(conn, :update, org.name, product.name, deployment.name) + conn = put(conn, path, deployment: %{"is_active" => true}) assert %{"is_active" => true} = json_response(conn, 200)["data"] - path = Routes.api_deployment_path(conn, :show, org.name, product.name, deployment.name) + path = + Routes.api_deployment_group_path(conn, :show, org.name, product.name, deployment.name) + conn = get(conn, path) assert json_response(conn, 200)["data"]["is_active"] end @@ -91,24 +110,30 @@ defmodule NervesHubWeb.API.DeploymentControllerTest do org: org, product: product } do - path = Routes.api_deployment_path(conn, :update, org.name, product.name, deployment.name) + path = + Routes.api_deployment_group_path(conn, :update, org.name, product.name, deployment.name) + refute deployment.is_active conn = put(conn, path, deployment: %{"state" => "on"}) assert %{"is_active" => true, "state" => "on"} = json_response(conn, 200)["data"] - path = Routes.api_deployment_path(conn, :show, org.name, product.name, deployment.name) + path = + Routes.api_deployment_group_path(conn, :show, org.name, product.name, deployment.name) + conn = get(conn, path) assert json_response(conn, 200)["data"]["is_active"] assert json_response(conn, 200)["data"]["state"] == "on" end test "audits on success", %{conn: conn, deployment: deployment, org: org, product: product} do - path = Routes.api_deployment_path(conn, :update, org.name, product.name, deployment.name) + path = + Routes.api_deployment_group_path(conn, :update, org.name, product.name, deployment.name) + conn = put(conn, path, deployment: %{"is_active" => true}) assert json_response(conn, 200)["data"] [audit_log] = AuditLogs.logs_for(deployment) - assert audit_log.resource_type == Deployment + assert audit_log.resource_type == DeploymentGroup end test "renders errors when data is invalid", %{ @@ -117,7 +142,9 @@ defmodule NervesHubWeb.API.DeploymentControllerTest do org: org, product: product } do - path = Routes.api_deployment_path(conn, :update, org.name, product.name, deployment.name) + path = + Routes.api_deployment_group_path(conn, :update, org.name, product.name, deployment.name) + conn = put(conn, path, deployment: %{is_active: "1234"}) assert json_response(conn, 422)["errors"] != %{} end @@ -135,7 +162,7 @@ defmodule NervesHubWeb.API.DeploymentControllerTest do conn = delete( conn, - Routes.api_deployment_path(conn, :delete, org.name, product.name, deployment.name) + Routes.api_deployment_group_path(conn, :delete, org.name, product.name, deployment.name) ) assert response(conn, 204) @@ -143,7 +170,7 @@ defmodule NervesHubWeb.API.DeploymentControllerTest do conn = get( conn, - Routes.api_deployment_path(conn, :show, org.name, product.name, deployment.name) + Routes.api_deployment_group_path(conn, :show, org.name, product.name, deployment.name) ) assert response(conn, 404) diff --git a/test/nerves_hub_web/controllers/api/device_controller_test.exs b/test/nerves_hub_web/controllers/api/device_controller_test.exs index b218f0a74..775d8dde5 100644 --- a/test/nerves_hub_web/controllers/api/device_controller_test.exs +++ b/test/nerves_hub_web/controllers/api/device_controller_test.exs @@ -147,7 +147,7 @@ defmodule NervesHubWeb.API.DeviceControllerTest do conn = post(conn, url, %{"uuid" => firmware_two.uuid}) assert response(conn, 204) - assert_broadcast("deployments/update", %{}) + assert_broadcast("deployment_groups/update", %{}) end end diff --git a/test/nerves_hub_web/live/deployments/edit_test.exs b/test/nerves_hub_web/live/deployment_groups/edit_test.exs similarity index 74% rename from test/nerves_hub_web/live/deployments/edit_test.exs rename to test/nerves_hub_web/live/deployment_groups/edit_test.exs index 8736fa575..d20746ec7 100644 --- a/test/nerves_hub_web/live/deployments/edit_test.exs +++ b/test/nerves_hub_web/live/deployment_groups/edit_test.exs @@ -1,9 +1,9 @@ -defmodule NervesHubWeb.Live.Deployments.EditTest do +defmodule NervesHubWeb.Live.DeploymentGroups.EditTest do use NervesHubWeb.ConnCase.Browser, async: true alias NervesHub.AuditLogs - alias NervesHub.Deployments - alias NervesHub.Deployments.Deployment + alias NervesHub.ManagedDeployments + alias NervesHub.ManagedDeployments.DeploymentGroup alias NervesHub.Fixtures test "update the chosen resource, and adds an audit log", %{ @@ -19,7 +19,7 @@ defmodule NervesHubWeb.Live.Deployments.EditTest do conn = conn - |> visit("/org/#{org.name}/#{product.name}/deployments/#{deployment.name}/edit") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups/#{deployment.name}/edit") |> assert_has("h1", text: "Edit Deployment") |> assert_has("a", text: product.name) |> fill_in("Deployment name", with: "Moussaka") @@ -28,11 +28,11 @@ defmodule NervesHubWeb.Live.Deployments.EditTest do |> fill_in("Firmware version", with: firmware.id) |> click_button("Save Change") - {:ok, reloaded_deployment} = Deployments.get_deployment(product, deployment.id) + {:ok, reloaded_deployment} = ManagedDeployments.get_deployment(product, deployment.id) conn |> assert_path( - URI.encode("/org/#{org.name}/#{product.name}/deployments/#{reloaded_deployment.name}") + URI.encode("/org/#{org.name}/#{product.name}/deployment_groups/#{reloaded_deployment.name}") ) |> assert_has("div", text: "Deployment updated") @@ -42,7 +42,7 @@ defmodule NervesHubWeb.Live.Deployments.EditTest do [audit_log_one, audit_log_two] = AuditLogs.logs_for(deployment) - assert audit_log_one.resource_type == Deployment + assert audit_log_one.resource_type == DeploymentGroup assert audit_log_two.description =~ ~r/conditions changed/ end @@ -58,13 +58,13 @@ defmodule NervesHubWeb.Live.Deployments.EditTest do deployment = Fixtures.deployment_fixture(org, firmware) conn - |> visit("/org/#{org.name}/#{product.name}/deployments/#{deployment.name}/edit") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups/#{deployment.name}/edit") |> assert_has("h1", text: "Edit Deployment") |> assert_has("a", text: product.name) |> fill_in("Tag(s) distributed to", with: "") |> fill_in("Version requirement", with: "") |> click_button("Save Change") - |> assert_path("/org/#{org.name}/#{product.name}/deployments/#{deployment.name}/edit") + |> assert_path("/org/#{org.name}/#{product.name}/deployment_groups/#{deployment.name}/edit") |> assert_has("div", text: "should have at least 1 item(s)") end end diff --git a/test/nerves_hub_web/live/deployments/index_test.exs b/test/nerves_hub_web/live/deployment_groups/index_test.exs similarity index 71% rename from test/nerves_hub_web/live/deployments/index_test.exs rename to test/nerves_hub_web/live/deployment_groups/index_test.exs index 352807455..afa396e77 100644 --- a/test/nerves_hub_web/live/deployments/index_test.exs +++ b/test/nerves_hub_web/live/deployment_groups/index_test.exs @@ -1,21 +1,21 @@ -defmodule NervesHubWeb.Live.Deployments.IndexTest do +defmodule NervesHubWeb.Live.DeploymentGroups.IndexTest do use NervesHubWeb.ConnCase.Browser, async: true - alias NervesHub.Deployments alias NervesHub.Devices alias NervesHub.Fixtures + alias NervesHub.ManagedDeployments test "no deployments", %{conn: conn, user: user, org: org} do product = Fixtures.product_fixture(user, org, %{name: "Spaghetti"}) conn - |> visit("/org/#{org.name}/#{product.name}/deployments") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups") |> assert_has("h3", text: "#{product.name} doesn’t have any deployments configured") end test "has deployments", %{conn: conn, org: org, product: product, deployment: deployment} do conn - |> visit("/org/#{org.name}/#{product.name}/deployments") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups") |> assert_has("h1", text: "Deployments") |> assert_has("a", text: deployment.name) |> assert_has("td div", text: "0") @@ -29,15 +29,15 @@ defmodule NervesHubWeb.Live.Deployments.IndexTest do device: device } do conn - |> visit("/org/#{org.name}/#{product.name}/deployments") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups") |> assert_has("h1", text: "Deployments") |> assert_has("a", text: deployment.name) |> assert_has("td div", text: "0") - Deployments.set_deployment(device) + ManagedDeployments.set_deployment(device) conn - |> visit("/org/#{org.name}/#{product.name}/deployments") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups") |> assert_has("h1", text: "Deployments") |> assert_has("a", text: deployment.name) |> assert_has("td div", text: "1") @@ -45,7 +45,7 @@ defmodule NervesHubWeb.Live.Deployments.IndexTest do Devices.delete_device(device) conn - |> visit("/org/#{org.name}/#{product.name}/deployments") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups") |> assert_has("h1", text: "Deployments") |> assert_has("a", text: deployment.name) |> assert_has("td div", text: "0") diff --git a/test/nerves_hub_web/live/deployments/new_test.exs b/test/nerves_hub_web/live/deployment_groups/new_test.exs similarity index 79% rename from test/nerves_hub_web/live/deployments/new_test.exs rename to test/nerves_hub_web/live/deployment_groups/new_test.exs index 89f87062b..51cd6879d 100644 --- a/test/nerves_hub_web/live/deployments/new_test.exs +++ b/test/nerves_hub_web/live/deployment_groups/new_test.exs @@ -1,8 +1,8 @@ -defmodule NervesHubWeb.Live.Deployments.NewTest do +defmodule NervesHubWeb.Live.DeploymentGroups.NewTest do use NervesHubWeb.ConnCase.Browser, async: true alias NervesHub.AuditLogs - alias NervesHub.Deployments.Deployment + alias NervesHub.ManagedDeployments.DeploymentGroup alias NervesHub.Fixtures describe "new deployment" do @@ -19,7 +19,7 @@ defmodule NervesHubWeb.Live.Deployments.NewTest do Fixtures.firmware_fixture(org_key, product, %{dir: tmp_dir, platform: "taramasalata"}) conn - |> visit("/org/#{org.name}/#{product.name}/deployments/new") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups/new") |> assert_has("h1", text: "Create Deployment") |> assert_has("option", text: "Choose a platform") |> select(firmware.platform, from: "Platform") @@ -27,12 +27,12 @@ defmodule NervesHubWeb.Live.Deployments.NewTest do |> fill_in("Tag(s) distributed to", with: "josh, lars") |> fill_in("Firmware version", with: firmware.id) |> click_button("Create Deployment") - |> assert_path(URI.encode("/org/#{org.name}/#{product.name}/deployments")) + |> assert_path(URI.encode("/org/#{org.name}/#{product.name}/deployment_groups")) |> assert_has("h1", text: "Deployments") - |> assert_has("div", text: "Deployment created") + |> assert_has("div", text: "Deployment Group created") |> assert_has("a", text: "Moussaka") - [%{resource_type: Deployment}] = AuditLogs.logs_by(user) + [%{resource_type: DeploymentGroup}] = AuditLogs.logs_by(user) end test "error message displayed if invalid firmware is selected", %{ @@ -48,14 +48,14 @@ defmodule NervesHubWeb.Live.Deployments.NewTest do Fixtures.firmware_fixture(org_key, product, %{dir: tmp_dir, platform: "taramasalata"}) conn - |> visit("/org/#{org.name}/#{product.name}/deployments/new") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups/new") |> select(firmware.platform, from: "Platform") |> unwrap(fn view -> view |> element("form") - |> render_submit(%{deployment: %{"firmware_id" => -1}}) + |> render_submit(%{deployment_group: %{"firmware_id" => -1}}) end) - |> assert_path("/org/#{org.name}/#{product.name}/deployments/new") + |> assert_path("/org/#{org.name}/#{product.name}/deployment_groups/new") |> assert_has("div", text: "Invalid firmware selected") end @@ -70,11 +70,11 @@ defmodule NervesHubWeb.Live.Deployments.NewTest do |> init_test_session(%{"auth_user_id" => user.id}) conn - |> visit("/org/#{org.name}/#{product.name}/deployments/new") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups/new") |> assert_path(URI.encode("/org/#{org.name}/#{product.name}/firmware/upload")) |> assert_has("h1", text: "Add Firmware") |> assert_has("div", - text: "You must upload a firmware version before creating a deployment" + text: "You must upload a firmware version before creating a Deployment Group" ) end end diff --git a/test/nerves_hub_web/live/deployments/show_test.exs b/test/nerves_hub_web/live/deployment_groups/show_test.exs similarity index 79% rename from test/nerves_hub_web/live/deployments/show_test.exs rename to test/nerves_hub_web/live/deployment_groups/show_test.exs index 026d90f1f..718fdfe35 100644 --- a/test/nerves_hub_web/live/deployments/show_test.exs +++ b/test/nerves_hub_web/live/deployment_groups/show_test.exs @@ -1,11 +1,11 @@ -defmodule NervesHubWeb.Live.Deployments.ShowTest do +defmodule NervesHubWeb.Live.DeploymentGroups.ShowTest do use NervesHubWeb.ConnCase.Browser, async: true alias NervesHub.AuditLogs - alias NervesHub.Deployments alias NervesHub.Devices alias NervesHub.Devices.Device alias NervesHub.Fixtures + alias NervesHub.ManagedDeployments test "shows the deployment", %{ conn: conn, @@ -19,7 +19,7 @@ defmodule NervesHubWeb.Live.Deployments.ShowTest do deployment = Fixtures.deployment_fixture(org, firmware) conn - |> visit("/org/#{org.name}/#{product.name}/deployments/#{deployment.name}") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups/#{deployment.name}") |> assert_has("h1", text: deployment.name) |> assert_has("p.deployment-state", text: "Off") |> assert_has("div#device-count p", text: "0") @@ -50,7 +50,7 @@ defmodule NervesHubWeb.Live.Deployments.ShowTest do %Device{} = Devices.update_deployment(device_2, deployment) conn - |> visit("/org/#{org.name}/#{product.name}/deployments/#{deployment.name}") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups/#{deployment.name}") |> assert_has("h1", text: deployment.name) |> assert_has("p.deployment-state", text: "Off") |> assert_has("div#device-count p", text: "1") @@ -75,13 +75,13 @@ defmodule NervesHubWeb.Live.Deployments.ShowTest do deployment = Fixtures.deployment_fixture(org, firmware) conn - |> visit("/org/#{org.name}/#{product.name}/deployments/#{deployment.name}") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups/#{deployment.name}") |> assert_has("h1", text: deployment.name) |> click_link("Delete") - |> assert_path(URI.encode("/org/#{org.name}/#{product.name}/deployments")) + |> assert_path(URI.encode("/org/#{org.name}/#{product.name}/deployment_groups")) |> assert_has("div", text: "Deployment successfully deleted") - assert Deployments.get_deployment(product, deployment.id) == {:error, :not_found} + assert ManagedDeployments.get_deployment(product, deployment.id) == {:error, :not_found} logs = AuditLogs.logs_for(deployment) @@ -100,12 +100,12 @@ defmodule NervesHubWeb.Live.Deployments.ShowTest do deployment = Fixtures.deployment_fixture(org, firmware) conn - |> visit("/org/#{org.name}/#{product.name}/deployments/#{deployment.name}") + |> visit("/org/#{org.name}/#{product.name}/deployment_groups/#{deployment.name}") |> assert_has("h1", text: deployment.name) |> click_link("Turn On") - |> assert_path("/org/#{org.name}/#{product.name}/deployments/#{deployment.name}") + |> assert_path("/org/#{org.name}/#{product.name}/deployment_groups/#{deployment.name}") |> then(fn conn -> - {:ok, reloaded_deployment} = Deployments.get_deployment(product, deployment.id) + {:ok, reloaded_deployment} = ManagedDeployments.get_deployment(product, deployment.id) assert reloaded_deployment.is_active assert_has(conn, "span", text: "Turn Off") @@ -116,9 +116,9 @@ defmodule NervesHubWeb.Live.Deployments.ShowTest do conn end) |> click_link("Turn Off") - |> assert_path("/org/#{org.name}/#{product.name}/deployments/#{deployment.name}") + |> assert_path("/org/#{org.name}/#{product.name}/deployment_groups/#{deployment.name}") |> then(fn conn -> - {:ok, reloaded_deployment} = Deployments.get_deployment(product, deployment.id) + {:ok, reloaded_deployment} = ManagedDeployments.get_deployment(product, deployment.id) refute reloaded_deployment.is_active assert_has(conn, "span", text: "Turn On") diff --git a/test/nerves_hub_web/live/devices/show_test.exs b/test/nerves_hub_web/live/devices/show_test.exs index d01e6ecfa..c57508f74 100644 --- a/test/nerves_hub_web/live/devices/show_test.exs +++ b/test/nerves_hub_web/live/devices/show_test.exs @@ -5,10 +5,10 @@ defmodule NervesHubWeb.Live.Devices.ShowTest do import Phoenix.ChannelTest alias NervesHub.AuditLogs - alias NervesHub.Deployments alias NervesHub.Devices alias NervesHub.Devices.Metrics alias NervesHub.Fixtures + alias NervesHub.ManagedDeployments alias NervesHub.Repo alias NervesHubWeb.Endpoint @@ -106,7 +106,7 @@ defmodule NervesHubWeb.Live.Devices.ShowTest do device: device, deployment: deployment } do - {:ok, deployment} = Deployments.update_deployment(deployment, %{is_active: true}) + {:ok, deployment} = ManagedDeployments.update_deployment(deployment, %{is_active: true}) # mismatch device and deployment firmware so "Send Update" form doesn't display original_firmware_platform = device.firmware_metadata.platform @@ -462,7 +462,7 @@ defmodule NervesHubWeb.Live.Devices.ShowTest do assert Repo.aggregate(NervesHub.Devices.InflightUpdate, :count) == 1 - assert_receive %Phoenix.Socket.Broadcast{event: "deployments/update"} + assert_receive %Phoenix.Socket.Broadcast{event: "deployment_groups/update"} end test "available update exists but deployment is not active", %{ diff --git a/test/support/fixtures.ex b/test/support/fixtures.ex index ccd7ab4cc..487e7f783 100644 --- a/test/support/fixtures.ex +++ b/test/support/fixtures.ex @@ -7,10 +7,10 @@ defmodule NervesHub.Fixtures do alias NervesHub.Archives alias NervesHub.AuditLogs alias NervesHub.Certificate - alias NervesHub.Deployments alias NervesHub.Devices alias NervesHub.Devices.InflightUpdate alias NervesHub.Firmwares + alias NervesHub.ManagedDeployments alias NervesHub.Products alias NervesHub.Products.Product alias NervesHub.Repo @@ -221,9 +221,9 @@ defmodule NervesHub.Fixtures do %{org_id: org.id, firmware_id: firmware.id} |> Enum.into(params) |> Enum.into(@deployment_params) - |> Deployments.create_deployment() + |> ManagedDeployments.create_deployment() - {:ok, deployment} = Deployments.update_deployment(deployment, %{is_active: is_active}) + {:ok, deployment} = ManagedDeployments.update_deployment(deployment, %{is_active: is_active}) deployment end