Skip to content
This repository was archived by the owner on Apr 17, 2023. It is now read-only.

Commit 9aa3ee2

Browse files
committed
api: added create and update methods to registries
Signed-off-by: Miquel Sabaté Solà <[email protected]>
1 parent a4c05fd commit 9aa3ee2

File tree

7 files changed

+313
-33
lines changed

7 files changed

+313
-33
lines changed

app/controllers/admin/registries_controller.rb

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,18 @@ def new
2626
# done. If this check is passed/skipped, then it will try to create the
2727
# registry.
2828
def create
29-
@registry = Registry.new(create_params)
29+
svc = ::Registries::CreateService.new(current_user, create_params)
30+
svc.force = params[:force]
31+
svc.execute
3032

31-
# Check the reachability of the registry.
32-
33-
check_reachability("new") unless params[:force]
34-
return if @unreachable
35-
36-
if @registry.save
37-
Namespace.update_all(registry_id: @registry.id)
33+
if svc.valid?
3834
redirect_to admin_registries_path, notice: "Registry was successfully created."
35+
elsif svc.reachable?
36+
flash[:alert] = svc.messages
37+
redirect_to new_admin_registry_path, alert: svc.messages
3938
else
40-
redirect_to new_admin_registry_path, alert: @registry.errors.full_messages
39+
flash[:alert] = svc.messages
40+
render :new, status: :unprocessable_entity
4141
end
4242
end
4343

@@ -55,40 +55,24 @@ def edit
5555
# Right now this just toggles the value of the "use_ssl" attribute for the
5656
# given registry. This might change in the future.
5757
def update
58-
@registry = Registry.find(params[:id])
59-
@registry.assign_attributes(update_params)
60-
@can_change_hostname = !Repository.any?
61-
62-
# Check the reachability of the registry.
63-
check_reachability("edit")
64-
return if @unreachable
58+
attrs = update_params.merge(id: params[:id])
59+
svc = ::Registries::UpdateService.new(current_user, attrs)
60+
@registry = svc.execute
6561

66-
if @registry.save
62+
if svc.valid?
6763
# NOTE: if we decide to use rails-observers at some point,
6864
# we can remove this from here and use it in observers
6965
Rails.cache.delete "registry#{@registry.id}_status"
7066
redirect_to admin_registries_path, notice: "Registry updated successfully!"
7167
else
72-
flash[:alert] = @registry.errors.full_messages
68+
flash[:alert] = svc.messages
69+
@can_change_hostname = !Repository.any?
7370
render "edit", status: :unprocessable_entity
7471
end
7572
end
7673

7774
private
7875

79-
# Checks if registry is reachable and sends `unprocessable_entity`
80-
# status if unreachable
81-
def check_reachability(action)
82-
msg = @registry.reachable?
83-
return if msg.blank?
84-
85-
logger.info "\nRegistry not reachable:\n#{@registry.inspect}\n#{msg}\n"
86-
flash[:alert] = "#{msg} You can skip this check by clicking on the
87-
\"Skip remote checks\" checkbox."
88-
render action, status: :unprocessable_entity
89-
@unreachable = true
90-
end
91-
9276
# Raises a routing error if there is already a registry in place.
9377
# NOTE: (mssola) remove this once we support multiple registries.
9478
def registry_created
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# frozen_string_literal: true
2+
3+
module Registries
4+
class BaseService < ::BaseService
5+
attr_reader :messages
6+
attr_accessor :force
7+
8+
def valid?
9+
@valid
10+
end
11+
12+
def reachable?
13+
@reachable
14+
end
15+
16+
protected
17+
18+
def check_reachability!
19+
msg = @registry.reachable?
20+
return if msg.blank?
21+
22+
Rails.logger.info "\nRegistry not reachable:\n#{@registry.inspect}\n#{msg}\n"
23+
@valid = false
24+
@reachable = false
25+
@messages[:hostname] = (@messages[:hostname] || []).push(msg)
26+
end
27+
end
28+
end
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# frozen_string_literal: true
2+
3+
module Registries
4+
class CreateService < ::Registries::BaseService
5+
def execute
6+
@registry = Registry.new(params)
7+
@valid = @registry.valid?
8+
@messages = @registry.errors.messages
9+
@reachable = true
10+
11+
check!
12+
if @valid
13+
Namespace.update_all(registry_id: @registry.id) if @registry.save
14+
end
15+
16+
@registry
17+
end
18+
19+
protected
20+
21+
def check!
22+
return unless @valid
23+
check_uniqueness!
24+
return unless @valid
25+
check_reachability! unless @force
26+
end
27+
28+
def check_uniqueness!
29+
return unless Registry.any?
30+
@valid = false
31+
@messages[:uniqueness] = "You can only create one registry"
32+
end
33+
end
34+
end
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# frozen_string_literal: true
2+
3+
module Registries
4+
class UpdateService < ::Registries::BaseService
5+
def execute
6+
@registry = Registry.find(params[:id])
7+
@messages = @registry.errors.messages
8+
@registry.assign_attributes(params)
9+
@valid = @registry.valid?
10+
11+
check!
12+
if @valid
13+
@messages.merge(@registry.errors.messages) unless @registry.save
14+
end
15+
16+
@registry
17+
end
18+
19+
protected
20+
21+
def check!
22+
return unless @valid
23+
check_hostname!
24+
return unless @valid
25+
check_reachability! unless @force
26+
end
27+
28+
def check_hostname!
29+
return if !Repository.any? || params[:hostname].blank?
30+
@valid = false
31+
@messages[:hostname] = "Registry is not empty, cannot change hostname"
32+
end
33+
end
34+
end

lib/api/v1/registries.rb

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,77 @@ class Registries < Grape::API
2525
present Registry.all, with: API::Entities::Registries
2626
end
2727

28+
desc "Create a registry",
29+
tags: ["registries"],
30+
detail: "Allow users to create a registry. " \
31+
"This will only work if no registry works yet",
32+
entity: API::Entities::Registries,
33+
consumes: ["application/x-www-form-urlencoded", "application/json"],
34+
failure: [
35+
[400, "Bad request", API::Entities::ApiErrors],
36+
[401, "Authentication fails"],
37+
[403, "Authorization fails"]
38+
]
39+
40+
params do
41+
requires :registry, type: Hash do
42+
requires :all,
43+
only: %i[name hostname use_ssl],
44+
using: API::Entities::Registries.documentation.slice(
45+
:name, :hostname, :use_ssl
46+
)
47+
optional :all,
48+
only: %i[use_ssl external_hostname],
49+
using: API::Entities::Registries.documentation.slice(:external_hostname)
50+
end
51+
end
52+
53+
post do
54+
svc = ::Registries::CreateService.new(current_user, permitted_params[:registry])
55+
obj = svc.execute
56+
57+
if svc.valid?
58+
present obj, with: API::Entities::Registries
59+
else
60+
status 400
61+
{ errors: svc.messages }
62+
end
63+
end
64+
65+
desc "Update registry",
66+
params: API::Entities::Registries.documentation.slice(:id),
67+
failure: [
68+
[400, "Bad request", API::Entities::ApiErrors],
69+
[401, "Authentication fails"],
70+
[403, "Authorization fails"],
71+
[404, "Not found"]
72+
],
73+
entity: API::Entities::Registries,
74+
consumes: ["application/x-www-form-urlencoded", "application/json"]
75+
76+
params do
77+
requires :registry, type: Hash do
78+
optional :all,
79+
only: %i[name hostname use_ssl external_hostname],
80+
using: API::Entities::Registries.documentation.slice(
81+
:name, :hostname, :use_ssl, :external_hostname
82+
)
83+
end
84+
end
85+
86+
put ":id" do
87+
attrs = declared(params, include_missing: false)[:registry].merge(id: params[:id])
88+
svc = ::Registries::UpdateService.new(current_user, attrs)
89+
obj = svc.execute
90+
91+
if svc.valid?
92+
present obj, with: API::Entities::Registries
93+
else
94+
status 400
95+
{ errors: svc.messages }
96+
end
97+
end
98+
2899
desc "Validates the given registry",
29100
tags: ["registries"],
30101
detail: "Besides containing the usual Status object, it adds the reachable " \

spec/api/grape_api/v1/registries_spec.rb

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,136 @@
99
@header = build_token_header(token)
1010
end
1111

12+
context "POST /api/v1/registries" do
13+
let(:data) do
14+
{
15+
registry: {
16+
name: "registry",
17+
hostname: "my.registry.cat",
18+
use_ssl: true
19+
}
20+
}
21+
end
22+
23+
let(:wrong_data) do
24+
{
25+
registry: {
26+
name: "registry",
27+
hostname: "my.registry.cat",
28+
use_ssl: "asd"
29+
}
30+
}
31+
end
32+
33+
it "creates a registry" do
34+
allow_any_instance_of(Registry).to receive(:reachable?).and_return(nil)
35+
post "/api/v1/registries", data, @header
36+
expect(response).to have_http_status(:success)
37+
38+
resp = JSON.parse(response.body)
39+
expect(resp["name"]).to eq(data[:registry][:name])
40+
end
41+
42+
it "does not create a registry on a wrong use_ssl value" do
43+
allow_any_instance_of(Registry).to receive(:reachable?).and_return(nil)
44+
post "/api/v1/registries", wrong_data, @header
45+
expect(response).to have_http_status(:bad_request)
46+
47+
resp = JSON.parse(response.body)
48+
msg = resp["errors"].first.last.first
49+
expect(msg).to eq("is invalid")
50+
end
51+
52+
it "does not allow to create multiple registries" do
53+
create :registry
54+
55+
allow_any_instance_of(Registry).to receive(:reachable?).and_return(nil)
56+
post "/api/v1/registries", data, @header
57+
expect(response).to have_http_status(:bad_request)
58+
59+
resp = JSON.parse(response.body)
60+
expect(resp["errors"]["uniqueness"]).to eq("You can only create one registry")
61+
end
62+
63+
it "returns an error on unreachable registry" do
64+
allow_any_instance_of(Registry).to receive(:reachable?).and_return("Not reachable")
65+
post "/api/v1/registries", data, @header
66+
expect(response).to have_http_status(:bad_request)
67+
68+
resp = JSON.parse(response.body)
69+
expect(resp["errors"]["hostname"].first).to eq("Not reachable")
70+
end
71+
end
72+
73+
context "PUT /api/v1/registries/:id" do
74+
let(:data) do
75+
{
76+
registry: {
77+
name: "registry",
78+
hostname: "my.registry.cat",
79+
use_ssl: true
80+
}
81+
}
82+
end
83+
84+
let(:just_name) do
85+
{ registry: { name: "newname" } }
86+
end
87+
88+
it "updates a registry" do
89+
r = create :registry, hostname: "lala"
90+
91+
allow_any_instance_of(Registry).to receive(:reachable?).and_return(nil)
92+
put "/api/v1/registries/#{r.id}", data, @header
93+
expect(response).to have_http_status(:success)
94+
95+
reg = Registry.first
96+
resp = JSON.parse(response.body)
97+
98+
expect(resp["hostname"]).to eq(data[:registry][:hostname])
99+
expect(reg.hostname).to eq(data[:registry][:hostname])
100+
end
101+
102+
it "does not allow to update the hostname if there are repositories" do
103+
r = create :registry
104+
namespace = create(:namespace, registry: r, team: create(:team))
105+
create(:repository, namespace: namespace)
106+
107+
allow_any_instance_of(Registry).to receive(:reachable?).and_return(nil)
108+
put "/api/v1/registries/#{r.id}", data, @header
109+
expect(response).to have_http_status(:bad_request)
110+
111+
resp = JSON.parse(response.body)
112+
expect(resp["errors"]["hostname"]).to eq("Registry is not empty, cannot change hostname")
113+
end
114+
115+
it "allows to update if there are repositories and you don't touch the hostname" do
116+
r = create :registry
117+
namespace = create(:namespace, registry: r, team: create(:team))
118+
create(:repository, namespace: namespace)
119+
120+
allow_any_instance_of(Registry).to receive(:reachable?).and_return(nil)
121+
put "/api/v1/registries/#{r.id}", just_name, @header
122+
expect(response).to have_http_status(:success)
123+
124+
reg = Registry.first
125+
resp = JSON.parse(response.body)
126+
expect(resp["name"]).to eq(just_name[:registry][:name])
127+
expect(reg.name).to eq(just_name[:registry][:name])
128+
end
129+
130+
it "returns an error on unreachable registry" do
131+
r = create :registry, hostname: "lala"
132+
133+
allow_any_instance_of(Registry).to receive(:reachable?).and_return("Not reachable")
134+
put "/api/v1/registries/#{r.id}", data, @header
135+
expect(response).to have_http_status(:bad_request)
136+
137+
resp = JSON.parse(response.body)
138+
expect(resp["errors"]["hostname"].first).to eq("Not reachable")
139+
end
140+
end
141+
12142
context "GET /api/v1/registries" do
13143
it "returns list of registries" do
14144
r = create :registry

spec/controllers/admin/registries_controller_spec.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,7 @@
117117
expect(response).to have_http_status(:unprocessable_entity)
118118
end
119119

120-
it "renders 'edit' with unprocessable entity status (422)
121-
when registry is invalid" do
120+
it "renders 'edit' with unprocessable entity status (422) when registry is invalid" do
122121
allow_any_instance_of(Registry).to receive(:reachable?).and_return(nil)
123122
expect do
124123
put :update, id: registry.id, registry: { name: "" }

0 commit comments

Comments
 (0)