Skip to content

Commit

Permalink
Merge pull request #35 from criteo-cookbooks/task-management
Browse files Browse the repository at this point in the history
Task management resource
  • Loading branch information
Annih committed Mar 6, 2018
2 parents 33128dc + 0511aaf commit 66e4c44
Show file tree
Hide file tree
Showing 10 changed files with 218 additions and 0 deletions.
1 change: 1 addition & 0 deletions .kitchen.docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ suites:
- nexus3_test::user
- nexus3_test::repositories
- nexus3_test::roles
- nexus3_test::tasks
attributes:
nexus3_test:
connection_retries: 10
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,28 @@ the help of `libraries/scripts_helper.rb`.
- `attributes` - Hash of attributes passed to the `:create` action, used to
specify repository attributes for creation or update.
- `online` - Whether to put the repository online or not (default: true).
- `api_endpoint` - Nexus 3 API endpoint (default: node['nexus3']['api']['endpoint']).
- `api_username` - Nexus 3 API user name (default: node['nexus3']['api']['username']).
- `api_password` - Nexus 3 API password (default: node['nexus3']['api']['password']).

## nexus3_task

Configures scheduled tasks via API.

### Actions

- `:create` - Creates or updates a scheduled task.
- `:delete` - Removes a scheduled task.

### Properties

- `task_name` - Name of task to act on, defaults to resource name.
- `task_source` - Source code of the script to run, for now it defaults to
running Groovy scripts (typeID: script).
- `task_crontab` - Actual schedule in the form of a crontab string.
- `api_endpoint` - Nexus 3 API endpoint (default: node['nexus3']['api']['endpoint']).
- `api_username` - Nexus 3 API user name (default: node['nexus3']['api']['username']).
- `api_password` - Nexus 3 API password (default: node['nexus3']['api']['password']).

## nexus3_user

Expand Down
12 changes: 12 additions & 0 deletions files/default/delete_task.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import org.sonatype.nexus.scheduling.TaskConfiguration;
import org.sonatype.nexus.scheduling.TaskInfo;
import org.sonatype.nexus.scheduling.TaskScheduler;

TaskScheduler taskScheduler = container.lookup(TaskScheduler.class.getName());
TaskInfo existingTask = taskScheduler.listsTasks().find { TaskInfo taskInfo ->
taskInfo.getName() == args;
}

if (existingTask && !existingTask.remove()) {
throw new RuntimeException("Could not remove currently running task: " + args);
}
15 changes: 15 additions & 0 deletions files/default/get_task.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import org.sonatype.nexus.scheduling.TaskConfiguration;
import org.sonatype.nexus.scheduling.TaskInfo;
import org.sonatype.nexus.scheduling.TaskScheduler;

import groovy.json.JsonOutput;

TaskScheduler taskScheduler = container.lookup(TaskScheduler.class.getName());

TaskInfo existingTask = taskScheduler.listsTasks().find { TaskInfo taskInfo ->
taskInfo.getName() == args;
}

if (existingTask) {
return JsonOutput.toJson(existingTask.getConfiguration().asMap());
}
25 changes: 25 additions & 0 deletions files/default/upsert_task.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Freely adapted from
// https://github.com/savoirfairelinux/ansible-nexus3-oss/blob/master/files/groovy/create_task.groovy
import org.sonatype.nexus.scheduling.TaskConfiguration;
import org.sonatype.nexus.scheduling.TaskInfo;
import org.sonatype.nexus.scheduling.TaskScheduler;
import org.sonatype.nexus.scheduling.schedule.Schedule;

import groovy.json.JsonSlurper;

def params = new JsonSlurper().parseText(args);

TaskScheduler taskScheduler = container.lookup(TaskScheduler.class.getName());
TaskInfo existingTask = taskScheduler.listsTasks().find { TaskInfo taskInfo ->
taskInfo.getName() == params.name;
}
if (existingTask && !existingTask.remove()) {
throw new RuntimeException("Could not remove currently running task: " + params.name);
}

TaskConfiguration taskConfiguration = taskScheduler.createTaskConfigurationInstance('script');
taskConfiguration.setName(params.name);
taskConfiguration.setString('source', params.source);
Schedule schedule = taskScheduler.scheduleFactory.cron(new Date(), params.crontab);

taskScheduler.scheduleTask(taskConfiguration, schedule);
84 changes: 84 additions & 0 deletions resources/task.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
property :task_name, String, name_property: true
property :task_source, String, default: ''.freeze
property :task_crontab, String, default: '0 1 * * * ?'.freeze
property :api_endpoint, String, identity: true, default: lazy { node['nexus3']['api']['endpoint'] }
property :api_username, String, identity: true, default: lazy { node['nexus3']['api']['username'] }
property :api_password, String, identity: true, sensitive: true, default: lazy { node['nexus3']['api']['password'] }

load_current_value do |desired|
begin
res = ::Nexus3::Api.new(api_endpoint, api_username, api_password).run_script('get_task', desired.task_name)
config = JSON.parse(res)
current_value_does_not_exist! if config.nil?
::Chef::Log.debug "Config is: #{config}"
task_source config['source']
# We rescue here because during the first run, the task will not exist yet, so we let Chef know that
# the resource has to be created.
rescue LoadError, ::Nexus3::ApiError => e
::Chef::Log.warn "A '#{e.class}' occured: #{e.message}"
current_value_does_not_exist!
end
end

action :create do
init

converge_if_changed do
nexus3_api "upsert_task #{new_resource.task_name}" do
script_name 'upsert_task'
args name: new_resource.task_name,
source: new_resource.task_source,
crontab: new_resource.task_crontab

action %i(create run)
endpoint new_resource.api_endpoint
username new_resource.api_username
password new_resource.api_password

content ::Nexus3::Scripts.groovy_content('upsert_task', node)
end
end
end

action :delete do
init

converge_if_changed do
nexus3_api "delete_task #{new_resource.task_name}" do
action %i(create run)
script_name 'delete_task'
args new_resource.task_name

content ::Nexus3::Scripts.groovy_content('delete_task', node)

endpoint new_resource.api_endpoint
username new_resource.api_username
password new_resource.api_password

not_if { current_resource.nil? }
end
end
end

action_class do
def init
chef_gem 'httpclient' do
compile_time true
end

nexus3_api "get_task #{new_resource.task_name}" do
action :create
script_name 'get_task'
args new_resource.task_name
endpoint new_resource.api_endpoint
username new_resource.api_username
password new_resource.api_password

content ::Nexus3::Scripts.groovy_content('get_task', node)
end
end

def whyrun_supported?
true
end
end
5 changes: 5 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ def role_response(rolename)
privileges: ['priv-spec-1', 'priv-spec-2']))
end

def task_response(taskname)
api_response(200, result: JSON.generate(name: taskname),
source: 'println("Hello, World!");')
end

RSpec.configure do |config|
# Disable all http requests
WebMock.disable_net_connect!(allow: /supermarket.chef.io|files.opscode.com/i)
Expand Down
19 changes: 19 additions & 0 deletions spec/unit/resources/tasks_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require 'spec_helper'

describe 'nexus3_test::tasks' do
context 'linux' do
let(:chef_run) do
ChefSpec::SoloRunner.new(platform: 'centos', version: CENTOS_VERSION,
file_cache_path: CACHE).converge(described_recipe)
end

it 'creates a task' do
expect(chef_run).to create_nexus3_task('foo')
end

it 'deletes a task' do
expect(chef_run).to create_nexus3_task('bar')
expect(chef_run).to delete_nexus3_task('bar')
end
end
end
26 changes: 26 additions & 0 deletions test/fixtures/cookbooks/nexus3_test/recipes/tasks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
nexus3_task 'foo'

nexus3_task 'foo again' do
task_name 'foo'
notifies :run, 'ruby_block[fail if foo is created again]', :immediately
end

ruby_block 'fail if foo is created again' do
action :nothing
block { raise 'nexus3_task is not idempotent!' }
end

nexus3_task 'bar' do
action %i(create delete)
end

nexus3_task 'bar again' do
task_name 'bar'
action :delete
notifies :run, 'ruby_block[fail if bar is deleted again]', :immediately
end

ruby_block 'fail if bar is deleted again' do
action :nothing
block { raise 'nexus3_task is not idempotent!' }
end
9 changes: 9 additions & 0 deletions test/integration/default/serverspec/tasks_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require 'serverspec_helper'

describe 'nexus::tasks' do
describe command('curl -uadmin:admin123 http://localhost:8081/service/siesta/rest/v1/script/get_task/run -X POST ' \
'-H "Content-Type: text/plain" -d foo') do
its(:exit_status) { should eq 0 }
its(:stdout) { should match(/result.*name.*foo/) }
end
end

0 comments on commit 66e4c44

Please sign in to comment.