mistral_client 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0746e5658fe10edda68289b63a11f8e8306f939d
4
+ data.tar.gz: fb5750de1c4bc90795b0b3bc49e06267f284c1d1
5
+ SHA512:
6
+ metadata.gz: 15483eea25a2f62af9a40b7efce8d7bfdaa2b1896ce905b2d55d48655114805ee9b29fb350d28bb28e827f9830ddf0f65dc500e50b38d8b878716eaad5665847
7
+ data.tar.gz: f246cac33c6292e134d8f1f56805ef90bf3721fd7987316085733293f6683e1dcbb62627de47c86267345cf91a379d1845ce3e440e567c836024bf257cb164d2
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,10 @@
1
+ Style/Documentation:
2
+ Enabled: false
3
+
4
+ Style/FrozenStringLiteralComment:
5
+ Enabled: false
6
+
7
+ Metrics/BlockLength:
8
+ Enabled: true
9
+ Exclude:
10
+ - 'spec/**/*'
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.4.2
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 2.3.5
5
+ - 2.4.2
6
+ before_install:
7
+ - rvm @global do gem install bundler
8
+ branches:
9
+ only:
10
+ - master
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ This project adheres to [Semantic Versioning](http://semver.org/).
5
+
6
+ ## [Unreleased]
7
+
8
+ ## 1.0.0 - 2017-10-09
9
+
10
+ * Initial Release
11
+
12
+ [Unreleased]: https://github.com/civisanalytics/mistral_client/compare/v1.0.0...HEAD
@@ -0,0 +1,75 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of
9
+ experience, nationality, personal appearance, race, religion, or sexual identity
10
+ and orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or reject
41
+ comments, commits, code, wiki edits, issues, and other contributions that are
42
+ not aligned to this Code of Conduct, or to ban temporarily or permanently any
43
+ contributor for other behaviors that they deem inappropriate, threatening,
44
+ offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at opensource@civisanalytics.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an
62
+ incident. Further details of specific enforcement policies may be posted
63
+ separately.
64
+
65
+ Project maintainers who do not follow or enforce the Code of Conduct in good
66
+ faith may face temporary or permanent repercussions as determined by other
67
+ members of the project's leadership.
68
+
69
+ ## Attribution
70
+
71
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
72
+ version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
73
+
74
+ [homepage]: http://contributor-covenant.org
75
+ [version]: http://contributor-covenant.org/version/1/4/
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,32 @@
1
+ # Contributing to MistralClient
2
+
3
+ We welcome bug reports and pull requests from everyone!
4
+
5
+ This project is intended to be a safe, welcoming space for collaboration, and
6
+ contributors are expected to adhere to
7
+ the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
8
+
9
+
10
+ ## Getting Started
11
+
12
+ 1. Fork it ( https://github.com/civisanalytics/mistral_client/fork )
13
+ 2. Install the development dependencies (`bundle install`)
14
+ 3. Make sure you are able to run the test suite locally (`rake`)
15
+ 4. Create a feature branch (`git checkout -b my-new-feature`)
16
+ 5. Make your change. Don't forget tests
17
+ 6. Make sure the test suite, including your new tests, passes (`rake`)
18
+ 7. Commit your changes (`git commit -am 'Add some feature'`)
19
+ 8. Push to the branch (`git push origin my-new-feature`)
20
+ 9. Create a new pull request
21
+ 10. If the Travis build fails, address any issues
22
+
23
+ ## Tips
24
+
25
+ - All pull requests must include test coverage. If you’re not sure how to test
26
+ your changes, feel free to ask for help.
27
+ - Contributions must conform to the
28
+ [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide).
29
+ - Don’t forget to add your change to the [CHANGELOG](CHANGELOG.md). See
30
+ [Keep a CHANGELOG](http://keepachangelog.com/) for guidelines.
31
+
32
+ Thank you for taking the time to contribute to MistralClient!
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mistral_client.gemspec
4
+ gemspec
data/LICENSE.md ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2017, Civis Analytics
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice, this
11
+ list of conditions and the following disclaimer in the documentation and/or
12
+ other materials provided with the distribution.
13
+
14
+ * Neither the name of Civis Analytics nor the names of its contributors may be
15
+ used to endorse or promote products derived from this software without
16
+ specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # MistralClient
2
+
3
+ [![Build Status](https://travis-ci.org/civisanalytics/mistral_client.svg?branch=master)](https://travis-ci.org/civisanalytics/mistral_client)
4
+ [![Gem Version](https://badge.fury.io/rb/mistral_client.svg)](http://badge.fury.io/rb/mistral_client)
5
+ [![Dependency Status](https://gemnasium.com/civisanalytics/mistral_client.svg)](https://gemnasium.com/civisanalytics/mistral_client)
6
+
7
+ MistralClient provides a Ruby interface to a limited subset of
8
+ the [Mistral] [API].
9
+
10
+ [Mistral]: https://wiki.openstack.org/wiki/Mistral
11
+ [API]: https://docs.openstack.org/mistral/latest/api/v2.html
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'mistral_client'
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install mistral_client
28
+
29
+ ## Usage
30
+
31
+ ### Instantiate a Client
32
+
33
+ The Mistral base URL is required:
34
+
35
+ ```ruby
36
+ client = MistralClient.new('https://mistral.local/v2')
37
+ ```
38
+
39
+ If running Mistral with a self-signed certificate (*e.g.*, during development),
40
+ you can bypass certificate verification by specifying `verify: false`:
41
+
42
+ ```ruby
43
+ client = MistralClient.new('https://mistral.local/v2', verify: false)
44
+ ```
45
+
46
+ You can also pass in other connection options in the same way you would with
47
+ [HTTParty](https://github.com/jnunemaker/httparty):
48
+
49
+ ```ruby
50
+ client = MistralClient.new('https://mistral.local/v2', timeout: 20)
51
+ ```
52
+
53
+ ### Environment Operations
54
+
55
+ #### Create an Environment
56
+
57
+ ```ruby
58
+ environment = client.environment(definition)
59
+ ```
60
+
61
+ #### Delete an Environment
62
+
63
+ ```ruby
64
+ environment.delete!
65
+ ```
66
+
67
+ ### Workflow Operations
68
+
69
+ #### Instantiate a Workflow
70
+
71
+ ```ruby
72
+ workflow = client.workflow(definition)
73
+ ```
74
+
75
+ You can instantiate a workflow object using an existing Mistral workflow ID:
76
+
77
+ ```ruby
78
+ workflow = client.workflow(id: id)
79
+ ```
80
+
81
+ or workflow name:
82
+
83
+ ```ruby
84
+ workflow = client.workflow(name: name)
85
+ ```
86
+
87
+ #### Execute a Workflow
88
+
89
+ ```ruby
90
+ workflow.execute!
91
+ ```
92
+
93
+ If your workflow uses an environment, you can specify the environment with the
94
+ `env` keyword argument:
95
+
96
+ ```ruby
97
+ workflow.execute!(env: name)
98
+ ```
99
+
100
+ #### Update a Workflow Execution
101
+
102
+ ```ruby
103
+ execution.patch(state: 'CANCELLED')
104
+ ```
105
+
106
+ #### Update a Task
107
+
108
+ ```ruby
109
+ task.patch(state: 'RUNNING', reset: true)
110
+ ```
111
+
112
+ #### Delete a Workflow
113
+
114
+ ```ruby
115
+ workflow.delete!
116
+ ```
117
+
118
+ #### Validate a Workflow
119
+
120
+ ```ruby
121
+ client.workflow.valid?(definition)
122
+ ```
123
+
124
+ You can also get validation error details:
125
+
126
+ ```ruby
127
+ client.workflow.validate(definition)
128
+ ```
129
+
130
+ ## Development
131
+
132
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
133
+ `rake` to run the tests. You can also run `bin/console` for an interactive
134
+ prompt that will allow you to experiment.
135
+
136
+ To install this gem onto your local machine, run `bundle exec rake install`. To
137
+ release a new version, update the version number in `version.rb`, and then run
138
+ `bundle exec rake release`, which will create a git tag for the version, push
139
+ git commits and tags, and push the `.gem` file
140
+ to [rubygems.org](https://rubygems.org).
141
+
142
+ ### To Record New VCR Cassettes
143
+
144
+ Recording new cassettes is as simple as ensuring you have a Mistral server
145
+ available at the `LOCAL_MISTRAL_URL` defined in `spec/spec_helper.rb` and
146
+ running `rake`. To rerecord existing cassettes, simply remove the relevant
147
+ cassettes from `spec/cassettes` and run `rake`.
148
+
149
+ ## Contributing
150
+
151
+ See [CONTRIBUTING](CONTRIBUTING.md).
152
+
153
+ ## License
154
+
155
+ MistralClient is released under the [BSD 3-Clause License](LICENSE.md).
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new
5
+
6
+ require 'rubocop/rake_task'
7
+ RuboCop::RakeTask.new
8
+
9
+ task default: %i[rubocop spec]
data/bin/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'mistral_client'
5
+
6
+ require 'pry'
7
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,37 @@
1
+ module MistralClient
2
+ class ActionExecution < Base
3
+ UNICODE_FIELDS = %w[
4
+ description
5
+ name
6
+ state
7
+ state_info
8
+ task_execution_id
9
+ task_name
10
+ workflow_name
11
+ ].freeze
12
+
13
+ JSON_FIELDS = %w[input output params].freeze
14
+ BOOL_FIELDS = %w[accepted].freeze
15
+
16
+ PATH = 'action_executions'.freeze
17
+
18
+ include MistralClient::Mixins::MistralObject
19
+
20
+ def initialize(server, id: nil)
21
+ @server = server
22
+ @path = 'action_executions'
23
+ @id = id
24
+ reload if @id
25
+ end
26
+
27
+ def patch(state: nil, output: nil)
28
+ body = {}
29
+ body[:state] = state unless state.nil?
30
+ body[:output] = output unless output.nil?
31
+ return if body.empty?
32
+
33
+ resp = @server.put("#{PATH}/#{@id}", body.to_json, json: true)
34
+ ivars_from_response(resp)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ module MistralClient
2
+ class Base
3
+ DATE_FIELDS = %w[created_at updated_at].freeze
4
+ end
5
+ end
@@ -0,0 +1,76 @@
1
+ module MistralClient
2
+ class Client
3
+ DEFAULT_HTTP_OPTIONS = { verify: true }.freeze
4
+
5
+ def initialize(base, options = {})
6
+ raise ConfigurationError, 'base is required' if base.to_s.empty?
7
+
8
+ @base = base
9
+ @http_options = DEFAULT_HTTP_OPTIONS.merge(options)
10
+ end
11
+
12
+ def delete(path)
13
+ HTTParty.delete("#{@base}/#{path}", @http_options)
14
+ end
15
+
16
+ def get(path)
17
+ resp = HTTParty.get("#{@base}/#{path}", @http_options)
18
+ check_for_error(resp)
19
+ JSON.parse(resp.body)
20
+ end
21
+
22
+ def post(path, body, json: false)
23
+ post_or_put(:post, path, body, json)
24
+ end
25
+
26
+ def put(path, body, json: false)
27
+ post_or_put(:put, path, body, json)
28
+ end
29
+
30
+ def self.resources
31
+ {
32
+ action_execution: MistralClient::ActionExecution,
33
+ environment: MistralClient::Environment,
34
+ execution: MistralClient::Execution,
35
+ task: MistralClient::Task,
36
+ workflow: MistralClient::Workflow
37
+ }
38
+ end
39
+
40
+ def method_missing(name, *args, &block)
41
+ if self.class.resources.keys.include?(name)
42
+ self.class.resources[name].new(self, *args)
43
+ else
44
+ super
45
+ end
46
+ end
47
+
48
+ def respond_to_missing?(name, include_private = false)
49
+ self.class.resources.keys.include?(name) || super
50
+ end
51
+
52
+ private
53
+
54
+ def post_or_put(verb, path, body, json)
55
+ raise ArgumentError unless %i[post put].include?(verb)
56
+ headers = if json
57
+ { 'Content-Type' => 'application/json' }
58
+ else
59
+ { 'Content-Type' => 'text/plain' }
60
+ end
61
+ options = @http_options.merge(headers: headers, body: body)
62
+ resp = HTTParty.send(verb, "#{@base}/#{path}", options)
63
+ check_for_error(resp)
64
+ JSON.parse(resp.body)
65
+ end
66
+
67
+ def check_for_error(resp)
68
+ return if resp.code >= 200 && resp.code < 300
69
+ if resp.code == 404
70
+ raise MissingObjectError, JSON.parse(resp.body)['faultstring']
71
+ end
72
+ raise MistralError,
73
+ "Could not perform the requested operation:\n#{resp.body}"
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,46 @@
1
+ module MistralClient
2
+ class Environment < Base
3
+ UNICODE_FIELDS = %w[name scope description].freeze
4
+ JSON_FIELDS = %w[variables].freeze
5
+ BOOL_FIELDS = [].freeze
6
+ PATH = 'environments'.freeze
7
+ include MistralClient::Mixins::MistralObject
8
+ include MistralClient::Mixins::Definable
9
+
10
+ def initialize(server, definition = nil, name: nil)
11
+ @server = server
12
+ @definition = parse_definition(definition) if definition
13
+ @name = name
14
+ if @name
15
+ reload
16
+ elsif @definition
17
+ create_environment
18
+ end
19
+ end
20
+
21
+ def reload
22
+ super(@name)
23
+ end
24
+
25
+ def delete!
26
+ resp = @server.delete("#{PATH}/#{@name}")
27
+ return true if resp.code == 204
28
+ raise MistralClient::MistralError,
29
+ "Could not perform the requested operation:\n#{resp.body}"
30
+ end
31
+
32
+ private
33
+
34
+ def create_environment
35
+ resp = @server.post(PATH, @definition.to_json, json: true)
36
+ ivars_from_response(resp)
37
+ end
38
+
39
+ def massage_definition(definition)
40
+ if definition['variables'].is_a? Hash
41
+ definition['variables'] = definition['variables'].to_json
42
+ end
43
+ definition
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,6 @@
1
+ module MistralClient
2
+ class MistralError < StandardError; end
3
+
4
+ class ConfigurationError < MistralError; end
5
+ class MissingObjectError < MistralError; end
6
+ end
@@ -0,0 +1,64 @@
1
+ module MistralClient
2
+ class Execution < Base
3
+ UNICODE_FIELDS = %w[
4
+ workflow_id
5
+ workflow_name
6
+ description
7
+ state
8
+ state_info
9
+ ].freeze
10
+
11
+ DATE_FIELDS = %w[created_at updated_at].freeze
12
+ JSON_FIELDS = %w[input output params].freeze
13
+ BOOL_FIELDS = [].freeze
14
+
15
+ PATH = 'executions'.freeze
16
+
17
+ include MistralClient::Mixins::MistralObject
18
+ include MistralClient::Mixins::Definable
19
+ include MistralClient::Mixins::Deletable
20
+
21
+ def initialize(server, workflow_id: nil, env: nil, task_name: nil,
22
+ id: nil)
23
+ set_attributes(server, workflow_id, env, task_name, id)
24
+ if @id
25
+ reload
26
+ elsif @workflow_id
27
+ create_execution
28
+ end
29
+ end
30
+
31
+ def patch(description: nil, state: nil, env: nil)
32
+ body = {}
33
+ body[:description] = description unless description.nil?
34
+ body[:state] = state if state
35
+ body[:params] = { env: env } if env
36
+
37
+ return if body.empty?
38
+
39
+ resp = @server.put("#{PATH}/#{@id}", body.to_json, json: true)
40
+ ivars_from_response(resp)
41
+ end
42
+
43
+ private
44
+
45
+ def set_attributes(server, workflow_id, env, task_name, id)
46
+ @server = server
47
+ @env = env
48
+ @task_name = task_name
49
+ @id = id
50
+ @workflow_id = workflow_id
51
+ end
52
+
53
+ def create_execution
54
+ body = { workflow_id: @workflow_id }
55
+ params = {}
56
+ params[:env] = @env if @env
57
+ params[:task_name] = @task_name if @task_name
58
+ body[:params] = params unless params.empty?
59
+
60
+ resp = @server.post(PATH, body.to_json, json: true)
61
+ ivars_from_response(resp)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,24 @@
1
+ module MistralClient
2
+ module Mixins
3
+ module Definable
4
+ # rubocop:disable Metrics/MethodLength
5
+ def parse_definition(definition)
6
+ if definition.is_a?(Hash) || definition.is_a?(Array)
7
+ return YAML.dump(definition)
8
+ end
9
+ definition = File.read(definition) if File.exist?(definition)
10
+ # Called outside the if/else to validate the YAML.
11
+ parsed = YAML.safe_load(definition, [], [], true)
12
+ if defined? massage_definition
13
+ massage_definition(parsed)
14
+ else
15
+ definition
16
+ end
17
+ rescue Psych::SyntaxError
18
+ raise ConfigurationError,
19
+ 'Only filenames or raw or parsed strings of YAML are supported.'
20
+ end
21
+ # rubocop:enable Metrics/MethodLength
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ module MistralClient
2
+ module Mixins
3
+ module Deletable
4
+ def delete!
5
+ resp = @server.delete("#{self.class::PATH}/#{id}")
6
+ return true if resp.code == 204
7
+ raise MistralClient::MistralError,
8
+ "Could not perform the requested operation:\n#{resp.body}"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,52 @@
1
+ module MistralClient
2
+ module Mixins
3
+ module MistralObject
4
+ def self.included(child)
5
+ child.send(:attr_reader, :id)
6
+
7
+ %w[UNICODE_FIELDS DATE_FIELDS JSON_FIELDS BOOL_FIELDS].each do |fields|
8
+ next unless child.const_defined? fields
9
+ child.send(:attr_reader, *child.const_get(fields))
10
+ end
11
+ end
12
+
13
+ def reload(id = @id)
14
+ resp = @server.get("#{self.class::PATH}/#{id}")
15
+ ivars_from_response(resp)
16
+ self
17
+ end
18
+
19
+ def list(params = {})
20
+ resp = @server.get("#{self.class::PATH}?#{URI.encode_www_form(params)}")
21
+ resp[self.class::PATH].map do |t|
22
+ task = self.class.new(@server)
23
+ task.ivars_from_response(t)
24
+ task
25
+ end
26
+ end
27
+
28
+ # rubocop:disable Metrics/AbcSize
29
+ # rubocop:disable Metrics/MethodLength
30
+ def ivars_from_response(resp)
31
+ klass = self.class
32
+ @id = resp['id']
33
+ klass::UNICODE_FIELDS.each do |var|
34
+ instance_variable_set("@#{var}", resp[var]) if resp[var]
35
+ end
36
+ klass::BOOL_FIELDS.each do |var|
37
+ instance_variable_set("@#{var}", resp[var]) if resp.key? var
38
+ end
39
+ klass::DATE_FIELDS.each do |var|
40
+ if resp[var]
41
+ instance_variable_set("@#{var}", DateTime.parse(resp[var]))
42
+ end
43
+ end
44
+ klass::JSON_FIELDS.each do |var|
45
+ instance_variable_set("@#{var}", JSON.parse(resp[var])) if resp[var]
46
+ end
47
+ end
48
+ # rubocop:enable Metrics/MethodLength
49
+ # rubocop:enable Metrics/AbcSize
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,41 @@
1
+ require 'uri'
2
+
3
+ module MistralClient
4
+ class Task < Base
5
+ UNICODE_FIELDS = %w[
6
+ workflow_id
7
+ workflow_execution_id
8
+ workflow_name
9
+ state
10
+ state_info
11
+ result
12
+ name
13
+ ].freeze
14
+
15
+ JSON_FIELDS = %w[published runtime_context].freeze
16
+ BOOL_FIELDS = %w[reset processed].freeze
17
+
18
+ PATH = 'tasks'.freeze
19
+
20
+ include MistralClient::Mixins::MistralObject
21
+
22
+ def initialize(server, id: nil)
23
+ @server = server
24
+ @path = 'tasks'
25
+ @id = id
26
+ reload if id
27
+ end
28
+
29
+ def patch(state: nil, reset: nil, env: nil)
30
+ body = {}
31
+ body[:state] = state if state
32
+ body[:reset] = reset if reset
33
+ body[:env] = env if env
34
+
35
+ return if body.empty?
36
+
37
+ resp = @server.put("#{PATH}/#{@id}", body.to_json, json: true)
38
+ ivars_from_response(resp)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module MistralClient
2
+ VERSION = '1.0.0'.freeze
3
+ end
@@ -0,0 +1,53 @@
1
+ module MistralClient
2
+ class Workflow < Base
3
+ UNICODE_FIELDS = %w[definition input name project_id scope].freeze
4
+ BOOL_FIELDS = [].freeze
5
+ JSON_FIELDS = [].freeze
6
+
7
+ PATH = 'workflows'.freeze
8
+
9
+ include MistralClient::Mixins::MistralObject
10
+ include MistralClient::Mixins::Definable
11
+ include MistralClient::Mixins::Deletable
12
+
13
+ def initialize(server, definition = nil, id: nil, name: nil)
14
+ @server = server
15
+ if definition
16
+ @definition = parse_definition(definition)
17
+ create_workflow
18
+ elsif id || name
19
+ @id = id || name
20
+ reload
21
+ end
22
+ end
23
+
24
+ def execute!(env: nil, task_name: nil)
25
+ Execution.new(@server, workflow_id: @id, env: env, task_name: task_name)
26
+ end
27
+
28
+ def valid?(definition)
29
+ resp = perform_validation(definition)
30
+ resp['valid']
31
+ end
32
+
33
+ def validate(definition)
34
+ resp = perform_validation(definition)
35
+ resp['error']&.split("\n")&.first
36
+ end
37
+
38
+ private
39
+
40
+ def create_workflow
41
+ resp = @server.post(PATH, @definition)
42
+ if resp['workflows'].length > 1
43
+ raise ConfigurationError,
44
+ 'Only one workflow per definition is supported'
45
+ end
46
+ ivars_from_response(resp['workflows'][0])
47
+ end
48
+
49
+ def perform_validation(definition)
50
+ @server.post("#{PATH}/validate", parse_definition(definition))
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,22 @@
1
+ require 'httparty'
2
+ require 'yaml'
3
+ require 'mistral_client/mixins/definable'
4
+ require 'mistral_client/mixins/deletable'
5
+ require 'mistral_client/mixins/mistral_object'
6
+ require 'mistral_client/base'
7
+ require 'mistral_client/action_execution'
8
+ require 'mistral_client/client'
9
+ require 'mistral_client/environment'
10
+ require 'mistral_client/error'
11
+ require 'mistral_client/execution'
12
+ require 'mistral_client/task'
13
+ require 'mistral_client/version'
14
+ require 'mistral_client/workflow'
15
+
16
+ module MistralClient
17
+ class << self
18
+ def new(base = nil, options = {})
19
+ Client.new(base, options)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,33 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'mistral_client/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'mistral_client'
7
+ spec.version = MistralClient::VERSION
8
+ spec.authors = ['Matt Brennan', 'Jeff Cousens', 'Mike Saelim']
9
+ spec.email = ['opensource@civisanalytics.com']
10
+
11
+ spec.summary = 'Ruby client for Mistral.'
12
+ spec.description = "A Ruby client for OpenStack's Mistral " \
13
+ '<https://wiki.openstack.org/wiki/Mistral>.'
14
+ spec.homepage = 'https://github.com/civisanalytics/mistral_client'
15
+ spec.license = 'BSD 3-Clause'
16
+
17
+ spec.required_ruby_version = '~> 2.3'
18
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(%r{^(test|spec|features)/})
20
+ end
21
+ spec.bindir = 'exe'
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_dependency 'httparty', '~> 0.15.6'
26
+ spec.add_development_dependency 'bundler', '~> 1.15'
27
+ spec.add_development_dependency 'pry', '~> 0.11.1'
28
+ spec.add_development_dependency 'rake', '~> 12.1'
29
+ spec.add_development_dependency 'rspec', '~> 3.6'
30
+ spec.add_development_dependency 'rubocop', '~> 0.50.0'
31
+ spec.add_development_dependency 'vcr', '~> 3.0'
32
+ spec.add_development_dependency 'webmock', '~> 3.1'
33
+ end
metadata ADDED
@@ -0,0 +1,186 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mistral_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Matt Brennan
8
+ - Jeff Cousens
9
+ - Mike Saelim
10
+ autorequire:
11
+ bindir: exe
12
+ cert_chain: []
13
+ date: 2017-10-13 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: httparty
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: 0.15.6
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: 0.15.6
29
+ - !ruby/object:Gem::Dependency
30
+ name: bundler
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '1.15'
36
+ type: :development
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '1.15'
43
+ - !ruby/object:Gem::Dependency
44
+ name: pry
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 0.11.1
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: 0.11.1
57
+ - !ruby/object:Gem::Dependency
58
+ name: rake
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '12.1'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '12.1'
71
+ - !ruby/object:Gem::Dependency
72
+ name: rspec
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '3.6'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '3.6'
85
+ - !ruby/object:Gem::Dependency
86
+ name: rubocop
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: 0.50.0
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: 0.50.0
99
+ - !ruby/object:Gem::Dependency
100
+ name: vcr
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '3.0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: '3.0'
113
+ - !ruby/object:Gem::Dependency
114
+ name: webmock
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: '3.1'
120
+ type: :development
121
+ prerelease: false
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - "~>"
125
+ - !ruby/object:Gem::Version
126
+ version: '3.1'
127
+ description: A Ruby client for OpenStack's Mistral <https://wiki.openstack.org/wiki/Mistral>.
128
+ email:
129
+ - opensource@civisanalytics.com
130
+ executables: []
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - ".gitignore"
135
+ - ".rspec"
136
+ - ".rubocop.yml"
137
+ - ".ruby-version"
138
+ - ".travis.yml"
139
+ - CHANGELOG.md
140
+ - CODE_OF_CONDUCT.md
141
+ - CONTRIBUTING.md
142
+ - Gemfile
143
+ - LICENSE.md
144
+ - README.md
145
+ - Rakefile
146
+ - bin/console
147
+ - bin/setup
148
+ - lib/mistral_client.rb
149
+ - lib/mistral_client/action_execution.rb
150
+ - lib/mistral_client/base.rb
151
+ - lib/mistral_client/client.rb
152
+ - lib/mistral_client/environment.rb
153
+ - lib/mistral_client/error.rb
154
+ - lib/mistral_client/execution.rb
155
+ - lib/mistral_client/mixins/definable.rb
156
+ - lib/mistral_client/mixins/deletable.rb
157
+ - lib/mistral_client/mixins/mistral_object.rb
158
+ - lib/mistral_client/task.rb
159
+ - lib/mistral_client/version.rb
160
+ - lib/mistral_client/workflow.rb
161
+ - mistral_client.gemspec
162
+ homepage: https://github.com/civisanalytics/mistral_client
163
+ licenses:
164
+ - BSD 3-Clause
165
+ metadata: {}
166
+ post_install_message:
167
+ rdoc_options: []
168
+ require_paths:
169
+ - lib
170
+ required_ruby_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - "~>"
173
+ - !ruby/object:Gem::Version
174
+ version: '2.3'
175
+ required_rubygems_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ requirements: []
181
+ rubyforge_project:
182
+ rubygems_version: 2.6.13
183
+ signing_key:
184
+ specification_version: 4
185
+ summary: Ruby client for Mistral.
186
+ test_files: []