Skip to content

Commit 60cce32

Browse files
Implement demo project transfer (#524)
This will allow users to reparent their demo project under their own gcp organization to keep it running
1 parent 670bc48 commit 60cce32

File tree

7 files changed

+94
-3
lines changed

7 files changed

+94
-3
lines changed

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,10 @@ migration:
7373

7474
yarn-add: ## adds a yarn dep to the react interface
7575
cd www && yarn add $(dep)
76+
77+
release-vsn: # tags and pushes a new release
78+
@read -p "Version: " tag; \
79+
git checkout master; \
80+
git pull --rebase; \
81+
git tag -a $$tag -m "new release"; \
82+
git push origin $$tag

apps/core/lib/core/services/shell/demo.ex

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ defmodule Core.Services.Shell.Demo do
33
alias Core.Schema.{User, DemoProject}
44
alias Core.Services.{Locks, Users, Upgrades, Repositories}
55
alias GoogleApi.CloudResourceManager.V3.Api.{Projects, Operations}
6-
alias GoogleApi.CloudResourceManager.V3.Model.{Project, Operation, SetIamPolicyRequest}
6+
alias GoogleApi.CloudResourceManager.V3.Model.{Project, Operation, SetIamPolicyRequest, MoveProjectRequest}
77
alias GoogleApi.CloudResourceManager.V3.Connection, as: ProjectsConnection
88
alias GoogleApi.IAM.V1.Connection, as: IAMConnection
99
alias GoogleApi.IAM.V1.Api.Projects, as: IAMProjects
@@ -18,13 +18,17 @@ defmodule Core.Services.Shell.Demo do
1818

1919

2020
@type error :: {:error, term}
21+
@type demo_resp :: {:ok, DemoProject.t} | error
2122

2223
@lock "demo-projects"
2324
@max_count 3
2425

2526
@spec get_demo_project(binary) :: DemoProject.t | nil
2627
def get_demo_project(id), do: Core.Repo.get(DemoProject, id)
2728

29+
@spec get_by_user_id(binary) :: DemoProject.t | nil
30+
def get_by_user_id(id), do: Core.Repo.get_by(DemoProject, user_id: id)
31+
2832
@doc """
2933
Returns at most `limit` demo projects for deletion. This operation should be atomic, and will mark each project with
3034
a heartbeat ts to track status in the deletion pipeline.
@@ -93,6 +97,33 @@ defmodule Core.Services.Shell.Demo do
9397
|> execute(extract: :final)
9498
end
9599

100+
@doc """
101+
Transfers a demo project to another organization
102+
"""
103+
@spec transfer_demo_project(binary, User.t) :: demo_resp
104+
def transfer_demo_project(org_id, %User{id: user_id}) do
105+
demo = get_by_user_id(user_id)
106+
projs = projects_conn()
107+
108+
start_transaction()
109+
|> add_operation(:move, fn _ ->
110+
Projects.cloudresourcemanager_projects_move(projs, demo.project_id, body: %MoveProjectRequest{
111+
destinationParent: "organizations/#{org_id}"
112+
})
113+
|> IO.inspect() # useful for testing
114+
end)
115+
|> add_operation(:shell, fn _ ->
116+
with %{} = shell <- Core.Services.Shell.get_shell(user_id) do
117+
Ecto.Changeset.change(shell, %{demo_id: nil})
118+
|> Core.Repo.update()
119+
else
120+
_ -> {:ok, nil}
121+
end
122+
end)
123+
|> add_operation(:delete, fn _ -> Core.Repo.delete(demo) end)
124+
|> execute(extract: :delete)
125+
end
126+
96127
@doc """
97128
Deletes the demo project and its associated gcp project
98129
"""

apps/core/test/services/shell/demo_test.exs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,26 @@ defmodule Core.Services.Shell.DemoTest do
151151
end
152152
end
153153

154+
describe "#transfer_demo_project/2" do
155+
test "it will transfer a users demo project" do
156+
demo = insert(:demo_project)
157+
shell = insert(:cloud_shell, demo: demo, user: demo.user)
158+
159+
proj_id = demo.project_id
160+
expect(Goth.Token, :for_scope, fn _ -> {:ok, %{token: "token"}} end)
161+
expect(Projects, :cloudresourcemanager_projects_move, fn _, ^proj_id, [body: %{destinationParent: "organizations/org-id"}] ->
162+
{:ok, %{}}
163+
end)
164+
165+
{:ok, res} = Demo.transfer_demo_project("org-id", demo.user)
166+
167+
assert res.id == demo.id
168+
169+
refute refetch(demo)
170+
refute refetch(shell).demo_id
171+
end
172+
end
173+
154174
describe "#poll/1" do
155175
test "it will fetch a list of projects to delete" do
156176
expired = Timex.now() |> Timex.shift(hours: -10)

apps/graphql/lib/graphql/resolvers/shell.ex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ defmodule GraphQl.Resolvers.Shell do
1717
def create_demo_project(_, %{context: %{current_user: user}}),
1818
do: Shell.Demo.create_demo_project(user)
1919

20+
def transfer_demo_project(%{organization_id: org}, %{context: %{current_user: user}}),
21+
do: Shell.Demo.transfer_demo_project(org, user)
22+
2023
def reboot(_, %{context: %{current_user: user}}), do: Shell.reboot(user.id)
2124

2225
def stop(_, %{context: %{current_user: user}}), do: Shell.stop(user)

apps/graphql/lib/graphql/schema/shell.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ defmodule GraphQl.Schema.Shell do
144144
safe_resolve &Shell.create_demo_project/2
145145
end
146146

147+
field :transfer_demo_project, :demo_project do
148+
middleware Authenticated
149+
arg :organization_id, non_null(:string)
150+
151+
safe_resolve &Shell.transfer_demo_project/2
152+
end
153+
147154
field :stop_shell, :boolean do
148155
middleware Authenticated
149156
safe_resolve &Shell.stop/2

apps/graphql/test/mutations/shell_mutations_test.exs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ defmodule GraphQl.ShellMutationsTest do
33
use Mimic
44
import GraphQl.TestHelpers
55
alias Core.Services.Shell.Pods
6+
alias GoogleApi.CloudResourceManager.V3.Api.Projects
67

78
describe "createShell" do
89
test "it will create a new shell instance" do
@@ -89,7 +90,7 @@ defmodule GraphQl.ShellMutationsTest do
8990
test "it will create a new demo project" do
9091
user = insert(:user)
9192
expect(Goth.Token, :for_scope, fn _ -> {:ok, %{token: "token"}} end)
92-
expect(GoogleApi.CloudResourceManager.V3.Api.Projects, :cloudresourcemanager_projects_create, fn _, [body: _] ->
93+
expect(Projects, :cloudresourcemanager_projects_create, fn _, [body: _] ->
9394
{:ok, %{name: "operations/123"}}
9495
end)
9596

@@ -144,4 +145,25 @@ defmodule GraphQl.ShellMutationsTest do
144145
assert refetch(shell)
145146
end
146147
end
148+
149+
describe "transferDemoProject" do
150+
test "it will send out a tranfer request" do
151+
demo = insert(:demo_project)
152+
153+
proj_id = demo.project_id
154+
expect(Goth.Token, :for_scope, fn _ -> {:ok, %{token: "token"}} end)
155+
expect(Projects, :cloudresourcemanager_projects_move, fn _, ^proj_id, [body: %{destinationParent: "organizations/org-id"}] ->
156+
{:ok, %{}}
157+
end)
158+
159+
{:ok, %{data: %{"transferDemoProject" => %{"id" => id}}}} = run_query("""
160+
mutation {
161+
transferDemoProject(organizationId: "org-id") { id }
162+
}
163+
""", %{}, %{current_user: demo.user})
164+
165+
assert id == demo.id
166+
refute refetch(demo)
167+
end
168+
end
147169
end

apps/worker/mix.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ defmodule Worker.MixProject do
5656
defp deps do
5757
[
5858
{:k8s_traffic_plug, github: "Financial-Times/k8s_traffic_plug"},
59-
{:core, in_umbrella: true}
59+
{:core, in_umbrella: true},
60+
{:email, in_umbrella: true}
6061
]
6162
end
6263
end

0 commit comments

Comments
 (0)