Skip to content

Commit

Permalink
[api] fix trigger route behaviour
Browse files Browse the repository at this point in the history
- fail when wrong operation type is used (instead of executing the token
  operation)
- allow to use /trigger without specifing the operation type (is defined
  by the token)
- verify given project/package parameter with possible token package
- fix triggering rebuilds and releases of entire projects
- improve test coverage and api docu
  • Loading branch information
adrianschroeter committed Oct 26, 2023
1 parent 4f002a5 commit 8ec8770
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 22 deletions.
53 changes: 48 additions & 5 deletions docs/api/api/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1478,21 +1478,64 @@ XmlResult: buildinfo

=== Trigger area

POST /trigger/runservice
POST /trigger

A global route which can be used for any of the routes below. Any operation
type is allowed and is defined by the token.

This call needs a token to identify the user and package which shall be updated
via a source service. See the cmd=create in the /person/<userid>/token route.
These calls needs a token to identify the user and operation type which shall be
be exectured. See the cmd=create in the /person/<userid>/token route.

The token itself must be delivered as a HTTP header:
Authorization: Token <TOKEN_STRING>

Parameters:
Parameters are operation specific, please read bellow.

XmlResult: status


POST /trigger/runservice

Executes a source service run.

Parameters
project: To be used together with "package" to identify the object. Must not be used
when the token is bound to a package.
project: To be used together with "package" to identify the object. Project global runs
are not supported.
package: see project parameter.

XmlResult: status


POST /trigger/rebuild

Rebuilds an entire project or single package.

Parameters
project: required for tokens which are not bound to a package
package: optional, must not be different with to token specific package
repository: optional, just for specified repository
arch: optional, just for specified scheduler architecture

XmlResult: status


POST /trigger/release

Releases an entire project or single package.

Parameters
project: required for tokens which are not bound to a package
package: optional, must not be different with to token specific package
repository: optional, just for specified repository
arch: optional, just for specified scheduler architecture
targetproject: optional, filter for target project
targetrepository: optional, filter for target repository
filter_source_repository: optional, filter for repository defined in origin project

XmlResult: status


=== Repository Information

GET /build/<project>/<repository>/<arch>/_repository
Expand Down
2 changes: 1 addition & 1 deletion src/api/app/controllers/concerns/triggerable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def set_package(package_find_options: {})
# If the token has no package, let's find one from the parameters
@package ||= Package.get_by_project_and_name(@project,
@package_name,
package_find_options)
package_find_options) if @package_name.present?
return unless @project.links_to_remote?

# The token has no package, we did not find a package in the database but the project has a link to remote.
Expand Down
4 changes: 1 addition & 3 deletions src/api/app/controllers/person/token_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ def set_user

def set_package
@package = nil
return unless params[:project] || params[:package]

raise MissingParameterError, 'The package and project parameters must be provided together.' unless params[:project] && params[:package]
return unless params[:project] && params[:package]

@package = Package.get_by_project_and_name(params[:project], params[:package])
end
Expand Down
5 changes: 4 additions & 1 deletion src/api/app/controllers/trigger_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def create
targetproject: params[:targetproject], targetrepository: params[:targetrepository],
filter_source_repository: params[:filter_source_repository] }
opts[:multibuild_flavor] = @multibuild_container if @multibuild_container.present?
raise InvalidToken, 'Invalid token found' unless request.path == '/trigger' || @token.class == Token.token_type(request.path.gsub('/trigger/',''))
@token.call(opts)
render_ok
end
Expand Down Expand Up @@ -66,10 +67,12 @@ def pundit_user

def set_project_name
@project_name = params[:project]
raise InvalidParameterError if @project_name.present? && @token.package && @token.package.project.name != @project_name
end

def set_package_name
@package_name = params[:package]
raise MissingPackage if @package_name.blank? && @token.package_id.nil? && @token.token_name.in?(['rebuild', 'release', 'service'])
raise InvalidParameterError if @package_name.present? && @token.package && @token.package.name != @project_name
raise MissingPackage if @package_name.blank? && @token.package_id.nil? && @token.token_name.in?(['service'])
end
end
5 changes: 5 additions & 0 deletions src/api/app/lib/backend/api/build/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ def self.suspend_scheduler(project_name, comment = nil)
http_post(['/build/:project', project_name], params: params)
end

def self.rebuild(project_name, options = {})
http_post(['/build/:project', project_name], defaults: { cmd: :rebuild },
params: options, accepted: [:repository, :arch])
end

def self.resume_scheduler(project_name, comment = nil)
params = { cmd: :resumeproject }
params[:comment] = comment if comment
Expand Down
7 changes: 6 additions & 1 deletion src/api/app/models/token/rebuild.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ def call(options)
set_triggered_at
package_name = options[:package].to_param
package_name += ':' + options[:multibuild_flavor] if options[:multibuild_flavor]
Backend::Api::Sources::Package.rebuild(options[:project].to_param,
if package_name.present?
Backend::Api::Sources::Package.rebuild(options[:project].to_param,
package_name,
options.slice(:repository, :arch).compact)
else
Backend::Api::Build::Project.rebuild(options[:project].to_param,
options.slice(:repository, :arch).compact)
end
end

def package_find_options
Expand Down
13 changes: 9 additions & 4 deletions src/api/app/models/token/release.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,15 @@ def release(package_to_release, source_repository, target_repository, time_now,
opts[:multibuild_container] = options[:multibuild_flavor] if options[:multibuild_flavor].present?
opts[:filter_architecture] = options[:arch] if options[:arch].present?

release_package(package_to_release,
target_repository,
package_to_release.release_target_name(target_repository, time_now),
opts)

if package_to_release.present?
release_package(package_to_release,
target_repository,
package_to_release.release_target_name(target_repository, time_now),
opts)
else
@project.do_project_release(opts)
end
end

def release_manually_triggered_targets(package_to_release, time_now, options)
Expand Down
1 change: 1 addition & 0 deletions src/api/config/routes/api_routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
resources :architectures, only: [:index, :show, :update] # create,delete currently disabled

### /trigger
post 'trigger' => 'trigger#create' # operation is read from the token
post 'trigger/rebuild' => 'trigger#create'
post 'trigger/release' => 'trigger#create'
post 'trigger/runservice' => 'trigger#create'
Expand Down
50 changes: 43 additions & 7 deletions src/api/test/functional/trigger_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,61 @@ def test_rebuild_via_token
post '/person/tom/token?cmd=create'
assert_response 401

login_Iggy
post '/person/Iggy/token?cmd=create&operation=rebuild'
assert_response :success
doc = REXML::Document.new(@response.body)
rebuild_global_token = doc.elements['//data'].text
rebuild_global_id = doc.elements['//data[@name="id"]'].text
assert_equal 24, rebuild_global_token.length

login_tom
put '/source/home:tom/test/_meta', params: "<package project='home:tom' name='test'> <title /> <description /> </package>"
assert_response :success

post '/person/tom/token?cmd=create&project=home:tom&package=test&operation=rebuild'
assert_response :success
doc = REXML::Document.new(@response.body)
token = doc.elements['//data'].text
assert_equal 24, token.length
rebuild_token = doc.elements['//data'].text
rebuild_id = doc.elements['//data[@name="id"]'].text
assert_equal 24, rebuild_token.length

post '/person/tom/token?cmd=create&project=home:tom&package=test'
assert_response :success
doc = REXML::Document.new(@response.body)
service_token = doc.elements['//data'].text
service_id = doc.elements['//data[@name="id"]'].text

# ANONYMOUS
reset_auth
post '/trigger/rebuild'
post '/trigger/runservice'
assert_response 403
assert_xml_tag tag: 'status', attributes: { code: 'invalid_token' }
assert_match(/No valid token found/, @response.body)

# with wrong token
post '/trigger/rebuild', headers: { 'Authorization' => 'Token wrong' }
post '/trigger/runservice', headers: { 'Authorization' => 'Token wrong' }
assert_response 404
assert_xml_tag tag: 'status', attributes: { code: 'not_found' }

# with right token
post '/trigger/rebuild', headers: { 'Authorization' => "Token #{token}" }
# backend output ignored atm :/
post '/trigger/runservice', headers: { 'Authorization' => "Token #{service_token}" }
assert_response 404
assert_match(/no source service defined/, @response.body) # request reached backend

# wrong operation
post '/trigger/release', headers: { 'Authorization' => "Token #{service_token}" }
assert_response 403
assert_xml_tag tag: 'status', attributes: { code: 'invalid_token' }
assert_match(/Invalid token found/, @response.body)

# wrong package
post '/trigger/rebuild?project="home:Iggy"&package="TestPack"', headers: { 'Authorization' => "Token #{rebuild_token}" }
assert_response 400
assert_xml_tag tag: 'status', attributes: { code: 'invalid_parameter' }

# entire project rebuild (using upper main route)
post '/trigger?project=home:Iggy', headers: { 'Authorization' => "Token #{rebuild_global_token}" }
assert_response :success

# reset and drop stuff as tom
Expand All @@ -49,11 +79,17 @@ def test_rebuild_via_token
id = doc.elements['//entry'].attributes['id']
assert_not_nil id
assert_not_nil doc.elements['//entry'].attributes['string']
delete "/person/tom/token/#{id}"
delete "/person/tom/token/#{service_id}"
assert_response :success
delete "/person/tom/token/#{rebuild_id}"
assert_response :success

# cleanup
delete '/source/home:tom/test'
assert_response :success

login_Iggy
delete "/person/Iggy/token/#{rebuild_global_id}"
assert_response :success
end
end

0 comments on commit 8ec8770

Please sign in to comment.